From fa9d9eb234edc45ba1ac4fd6a923690aab2ebd8c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 2 Sep 2025 22:17:11 -0400 Subject: [PATCH 001/200] update version number for next dev version; --- src/common/Defines.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/Defines.h b/src/common/Defines.h index bb9db1e2..47f21ddd 100644 --- a/src/common/Defines.h +++ b/src/common/Defines.h @@ -117,8 +117,8 @@ typedef unsigned long long ulong64_t; #define __EXE_NAME__ "" #define VERSION_MAJOR "04" -#define VERSION_MINOR "32" -#define VERSION_REV "J" +#define VERSION_MINOR "34" +#define VERSION_REV "K" #define __NETVER__ "DVM_R" VERSION_MAJOR VERSION_REV VERSION_MINOR #define __VER__ VERSION_MAJOR "." VERSION_MINOR VERSION_REV " (R" VERSION_MAJOR VERSION_REV VERSION_MINOR " " __GIT_VER__ ")" From f3f1e9780929dfc77ddc73783665d079b7d4452b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 7 Sep 2025 21:23:22 -0400 Subject: [PATCH 002/200] implement passing complex types as refs in lambda functions; --- src/common/lookups/AdjSiteMapLookup.cpp | 11 ++++++----- src/common/lookups/TalkgroupRulesLookup.cpp | 19 +++++++++---------- src/fne/CryptoContainer.cpp | 10 ++++++---- src/fne/network/FNENetwork.cpp | 6 +++--- src/fne/network/callhandler/TagAnalogData.cpp | 4 ++-- src/fne/network/callhandler/TagDMRData.cpp | 12 ++++++------ src/fne/network/callhandler/TagNXDNData.cpp | 10 +++++----- src/fne/network/callhandler/TagP25Data.cpp | 16 ++++++++-------- src/peered/PeerEditWnd.h | 6 ++---- src/sysview/SysViewMain.cpp | 18 +++++++++--------- src/tged/TGEditWnd.h | 6 ++---- 11 files changed, 58 insertions(+), 60 deletions(-) diff --git a/src/common/lookups/AdjSiteMapLookup.cpp b/src/common/lookups/AdjSiteMapLookup.cpp index 9d5686ff..383f7033 100644 --- a/src/common/lookups/AdjSiteMapLookup.cpp +++ b/src/common/lookups/AdjSiteMapLookup.cpp @@ -133,8 +133,7 @@ void AdjSiteMapLookup::addEntry(AdjPeerMapEntry entry) __LOCK_TABLE(); auto it = std::find_if(m_adjPeerMap.begin(), m_adjPeerMap.end(), - [&](AdjPeerMapEntry x) - { + [&](AdjPeerMapEntry& x) { return x.peerId() == id; }); if (it != m_adjPeerMap.end()) { @@ -153,7 +152,10 @@ void AdjSiteMapLookup::eraseEntry(uint32_t id) { __LOCK_TABLE(); - auto it = std::find_if(m_adjPeerMap.begin(), m_adjPeerMap.end(), [&](AdjPeerMapEntry x) { return x.peerId() == id; }); + auto it = std::find_if(m_adjPeerMap.begin(), m_adjPeerMap.end(), + [&](AdjPeerMapEntry& x) { + return x.peerId() == id; + }); if (it != m_adjPeerMap.end()) { m_adjPeerMap.erase(it); } @@ -171,8 +173,7 @@ AdjPeerMapEntry AdjSiteMapLookup::find(uint32_t id) std::lock_guard lock(m_mutex); auto it = std::find_if(m_adjPeerMap.begin(), m_adjPeerMap.end(), - [&](AdjPeerMapEntry x) - { + [&](AdjPeerMapEntry& x) { return x.peerId() == id; }); if (it != m_adjPeerMap.end()) { diff --git a/src/common/lookups/TalkgroupRulesLookup.cpp b/src/common/lookups/TalkgroupRulesLookup.cpp index 5e6d175c..5641f8b1 100644 --- a/src/common/lookups/TalkgroupRulesLookup.cpp +++ b/src/common/lookups/TalkgroupRulesLookup.cpp @@ -143,8 +143,7 @@ void TalkgroupRulesLookup::addEntry(uint32_t id, uint8_t slot, bool enabled, boo __LOCK_TABLE(); auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(), - [&](TalkgroupRuleGroupVoice x) - { + [&](TalkgroupRuleGroupVoice& x) { if (slot != 0U) { return x.source().tgId() == id && x.source().tgSlot() == slot; } @@ -192,8 +191,7 @@ void TalkgroupRulesLookup::addEntry(TalkgroupRuleGroupVoice groupVoice) __LOCK_TABLE(); auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(), - [&](TalkgroupRuleGroupVoice x) - { + [&](TalkgroupRuleGroupVoice& x) { if (slot != 0U) { return x.source().tgId() == id && x.source().tgSlot() == slot; } @@ -216,7 +214,10 @@ void TalkgroupRulesLookup::eraseEntry(uint32_t id, uint8_t slot) { __LOCK_TABLE(); - auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(), [&](TalkgroupRuleGroupVoice x) { return x.source().tgId() == id && x.source().tgSlot() == slot; }); + auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(), + [&](TalkgroupRuleGroupVoice& x) { + return x.source().tgId() == id && x.source().tgSlot() == slot; + }); if (it != m_groupVoice.end()) { m_groupVoice.erase(it); } @@ -234,7 +235,7 @@ TalkgroupRuleGroupVoice TalkgroupRulesLookup::find(uint32_t id, uint8_t slot) std::lock_guard lock(m_mutex); auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(), - [&](TalkgroupRuleGroupVoice x) + [&](TalkgroupRuleGroupVoice& x) { if (slot != 0U) { return x.source().tgId() == id && x.source().tgSlot() == slot; @@ -261,15 +262,13 @@ TalkgroupRuleGroupVoice TalkgroupRulesLookup::findByRewrite(uint32_t peerId, uin std::lock_guard lock(m_mutex); auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(), - [&](TalkgroupRuleGroupVoice x) - { + [&](TalkgroupRuleGroupVoice& x) { if (x.config().rewrite().size() == 0) return false; std::vector rewrite = x.config().rewrite(); auto innerIt = std::find_if(rewrite.begin(), rewrite.end(), - [&](TalkgroupRuleRewrite y) - { + [&](TalkgroupRuleRewrite& y) { if (slot != 0U) { return y.peerId() == peerId && y.tgId() == id && y.tgSlot() == slot; } diff --git a/src/fne/CryptoContainer.cpp b/src/fne/CryptoContainer.cpp index d6ae6099..1d7be4e3 100644 --- a/src/fne/CryptoContainer.cpp +++ b/src/fne/CryptoContainer.cpp @@ -237,7 +237,7 @@ void CryptoContainer::addEntry(KeyItem key) std::lock_guard lock(m_mutex); auto it = std::find_if(m_keys.begin(), m_keys.end(), - [&](KeyItem x) + [&](KeyItem& x) { return x.id() == id && x.kId() == kId; }); @@ -254,7 +254,10 @@ void CryptoContainer::addEntry(KeyItem key) void CryptoContainer::eraseEntry(uint32_t id) { std::lock_guard lock(m_mutex); - auto it = std::find_if(m_keys.begin(), m_keys.end(), [&](KeyItem x) { return x.id() == id; }); + auto it = std::find_if(m_keys.begin(), m_keys.end(), + [&](KeyItem& x) { + return x.id() == id; + }); if (it != m_keys.end()) { m_keys.erase(it); } @@ -268,8 +271,7 @@ KeyItem CryptoContainer::find(uint32_t kId) std::lock_guard lock(m_mutex); auto it = std::find_if(m_keys.begin(), m_keys.end(), - [&](KeyItem x) - { + [&](KeyItem& x) { return x.kId() == kId; }); if (it != m_keys.end()) { diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 7f04225c..97060581 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1647,7 +1647,7 @@ void FNENetwork::erasePeer(uint32_t peerId) // erase any CC maps for this peer { - auto it = std::find_if(m_ccPeerMap.begin(), m_ccPeerMap.end(), [&](auto x) { return x.first == peerId; }); + auto it = std::find_if(m_ccPeerMap.begin(), m_ccPeerMap.end(), [&](auto& x) { return x.first == peerId; }); if (it != m_ccPeerMap.end()) { m_ccPeerMap.erase(peerId); } @@ -1655,7 +1655,7 @@ void FNENetwork::erasePeer(uint32_t peerId) // erase any Peer-Link entries for this peer { - auto it = std::find_if(m_peerLinkPeers.begin(), m_peerLinkPeers.end(), [&](auto x) { return x.first == peerId; }); + auto it = std::find_if(m_peerLinkPeers.begin(), m_peerLinkPeers.end(), [&](auto& x) { return x.first == peerId; }); if (it != m_peerLinkPeers.end()) { m_peerLinkPeers.erase(peerId); } @@ -1709,7 +1709,7 @@ json::object FNENetwork::fneConnObject(uint32_t peerId, FNEPeerConnection *conn) peerObj["config"].set(peerConfig); json::array voiceChannels = json::array(); - auto it = std::find_if(m_ccPeerMap.begin(), m_ccPeerMap.end(), [&](auto x) { return x.first == peerId; }); + auto it = std::find_if(m_ccPeerMap.begin(), m_ccPeerMap.end(), [&](auto& x) { return x.first == peerId; }); if (it != m_ccPeerMap.end()) { std::vector vcPeers = m_ccPeerMap[peerId]; for (uint32_t vcEntry : vcPeers) { diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index c809ab17..415708a1 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -103,7 +103,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee RxStatus status = m_status[dstId]; uint64_t duration = hrc::diff(pktTime, status.callStartTime); - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -152,7 +152,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return false; } - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 87e0c793..4d995a8e 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -147,7 +147,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId RxStatus status; { - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId && x.second.slotNo == slotNo) { return true; } @@ -164,7 +164,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId uint64_t duration = hrc::diff(pktTime, status.callStartTime); - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId && x.second.slotNo == slotNo) { if (x.second.activeCall) return true; @@ -185,7 +185,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } // is this a private call? - auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -230,7 +230,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId bool switchOver = (data[14U] & network::NET_CTRL_SWITCH_OVER) == network::NET_CTRL_SWITCH_OVER; - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId && x.second.slotNo == slotNo) { if (x.second.activeCall) return true; @@ -405,7 +405,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // is this a private call? if ((flco == FLCO::PRIVATE) && !external) { // is this a private call? if so only repeat to the peer that registered the unit - auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -517,7 +517,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId bool TagDMRData::processGrantReq(uint32_t srcId, uint32_t dstId, uint8_t slot, bool unitToUnit, uint32_t peerId, uint16_t pktSeq, uint32_t streamId) { // if we have an Rx status for the destination deny the grant - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId/* && x.second.slotNo == slot*/) { if (x.second.activeCall) return true; diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index cfd7a9ae..61149508 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -203,7 +203,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI RxStatus status = m_status[dstId]; uint64_t duration = hrc::diff(pktTime, status.callStartTime); - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -224,7 +224,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI } // is this a private call? - auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -268,7 +268,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI bool switchOver = (data[14U] & network::NET_CTRL_SWITCH_OVER) == network::NET_CTRL_SWITCH_OVER; - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -435,7 +435,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // is this a private call? if (!group && !external) { // is this a private call? if so only repeat to the peer that registered the unit - auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -547,7 +547,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI bool TagNXDNData::processGrantReq(uint32_t srcId, uint32_t dstId, bool unitToUnit, uint32_t peerId, uint16_t pktSeq, uint32_t streamId) { // if we have an Rx status for the destination deny the grant - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 4728f7d4..c32fe8b1 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -200,7 +200,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId bool switchOver = (data[14U] & network::NET_CTRL_SWITCH_OVER) == network::NET_CTRL_SWITCH_OVER; - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -228,7 +228,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } // is this a private call? - auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -273,7 +273,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId bool switchOver = (data[14U] & network::NET_CTRL_SWITCH_OVER) == network::NET_CTRL_SWITCH_OVER; - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -392,7 +392,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (m_network->m_restrictPVCallToRegOnly) { if ((control.getLCO() != LCO::PRIVATE) && !control.getGroup()) { // is this a private call? if so only repeat to the peer that registered the unit - auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == control.getDstId()) { if (x.second.activeCall) return true; @@ -459,7 +459,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // is this a private call? if ((lco == LCO::PRIVATE) && !external) { // is this a private call? if so only repeat to the peer that registered the unit - auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -579,7 +579,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId bool TagP25Data::processGrantReq(uint32_t srcId, uint32_t dstId, bool unitToUnit, uint32_t peerId, uint16_t pktSeq, uint32_t streamId) { // if we have an Rx status for the destination deny the grant - auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { if (x.second.activeCall) return true; @@ -1291,7 +1291,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const if (m_network->m_filterTerminators) { if ((duid == DUID::TDU || duid == DUID::TDULC) && control.getDstId() != 0U) { // is this a private call? - auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == control.getDstId()) { if (x.second.activeCall) return true; @@ -1328,7 +1328,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const bool privateCallInProgress = false; if ((control.getLCO() != LCO::PRIVATE) && !control.getGroup()) { // is this a private call? if so only repeat to the peer that registered the unit - auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair x) { + auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == control.getDstId()) { if (x.second.activeCall) return true; diff --git a/src/peered/PeerEditWnd.h b/src/peered/PeerEditWnd.h index 1dabf287..68a92080 100644 --- a/src/peered/PeerEditWnd.h +++ b/src/peered/PeerEditWnd.h @@ -357,8 +357,7 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { // update peer auto peers = g_pidLookups->tableAsList(); auto it = std::find_if(peers.begin(), peers.end(), - [&](lookups::PeerId x) - { + [&](lookups::PeerId& x) { return x.peerId() == m_origPeerId; }); if (it != peers.end()) { @@ -383,8 +382,7 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { auto peers = g_pidLookups->tableAsList(); auto it = std::find_if(peers.begin(), peers.end(), - [&](lookups::PeerId x) - { + [&](lookups::PeerId& x) { return x.peerId() == m_rule.peerId(); }); if (it != peers.end()) { diff --git a/src/sysview/SysViewMain.cpp b/src/sysview/SysViewMain.cpp index 21a3e11c..c828c5cd 100644 --- a/src/sysview/SysViewMain.cpp +++ b/src/sysview/SysViewMain.cpp @@ -364,7 +364,7 @@ void* threadNetworkPump(void* arg) } RxStatus status; - auto it = std::find_if(g_dmrStatus.begin(), g_dmrStatus.end(), [&](StatusMapPair x) { return (x.second.dstId == dstId && x.second.slotNo == slotNo); }); + auto it = std::find_if(g_dmrStatus.begin(), g_dmrStatus.end(), [&](StatusMapPair& x) { return (x.second.dstId == dstId && x.second.slotNo == slotNo); }); if (it == g_dmrStatus.end()) { LogError(LOG_NET, "DMR, tried to end call for non-existent call in progress?, srcId = %u (%s), dstId = %u (%s)", srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); @@ -375,7 +375,7 @@ void* threadNetworkPump(void* arg) uint64_t duration = hrc::diff(pktTime, status.callStartTime); - if (std::find_if(g_dmrStatus.begin(), g_dmrStatus.end(), [&](StatusMapPair x) { return (x.second.dstId == dstId && x.second.slotNo == slotNo); }) != g_dmrStatus.end()) { + if (std::find_if(g_dmrStatus.begin(), g_dmrStatus.end(), [&](StatusMapPair& x) { return (x.second.dstId == dstId && x.second.slotNo == slotNo); }) != g_dmrStatus.end()) { g_dmrStatus.erase(dstId); LogMessage(LOG_NET, "DMR, Call End, srcId = %u (%s), dstId = %u (%s), duration = %u", @@ -390,7 +390,7 @@ void* threadNetworkPump(void* arg) srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); } - auto it = std::find_if(g_dmrStatus.begin(), g_dmrStatus.end(), [&](StatusMapPair x) { return (x.second.dstId == dstId && x.second.slotNo == slotNo); }); + auto it = std::find_if(g_dmrStatus.begin(), g_dmrStatus.end(), [&](StatusMapPair& x) { return (x.second.dstId == dstId && x.second.slotNo == slotNo); }); if (it == g_dmrStatus.end()) { // this is a new call stream RxStatus status = RxStatus(); @@ -517,7 +517,7 @@ void* threadNetworkPump(void* arg) RxStatus status = g_p25Status[dstId]; uint64_t duration = hrc::diff(pktTime, status.callStartTime); - if (std::find_if(g_p25Status.begin(), g_p25Status.end(), [&](StatusMapPair x) { return x.second.dstId == dstId; }) != g_p25Status.end()) { + if (std::find_if(g_p25Status.begin(), g_p25Status.end(), [&](StatusMapPair& x) { return x.second.dstId == dstId; }) != g_p25Status.end()) { g_p25Status.erase(dstId); LogMessage(LOG_NET, "P25, Call End, srcId = %u (%s), dstId = %u (%s), sysId = $%03X, netId = $%05X, duration = %u", @@ -532,7 +532,7 @@ void* threadNetworkPump(void* arg) srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); } - auto it = std::find_if(g_p25Status.begin(), g_p25Status.end(), [&](StatusMapPair x) { return x.second.dstId == dstId; }); + auto it = std::find_if(g_p25Status.begin(), g_p25Status.end(), [&](StatusMapPair& x) { return x.second.dstId == dstId; }); if (it == g_p25Status.end()) { // this is a new call stream RxStatus status = RxStatus(); @@ -950,7 +950,7 @@ void* threadNetworkPump(void* arg) RxStatus status = g_nxdnStatus[dstId]; uint64_t duration = hrc::diff(pktTime, status.callStartTime); - if (std::find_if(g_nxdnStatus.begin(), g_nxdnStatus.end(), [&](StatusMapPair x) { return x.second.dstId == dstId; }) != g_nxdnStatus.end()) { + if (std::find_if(g_nxdnStatus.begin(), g_nxdnStatus.end(), [&](StatusMapPair& x) { return x.second.dstId == dstId; }) != g_nxdnStatus.end()) { g_nxdnStatus.erase(dstId); LogMessage(LOG_NET, "NXDN, Call End, srcId = %u (%s), dstId = %u (%s), duration = %u", @@ -965,7 +965,7 @@ void* threadNetworkPump(void* arg) srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); } - auto it = std::find_if(g_nxdnStatus.begin(), g_nxdnStatus.end(), [&](StatusMapPair x) { return x.second.dstId == dstId; }); + auto it = std::find_if(g_nxdnStatus.begin(), g_nxdnStatus.end(), [&](StatusMapPair& x) { return x.second.dstId == dstId; }); if (it == g_nxdnStatus.end()) { // this is a new call stream RxStatus status = RxStatus(); @@ -1003,7 +1003,7 @@ void* threadNetworkPump(void* arg) RxStatus status = g_analogStatus[dstId]; uint64_t duration = hrc::diff(pktTime, status.callStartTime); - if (std::find_if(g_analogStatus.begin(), g_analogStatus.end(), [&](StatusMapPair x) { return x.second.dstId == dstId; }) != g_analogStatus.end()) { + if (std::find_if(g_analogStatus.begin(), g_analogStatus.end(), [&](StatusMapPair& x) { return x.second.dstId == dstId; }) != g_analogStatus.end()) { g_analogStatus.erase(dstId); LogMessage(LOG_NET, "Analog, Call End, srcId = %u (%s), dstId = %u (%s), duration = %u", @@ -1018,7 +1018,7 @@ void* threadNetworkPump(void* arg) srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); } - auto it = std::find_if(g_analogStatus.begin(), g_analogStatus.end(), [&](StatusMapPair x) { return x.second.dstId == dstId; }); + auto it = std::find_if(g_analogStatus.begin(), g_analogStatus.end(), [&](StatusMapPair& x) { return x.second.dstId == dstId; }); if (it == g_analogStatus.end()) { // this is a new call stream RxStatus status = RxStatus(); diff --git a/src/tged/TGEditWnd.h b/src/tged/TGEditWnd.h index 398c60ef..cbdbb918 100644 --- a/src/tged/TGEditWnd.h +++ b/src/tged/TGEditWnd.h @@ -444,8 +444,7 @@ class HOST_SW_API TGEditWnd final : public CloseWndBase { // update TG auto groupVoice = g_tidLookups->groupVoice(); auto it = std::find_if(groupVoice.begin(), groupVoice.end(), - [&](lookups::TalkgroupRuleGroupVoice x) - { + [&](lookups::TalkgroupRuleGroupVoice& x) { return x.source().tgId() == m_origTgId && x.source().tgSlot() == m_origTgSlot; }); if (it != groupVoice.end()) { @@ -464,8 +463,7 @@ class HOST_SW_API TGEditWnd final : public CloseWndBase { auto groupVoice = g_tidLookups->groupVoice(); auto it = std::find_if(groupVoice.begin(), groupVoice.end(), - [&](lookups::TalkgroupRuleGroupVoice x) - { + [&](lookups::TalkgroupRuleGroupVoice& x) { return x.source().tgId() == m_rule.source().tgId() && x.source().tgSlot() == m_rule.source().tgSlot(); }); if (it != groupVoice.end()) { From 7772127eef414cd790c51d85362aa47f7ca50ce8 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 8 Sep 2025 14:27:51 -0400 Subject: [PATCH 003/200] move KMM NoService response into its own helper function; --- .../callhandler/packetdata/P25PacketData.cpp | 51 +++++++++++-------- .../callhandler/packetdata/P25PacketData.h | 6 +++ 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index 53fa0772..8f79e9d3 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -942,28 +942,7 @@ bool P25PacketData::processKMM(RxStatus* status) kmm->getFlag()); // respond with No-Service - // assemble a P25 PDU frame header for transport... - data::DataHeader dataHeader = data::DataHeader(); - dataHeader.setFormat(PDUFormatType::UNCONFIRMED); - dataHeader.setMFId(MFG_STANDARD); - dataHeader.setAckNeeded(false); - dataHeader.setOutbound(true); - dataHeader.setSAP(PDUSAP::UNENC_KMM); - dataHeader.setLLId(status->llId); - dataHeader.setBlocksToFollow(1U); - - dataHeader.calculateLength(KMM_NO_SERVICE_LENGTH); - uint32_t pduLength = dataHeader.getPDULength(); - - DECLARE_UINT8_ARRAY(pduUserData, pduLength); - - uint8_t buffer[KMM_NO_SERVICE_LENGTH]; - KMMNoService outKmm = KMMNoService(); - outKmm.encode(buffer); - - ::memcpy(pduUserData, buffer, KMM_NO_SERVICE_LENGTH); - - dispatchUserFrameToFNE(dataHeader, false, pduUserData); + write_PDU_KMM_NoService(llId); } break; @@ -974,6 +953,34 @@ bool P25PacketData::processKMM(RxStatus* status) return true; } +/* Helper used to return a No-Service KMM to the calling SU. */ + +void P25PacketData::write_PDU_KMM_NoService(uint32_t llId) +{ + // assemble a P25 PDU frame header for transport... + data::DataHeader dataHeader = data::DataHeader(); + dataHeader.setFormat(PDUFormatType::UNCONFIRMED); + dataHeader.setMFId(MFG_STANDARD); + dataHeader.setAckNeeded(false); + dataHeader.setOutbound(true); + dataHeader.setSAP(PDUSAP::UNENC_KMM); + dataHeader.setLLId(llId); + dataHeader.setBlocksToFollow(1U); + + dataHeader.calculateLength(KMM_NO_SERVICE_LENGTH); + uint32_t pduLength = dataHeader.getPDULength(); + + DECLARE_UINT8_ARRAY(pduUserData, pduLength); + + uint8_t buffer[KMM_NO_SERVICE_LENGTH]; + KMMNoService outKmm = KMMNoService(); + outKmm.encode(buffer); + + ::memcpy(pduUserData, buffer, KMM_NO_SERVICE_LENGTH); + + dispatchUserFrameToFNE(dataHeader, false, pduUserData); +} + /* Helper write ARP request to the network. */ void P25PacketData::write_PDU_ARP(uint32_t addr) diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index 30392ade..02b89438 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -215,6 +215,12 @@ namespace network */ bool processKMM(RxStatus* status); + /** + * @brief Helper used to return a No-Service KMM to the calling SU. + * @param llId Logical Link Address. + */ + void write_PDU_KMM_NoService(uint32_t llId); + /** * @brief Helper write ARP request to the network. * @param addr IP Address. From 234c7a46ed725d90385a70c38714b3d4b13f6670 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 8 Sep 2025 17:02:21 -0400 Subject: [PATCH 004/200] remove data call collisions (this code was really iffy); refactor log messages; refactor default handling of packets the FNE doesn't process; insert null bits before PDU; --- .../callhandler/packetdata/P25PacketData.cpp | 46 +++++-------------- .../callhandler/packetdata/P25PacketData.h | 3 +- src/host/p25/packet/Data.cpp | 31 +++++++------ 3 files changed, 31 insertions(+), 49 deletions(-) diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index 8f79e9d3..dd583398 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -89,32 +89,10 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee ::memcpy(buffer, data + 24U, P25_PDU_FEC_LENGTH_BYTES); auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { return x.second->peerId == peerId; }); - if (it != m_status.end()) { - RxStatus* status = m_status[peerId]; - if (streamId != status->streamId) { - LogWarning(LOG_NET, "P25, Data Call Collision, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxStreamId = %u, external = %u", - peerId, streamId, status->peerId, status->llId, status->streamId, external); - - LogWarning(LOG_NET, "P25, clearing previous data call, timeout, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxStreamId = %u, external = %u", - peerId, streamId, status->peerId, status->llId, status->streamId, external); - - delete status; - m_status.erase(peerId); - - // create a new status entry - m_status.lock(true); - RxStatus *status = new RxStatus(); - status->callStartTime = pktTime; - status->streamId = streamId; - status->peerId = peerId; - m_status.unlock(); - - m_status.insert(peerId, status); - } - } else { + if (it == m_status.end()) { // create a new status entry m_status.lock(true); - RxStatus *status = new RxStatus(); + RxStatus* status = new RxStatus(); status->callStartTime = pktTime; status->streamId = streamId; status->peerId = peerId; @@ -124,6 +102,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } RxStatus* status = m_status[peerId]; + status->streamId = streamId; // make sure we don't get a PDU with more blocks then we support if (currentBlock >= P25_MAX_PDU_BLOCKS) { @@ -580,12 +559,6 @@ void P25PacketData::dispatch(uint32_t peerId) uint8_t sap = (status->extendedAddress) ? status->header.getEXSAP() : status->header.getSAP(); - // don't dispatch SNDCP control, conventional data registration or ARP - if (sap != PDUSAP::SNDCP_CTRL_DATA && sap != PDUSAP::CONV_DATA_REG && - sap != PDUSAP::ARP) { - dispatchToFNE(peerId); - } - // handle standard P25 service access points switch (sap) { case PDUSAP::ARP: @@ -740,9 +713,11 @@ void P25PacketData::dispatch(uint32_t peerId) LogMessage(LOG_NET, P25_PDU_STR ", KMM (Key Management Message), peer = %u, blocksToFollow = %u", peerId, status->header.getBlocksToFollow()); - processKMM(status); + processKMM(status, sap == PDUSAP::ENC_KMM); } + break; default: + dispatchToFNE(peerId); break; } } @@ -924,7 +899,7 @@ bool P25PacketData::processSNDCPControl(RxStatus* status) /* Helper used to process KMM frames from PDU data. */ -bool P25PacketData::processKMM(RxStatus* status) +bool P25PacketData::processKMM(RxStatus* status, bool encrypted) { std::unique_ptr frame = KMMFactory::create(status->pduUserData); if (frame == nullptr) { @@ -934,6 +909,9 @@ bool P25PacketData::processKMM(RxStatus* status) uint32_t llId = status->header.getLLId(); + // ack KMM frame + write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->header.getNs(), llId, false); + switch (frame->getMessageId()) { case KMM_MessageType::HELLO: { @@ -959,9 +937,9 @@ void P25PacketData::write_PDU_KMM_NoService(uint32_t llId) { // assemble a P25 PDU frame header for transport... data::DataHeader dataHeader = data::DataHeader(); - dataHeader.setFormat(PDUFormatType::UNCONFIRMED); + dataHeader.setFormat(PDUFormatType::CONFIRMED); dataHeader.setMFId(MFG_STANDARD); - dataHeader.setAckNeeded(false); + dataHeader.setAckNeeded(true); dataHeader.setOutbound(true); dataHeader.setSAP(PDUSAP::UNENC_KMM); dataHeader.setLLId(llId); diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index 02b89438..45215a4a 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -211,9 +211,10 @@ namespace network /** * @brief Helper used to process KMM frames from PDU data. * @param status Instance of the RxStatus class. + * @param encrypted Flag indicating whether or not the KMM frame is encrypted. * @returns bool True, if KMM data was processed, otherwise false. */ - bool processKMM(RxStatus* status); + bool processKMM(RxStatus* status, bool encrypted); /** * @brief Helper used to return a No-Service KMM to the calling SU. diff --git a/src/host/p25/packet/Data.cpp b/src/host/p25/packet/Data.cpp index 017c2c47..915679ba 100644 --- a/src/host/p25/packet/Data.cpp +++ b/src/host/p25/packet/Data.cpp @@ -528,7 +528,7 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) } if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", ISP, ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, hdrOffset = %u, llId = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, hdrOffset = %u, llId = %u", m_netDataHeader.getAckNeeded(), m_netDataHeader.getOutbound(), m_netDataHeader.getFormat(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMessage(), m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadLength(), m_netDataHeader.getPacketLength(), m_netDataHeader.getSynchronize(), m_netDataHeader.getNs(), m_netDataHeader.getFSN(), m_netDataHeader.getHeaderOffset(), m_netDataHeader.getLLId()); @@ -567,31 +567,31 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) m_p25->m_netState = RS_NET_IDLE; if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", m_netDataHeader.getFormat(), m_netDataHeader.getResponseClass(), m_netDataHeader.getResponseType(), m_netDataHeader.getResponseStatus(), m_netDataHeader.getLLId(), m_netDataHeader.getSrcLLId()); if (m_netDataHeader.getResponseClass() == PDUAckClass::ACK && m_netDataHeader.getResponseType() == PDUAckType::ACK) { - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, OSP ACK, llId = %u, all blocks received OK, n = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP ACK, llId = %u, all blocks received OK, n = %u", m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); } else { if (m_netDataHeader.getResponseClass() == PDUAckClass::NACK) { switch (m_netDataHeader.getResponseType()) { case PDUAckType::NACK_ILLEGAL: - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, OSP NACK, illegal format, llId = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, illegal format, llId = %u", m_netDataHeader.getLLId()); break; case PDUAckType::NACK_PACKET_CRC: - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, OSP NACK, packet CRC error, llId = %u, n = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet CRC error, llId = %u, n = %u", m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); break; case PDUAckType::NACK_SEQ: case PDUAckType::NACK_OUT_OF_SEQ: - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, OSP NACK, packet out of sequence, llId = %u, seqNo = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet out of sequence, llId = %u, seqNo = %u", m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); break; case PDUAckType::NACK_UNDELIVERABLE: - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, OSP NACK, packet undeliverable, llId = %u, n = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet undeliverable, llId = %u, n = %u", m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); break; @@ -649,7 +649,7 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) } if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", ISP, extended address, sap = $%02X, srcLlId = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, extended address, sap = $%02X, srcLlId = %u", m_netDataHeader.getEXSAP(), m_netDataHeader.getSrcLLId()); } @@ -690,14 +690,14 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) m_netDataHeader.decodeExtAddr(secondHeader); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", m_netData[i].getSerialNo(), m_netData[i].getFormat(), m_netData[i].getLastBlock(), m_netDataHeader.getEXSAP(), m_netDataHeader.getSrcLLId()); } m_netExtendedAddress = true; } else { - LogMessage(LOG_NET, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u", (m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_netData[i].getSerialNo() : m_netDataBlockCnt, m_netData[i].getFormat(), m_netData[i].getLastBlock()); } @@ -927,7 +927,7 @@ void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(), dataHeader.getSynchronize(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), dataHeader.getHeaderOffset(), bitLength, dataHeader.getLLId()); @@ -958,7 +958,7 @@ void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, blocksToFollow--; if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE OSP, extended address, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } } @@ -968,7 +968,7 @@ void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, dataHeader.encodeExtAddr(pduUserData); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", OSP, sap = $%02X, srcLlId = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE OSP, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } } @@ -986,7 +986,7 @@ void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, dataBlock.setLastBlock((i + 1U) == blocksToFollow); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u", + LogMessage(LOG_NET, P25_PDU_STR ", FNE OSP, block %u, fmt = $%02X, lastBlock = %u", (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? dataBlock.getSerialNo() : i, dataBlock.getFormat(), dataBlock.getLastBlock()); } @@ -1457,6 +1457,9 @@ void Data::writeRF_PDU(const uint8_t* pdu, uint32_t bitLength, bool imm, bool ac m_p25->writeRF_TDU(true, imm); + for (uint8_t i = 0U; i < 5U; i++) + m_p25->writeRF_Nulls(); + if (!ackRetry) { if (m_retryPDUData != nullptr) delete m_retryPDUData; From acc192f9f213ba137ac87a006746e1d6252c0b22 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 8 Sep 2025 20:06:27 -0400 Subject: [PATCH 005/200] set RSI data properly for outgoing KMM frame; --- src/fne/network/callhandler/packetdata/P25PacketData.cpp | 7 +++++-- src/fne/network/callhandler/packetdata/P25PacketData.h | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index dd583398..4d89781d 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -920,7 +920,7 @@ bool P25PacketData::processKMM(RxStatus* status, bool encrypted) kmm->getFlag()); // respond with No-Service - write_PDU_KMM_NoService(llId); + write_PDU_KMM_NoService(llId, kmm->getSrcLLId()); } break; @@ -933,7 +933,7 @@ bool P25PacketData::processKMM(RxStatus* status, bool encrypted) /* Helper used to return a No-Service KMM to the calling SU. */ -void P25PacketData::write_PDU_KMM_NoService(uint32_t llId) +void P25PacketData::write_PDU_KMM_NoService(uint32_t llId, uint32_t kmmRSI) { // assemble a P25 PDU frame header for transport... data::DataHeader dataHeader = data::DataHeader(); @@ -952,6 +952,9 @@ void P25PacketData::write_PDU_KMM_NoService(uint32_t llId) uint8_t buffer[KMM_NO_SERVICE_LENGTH]; KMMNoService outKmm = KMMNoService(); + outKmm.setSrcLLId(WUID_FNE); + outKmm.setDstLLId(kmmRSI); + outKmm.encode(buffer); ::memcpy(pduUserData, buffer, KMM_NO_SERVICE_LENGTH); diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index 45215a4a..dc2972c3 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -219,8 +219,9 @@ namespace network /** * @brief Helper used to return a No-Service KMM to the calling SU. * @param llId Logical Link Address. + * @param kmmRSI KMM Radio Set Identifier. */ - void write_PDU_KMM_NoService(uint32_t llId); + void write_PDU_KMM_NoService(uint32_t llId, uint32_t kmmRSI); /** * @brief Helper write ARP request to the network. From cfc428d5597993754caef1fef65bd33e477f505e Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 9 Sep 2025 12:43:32 -0400 Subject: [PATCH 006/200] implement TEK encryption with an AES-256 KEK; --- src/common/p25/Crypto.cpp | 63 +++++++++++++++++++++++++++++++++++++++ src/common/p25/Crypto.h | 10 +++++++ 2 files changed, 73 insertions(+) diff --git a/src/common/p25/Crypto.cpp b/src/common/p25/Crypto.cpp index 5cb1608a..d672ae8d 100644 --- a/src/common/p25/Crypto.cpp +++ b/src/common/p25/Crypto.cpp @@ -16,6 +16,11 @@ #include "Log.h" #include "Utils.h" +#if defined(ENABLE_SSL) +#include +#include +#endif // ENABLE_SSL + using namespace ::crypto; using namespace p25; using namespace p25::defines; @@ -195,6 +200,64 @@ void P25Crypto::resetKeystream() } } +/* Helper to crypt a P25 TEK with the given AES-256 KEK. */ + +UInt8Array P25Crypto::cryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tekLen) +{ +#if defined(ENABLE_SSL) + // static IV with $A6 pattern defined in TIA-102.AACA-C-2023 13.3 + uint8_t iv[] = { + 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U + }; + + int len; + uint8_t tempBuf[1024U]; + ::memset(tempBuf, 0x00U, 1024U); + + EVP_CIPHER_CTX* ctx; + + // create and initialize a cipher context + if (!(ctx = EVP_CIPHER_CTX_new())) { + LogError(LOG_P25, "EVP_CIPHER_CTX_new(), failed to initialize cipher context"); + return nullptr; + } + + // initialize the wrapper context with AES-256-WRAP + if (EVP_EncryptInit_ex(ctx, EVP_aes_256_wrap(), NULL, kek, iv) != 1) { + LogError(LOG_P25, "EVP_EncryptInit_ex(), failed to initialize cipher wrapping context"); + EVP_CIPHER_CTX_free(ctx); + return nullptr; + } + + // perform the wrapping operation + if (EVP_EncryptUpdate(ctx, tempBuf, &len, tek, tekLen) != 1) { + LogError(LOG_P25, "EVP_EncryptUpdate(), failed to wrap TEK"); + EVP_CIPHER_CTX_free(ctx); + return nullptr; + } + + // finalize the wrapping (no output, just padding) + int tempLen; + if (EVP_EncryptFinal_ex(ctx, tempBuf + len, &tempLen) != 1) { + LogError(LOG_P25, "EVP_EncryptFinal_ex(), failed to finalize wrapping TEK"); + EVP_CIPHER_CTX_free(ctx); + return nullptr; + } + len += tempLen; + + EVP_CIPHER_CTX_free(ctx); + + UInt8Array wrappedKey = std::unique_ptr(new uint8_t[len]); + ::memset(wrappedKey.get(), 0x00U, len); + ::memcpy(wrappedKey.get(), tempBuf, len); + + return wrappedKey; +#else + LogError(LOG_P25, "No OpenSSL, TEK encryption is not supported!"); + return nullptr; +#endif // ENABLE_SSL +} + /* Helper to crypt IMBE audio using AES-256. */ void P25Crypto::cryptAES_IMBE(uint8_t* imbe, DUID::E duid) diff --git a/src/common/p25/Crypto.h b/src/common/p25/Crypto.h index 7ced74ae..17109943 100644 --- a/src/common/p25/Crypto.h +++ b/src/common/p25/Crypto.h @@ -73,6 +73,16 @@ namespace p25 */ void resetKeystream(); + /** + * @brief Helper to crypt a P25 TEK with the given AES-256 KEK. + * @note This assumes AES-256 KEK and will not work with other key types. + * @param kek Key Encryption Key + * @param tek Traffic Encryption Key + * @param tekLen Traffic Encryption Key Length + * @returns UInt8Array Buffer containing the encryption wrapped TEK. + */ + UInt8Array cryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tekLen); + /** * @brief Helper to crypt P25 IMBE audio using AES-256. * @param imbe Buffer containing IMBE to crypt. From 361abfb4c9ca07cd06198fd2bd128b37e0f7c872 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 9 Sep 2025 12:53:05 -0400 Subject: [PATCH 007/200] add support for unwrapping (decrypting) a KEK encrypted TEK; --- src/common/p25/Crypto.cpp | 58 +++++++++++++++++++++++++++++++++++++++ src/common/p25/Crypto.h | 9 ++++++ 2 files changed, 67 insertions(+) diff --git a/src/common/p25/Crypto.cpp b/src/common/p25/Crypto.cpp index d672ae8d..f36b3120 100644 --- a/src/common/p25/Crypto.cpp +++ b/src/common/p25/Crypto.cpp @@ -258,6 +258,64 @@ UInt8Array P25Crypto::cryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tek #endif // ENABLE_SSL } +/* Helper to decrypt a P25 TEK with the given AES-256 KEK. */ + +UInt8Array P25Crypto::decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tekLen) +{ +#if defined(ENABLE_SSL) + // static IV with $A6 pattern defined in TIA-102.AACA-C-2023 13.3 + uint8_t iv[] = { + 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U + }; + + int len; + uint8_t tempBuf[1024U]; + ::memset(tempBuf, 0x00U, 1024U); + + EVP_CIPHER_CTX* ctx; + + // create and initialize a cipher context + if (!(ctx = EVP_CIPHER_CTX_new())) { + LogError(LOG_P25, "EVP_CIPHER_CTX_new(), failed to initialize cipher context"); + return nullptr; + } + + // initialize the wrapper context with AES-256-WRAP + if (EVP_DecryptInit_ex(ctx, EVP_aes_256_wrap(), NULL, kek, iv) != 1) { + LogError(LOG_P25, "EVP_DecryptInit_ex(), failed to initialize cipher wrapping context"); + EVP_CIPHER_CTX_free(ctx); + return nullptr; + } + + // perform the wrapping operation + if (EVP_DecryptUpdate(ctx, tempBuf, &len, tek, tekLen) != 1) { + LogError(LOG_P25, "EVP_DecryptUpdate(), failed to unwrap TEK"); + EVP_CIPHER_CTX_free(ctx); + return nullptr; + } + + // finalize the wrapping (no output, just padding) + int tempLen; + if (EVP_DecryptFinal_ex(ctx, tempBuf + len, &tempLen) != 1) { + LogError(LOG_P25, "EVP_DecryptFinal_ex(), failed to finalize unwrapping TEK"); + EVP_CIPHER_CTX_free(ctx); + return nullptr; + } + len += tempLen; + + EVP_CIPHER_CTX_free(ctx); + + UInt8Array unwrappedKey = std::unique_ptr(new uint8_t[len]); + ::memset(unwrappedKey.get(), 0x00U, len); + ::memcpy(unwrappedKey.get(), tempBuf, len); + + return unwrappedKey; +#else + LogError(LOG_P25, "No OpenSSL, TEK encryption is not supported!"); + return nullptr; +#endif // ENABLE_SSL +} + /* Helper to crypt IMBE audio using AES-256. */ void P25Crypto::cryptAES_IMBE(uint8_t* imbe, DUID::E duid) diff --git a/src/common/p25/Crypto.h b/src/common/p25/Crypto.h index 17109943..1f9a69b3 100644 --- a/src/common/p25/Crypto.h +++ b/src/common/p25/Crypto.h @@ -82,6 +82,15 @@ namespace p25 * @returns UInt8Array Buffer containing the encryption wrapped TEK. */ UInt8Array cryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tekLen); + /** + * @brief Helper to decrypt a P25 TEK with the given AES-256 KEK. + * @note This assumes AES-256 KEK and will not work with other key types. + * @param kek Key Encryption Key + * @param tek Wrapped Traffic Encryption Key + * @param tekLen Wrapped Traffic Encryption Key Length + * @returns UInt8Array Buffer containing the decrypted TEK. + */ + UInt8Array decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tekLen); /** * @brief Helper to crypt P25 IMBE audio using AES-256. From aa2b6ea699cdac485710b101097a9d49f49a34d0 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 19 Sep 2025 10:01:25 -0400 Subject: [PATCH 008/200] reorganize code and organization for handling P25 OTAR KMMs for better separation and robustness (this is still a WIP and does not function!); --- configs/fne-config.example.yml | 3 + src/fne/network/FNENetwork.cpp | 11 ++ src/fne/network/FNENetwork.h | 8 +- src/fne/network/P25OTARService.cpp | 119 ++++++++++++++ src/fne/network/P25OTARService.h | 94 +++++++++++ .../callhandler/packetdata/P25PacketData.cpp | 150 +++++++----------- .../callhandler/packetdata/P25PacketData.h | 48 +++--- 7 files changed, 310 insertions(+), 123 deletions(-) create mode 100644 src/fne/network/P25OTARService.cpp create mode 100644 src/fne/network/P25OTARService.h diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 3ef1f78f..0f61bcfb 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -91,6 +91,9 @@ master: # Flag indicating whether or not a parrot TG call will only be sent to the originating peer. parrotOnlyToOrginiatingPeer: false + # Flag indicating whether or not P25 OTAR KMF services are enabled. + kmfServicesEnabled: false + # Flag indicating whether or not a grant responses will only be sent to TGs with affiliations, if the TG is configured for affiliation gating. restrictGrantToAffiliatedOnly: false # Flag indicating whether or not a private call will only be routed to the network peers the RID registers with. diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 97060581..419e7042 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -19,6 +19,7 @@ #include "network/callhandler/TagP25Data.h" #include "network/callhandler/TagNXDNData.h" #include "network/callhandler/TagAnalogData.h" +#include "network/P25OTARService.h" #include "fne/ActivityLog.h" #include "HostFNE.h" @@ -64,6 +65,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_tagP25(nullptr), m_tagNXDN(nullptr), m_tagAnalog(nullptr), + m_p25OTARService(nullptr), m_host(host), m_address(address), m_port(port), @@ -76,6 +78,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_parrotDelayTimer(1000U, 0U, parrotDelay), m_parrotGrantDemand(parrotGrantDemand), m_parrotOnlyOriginating(false), + m_kmfServicesEnabled(false), m_ridLookup(nullptr), m_tidLookup(nullptr), m_peerListLookup(nullptr), @@ -130,12 +133,16 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_tagP25 = new TagP25Data(this, debug); m_tagNXDN = new TagNXDNData(this, debug); m_tagAnalog = new TagAnalogData(this, debug); + + m_p25OTARService = new P25OTARService(this, m_tagP25->packetData(), debug); } /* Finalizes a instance of the FNENetwork class. */ FNENetwork::~FNENetwork() { + delete m_p25OTARService; + delete m_tagDMR; delete m_tagP25; delete m_tagNXDN; @@ -178,6 +185,9 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) } m_parrotOnlyOriginating = conf["parrotOnlyToOrginiatingPeer"].as(false); + + m_kmfServicesEnabled = conf["kmfServicesEnabled"].as(false); + m_restrictGrantToAffOnly = conf["restrictGrantToAffiliatedOnly"].as(false); m_restrictPVCallToRegOnly = conf["restrictPrivateCallToRegOnly"].as(false); m_filterTerminators = conf["filterTerminators"].as(true); @@ -235,6 +245,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogInfo(" InfluxDB Log Raw TSBK/CSBK/RCCH: %s", m_influxLogRawData ? "yes" : "no"); } LogInfo(" Parrot Repeat to Only Originating Peer: %s", m_parrotOnlyOriginating ? "yes" : "no"); + LogInfo(" P25 OTAR KMF Services Enabled: %s", m_kmfServicesEnabled ? "yes" : "no"); } } diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index a0a230b2..3c874637 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -54,6 +54,7 @@ namespace network { namespace callhandler { class HOST_SW_API TagDMRData; } } namespace network { namespace callhandler { namespace packetdata { class HOST_SW_API DMRPacketData; } } } namespace network { namespace callhandler { class HOST_SW_API TagP25Data; } } namespace network { namespace callhandler { namespace packetdata { class HOST_SW_API P25PacketData; } } } +namespace network { class HOST_SW_API P25OTARService; } namespace network { namespace callhandler { class HOST_SW_API TagNXDNData; } } namespace network { namespace callhandler { class HOST_SW_API TagAnalogData; } } @@ -540,7 +541,10 @@ namespace network callhandler::TagNXDNData* m_tagNXDN; friend class callhandler::TagAnalogData; callhandler::TagAnalogData* m_tagAnalog; - + + friend class P25OTARService; + P25OTARService* m_p25OTARService; + friend class ::RESTAPI; HostFNE* m_host; @@ -559,6 +563,8 @@ namespace network bool m_parrotGrantDemand; bool m_parrotOnlyOriginating; + bool m_kmfServicesEnabled; + lookups::RadioIdLookup* m_ridLookup; lookups::TalkgroupRulesLookup* m_tidLookup; lookups::PeerListLookup* m_peerListLookup; diff --git a/src/fne/network/P25OTARService.cpp b/src/fne/network/P25OTARService.cpp new file mode 100644 index 00000000..53fd6cd5 --- /dev/null +++ b/src/fne/network/P25OTARService.cpp @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Converged FNE Software + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "fne/Defines.h" +#include "common/p25/kmm/KMMFactory.h" +#include "common/Log.h" +#include "common/Thread.h" +#include "common/Utils.h" +#include "network/FNENetwork.h" +#include "network/P25OTARService.h" +#include "HostFNE.h" + +using namespace network; +using namespace network::callhandler; +using namespace network::callhandler::packetdata; +using namespace p25; +using namespace p25::defines; +using namespace p25::kmm; + +#include +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the P25OTARService class. */ + +P25OTARService::P25OTARService(FNENetwork* network, P25PacketData* packetData, bool debug) : + m_network(network), + m_packetData(packetData), + m_debug(debug) +{ + assert(network != nullptr); + assert(packetData != nullptr); +} + +/* Finalizes a instance of the P25OTARService class. */ + +P25OTARService::~P25OTARService() = default; + +/* Helper used to process KMM frames from PDU data. */ + +bool P25OTARService::processDLD(const uint8_t* data, uint32_t len, uint32_t llId, uint8_t n, bool encrypted) +{ + m_packetData->write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, n, llId, false); + + uint32_t payloadSize = 0U; + UInt8Array pduUserData = processKMM(data, len, llId, encrypted, &payloadSize); + m_packetData->write_PDU_KMM(pduUserData.get(), payloadSize, llId, encrypted); + + if (pduUserData == nullptr) + return false; + + return true; +} + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +/* Helper used to process KMM frames. */ + +UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_t llId, bool encrypted, uint32_t* payloadSize) +{ + if (payloadSize != nullptr) + *payloadSize = 0U; + + std::unique_ptr frame = KMMFactory::create(data); + if (frame == nullptr) { + LogWarning(LOG_NET, P25_PDU_STR ", undecodable KMM packet"); + return nullptr; + } + + switch (frame->getMessageId()) { + case KMM_MessageType::HELLO: + { + KMMHello* kmm = static_cast(frame.get()); + LogMessage(LOG_NET, P25_PDU_STR ", KMM Hello, llId = %u, flag = $%02X", llId, + kmm->getFlag()); + + // respond with No-Service if KMF services are disabled +// if (!m_network->m_kmfServicesEnabled) + return write_KMM_NoService(llId, kmm->getSrcLLId(), payloadSize); + } + break; + + default: + break; + } // switch (packet->getPDUType()) + + return nullptr; +} + +/* Helper used to return a No-Service KMM to the calling SU. */ + +UInt8Array P25OTARService::write_KMM_NoService(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize) +{ + if (payloadSize != nullptr) + *payloadSize = KMM_NO_SERVICE_LENGTH; + + UInt8Array kmmFrame = std::make_unique(KMM_NO_SERVICE_LENGTH); + + uint8_t buffer[KMM_NO_SERVICE_LENGTH]; + KMMNoService outKmm = KMMNoService(); + outKmm.setSrcLLId(WUID_FNE); + outKmm.setDstLLId(kmmRSI); + + outKmm.encode(buffer); + + ::memcpy(kmmFrame.get(), buffer, KMM_NO_SERVICE_LENGTH); + return kmmFrame; +} diff --git a/src/fne/network/P25OTARService.h b/src/fne/network/P25OTARService.h new file mode 100644 index 00000000..27057f5c --- /dev/null +++ b/src/fne/network/P25OTARService.h @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Converged FNE Software + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file P25OTARService.h + * @ingroup fne_network + * @file P25OTARService.cpp + * @ingroup fne_network + */ +#if !defined(__P25_OTAR_SERVICE_H__) +#define __P25_OTAR_SERVICE_H__ + +#include "fne/Defines.h" +#include "common/p25/P25Defines.h" +#include "network/FNENetwork.h" +#include "network/callhandler/packetdata/P25PacketData.h" + +namespace network +{ + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Implements the P25 OTAR service. + * @ingroup fne_network + */ + class HOST_SW_API P25OTARService { + public: + /** + * @brief Initializes a new instance of the P25OTARService class. + * @param network Instance of the FNENetwork class. + * @param packetData Instance of the P25PacketData class. + * @param debug Flag indicating whether network debug is enabled. + */ + P25OTARService(FNENetwork* network, network::callhandler::packetdata::P25PacketData* packetData, bool debug); + /** + * @brief Finalizes a instance of the P25OTARService class. + */ + ~P25OTARService(); + + /** + * @brief Helper used to process KMM frames from PDU data. + * @param[in] data Network data buffer. + * @param len Length of data. + * @param encrypted Flag indicating whether or not the KMM frame is encrypted. + * @param llId Logical Link ID. + * @param n Send Sequence Number. + * @param encrypted Flag indicating whether or not the KMM frame is encrypted. + * @returns bool True, if KMM processed, otherwise false. + */ + bool processDLD(const uint8_t* data, uint32_t len, uint32_t llId, uint8_t n, bool encrypted); + + /** + * @brief Updates the timer by the passed number of milliseconds. + * @param ms Number of milliseconds. + */ + void clock(uint32_t ms); + + private: + FNENetwork* m_network; + network::callhandler::packetdata::P25PacketData* m_packetData; + + bool m_debug; + + /** + * @brief Helper used to process KMM frames. + * @param[in] data Network data buffer. + * @param len Length of data. + * @param encrypted Flag indicating whether or not the KMM frame is encrypted. + * @param llId Logical Link ID. + * @param[out] payloadSize Size of the returned KMM payload. + * @returns UInt8Array Buffer containing the processed KMM frame (if any). + */ + UInt8Array processKMM(const uint8_t* data, uint32_t len, uint32_t llId, bool encrypted, uint32_t* payloadSize); + + /** + * @brief Helper used to return a No-Service KMM to the calling SU. + * @param llId Logical Link Address. + * @param kmmRSI KMM Radio Set Identifier. + * @param[out] payloadSize Size of the returned KMM payload. + * @returns UInt8Array Buffer containing the processed KMM frame (if any). + */ + UInt8Array write_KMM_NoService(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize); + }; +} // namespace network + +#endif // __P25_OTAR_SERVICE_H__ diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index 4d89781d..eec1766f 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -17,6 +17,7 @@ #include "common/Thread.h" #include "common/Utils.h" #include "network/FNENetwork.h" +#include "network/P25OTARService.h" #include "network/callhandler/packetdata/P25PacketData.h" #include "HostFNE.h" @@ -395,6 +396,58 @@ void P25PacketData::processPacketFrame(const uint8_t* data, uint32_t len, bool a #endif // !defined(_WIN32) } +/* Helper to write a PDU acknowledge response. */ + +void P25PacketData::write_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint8_t ackStatus, uint32_t llId, bool extendedAddress, uint32_t srcLlId) +{ + if (ackClass == PDUAckClass::ACK && ackType != PDUAckType::ACK) + return; + + data::DataHeader rspHeader = data::DataHeader(); + rspHeader.setFormat(PDUFormatType::RSP); + rspHeader.setMFId(MFG_STANDARD); + rspHeader.setOutbound(true); + rspHeader.setResponseClass(ackClass); + rspHeader.setResponseType(ackType); + rspHeader.setResponseStatus(ackStatus); + rspHeader.setLLId(llId); + if (srcLlId > 0U) { + rspHeader.setSrcLLId(srcLlId); + } + + if (!extendedAddress) + rspHeader.setFullMessage(true); + else + rspHeader.setFullMessage(false); + + rspHeader.setBlocksToFollow(0U); + + dispatchUserFrameToFNE(rspHeader, srcLlId > 0U, nullptr); +} + +/* Helper used to return a KMM to the calling SU. */ + +void P25PacketData::write_PDU_KMM(const uint8_t* data, uint32_t len, uint32_t llId, bool encrypted) +{ + // assemble a P25 PDU frame header for transport... + data::DataHeader dataHeader = data::DataHeader(); + dataHeader.setFormat(PDUFormatType::CONFIRMED); + dataHeader.setMFId(MFG_STANDARD); + dataHeader.setAckNeeded(true); + dataHeader.setOutbound(true); + dataHeader.setSAP((encrypted) ? PDUSAP::ENC_KMM : PDUSAP::UNENC_KMM); + dataHeader.setLLId(llId); + dataHeader.setBlocksToFollow(1U); + + dataHeader.calculateLength(len); + uint32_t pduLength = dataHeader.getPDULength(); + + DECLARE_UINT8_ARRAY(pduUserData, pduLength); + ::memcpy(pduUserData, data, len); + + dispatchUserFrameToFNE(dataHeader, false, pduUserData); +} + /* Updates the timer by the passed number of milliseconds. */ void P25PacketData::clock(uint32_t ms) @@ -713,7 +766,8 @@ void P25PacketData::dispatch(uint32_t peerId) LogMessage(LOG_NET, P25_PDU_STR ", KMM (Key Management Message), peer = %u, blocksToFollow = %u", peerId, status->header.getBlocksToFollow()); - processKMM(status, sap == PDUSAP::ENC_KMM); + bool encrypted = (sap == PDUSAP::ENC_KMM); + m_network->m_p25OTARService->processDLD(status->pduUserData, status->pduUserDataLength, status->llId, status->header.getNs(), encrypted); } break; default: @@ -897,71 +951,6 @@ bool P25PacketData::processSNDCPControl(RxStatus* status) return true; } -/* Helper used to process KMM frames from PDU data. */ - -bool P25PacketData::processKMM(RxStatus* status, bool encrypted) -{ - std::unique_ptr frame = KMMFactory::create(status->pduUserData); - if (frame == nullptr) { - LogWarning(LOG_NET, P25_PDU_STR ", undecodable KMM packet"); - return false; - } - - uint32_t llId = status->header.getLLId(); - - // ack KMM frame - write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->header.getNs(), llId, false); - - switch (frame->getMessageId()) { - case KMM_MessageType::HELLO: - { - KMMHello* kmm = static_cast(frame.get()); - LogMessage(LOG_NET, P25_PDU_STR ", KMM Hello, llId = %u, flag = $%02X", llId, - kmm->getFlag()); - - // respond with No-Service - write_PDU_KMM_NoService(llId, kmm->getSrcLLId()); - } - break; - - default: - break; - } // switch (packet->getPDUType()) - - return true; -} - -/* Helper used to return a No-Service KMM to the calling SU. */ - -void P25PacketData::write_PDU_KMM_NoService(uint32_t llId, uint32_t kmmRSI) -{ - // assemble a P25 PDU frame header for transport... - data::DataHeader dataHeader = data::DataHeader(); - dataHeader.setFormat(PDUFormatType::CONFIRMED); - dataHeader.setMFId(MFG_STANDARD); - dataHeader.setAckNeeded(true); - dataHeader.setOutbound(true); - dataHeader.setSAP(PDUSAP::UNENC_KMM); - dataHeader.setLLId(llId); - dataHeader.setBlocksToFollow(1U); - - dataHeader.calculateLength(KMM_NO_SERVICE_LENGTH); - uint32_t pduLength = dataHeader.getPDULength(); - - DECLARE_UINT8_ARRAY(pduUserData, pduLength); - - uint8_t buffer[KMM_NO_SERVICE_LENGTH]; - KMMNoService outKmm = KMMNoService(); - outKmm.setSrcLLId(WUID_FNE); - outKmm.setDstLLId(kmmRSI); - - outKmm.encode(buffer); - - ::memcpy(pduUserData, buffer, KMM_NO_SERVICE_LENGTH); - - dispatchUserFrameToFNE(dataHeader, false, pduUserData); -} - /* Helper write ARP request to the network. */ void P25PacketData::write_PDU_ARP(uint32_t addr) @@ -1068,35 +1057,6 @@ void P25PacketData::write_PDU_ARP_Reply(uint32_t targetAddr, uint32_t requestorL dispatchUserFrameToFNE(rspHeader, true, pduUserData); } -/* Helper to write a PDU acknowledge response. */ - -void P25PacketData::write_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint8_t ackStatus, uint32_t llId, bool extendedAddress, uint32_t srcLlId) -{ - if (ackClass == PDUAckClass::ACK && ackType != PDUAckType::ACK) - return; - - data::DataHeader rspHeader = data::DataHeader(); - rspHeader.setFormat(PDUFormatType::RSP); - rspHeader.setMFId(MFG_STANDARD); - rspHeader.setOutbound(true); - rspHeader.setResponseClass(ackClass); - rspHeader.setResponseType(ackType); - rspHeader.setResponseStatus(ackStatus); - rspHeader.setLLId(llId); - if (srcLlId > 0U) { - rspHeader.setSrcLLId(srcLlId); - } - - if (!extendedAddress) - rspHeader.setFullMessage(true); - else - rspHeader.setFullMessage(false); - - rspHeader.setBlocksToFollow(0U); - - dispatchUserFrameToFNE(rspHeader, srcLlId > 0U, nullptr); -} - /* Helper to write user data as a P25 PDU packet. */ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network::PeerNetwork* peerNet, data::DataHeader& dataHeader, diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index dc2972c3..d10c6337 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -77,6 +77,27 @@ namespace network */ void processPacketFrame(const uint8_t* data, uint32_t len, bool alreadyQueued = false); + /** + * @brief Helper to write a PDU acknowledge response. + * @param ackClass Acknowledgement Class. + * @param ackType Acknowledgement Type. + * @param ackStatus + * @param llId Logical Link ID. + * @param extendedAddress Flag indicating whether or not to extended addressing is in use. + * @param srcLlId Source Logical Link ID. + */ + void write_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint8_t ackStatus, uint32_t llId, bool extendedAddress, + uint32_t srcLlId = 0U); + + /** + * @brief Helper used to return a KMM to the calling SU. + * @param data Network data buffer. + * @param len Length of data. + * @param llId Logical Link ID. + * @param encrypted Flag indicating whether or not the KMM frame is encrypted. + */ + void write_PDU_KMM(const uint8_t* data, uint32_t len, uint32_t llId, bool encrypted); + /** * @brief Updates the timer by the passed number of milliseconds. * @param ms Number of milliseconds. @@ -208,21 +229,6 @@ namespace network */ bool processSNDCPControl(RxStatus* status); - /** - * @brief Helper used to process KMM frames from PDU data. - * @param status Instance of the RxStatus class. - * @param encrypted Flag indicating whether or not the KMM frame is encrypted. - * @returns bool True, if KMM data was processed, otherwise false. - */ - bool processKMM(RxStatus* status, bool encrypted); - - /** - * @brief Helper used to return a No-Service KMM to the calling SU. - * @param llId Logical Link Address. - * @param kmmRSI KMM Radio Set Identifier. - */ - void write_PDU_KMM_NoService(uint32_t llId, uint32_t kmmRSI); - /** * @brief Helper write ARP request to the network. * @param addr IP Address. @@ -237,18 +243,6 @@ namespace network */ void write_PDU_ARP_Reply(uint32_t targetAddr, uint32_t requestorLlid, uint32_t requestorAddr, uint32_t targetLlid = 0U); - /** - * @brief Helper to write a PDU acknowledge response. - * @param ackClass Acknowledgement Class. - * @param ackType Acknowledgement Type. - * @param ackStatus - * @param llId Logical Link ID. - * @param extendedAddress Flag indicating whether or not to extended addressing is in use. - * @param srcLlId Source Logical Link ID. - */ - void write_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint8_t ackStatus, uint32_t llId, bool extendedAddress, - uint32_t srcLlId = 0U); - /** * @brief Helper to write user data as a P25 PDU packet. * @param peerId Peer ID. From a3726c4c50832efcf43f1847843b11d3853b80ff Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 19 Sep 2025 10:11:21 -0400 Subject: [PATCH 009/200] force hard disable KMF services if OpenSSL isn't compiled in; add instance of P25 crypto to P25OTARService; --- src/fne/network/FNENetwork.cpp | 5 +++++ src/fne/network/P25OTARService.cpp | 1 + src/fne/network/P25OTARService.h | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 419e7042..9532d0be 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -186,7 +186,12 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) m_parrotOnlyOriginating = conf["parrotOnlyToOrginiatingPeer"].as(false); +#if defined(ENABLE_SSL) m_kmfServicesEnabled = conf["kmfServicesEnabled"].as(false); +#else + m_kmfServicesEnabled = false; + LogWarning(LOG_P25, "FNE is compiled without OpenSSL support, KMF services are unavailable."); +#endif // ENABLE_SSL m_restrictGrantToAffOnly = conf["restrictGrantToAffiliatedOnly"].as(false); m_restrictPVCallToRegOnly = conf["restrictPrivateCallToRegOnly"].as(false); diff --git a/src/fne/network/P25OTARService.cpp b/src/fne/network/P25OTARService.cpp index 53fd6cd5..c2c09cc1 100644 --- a/src/fne/network/P25OTARService.cpp +++ b/src/fne/network/P25OTARService.cpp @@ -35,6 +35,7 @@ using namespace p25::kmm; P25OTARService::P25OTARService(FNENetwork* network, P25PacketData* packetData, bool debug) : m_network(network), m_packetData(packetData), + m_crypto(), m_debug(debug) { assert(network != nullptr); diff --git a/src/fne/network/P25OTARService.h b/src/fne/network/P25OTARService.h index 27057f5c..90c3285a 100644 --- a/src/fne/network/P25OTARService.h +++ b/src/fne/network/P25OTARService.h @@ -18,6 +18,7 @@ #include "fne/Defines.h" #include "common/p25/P25Defines.h" +#include "common/p25/Crypto.h" #include "network/FNENetwork.h" #include "network/callhandler/packetdata/P25PacketData.h" @@ -67,6 +68,8 @@ namespace network FNENetwork* m_network; network::callhandler::packetdata::P25PacketData* m_packetData; + p25::crypto::P25Crypto* m_crypto; + bool m_debug; /** From bedba9c9ee36e0ade7c998c3c08bb50416f3bec2 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 19 Sep 2025 11:55:01 -0400 Subject: [PATCH 010/200] add support to expose KMF services via DLI UDP; add plumbing to support encrypted KMM frames; --- configs/fne-config.example.yml | 2 + src/fne/network/FNENetwork.cpp | 16 ++ src/fne/network/P25OTARService.cpp | 269 ++++++++++++++++++++++++++++- src/fne/network/P25OTARService.h | 55 +++++- 4 files changed, 335 insertions(+), 7 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 0f61bcfb..f19c02c1 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -93,6 +93,8 @@ master: # Flag indicating whether or not P25 OTAR KMF services are enabled. kmfServicesEnabled: false + # Port number to listen on for P25 OTAR KMF services. + kmfOtarPort: 64414 # Flag indicating whether or not a grant responses will only be sent to TGs with affiliations, if the TG is configured for affiliation gating. restrictGrantToAffiliatedOnly: false diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 9532d0be..c4639b02 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -141,6 +141,10 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, FNENetwork::~FNENetwork() { + if (m_kmfServicesEnabled) { + m_p25OTARService->close(); + } + delete m_p25OTARService; delete m_tagDMR; @@ -188,6 +192,13 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) #if defined(ENABLE_SSL) m_kmfServicesEnabled = conf["kmfServicesEnabled"].as(false); + uint16_t kmfOtarPort = conf["kmfOtarPort"].as(64414U); + if (m_kmfServicesEnabled) { + if (!m_p25OTARService->open(m_address, kmfOtarPort)) { + m_kmfServicesEnabled = false; + LogError(LOG_P25, "FNE OTAR KMF services failed to start, OTAR service disabled."); + } + } #else m_kmfServicesEnabled = false; LogWarning(LOG_P25, "FNE is compiled without OpenSSL support, KMF services are unavailable."); @@ -251,6 +262,8 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) } LogInfo(" Parrot Repeat to Only Originating Peer: %s", m_parrotOnlyOriginating ? "yes" : "no"); LogInfo(" P25 OTAR KMF Services Enabled: %s", m_kmfServicesEnabled ? "yes" : "no"); + LogInfo(" P25 OTAR KMF Listening Address: %s", m_address.c_str()); + LogInfo(" P25 OTAR KMF Listening Port: %u", kmfOtarPort); } } @@ -489,6 +502,9 @@ void FNENetwork::clock(uint32_t ms) m_parrotDelayTimer.isRunning() && m_parrotDelayTimer.hasExpired()) { m_parrotDelayTimer.stop(); } + + if (m_kmfServicesEnabled) + m_p25OTARService->clock(ms); } /* Opens connection to the network. */ diff --git a/src/fne/network/P25OTARService.cpp b/src/fne/network/P25OTARService.cpp index c2c09cc1..21e81c30 100644 --- a/src/fne/network/P25OTARService.cpp +++ b/src/fne/network/P25OTARService.cpp @@ -19,13 +19,22 @@ using namespace network; using namespace network::callhandler; using namespace network::callhandler::packetdata; +using namespace network::frame; +using namespace network::udp; using namespace p25; using namespace p25::defines; +using namespace p25::crypto; using namespace p25::kmm; #include #include +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +#define MAX_THREAD_CNT 4U + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -33,9 +42,11 @@ using namespace p25::kmm; /* Initializes a new instance of the P25OTARService class. */ P25OTARService::P25OTARService(FNENetwork* network, P25PacketData* packetData, bool debug) : + m_socket(nullptr), + m_frameQueue(nullptr), + m_threadPool(MAX_THREAD_CNT, "otar"), m_network(network), m_packetData(packetData), - m_crypto(), m_debug(debug) { assert(network != nullptr); @@ -44,7 +55,13 @@ P25OTARService::P25OTARService(FNENetwork* network, P25PacketData* packetData, b /* Finalizes a instance of the P25OTARService class. */ -P25OTARService::~P25OTARService() = default; +P25OTARService::~P25OTARService() +{ + if (m_frameQueue != nullptr) + delete m_frameQueue; + if (m_socket != nullptr) + delete m_socket; +} /* Helper used to process KMM frames from PDU data. */ @@ -52,20 +69,234 @@ bool P25OTARService::processDLD(const uint8_t* data, uint32_t len, uint32_t llId { m_packetData->write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, n, llId, false); + if (m_debug) + Utils::dump(1U, "P25OTARService::processDLD(), KMM Network Message", data, len); + uint32_t payloadSize = 0U; UInt8Array pduUserData = processKMM(data, len, llId, encrypted, &payloadSize); - m_packetData->write_PDU_KMM(pduUserData.get(), payloadSize, llId, encrypted); - if (pduUserData == nullptr) return false; + // handle DLD encrypted KMM frame + if (encrypted) { + // read crypto parameters from KMM header + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + for (uint8_t i = 0; i < MI_LENGTH_BYTES; i++) { + mi[i] = data[i]; + } + + uint8_t algoId = data[9U]; + uint16_t kid = GET_UINT16(data, 10U); + + // re-encrypt the KMM response + pduUserData = cryptKMM(algoId, kid, mi, pduUserData.get(), payloadSize, true); + if (pduUserData == nullptr) { + LogError(LOG_P25, "OTAR KMM, unable to encrypt KMM response, algoId = $%02X, kID = $%04X", algoId, kid); + return false; + } + } + + m_packetData->write_PDU_KMM(pduUserData.get(), payloadSize, llId, encrypted); return true; } +/* Updates the timer by the passed number of milliseconds. */ + +void P25OTARService::clock(uint32_t ms) +{ + if (m_socket != nullptr) { + sockaddr_storage address; + uint32_t addrLen; + int length = 0U; + + // read message + UInt8Array buffer = m_frameQueue->read(length, address, addrLen); + if (length > 0) { + if (m_debug) + Utils::dump(1U, "P25OTARService::clock(), KMM Network Message", buffer.get(), length); + + OTARPacketRequest* req = new OTARPacketRequest(); + req->obj = this; + + req->address = address; + req->addrLen = addrLen; + + req->length = length; + req->buffer = new uint8_t[length]; + ::memcpy(req->buffer, buffer.get(), length); + + // enqueue the task + if (!m_threadPool.enqueue(new_pooltask(taskNetworkRx, req))) { + LogError(LOG_NET, "Failed to task enqueue control network packet request, %s:%u", + udp::Socket::address(address).c_str(), udp::Socket::port(address)); + if (req != nullptr) { + if (req->buffer != nullptr) + delete[] req->buffer; + delete req; + } + } + } + } +} + +/* Opens a connection to the OTAR port. */ + +bool P25OTARService::open(const std::string& address, uint16_t port) +{ + m_socket = new Socket(port); + m_frameQueue = new RawFrameQueue(m_socket, m_debug); + + sockaddr_storage addr; + uint32_t addrLen; + if (udp::Socket::lookup(address, port, addr, addrLen) != 0) + addrLen = 0U; + + if (addrLen > 0U) { + m_threadPool.start(); + + if (m_socket != nullptr) { + return m_socket->open(addr); + } + } + + return false; +} + +/* Closes the connection to the OTAR port. */ + +void P25OTARService::close() +{ + if (m_socket != nullptr) { + m_threadPool.stop(); + m_threadPool.wait(); + + m_socket->close(); + } +} + // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- +/* Process a data frames from the network. */ + +void P25OTARService::taskNetworkRx(OTARPacketRequest* req) +{ + if (req != nullptr) { + P25OTARService* network = static_cast(req->obj); + if (network == nullptr) { + delete req; + return; + } + + if (req->length >= 13) { + // read crypto parameters from KMM header + uint8_t mfId = req->buffer[1U]; + uint8_t algoId = req->buffer[2U]; + uint16_t kid = GET_UINT16(req->buffer, 3U); + + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + for (uint8_t i = 0; i < MI_LENGTH_BYTES; i++) { + mi[i] = req->buffer[4U + i]; + } + + // KMM frame + UInt8Array buffer = std::make_unique(req->length - 13U); + ::memset(buffer.get(), 0x00U, req->length - 13U); + ::memcpy(buffer.get(), req->buffer + 13U, req->length - 13U); + + bool encrypted = (mfId != ALGO_UNENCRYPT); + if (encrypted) { + buffer = network->cryptKMM(algoId, kid, mi, buffer.get(), req->length - 13U, false); + if (buffer == nullptr) { + LogError(LOG_P25, "OTAR KMM, unable to decrypt KMM, algoId = $%02X, kID = $%04X", algoId, kid); + if (req->buffer != nullptr) + delete[] req->buffer; + delete req; + return; + } + } + + uint32_t payloadSize = 0U; + UInt8Array pduUserData = network->processKMM(buffer.get(), req->length - 13U, 0U, false, &payloadSize); + + if (encrypted) { + // re-encrypt the KMM response + pduUserData = network->cryptKMM(algoId, kid, mi, pduUserData.get(), payloadSize, true); + if (pduUserData == nullptr) { + LogError(LOG_P25, "OTAR KMM, unable to encrypt KMM response, algoId = $%02X, kID = $%04X", algoId, kid); + if (req->buffer != nullptr) + delete[] req->buffer; + delete req; + return; + } + } + + network->m_frameQueue->write(pduUserData.get(), payloadSize, req->address, req->addrLen); + } + + if (req->buffer != nullptr) + delete[] req->buffer; + delete req; + } +} + +/* Encrypt/decrypt KMM frame. */ + +UInt8Array P25OTARService::cryptKMM(uint8_t algoId, uint16_t kid, uint8_t* mi, const uint8_t* buffer, uint32_t len, bool encrypt) +{ + assert(buffer != nullptr); + + P25Crypto crypto; + if (!encrypt) + crypto.setMI(mi); + else { + crypto.generateMI(); + crypto.getMI(mi); + } + + UInt8Array outBuffer = std::make_unique(len); + ::memset(outBuffer.get(), 0x00U, len); + ::memcpy(outBuffer.get(), buffer, len); + + if (algoId == P25DEF::ALGO_UNENCRYPT) + return outBuffer; + + /* + ** bryanb: Architecturally this is a problem. Because KMF services would essentially be limited to the local FNE + ** because we aren't performing FNE KEY_REQ's to upstream peer'ed FNEs to find the key used to encrypt the KMM. + */ + + ::KeyItem keyItem = m_network->m_cryptoLookup->find(kid); + if (!keyItem.isInvalid()) { + uint8_t key[P25DEF::MAX_ENC_KEY_LENGTH_BYTES]; + ::memset(key, 0x00U, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); + uint8_t keyLength = keyItem.getKey(key); + + if (m_network->m_debug) { + LogDebugEx(LOG_HOST, "P25OTARService::cryptKMM()", "keyLength = %u", keyLength); + Utils::dump(1U, "P25OTARService::cryptKMM(), Key", key, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); + } + + LogMessage(LOG_P25, "OTAR KMM, algId = $%02X, kID = $%04X", algoId, kid); + crypto.setKey(key, keyLength); + crypto.generateKeystream(); + + switch (algoId) { + case P25DEF::ALGO_AES_256: + // TODO: implement handling AES-256 KMM encryption/decryption for data blocks + return outBuffer; + default: + LogError(LOG_HOST, "unsupported KEK algorithm, algoId = $%02X", algoId); + break; + } + } + + return nullptr; +} + /* Helper used to process KMM frames. */ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_t llId, bool encrypted, uint32_t* payloadSize) @@ -73,12 +304,40 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ if (payloadSize != nullptr) *payloadSize = 0U; - std::unique_ptr frame = KMMFactory::create(data); + UInt8Array buffer = std::make_unique(len); + ::memset(buffer.get(), 0x00U, len); + ::memcpy(buffer.get(), data, len); + + // handle DLD encrypted KMM frame + if (encrypted) { + // read crypto parameters from KMM header + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + for (uint8_t i = 0; i < MI_LENGTH_BYTES; i++) { + mi[i] = data[i]; + } + + uint8_t algoId = data[9U]; + uint16_t kid = GET_UINT16(data, 10U); + + // decrypt frame before processing + buffer = cryptKMM(algoId, kid, mi, data + 10U, len - 10U, false); + if (buffer == nullptr) { + LogError(LOG_P25, "OTAR KMM, unable to decrypt KMM, algoId = $%02X, kID = $%04X", algoId, kid); + return nullptr; + } + } + + std::unique_ptr frame = KMMFactory::create(buffer.get()); if (frame == nullptr) { LogWarning(LOG_NET, P25_PDU_STR ", undecodable KMM packet"); return nullptr; } + if (llId == 0U) { + llId = frame->getSrcLLId(); + } + switch (frame->getMessageId()) { case KMM_MessageType::HELLO: { diff --git a/src/fne/network/P25OTARService.h b/src/fne/network/P25OTARService.h index 90c3285a..eb3636fd 100644 --- a/src/fne/network/P25OTARService.h +++ b/src/fne/network/P25OTARService.h @@ -19,11 +19,28 @@ #include "fne/Defines.h" #include "common/p25/P25Defines.h" #include "common/p25/Crypto.h" +#include "common/network/udp/Socket.h" +#include "common/network/RawFrameQueue.h" #include "network/FNENetwork.h" #include "network/callhandler/packetdata/P25PacketData.h" namespace network { + // --------------------------------------------------------------------------- + // Structure Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents the data required for a OTAR network packet handler thread. + * @ingroup fne_network + */ + struct OTARPacketRequest : thread_t { + sockaddr_storage address; //! IP Address and Port. + uint32_t addrLen; //! + int length = 0U; //! Length of raw data buffer + uint8_t *buffer; //! Raw data buffer + }; + // --------------------------------------------------------------------------- // Class Declaration // --------------------------------------------------------------------------- @@ -64,14 +81,48 @@ namespace network */ void clock(uint32_t ms); + /** + * @brief Opens a connection to the OTAR port. + * @param address Hostname/IP address to listen on. + * @param port Port number. + * @returns bool True, if connection is opened, otherwise false. + */ + bool open(const std::string& address, uint16_t port); + + /** + * @brief Closes the connection to the OTAR port. + */ + void close(); + private: + network::udp::Socket* m_socket; + network::RawFrameQueue* m_frameQueue; + + ThreadPool m_threadPool; + FNENetwork* m_network; network::callhandler::packetdata::P25PacketData* m_packetData; - p25::crypto::P25Crypto* m_crypto; - bool m_debug; + /** + * @brief Entry point to process a given network packet. + * @param arg Instance of the OTARPacketRequest structure. + */ + static void taskNetworkRx(OTARPacketRequest* req); + + /** + * @brief Encrypt/decrypt KMM frame. + * @param[in] algoId Algorithm ID. + * @param[in] kid Key ID. + * @param mi Message Indicator. + * @param[in] buffer KMM frame buffer. + * @param len Length of KMM frame buffer. + * @param encrypt True to encrypt, false to decrypt. + * @returns UInt8Array Buffer containing the encrypted/decrypted KMM frame. + */ + UInt8Array cryptKMM(uint8_t algoId, uint16_t kid, uint8_t* mi, const uint8_t* buffer, uint32_t len, bool encrypt = false); + /** * @brief Helper used to process KMM frames. * @param[in] data Network data buffer. From 548f79df790a5d5dae2b01b32113cc377c1b5940 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 19 Sep 2025 12:28:47 -0400 Subject: [PATCH 011/200] add some debug dumping; --- src/fne/network/P25OTARService.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fne/network/P25OTARService.cpp b/src/fne/network/P25OTARService.cpp index 21e81c30..87150361 100644 --- a/src/fne/network/P25OTARService.cpp +++ b/src/fne/network/P25OTARService.cpp @@ -326,6 +326,9 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ LogError(LOG_P25, "OTAR KMM, unable to decrypt KMM, algoId = $%02X, kID = $%04X", algoId, kid); return nullptr; } + + if (m_debug) + Utils::dump(1U, "P25OTARService::processKMM(), (Decrypted) KMM Network Message", buffer.get(), len); } std::unique_ptr frame = KMMFactory::create(buffer.get()); From ae9703a38ac45ce6e0c39b7fb908007eff9b4d3e Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 19 Sep 2025 12:36:42 -0400 Subject: [PATCH 012/200] BUGFIX: null reference when trying to perform old style lookup of timestamp when debugging is enabled; --- src/common/network/FrameQueue.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/network/FrameQueue.cpp b/src/common/network/FrameQueue.cpp index 0ca7eeab..b5442fe1 100644 --- a/src/common/network/FrameQueue.cpp +++ b/src/common/network/FrameQueue.cpp @@ -301,9 +301,10 @@ uint8_t* FrameQueue::generateMessage(const uint8_t* message, uint32_t length, ui } if (timestamp != INVALID_TS) { + uint32_t prevTimestamp = timestamp; timestamp += (RTP_GENERIC_CLOCK_RATE / 133); if (m_debug) - LogDebugEx(LOG_NET, "FrameQueue::generateMessage()", "RTP streamId = %u, previous TS = %u, TS = %u, rtpSeq = %u", streamId, m_streamTimestamps[streamId], timestamp, rtpSeq); + LogDebugEx(LOG_NET, "FrameQueue::generateMessage()", "RTP streamId = %u, previous TS = %u, TS = %u, rtpSeq = %u", streamId, prevTimestamp, timestamp, rtpSeq); updateTimestamp(streamId, timestamp); } } From a23b387679236cddde24a87d6e993d1764c78d3c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 19 Sep 2025 13:13:09 -0400 Subject: [PATCH 013/200] standardize KMM logging; --- configs/fne-config.example.yml | 2 + src/common/p25/P25Defines.h | 4 +- .../p25/kmm/KMMDeregistrationCommand.cpp | 7 ++++ src/common/p25/kmm/KMMDeregistrationCommand.h | 6 +++ .../p25/kmm/KMMDeregistrationResponse.cpp | 7 ++++ .../p25/kmm/KMMDeregistrationResponse.h | 6 +++ src/common/p25/kmm/KMMFrame.cpp | 7 ++++ src/common/p25/kmm/KMMFrame.h | 6 +++ src/common/p25/kmm/KMMHello.cpp | 7 ++++ src/common/p25/kmm/KMMHello.h | 6 +++ src/common/p25/kmm/KMMInventoryCommand.cpp | 7 ++++ src/common/p25/kmm/KMMInventoryCommand.h | 6 +++ .../p25/kmm/KMMInventoryResponseHeader.cpp | 7 ++++ .../p25/kmm/KMMInventoryResponseHeader.h | 6 +++ .../kmm/KMMInventoryResponseListKeyIDs.cpp | 7 ++++ .../p25/kmm/KMMInventoryResponseListKeyIDs.h | 6 +++ .../kmm/KMMInventoryResponseListKeysets.cpp | 7 ++++ .../p25/kmm/KMMInventoryResponseListKeysets.h | 6 +++ src/common/p25/kmm/KMMModifyKey.cpp | 7 ++++ src/common/p25/kmm/KMMModifyKey.h | 6 +++ src/common/p25/kmm/KMMNegativeAck.cpp | 7 ++++ src/common/p25/kmm/KMMNegativeAck.h | 6 +++ src/common/p25/kmm/KMMNoService.cpp | 7 ++++ src/common/p25/kmm/KMMNoService.h | 6 +++ src/common/p25/kmm/KMMRegistrationCommand.cpp | 7 ++++ src/common/p25/kmm/KMMRegistrationCommand.h | 6 +++ .../p25/kmm/KMMRegistrationResponse.cpp | 9 +++- src/common/p25/kmm/KMMRegistrationResponse.h | 6 +++ src/common/p25/kmm/KMMZeroize.cpp | 7 ++++ src/common/p25/kmm/KMMZeroize.h | 6 +++ src/fne/HostFNE.cpp | 7 +++- src/fne/network/FNENetwork.cpp | 7 ++-- src/fne/network/FNENetwork.h | 6 ++- src/fne/network/P25OTARService.cpp | 42 +++++++++++++------ src/fne/network/P25OTARService.h | 6 ++- 35 files changed, 236 insertions(+), 22 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index f19c02c1..f4831673 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -95,6 +95,8 @@ master: kmfServicesEnabled: false # Port number to listen on for P25 OTAR KMF services. kmfOtarPort: 64414 + # Flag indicating whether or not verbose debug logging for P25 OTAR KMF services is enabled. + kmfDebug: false # Flag indicating whether or not a grant responses will only be sent to TGs with affiliations, if the TG is configured for affiliation gating. restrictGrantToAffiliatedOnly: false diff --git a/src/common/p25/P25Defines.h b/src/common/p25/P25Defines.h index 1b7ff9cb..7c3ac5fa 100644 --- a/src/common/p25/P25Defines.h +++ b/src/common/p25/P25Defines.h @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2016 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -816,6 +816,8 @@ namespace p25 #define P25_LDU2_STR "P25, LDU2 (Logical Link Data Unit 2)" #define P25_PDU_STR "P25, PDU (Packet Data Unit)" #define P25_TDULC_STR "P25, TDULC (Terminator Data Unit with Link Control)" + + #define P25_KMM_STR "P25, KMM (Key Management Message)" } // namespace defines } // namespace p25 diff --git a/src/common/p25/kmm/KMMDeregistrationCommand.cpp b/src/common/p25/kmm/KMMDeregistrationCommand.cpp index c26a81b6..5353938a 100644 --- a/src/common/p25/kmm/KMMDeregistrationCommand.cpp +++ b/src/common/p25/kmm/KMMDeregistrationCommand.cpp @@ -64,6 +64,13 @@ void KMMDeregistrationCommand::encode(uint8_t* data) SET_UINT24(m_kmfRSI, data, 11U); // KMF RSI } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMDeregistrationCommand::toString() +{ + return std::string("KMM, DEREG_CMD (Deregistration Command)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMDeregistrationCommand.h b/src/common/p25/kmm/KMMDeregistrationCommand.h index e93c8a27..5e27b908 100644 --- a/src/common/p25/kmm/KMMDeregistrationCommand.h +++ b/src/common/p25/kmm/KMMDeregistrationCommand.h @@ -67,6 +67,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + public: /** * @brief diff --git a/src/common/p25/kmm/KMMDeregistrationResponse.cpp b/src/common/p25/kmm/KMMDeregistrationResponse.cpp index f3ace584..788c08cf 100644 --- a/src/common/p25/kmm/KMMDeregistrationResponse.cpp +++ b/src/common/p25/kmm/KMMDeregistrationResponse.cpp @@ -60,6 +60,13 @@ void KMMDeregistrationResponse::encode(uint8_t* data) data[10U] = m_status; // Status } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMDeregistrationResponse::toString() +{ + return std::string("KMM, DEREG_RSP (Deregistration Response)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMDeregistrationResponse.h b/src/common/p25/kmm/KMMDeregistrationResponse.h index 2b1badba..68d2cd5e 100644 --- a/src/common/p25/kmm/KMMDeregistrationResponse.h +++ b/src/common/p25/kmm/KMMDeregistrationResponse.h @@ -67,6 +67,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + public: /** * @brief Deregistration response status. diff --git a/src/common/p25/kmm/KMMFrame.cpp b/src/common/p25/kmm/KMMFrame.cpp index dbd9a586..73601197 100644 --- a/src/common/p25/kmm/KMMFrame.cpp +++ b/src/common/p25/kmm/KMMFrame.cpp @@ -46,6 +46,13 @@ KMMFrame::KMMFrame() : KMMFrame::~KMMFrame() = default; +/* Returns a string that represents the current KMM frame. */ + +std::string KMMFrame::toString() +{ + return std::string("KMM, UNKNOWN (Unknown KMM)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMFrame.h b/src/common/p25/kmm/KMMFrame.h index 5b758132..389ec522 100644 --- a/src/common/p25/kmm/KMMFrame.h +++ b/src/common/p25/kmm/KMMFrame.h @@ -84,6 +84,12 @@ namespace p25 */ virtual void encode(uint8_t* data) = 0; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + virtual std::string toString(); + public: // Common Data /** diff --git a/src/common/p25/kmm/KMMHello.cpp b/src/common/p25/kmm/KMMHello.cpp index e18a0139..2f8f0440 100644 --- a/src/common/p25/kmm/KMMHello.cpp +++ b/src/common/p25/kmm/KMMHello.cpp @@ -60,6 +60,13 @@ void KMMHello::encode(uint8_t* data) data[10U] = m_flag; // Hello Flag } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMHello::toString() +{ + return std::string("KMM, HELLO (Hello)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMHello.h b/src/common/p25/kmm/KMMHello.h index 251b18f8..ab44975d 100644 --- a/src/common/p25/kmm/KMMHello.h +++ b/src/common/p25/kmm/KMMHello.h @@ -67,6 +67,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + public: /** * @brief diff --git a/src/common/p25/kmm/KMMInventoryCommand.cpp b/src/common/p25/kmm/KMMInventoryCommand.cpp index c36aeb02..e1383bf0 100644 --- a/src/common/p25/kmm/KMMInventoryCommand.cpp +++ b/src/common/p25/kmm/KMMInventoryCommand.cpp @@ -60,6 +60,13 @@ void KMMInventoryCommand::encode(uint8_t* data) data[10U] = m_inventoryType; // Inventory Type } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMInventoryCommand::toString() +{ + return std::string("KMM, INVENTORY_CMD (Inventory Command)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMInventoryCommand.h b/src/common/p25/kmm/KMMInventoryCommand.h index 81d1c3a1..b3825a11 100644 --- a/src/common/p25/kmm/KMMInventoryCommand.h +++ b/src/common/p25/kmm/KMMInventoryCommand.h @@ -67,6 +67,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + public: /** * @brief Inventory type. diff --git a/src/common/p25/kmm/KMMInventoryResponseHeader.cpp b/src/common/p25/kmm/KMMInventoryResponseHeader.cpp index 89bc8744..c02ac740 100644 --- a/src/common/p25/kmm/KMMInventoryResponseHeader.cpp +++ b/src/common/p25/kmm/KMMInventoryResponseHeader.cpp @@ -62,6 +62,13 @@ void KMMInventoryResponseHeader::encode(uint8_t* data) SET_UINT16(m_numberOfItems, data, 11U); // Number of Items } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMInventoryResponseHeader::toString() +{ + return std::string("KMM, INVENTORY_RSP (Inventory Response)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMInventoryResponseHeader.h b/src/common/p25/kmm/KMMInventoryResponseHeader.h index 816b60eb..8ff3f43c 100644 --- a/src/common/p25/kmm/KMMInventoryResponseHeader.h +++ b/src/common/p25/kmm/KMMInventoryResponseHeader.h @@ -67,6 +67,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + virtual std::string toString() override; + public: /** * @brief Inventory type. diff --git a/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.cpp b/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.cpp index 38ea812f..e0201d8f 100644 --- a/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.cpp +++ b/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.cpp @@ -97,6 +97,13 @@ void KMMInventoryResponseListKeyIDs::encode(uint8_t* data) } } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMInventoryResponseListKeyIDs::toString() +{ + return std::string("KMM, INVENTORY_RSP (Inventory Response, Active Key IDs)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.h b/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.h index 0ab5ea9e..db012c39 100644 --- a/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.h +++ b/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.h @@ -61,6 +61,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + public: /** * @brief Encryption keyset ID. diff --git a/src/common/p25/kmm/KMMInventoryResponseListKeysets.cpp b/src/common/p25/kmm/KMMInventoryResponseListKeysets.cpp index 9844937d..aa52c74f 100644 --- a/src/common/p25/kmm/KMMInventoryResponseListKeysets.cpp +++ b/src/common/p25/kmm/KMMInventoryResponseListKeysets.cpp @@ -79,6 +79,13 @@ void KMMInventoryResponseListKeysets::encode(uint8_t* data) } } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMInventoryResponseListKeysets::toString() +{ + return std::string("KMM, INVENTORY_RSP (Inventory Response, Active Keyset IDs)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMInventoryResponseListKeysets.h b/src/common/p25/kmm/KMMInventoryResponseListKeysets.h index a04f06d2..617752a3 100644 --- a/src/common/p25/kmm/KMMInventoryResponseListKeysets.h +++ b/src/common/p25/kmm/KMMInventoryResponseListKeysets.h @@ -61,6 +61,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + public: /** * @brief List of keyset IDs. diff --git a/src/common/p25/kmm/KMMModifyKey.cpp b/src/common/p25/kmm/KMMModifyKey.cpp index cefb1434..a43a2d62 100644 --- a/src/common/p25/kmm/KMMModifyKey.cpp +++ b/src/common/p25/kmm/KMMModifyKey.cpp @@ -155,6 +155,13 @@ void KMMModifyKey::encode(uint8_t* data) } } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMModifyKey::toString() +{ + return std::string("KMM, MODIFY_KEY_CMD (Modify Key)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMModifyKey.h b/src/common/p25/kmm/KMMModifyKey.h index 86fc9501..10af6282 100644 --- a/src/common/p25/kmm/KMMModifyKey.h +++ b/src/common/p25/kmm/KMMModifyKey.h @@ -74,6 +74,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + /** @name Encryption data */ /** * @brief Sets the encryption message indicator. diff --git a/src/common/p25/kmm/KMMNegativeAck.cpp b/src/common/p25/kmm/KMMNegativeAck.cpp index c3c45ce0..39a8df7b 100644 --- a/src/common/p25/kmm/KMMNegativeAck.cpp +++ b/src/common/p25/kmm/KMMNegativeAck.cpp @@ -66,6 +66,13 @@ void KMMNegativeAck::encode(uint8_t* data) data[13U] = m_status; } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMNegativeAck::toString() +{ + return std::string("KMM, NAK (Negative Acknowledge)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMNegativeAck.h b/src/common/p25/kmm/KMMNegativeAck.h index bf3669ff..a4750be8 100644 --- a/src/common/p25/kmm/KMMNegativeAck.h +++ b/src/common/p25/kmm/KMMNegativeAck.h @@ -67,6 +67,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + public: /** * @brief diff --git a/src/common/p25/kmm/KMMNoService.cpp b/src/common/p25/kmm/KMMNoService.cpp index d231d283..feca70bf 100644 --- a/src/common/p25/kmm/KMMNoService.cpp +++ b/src/common/p25/kmm/KMMNoService.cpp @@ -55,6 +55,13 @@ void KMMNoService::encode(uint8_t* data) KMMFrame::encodeHeader(data); } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMNoService::toString() +{ + return std::string("KMM, NO_SERVICE (No Service)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMNoService.h b/src/common/p25/kmm/KMMNoService.h index 62d5540a..0637169c 100644 --- a/src/common/p25/kmm/KMMNoService.h +++ b/src/common/p25/kmm/KMMNoService.h @@ -67,6 +67,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + public: DECLARE_COPY(KMMNoService); }; diff --git a/src/common/p25/kmm/KMMRegistrationCommand.cpp b/src/common/p25/kmm/KMMRegistrationCommand.cpp index c45101ec..2eb50c87 100644 --- a/src/common/p25/kmm/KMMRegistrationCommand.cpp +++ b/src/common/p25/kmm/KMMRegistrationCommand.cpp @@ -64,6 +64,13 @@ void KMMRegistrationCommand::encode(uint8_t* data) SET_UINT24(m_kmfRSI, data, 11U); // KMF RSI } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMRegistrationCommand::toString() +{ + return std::string("KMM, REG_CMD (Registration Command)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMRegistrationCommand.h b/src/common/p25/kmm/KMMRegistrationCommand.h index 95a060fa..85d1fce3 100644 --- a/src/common/p25/kmm/KMMRegistrationCommand.h +++ b/src/common/p25/kmm/KMMRegistrationCommand.h @@ -67,6 +67,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + public: /** * @brief diff --git a/src/common/p25/kmm/KMMRegistrationResponse.cpp b/src/common/p25/kmm/KMMRegistrationResponse.cpp index eeba92ff..9f735af3 100644 --- a/src/common/p25/kmm/KMMRegistrationResponse.cpp +++ b/src/common/p25/kmm/KMMRegistrationResponse.cpp @@ -27,7 +27,7 @@ using namespace p25::kmm; KMMRegistrationResponse::KMMRegistrationResponse() : KMMFrame(), m_status(KMM_Status::CMD_PERFORMED) { - m_messageId = KMM_MessageType::DEREG_RSP; + m_messageId = KMM_MessageType::REG_RSP; m_respKind = KMM_ResponseKind::IMMEDIATE; } @@ -60,6 +60,13 @@ void KMMRegistrationResponse::encode(uint8_t* data) data[10U] = m_status; // Status } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMRegistrationResponse::toString() +{ + return std::string("KMM, REG_RSP (Registration Response)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMRegistrationResponse.h b/src/common/p25/kmm/KMMRegistrationResponse.h index 5dadbeb3..f70e5ac0 100644 --- a/src/common/p25/kmm/KMMRegistrationResponse.h +++ b/src/common/p25/kmm/KMMRegistrationResponse.h @@ -67,6 +67,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + public: /** * @brief diff --git a/src/common/p25/kmm/KMMZeroize.cpp b/src/common/p25/kmm/KMMZeroize.cpp index ac31089b..4d31b236 100644 --- a/src/common/p25/kmm/KMMZeroize.cpp +++ b/src/common/p25/kmm/KMMZeroize.cpp @@ -55,6 +55,13 @@ void KMMZeroize::encode(uint8_t* data) KMMFrame::encodeHeader(data); } +/* Returns a string that represents the current KMM frame. */ + +std::string KMMZeroize::toString() +{ + return std::string("KMM, ZEROIZE_CMD (Zeroize Command)"); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMZeroize.h b/src/common/p25/kmm/KMMZeroize.h index a405a69e..e4d18d13 100644 --- a/src/common/p25/kmm/KMMZeroize.h +++ b/src/common/p25/kmm/KMMZeroize.h @@ -67,6 +67,12 @@ namespace p25 */ void encode(uint8_t* data) override; + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + public: DECLARE_COPY(KMMZeroize); }; diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 247690fd..632e5a26 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -540,6 +540,7 @@ bool HostFNE::createMasterNetwork() std::string password = masterConf["password"].as(); bool verbose = masterConf["verbose"].as(false); bool debug = masterConf["debug"].as(false); + bool kmfDebug = masterConf["kmfDebug"].as(false); uint16_t workerCnt = (uint16_t)masterConf["workers"].as(16U); // clamp worker thread count properly @@ -626,8 +627,12 @@ bool HostFNE::createMasterNetwork() LogInfo(" Debug: yes"); } + if (kmfDebug) { + LogInfo(" P25 OTAR KMF Services Debug: yes"); + } + // initialize networking - m_network = new FNENetwork(this, address, port, id, password, debug, verbose, reportPeerPing, m_dmrEnabled, m_p25Enabled, m_nxdnEnabled, m_analogEnabled, + m_network = new FNENetwork(this, address, port, id, password, debug, kmfDebug, verbose, reportPeerPing, m_dmrEnabled, m_p25Enabled, m_nxdnEnabled, m_analogEnabled, parrotDelay, parrotGrantDemand, m_allowActivityTransfer, m_allowDiagnosticTransfer, m_pingTime, m_updateLookupTime, workerCnt); m_network->setOptions(masterConf, true); diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index c4639b02..74fdcde5 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -58,8 +58,9 @@ std::timed_mutex FNENetwork::m_keyQueueMutex; /* Initializes a new instance of the FNENetwork class. */ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, uint32_t peerId, const std::string& password, - bool debug, bool verbose, bool reportPeerPing, bool dmr, bool p25, bool nxdn, bool analog, uint32_t parrotDelay, bool parrotGrantDemand, - bool allowActivityTransfer, bool allowDiagnosticTransfer, uint32_t pingTime, uint32_t updateLookupTime, uint16_t workerCnt) : + bool debug, bool kmfDebug, bool verbose, bool reportPeerPing, bool dmr, bool p25, bool nxdn, bool analog, + uint32_t parrotDelay, bool parrotGrantDemand, bool allowActivityTransfer, bool allowDiagnosticTransfer, + uint32_t pingTime, uint32_t updateLookupTime, uint16_t workerCnt) : BaseNetwork(peerId, true, debug, true, true, allowActivityTransfer, allowDiagnosticTransfer), m_tagDMR(nullptr), m_tagP25(nullptr), @@ -134,7 +135,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_tagNXDN = new TagNXDNData(this, debug); m_tagAnalog = new TagAnalogData(this, debug); - m_p25OTARService = new P25OTARService(this, m_tagP25->packetData(), debug); + m_p25OTARService = new P25OTARService(this, m_tagP25->packetData(), kmfDebug, verbose); } /* Finalizes a instance of the FNENetwork class. */ diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 3c874637..d3ae0b1e 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -420,6 +420,7 @@ namespace network * @param peerId Unique ID on the network. * @param password Network authentication password. * @param debug Flag indicating whether network debug is enabled. + * @param kmfDebug Flag indicating whether P25 OTAR KMF services debug is enabled. * @param verbose Flag indicating whether network verbose logging is enabled. * @param reportPeerPing Flag indicating whether peer pinging is reported. * @param dmr Flag indicating whether DMR is enabled. @@ -435,8 +436,9 @@ namespace network * @param workerCnt Number of worker threads. */ FNENetwork(HostFNE* host, const std::string& address, uint16_t port, uint32_t peerId, const std::string& password, - bool debug, bool verbose, bool reportPeerPing, bool dmr, bool p25, bool nxdn, bool analog, uint32_t parrotDelay, bool parrotGrantDemand, - bool allowActivityTransfer, bool allowDiagnosticTransfer, uint32_t pingTime, uint32_t updateLookupTime, uint16_t workerCnt); + bool debug, bool kmfDebug, bool verbose, bool reportPeerPing, bool dmr, bool p25, bool nxdn, bool analog, + uint32_t parrotDelay, bool parrotGrantDemand, bool allowActivityTransfer, bool allowDiagnosticTransfer, + uint32_t pingTime, uint32_t updateLookupTime, uint16_t workerCnt); /** * @brief Finalizes a instance of the FNENetwork class. */ diff --git a/src/fne/network/P25OTARService.cpp b/src/fne/network/P25OTARService.cpp index 87150361..ecd57273 100644 --- a/src/fne/network/P25OTARService.cpp +++ b/src/fne/network/P25OTARService.cpp @@ -29,6 +29,16 @@ using namespace p25::kmm; #include #include +// --------------------------------------------------------------------------- +// Macros +// --------------------------------------------------------------------------- + +// Macro helper to verbose log a generic KMM. +#define VERBOSE_LOG_KMM(_PCKT_STR, __LLID) \ + if (m_verbose) { \ + LogMessage(LOG_NET, "KMM, %s, llId = %u", _PCKT_STR.c_str(), __LLID); \ + } + // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- @@ -41,13 +51,14 @@ using namespace p25::kmm; /* Initializes a new instance of the P25OTARService class. */ -P25OTARService::P25OTARService(FNENetwork* network, P25PacketData* packetData, bool debug) : +P25OTARService::P25OTARService(FNENetwork* network, P25PacketData* packetData, bool debug, bool verbose) : m_socket(nullptr), m_frameQueue(nullptr), m_threadPool(MAX_THREAD_CNT, "otar"), m_network(network), m_packetData(packetData), - m_debug(debug) + m_debug(debug), + m_verbose(verbose) { assert(network != nullptr); assert(packetData != nullptr); @@ -92,7 +103,7 @@ bool P25OTARService::processDLD(const uint8_t* data, uint32_t len, uint32_t llId // re-encrypt the KMM response pduUserData = cryptKMM(algoId, kid, mi, pduUserData.get(), payloadSize, true); if (pduUserData == nullptr) { - LogError(LOG_P25, "OTAR KMM, unable to encrypt KMM response, algoId = $%02X, kID = $%04X", algoId, kid); + LogError(LOG_P25, P25_KMM_STR ", unable to encrypt KMM response, algoId = $%02X, kID = $%04X", algoId, kid); return false; } } @@ -128,7 +139,7 @@ void P25OTARService::clock(uint32_t ms) // enqueue the task if (!m_threadPool.enqueue(new_pooltask(taskNetworkRx, req))) { - LogError(LOG_NET, "Failed to task enqueue control network packet request, %s:%u", + LogError(LOG_P25, "Failed to task enqueue KMM network packet request, %s:%u", udp::Socket::address(address).c_str(), udp::Socket::port(address)); if (req != nullptr) { if (req->buffer != nullptr) @@ -211,7 +222,7 @@ void P25OTARService::taskNetworkRx(OTARPacketRequest* req) if (encrypted) { buffer = network->cryptKMM(algoId, kid, mi, buffer.get(), req->length - 13U, false); if (buffer == nullptr) { - LogError(LOG_P25, "OTAR KMM, unable to decrypt KMM, algoId = $%02X, kID = $%04X", algoId, kid); + LogError(LOG_P25, P25_KMM_STR ", unable to decrypt KMM, algoId = $%02X, kID = $%04X", algoId, kid); if (req->buffer != nullptr) delete[] req->buffer; delete req; @@ -226,7 +237,7 @@ void P25OTARService::taskNetworkRx(OTARPacketRequest* req) // re-encrypt the KMM response pduUserData = network->cryptKMM(algoId, kid, mi, pduUserData.get(), payloadSize, true); if (pduUserData == nullptr) { - LogError(LOG_P25, "OTAR KMM, unable to encrypt KMM response, algoId = $%02X, kID = $%04X", algoId, kid); + LogError(LOG_P25, P25_KMM_STR ", unable to encrypt KMM response, algoId = $%02X, kID = $%04X", algoId, kid); if (req->buffer != nullptr) delete[] req->buffer; delete req; @@ -280,7 +291,7 @@ UInt8Array P25OTARService::cryptKMM(uint8_t algoId, uint16_t kid, uint8_t* mi, c Utils::dump(1U, "P25OTARService::cryptKMM(), Key", key, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); } - LogMessage(LOG_P25, "OTAR KMM, algId = $%02X, kID = $%04X", algoId, kid); + LogMessage(LOG_P25, P25_KMM_STR ", algId = $%02X, kID = $%04X", algoId, kid); crypto.setKey(key, keyLength); crypto.generateKeystream(); @@ -323,7 +334,7 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ // decrypt frame before processing buffer = cryptKMM(algoId, kid, mi, data + 10U, len - 10U, false); if (buffer == nullptr) { - LogError(LOG_P25, "OTAR KMM, unable to decrypt KMM, algoId = $%02X, kID = $%04X", algoId, kid); + LogError(LOG_P25, P25_KMM_STR ", unable to decrypt KMM, algoId = $%02X, kID = $%04X", algoId, kid); return nullptr; } @@ -333,7 +344,7 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ std::unique_ptr frame = KMMFactory::create(buffer.get()); if (frame == nullptr) { - LogWarning(LOG_NET, P25_PDU_STR ", undecodable KMM packet"); + LogWarning(LOG_P25, P25_KMM_STR ", undecodable KMM packet"); return nullptr; } @@ -345,8 +356,10 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ case KMM_MessageType::HELLO: { KMMHello* kmm = static_cast(frame.get()); - LogMessage(LOG_NET, P25_PDU_STR ", KMM Hello, llId = %u, flag = $%02X", llId, - kmm->getFlag()); + if (m_verbose) { + LogMessage(LOG_P25, P25_KMM_STR ", %s, llId = %u, flag = $%02X", kmm->toString().c_str(), + llId, kmm->getFlag()); + } // respond with No-Service if KMF services are disabled // if (!m_network->m_kmfServicesEnabled) @@ -356,7 +369,7 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ default: break; - } // switch (packet->getPDUType()) + } // switch (frame->getMessageId()) return nullptr; } @@ -375,6 +388,11 @@ UInt8Array P25OTARService::write_KMM_NoService(uint32_t llId, uint32_t kmmRSI, u outKmm.setSrcLLId(WUID_FNE); outKmm.setDstLLId(kmmRSI); + if (m_verbose) { + LogMessage(LOG_P25, P25_KMM_STR ", %s, llId = %u, RSI = %u", outKmm.toString().c_str(), + outKmm.getSrcLLId(), outKmm.getDstLLId()); + } + outKmm.encode(buffer); ::memcpy(kmmFrame.get(), buffer, KMM_NO_SERVICE_LENGTH); diff --git a/src/fne/network/P25OTARService.h b/src/fne/network/P25OTARService.h index eb3636fd..49063a01 100644 --- a/src/fne/network/P25OTARService.h +++ b/src/fne/network/P25OTARService.h @@ -55,9 +55,10 @@ namespace network * @brief Initializes a new instance of the P25OTARService class. * @param network Instance of the FNENetwork class. * @param packetData Instance of the P25PacketData class. - * @param debug Flag indicating whether network debug is enabled. + * @param debug Flag indicating whether debug is enabled. + * @param verbose Flag indicating whether verbose logging is enabled. */ - P25OTARService(FNENetwork* network, network::callhandler::packetdata::P25PacketData* packetData, bool debug); + P25OTARService(FNENetwork* network, network::callhandler::packetdata::P25PacketData* packetData, bool debug, bool verbose); /** * @brief Finalizes a instance of the P25OTARService class. */ @@ -104,6 +105,7 @@ namespace network network::callhandler::packetdata::P25PacketData* m_packetData; bool m_debug; + bool m_verbose; /** * @brief Entry point to process a given network packet. From a36983f932537e57a2c649eed97e6f7a38309e6c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 19 Sep 2025 14:36:29 -0400 Subject: [PATCH 014/200] begin adding support for V.24 PDU data; --- src/common/p25/dfsi/DFSIDefines.h | 105 ++++++++++++--------- src/host/modem/ModemV24.cpp | 149 +++++++++++++++++++++++++++++- src/host/modem/ModemV24.h | 35 +++++++ 3 files changed, 243 insertions(+), 46 deletions(-) diff --git a/src/common/p25/dfsi/DFSIDefines.h b/src/common/p25/dfsi/DFSIDefines.h index 9630cd99..e8f2f80f 100644 --- a/src/common/p25/dfsi/DFSIDefines.h +++ b/src/common/p25/dfsi/DFSIDefines.h @@ -78,59 +78,76 @@ namespace p25 * @{ */ - const uint8_t DFSI_RTP_PAYLOAD_TYPE = 0x64U; //! - const uint8_t DFSI_RTP_MOT_PAYLOAD_TYPE = 0x5DU; //! + const uint8_t DFSI_RTP_PAYLOAD_TYPE = 0x64U; //! + const uint8_t DFSI_RTP_MOT_PAYLOAD_TYPE = 0x5DU; //! + const uint8_t DFSI_RTP_MOT_DATA_PAYLOAD_TYPE = 0x5EU; //! - const uint8_t DFSI_RTP_SEQ_HANDSHAKE = 0x00U; //! - const uint8_t DFSI_RTP_SEQ_STARTSTOP = 0x01U; //! + const uint8_t DFSI_RTP_SEQ_HANDSHAKE = 0x00U; //! + const uint8_t DFSI_RTP_SEQ_STARTSTOP = 0x01U; //! - const uint8_t DFSI_MOT_ICW_FMT_TYPE3 = 0x02U; //! + const uint8_t DFSI_MOT_ICW_FMT_TYPE3 = 0x02U; //! - const uint8_t DFSI_MOT_ICW_PARM_NOP = 0x00U; //! No Operation - const uint8_t DSFI_MOT_ICW_PARM_PAYLOAD = 0x0CU; //! Stream Payload - const uint8_t DFSI_MOT_ICW_PARM_RSSI1 = 0x1AU; //! RSSI Data - const uint8_t DFSI_MOT_ICW_PARM_RSSI2 = 0x1BU; //! RSSI Data - const uint8_t DFSI_MOT_ICW_PARM_STOP = 0x25U; //! Stop Stream - const uint8_t DFSI_MOT_ICW_TX_ADDRESS = 0x2CU; //! Tx Device Address - const uint8_t DFSI_MOT_ICW_RX_ADDRESS = 0x35U; //! Rx Device Address + const uint8_t DFSI_MOT_ICW_PARM_NOP = 0x00U; //! No Operation + const uint8_t DSFI_MOT_ICW_PARM_PAYLOAD = 0x0CU; //! Stream Payload + const uint8_t DFSI_MOT_ICW_PARM_RSSI1 = 0x1AU; //! RSSI Data + const uint8_t DFSI_MOT_ICW_PARM_RSSI2 = 0x1BU; //! RSSI Data + const uint8_t DFSI_MOT_ICW_PARM_STOP = 0x25U; //! Stop Stream + const uint8_t DFSI_MOT_ICW_TX_ADDRESS = 0x2CU; //! Tx Device Address + const uint8_t DFSI_MOT_ICW_RX_ADDRESS = 0x35U; //! Rx Device Address - const uint8_t DFSI_BUSY_BITS_TALKAROUND = 0x00U; //! Talkaround - const uint8_t DFSI_BUSY_BITS_BUSY = 0x01U; //! Busy - const uint8_t DFSI_BUSY_BITS_INBOUND = 0x02U; //! Inbound - const uint8_t DFSI_BUSY_BITS_IDLE = 0x03U; //! Idle + const uint8_t DFSI_BUSY_BITS_TALKAROUND = 0x00U; //! Talkaround + const uint8_t DFSI_BUSY_BITS_BUSY = 0x01U; //! Busy + const uint8_t DFSI_BUSY_BITS_INBOUND = 0x02U; //! Inbound + const uint8_t DFSI_BUSY_BITS_IDLE = 0x03U; //! Idle /** @brief DFSI Frame Type */ namespace DFSIFrameType { /** @brief DFSI Frame Type */ enum E : uint8_t { - MOT_START_STOP = 0x00U, // Motorola/V.24 Start/Stop Stream - - MOT_VHDR_1 = 0x60U, // Motorola/V.24 Voice Header 1 - MOT_VHDR_2 = 0x61U, // Motorola/V.24 Voice Header 2 - - LDU1_VOICE1 = 0x62U, // IMBE LDU1 - Voice 1 - LDU1_VOICE2 = 0x63U, // IMBE LDU1 - Voice 2 - LDU1_VOICE3 = 0x64U, // IMBE LDU1 - Voice 3 + Link Control - LDU1_VOICE4 = 0x65U, // IMBE LDU1 - Voice 4 + Link Control - LDU1_VOICE5 = 0x66U, // IMBE LDU1 - Voice 5 + Link Control - LDU1_VOICE6 = 0x67U, // IMBE LDU1 - Voice 6 + Link Control - LDU1_VOICE7 = 0x68U, // IMBE LDU1 - Voice 7 + Link Control - LDU1_VOICE8 = 0x69U, // IMBE LDU1 - Voice 8 + Link Control - LDU1_VOICE9 = 0x6AU, // IMBE LDU1 - Voice 9 + Low Speed Data - - LDU2_VOICE10 = 0x6BU, // IMBE LDU2 - Voice 10 - LDU2_VOICE11 = 0x6CU, // IMBE LDU2 - Voice 11 - LDU2_VOICE12 = 0x6DU, // IMBE LDU2 - Voice 12 + Encryption Sync - LDU2_VOICE13 = 0x6EU, // IMBE LDU2 - Voice 13 + Encryption Sync - LDU2_VOICE14 = 0x6FU, // IMBE LDU2 - Voice 14 + Encryption Sync - LDU2_VOICE15 = 0x70U, // IMBE LDU2 - Voice 15 + Encryption Sync - LDU2_VOICE16 = 0x71U, // IMBE LDU2 - Voice 16 + Encryption Sync - LDU2_VOICE17 = 0x72U, // IMBE LDU2 - Voice 17 + Encryption Sync - LDU2_VOICE18 = 0x73U, // IMBE LDU2 - Voice 18 + Low Speed Data - - MOT_TDULC = 0x74U, // Motorola/V.24 TDULC - MOT_PDU_SINGLE = 0x87U, // Motorola/V.24 PDU (Single Block) - MOT_TSBK = 0xA1U // Motorola/V.24 TSBK (Single Block) + MOT_START_STOP = 0x00U, // Motorola/V.24 Start/Stop Stream + + MOT_VHDR_1 = 0x60U, // Motorola/V.24 Voice Header 1 + MOT_VHDR_2 = 0x61U, // Motorola/V.24 Voice Header 2 + + LDU1_VOICE1 = 0x62U, // IMBE LDU1 - Voice 1 + LDU1_VOICE2 = 0x63U, // IMBE LDU1 - Voice 2 + LDU1_VOICE3 = 0x64U, // IMBE LDU1 - Voice 3 + Link Control + LDU1_VOICE4 = 0x65U, // IMBE LDU1 - Voice 4 + Link Control + LDU1_VOICE5 = 0x66U, // IMBE LDU1 - Voice 5 + Link Control + LDU1_VOICE6 = 0x67U, // IMBE LDU1 - Voice 6 + Link Control + LDU1_VOICE7 = 0x68U, // IMBE LDU1 - Voice 7 + Link Control + LDU1_VOICE8 = 0x69U, // IMBE LDU1 - Voice 8 + Link Control + LDU1_VOICE9 = 0x6AU, // IMBE LDU1 - Voice 9 + Low Speed Data + + LDU2_VOICE10 = 0x6BU, // IMBE LDU2 - Voice 10 + LDU2_VOICE11 = 0x6CU, // IMBE LDU2 - Voice 11 + LDU2_VOICE12 = 0x6DU, // IMBE LDU2 - Voice 12 + Encryption Sync + LDU2_VOICE13 = 0x6EU, // IMBE LDU2 - Voice 13 + Encryption Sync + LDU2_VOICE14 = 0x6FU, // IMBE LDU2 - Voice 14 + Encryption Sync + LDU2_VOICE15 = 0x70U, // IMBE LDU2 - Voice 15 + Encryption Sync + LDU2_VOICE16 = 0x71U, // IMBE LDU2 - Voice 16 + Encryption Sync + LDU2_VOICE17 = 0x72U, // IMBE LDU2 - Voice 17 + Encryption Sync + LDU2_VOICE18 = 0x73U, // IMBE LDU2 - Voice 18 + Low Speed Data + + MOT_TDULC = 0x74U, // Motorola/V.24 TDULC + + MOT_PDU_UNCONF_HEADER = 0x80U, // Motorola/V.24 PDU (Unconfirmed Block Header) + MOT_PDU_UNCONF_BLOCK_1 = 0x81U, // Motorola/V.24 PDU (Unconfirmed Block 1) + MOT_PDU_UNCONF_BLOCK_2 = 0x82U, // Motorola/V.24 PDU (Unconfirmed Block 2) + MOT_PDU_UNCONF_BLOCK_3 = 0x83U, // Motorola/V.24 PDU (Unconfirmed Block 3) + MOT_PDU_UNCONF_BLOCK_4 = 0x84U, // Motorola/V.24 PDU (Unconfirmed Block 4) + MOT_PDU_UNCONF_END = 0x85U, // Motorola/V.24 PDU (Unconfirmed Block End) + MOT_PDU_SINGLE_UNCONF = 0x87U, // Motorola/V.24 PDU (Single Unconfirmed Block) + + MOD_PDU_CONF_HEADER = 0x88U, // Motorola/V.24 PDU (Confirmed Block Header) + MOT_PDU_CONF_BLOCK_1 = 0x89U, // Motorola/V.24 PDU (Confirmed Block 1) + MOT_PDU_CONF_BLOCK_2 = 0x8AU, // Motorola/V.24 PDU (Confirmed Block 2) + MOT_PDU_CONF_BLOCK_3 = 0x8BU, // Motorola/V.24 PDU (Confirmed Block 3) + MOT_PDU_CONF_BLOCK_4 = 0x8CU, // Motorola/V.24 PDU (Confirmed Block 4) + MOT_PDU_CONF_END = 0x8DU, // Motorola/V.24 PDU (Confirmed Block End) + MOT_PDU_SINGLE_CONF = 0x8FU, // Motorola/V.24 PDU (Single Confirmed Block) + + MOT_TSBK = 0xA1U // Motorola/V.24 TSBK (Single Block) }; } diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index 5baf0d4d..8c8a7243 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -551,6 +551,77 @@ void ModemV24::storeConvertedRx(const uint8_t* buffer, uint32_t length) m_rxP25Queue.addData(buffer, length); } +/* Internal helper to store converted PDU Rx frames. */ + +void ModemV24::storeConvertedRxPDU(data::DataHeader& dataHeader, uint8_t* pduUserData) +{ + assert(pduUserData != nullptr); + + uint32_t bitLength = ((dataHeader.getBlocksToFollow() + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; + if (dataHeader.getPadLength() > 0U) + bitLength += (dataHeader.getPadLength() * 8U); + + uint32_t offset = P25_PREAMBLE_LENGTH_BITS; + + DECLARE_UINT8_ARRAY(pdu, (bitLength / 8U) + 1U); + + uint8_t block[P25_PDU_FEC_LENGTH_BYTES]; + ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + + uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); + + // generate the PDU header and 1/2 rate Trellis + dataHeader.encode(block); + Utils::setBitRange(block, pdu, offset, P25_PDU_FEC_LENGTH_BITS); + offset += P25_PDU_FEC_LENGTH_BITS; + + if (blocksToFollow > 0U) { + uint32_t dataOffset = 0U; + uint32_t packetLength = dataHeader.getPDULength(); + + // generate the PDU data + for (uint32_t i = 0U; i < blocksToFollow; i++) { + data::DataBlock dataBlock = data::DataBlock(); + dataBlock.setFormat(dataHeader); + dataBlock.setSerialNo(i); + dataBlock.setData(pduUserData + dataOffset); + dataBlock.setLastBlock((i + 1U) == blocksToFollow); + + ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + dataBlock.encode(block); + Utils::setBitRange(block, pdu, offset, P25_PDU_FEC_LENGTH_BITS); + + offset += P25_PDU_FEC_LENGTH_BITS; + dataOffset += (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; + } + } + + uint8_t data[P25_PDU_FRAME_LENGTH_BYTES + 2U]; + ::memset(data, 0x00U, P25_PDU_FRAME_LENGTH_BYTES + 2U); + + // add the data + uint32_t newBitLength = P25Utils::encodeByLength(pdu, data + 2U, bitLength); + uint32_t newByteLength = newBitLength / 8U; + if ((newBitLength % 8U) > 0U) + newByteLength++; + + // generate Sync + Sync::addP25Sync(data + 2U); + + // generate NID + m_nid->encode(data + 2U, DUID::PDU); + + // add status bits + P25Utils::addStatusBits(data + 2U, newBitLength, true, true); + P25Utils::setStatusBitsStartIdle(data + 2U); + + //Utils::dump("P25, Data::writeRF_PDU(), Raw PDU OSP", data, newByteLength + 2U); + + data[0U] = modem::TAG_DATA; + data[1U] = 0x00U; + storeConvertedRx(data, newByteLength + 2U); +} + /* Helper to generate a P25 TDU packet. */ void ModemV24::create_TDU(uint8_t* buffer) @@ -869,8 +940,82 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) } break; - case DFSIFrameType::MOT_PDU_SINGLE: - break; + case DFSIFrameType::MOT_PDU_SINGLE_UNCONF: + { + m_rxCall->resetCallData(); + + uint8_t header[P25_PDU_HEADER_LENGTH_BYTES]; + ::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); + + ::memcpy(header, dfsiData + 1U, P25_PDU_HEADER_LENGTH_BYTES); + data::DataHeader pduHeader = data::DataHeader(); + bool ret = pduHeader.decode(header, true); + if (!ret) { + LogWarning(LOG_MODEM, P25_PDU_STR ", unfixable RF 1/2 rate header data"); + Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); + break; + } + + if (m_debug) { + ::LogDebugEx(LOG_MODEM, "ModemV24::convertToAirV24()", "V.24 RX, PDU ISP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", + pduHeader.getAckNeeded(), pduHeader.getOutbound(), pduHeader.getFormat(), pduHeader.getMFId(), pduHeader.getSAP(), pduHeader.getFullMessage(), + pduHeader.getBlocksToFollow(), pduHeader.getPadLength(), pduHeader.getPacketLength(), pduHeader.getSynchronize(), pduHeader.getNs(), pduHeader.getFSN(), pduHeader.getLastFragment(), + pduHeader.getHeaderOffset(), pduHeader.getLLId()); + } + + m_rxCall->dataCall = true; + m_rxCall->dataHeader = pduHeader; + + for (uint8_t i = 0U; i < pduHeader.getBlocksToFollow() + 1U; i++) { + uint8_t dataBlock[P25_PDU_UNCONFIRMED_LENGTH_BYTES]; + ::memset(dataBlock, 0x00U, P25_PDU_UNCONFIRMED_LENGTH_BYTES); + ::memcpy(dataBlock, dfsiData + 1U + P25_PDU_HEADER_LENGTH_BYTES + (i * P25_PDU_UNCONFIRMED_LENGTH_BYTES), P25_PDU_UNCONFIRMED_LENGTH_BYTES); + + uint32_t offset = i * P25_PDU_UNCONFIRMED_LENGTH_BYTES; + ::memcpy(m_rxCall->pduUserData + offset, dataBlock, P25_PDU_UNCONFIRMED_LENGTH_BYTES); + } + + storeConvertedRxPDU(pduHeader, m_rxCall->pduUserData); + } + break; + case DFSIFrameType::MOT_PDU_SINGLE_CONF: + { + m_rxCall->resetCallData(); + + uint8_t header[P25_PDU_HEADER_LENGTH_BYTES]; + ::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); + + ::memcpy(header, dfsiData + 1U, P25_PDU_HEADER_LENGTH_BYTES); + data::DataHeader pduHeader = data::DataHeader(); + bool ret = pduHeader.decode(header, true); + if (!ret) { + LogWarning(LOG_MODEM, P25_PDU_STR ", unfixable RF 1/2 rate header data"); + Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); + break; + } + + if (m_debug) { + ::LogDebugEx(LOG_MODEM, "ModemV24::convertToAirV24()", "V.24 RX, PDU ISP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", + pduHeader.getAckNeeded(), pduHeader.getOutbound(), pduHeader.getFormat(), pduHeader.getMFId(), pduHeader.getSAP(), pduHeader.getFullMessage(), + pduHeader.getBlocksToFollow(), pduHeader.getPadLength(), pduHeader.getPacketLength(), pduHeader.getSynchronize(), pduHeader.getNs(), pduHeader.getFSN(), pduHeader.getLastFragment(), + pduHeader.getHeaderOffset(), pduHeader.getLLId()); + } + + m_rxCall->dataCall = true; + m_rxCall->dataHeader = pduHeader; + + for (uint8_t i = 0U; i < pduHeader.getBlocksToFollow() + 1U; i++) { + uint8_t dataBlock[P25_PDU_CONFIRMED_LENGTH_BYTES]; + ::memset(dataBlock, 0x00U, P25_PDU_CONFIRMED_LENGTH_BYTES); + ::memcpy(dataBlock, dfsiData + 1U + P25_PDU_HEADER_LENGTH_BYTES + (i * P25_PDU_CONFIRMED_LENGTH_BYTES), P25_PDU_CONFIRMED_LENGTH_BYTES); + + uint32_t offset = i * P25_PDU_CONFIRMED_LENGTH_BYTES; + ::memcpy(m_rxCall->pduUserData + offset, dataBlock, P25_PDU_CONFIRMED_LENGTH_BYTES); + } + + storeConvertedRxPDU(pduHeader, m_rxCall->pduUserData); + } + break; case DFSIFrameType::MOT_TSBK: { diff --git a/src/host/modem/ModemV24.h b/src/host/modem/ModemV24.h index 8c05bb19..a26be224 100644 --- a/src/host/modem/ModemV24.h +++ b/src/host/modem/ModemV24.h @@ -18,6 +18,8 @@ #include "Defines.h" #include "common/edac/RS634717.h" +#include "common/p25/data/DataHeader.h" +#include "common/p25/data/DataBlock.h" #include "common/p25/lc/LC.h" #include "common/p25/Audio.h" #include "common/p25/NID.h" @@ -79,6 +81,9 @@ namespace modem VHDR2(nullptr), netLDU1(nullptr), netLDU2(nullptr), + pduUserData(nullptr), + dataHeader(), + dataCall(false), errors(0U) { MI = new uint8_t[P25DEF::MI_LENGTH_BYTES]; @@ -92,6 +97,9 @@ namespace modem ::memset(netLDU1, 0x00U, 9U * 25U); ::memset(netLDU2, 0x00U, 9U * 25U); + pduUserData = new uint8_t[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + ::memset(pduUserData, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); + resetCallData(); } /** @@ -111,6 +119,8 @@ namespace modem delete[] netLDU1; if (netLDU2 != nullptr) delete[] netLDU2; + if (pduUserData != nullptr) + delete[] pduUserData; } /** @@ -149,6 +159,11 @@ namespace modem n = 0U; seqNo = 0U; + if (pduUserData != nullptr) + ::memset(pduUserData, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); + dataHeader.reset(); + dataCall = false; + errors = 0U; } @@ -230,6 +245,20 @@ namespace modem */ uint8_t* netLDU2; + /** + * @brief User data associated with this call. + */ + uint8_t* pduUserData; + /** + * @brief Data call header. + */ + data::DataHeader dataHeader; + + /** + * @brief Flag indicating the current call is a data call. + */ + bool dataCall; + /** * @brief Total errors for a given call sequence. */ @@ -556,6 +585,12 @@ namespace modem * @param length Length of buffer. */ void storeConvertedRx(const uint8_t* buffer, uint32_t length); + /** + * @brief Internal helper to store converted PDU Rx frames. + * @param dataHeader Instance of a PDU data header. + * @param pduUserData Buffer containing user data to transmit. + */ + void storeConvertedRxPDU(p25::data::DataHeader& dataHeader, uint8_t* pduUserData); /** * @brief Helper to generate a P25 TDU packet. * @param buffer Buffer to create TDU. From 14181e6089d979366db40e21e5b4547ea7a5ded9 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 19 Sep 2025 14:42:30 -0400 Subject: [PATCH 015/200] fix C++ namespace shenanigans; --- src/host/modem/ModemV24.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/host/modem/ModemV24.h b/src/host/modem/ModemV24.h index a26be224..fbd02484 100644 --- a/src/host/modem/ModemV24.h +++ b/src/host/modem/ModemV24.h @@ -97,8 +97,8 @@ namespace modem ::memset(netLDU1, 0x00U, 9U * 25U); ::memset(netLDU2, 0x00U, 9U * 25U); - pduUserData = new uint8_t[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; - ::memset(pduUserData, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); + pduUserData = new uint8_t[P25DEF::P25_MAX_PDU_BLOCKS * P25DEF::P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + ::memset(pduUserData, 0x00U, P25DEF::P25_MAX_PDU_BLOCKS * P25DEF::P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); resetCallData(); } @@ -160,7 +160,7 @@ namespace modem seqNo = 0U; if (pduUserData != nullptr) - ::memset(pduUserData, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); + ::memset(pduUserData, 0x00U, P25DEF::P25_MAX_PDU_BLOCKS * P25DEF::P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); dataHeader.reset(); dataCall = false; @@ -252,7 +252,7 @@ namespace modem /** * @brief Data call header. */ - data::DataHeader dataHeader; + p25::data::DataHeader dataHeader; /** * @brief Flag indicating the current call is a data call. From f061cc82be151b83dee28700c125edabf16abf98 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 19 Sep 2025 14:51:25 -0400 Subject: [PATCH 016/200] fix documentation error; --- src/fne/CryptoContainer.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fne/CryptoContainer.h b/src/fne/CryptoContainer.h index d72a0256..705651e7 100644 --- a/src/fne/CryptoContainer.h +++ b/src/fne/CryptoContainer.h @@ -157,9 +157,9 @@ class HOST_SW_API KeyItem { // --------------------------------------------------------------------------- /** - * @brief Implements a threading lookup table class that contains routing - * rules information. - * @ingroup lookups_tgid + * @brief Implements a threading lookup class that contains encryption key container + * information from KFDtool EKC files. + * @ingroup fne */ class HOST_SW_API CryptoContainer : public Thread { public: From db857558274003b3e990d7d6e20033f07d918a58 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 19 Sep 2025 20:35:56 -0400 Subject: [PATCH 017/200] add support for outgoing V.24 PDU data; --- src/common/p25/data/DataBlock.cpp | 42 +++++++---- src/common/p25/data/DataBlock.h | 8 +- src/host/modem/ModemV24.cpp | 121 +++++++++++++++++++++++++++++- src/host/modem/ModemV24.h | 7 ++ 4 files changed, 161 insertions(+), 17 deletions(-) diff --git a/src/common/p25/data/DataBlock.cpp b/src/common/p25/data/DataBlock.cpp index e750ef32..0ba28984 100644 --- a/src/common/p25/data/DataBlock.cpp +++ b/src/common/p25/data/DataBlock.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2018-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2018-2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" @@ -48,7 +48,7 @@ DataBlock::~DataBlock() /* Decodes P25 PDU data block. */ -bool DataBlock::decode(const uint8_t* data, const DataHeader& header) +bool DataBlock::decode(const uint8_t* data, const DataHeader& header, bool noTrellis) { assert(data != nullptr); assert(m_data != nullptr); @@ -66,10 +66,14 @@ bool DataBlock::decode(const uint8_t* data, const DataHeader& header) if (m_fmt == PDUFormatType::CONFIRMED) { // decode 3/4 rate Trellis try { - bool valid = m_trellis.decode34(data, buffer); - if (!valid) { - LogError(LOG_P25, "DataBlock::decode(), failed to decode Trellis 3/4 rate coding"); - return false; + if (!noTrellis) { + bool valid = m_trellis.decode34(data, buffer); + if (!valid) { + LogError(LOG_P25, "DataBlock::decode(), failed to decode Trellis 3/4 rate coding"); + return false; + } + } else { + ::memcpy(buffer, data, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); } #if DEBUG_P25_PDU_DATA @@ -113,10 +117,14 @@ bool DataBlock::decode(const uint8_t* data, const DataHeader& header) else if ((m_fmt == PDUFormatType::UNCONFIRMED) || (m_fmt == PDUFormatType::RSP) || (m_fmt == PDUFormatType::AMBT)) { // decode 1/2 rate Trellis try { - bool valid = m_trellis.decode12(data, buffer); - if (!valid) { - LogError(LOG_P25, "DataBlock::decode(), failed to decode Trellis 1/2 rate coding"); - return false; + if (!noTrellis) { + bool valid = m_trellis.decode12(data, buffer); + if (!valid) { + LogError(LOG_P25, "DataBlock::decode(), failed to decode Trellis 1/2 rate coding"); + return false; + } + } else { + ::memcpy(buffer, data, P25_PDU_UNCONFIRMED_LENGTH_BYTES); } ::memset(m_data, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); @@ -141,7 +149,7 @@ bool DataBlock::decode(const uint8_t* data, const DataHeader& header) /* Encodes a P25 PDU data block. */ -void DataBlock::encode(uint8_t* data) +void DataBlock::encode(uint8_t* data, bool noTrellis) { assert(data != nullptr); assert(m_data != nullptr); @@ -175,7 +183,11 @@ void DataBlock::encode(uint8_t* data) Utils::dump(1U, "P25, DataBlock::encode(), Confirmed PDU Data Block", buffer, P25_PDU_CONFIRMED_LENGTH_BYTES); #endif - m_trellis.encode34(buffer, data); + if (!noTrellis) { + m_trellis.encode34(buffer, data); + } else { + ::memcpy(data, buffer, P25_PDU_CONFIRMED_LENGTH_BYTES); + } } else if (m_fmt == PDUFormatType::UNCONFIRMED || m_fmt == PDUFormatType::RSP || m_fmt == PDUFormatType::AMBT) { uint8_t buffer[P25_PDU_UNCONFIRMED_LENGTH_BYTES]; @@ -187,7 +199,11 @@ void DataBlock::encode(uint8_t* data) Utils::dump(1U, "P25, DataBlock::encode(), Unconfirmed PDU Data Block", buffer, P25_PDU_UNCONFIRMED_LENGTH_BYTES); #endif - m_trellis.encode12(buffer, data); + if (!noTrellis) { + m_trellis.encode12(buffer, data); + } else { + ::memcpy(data, buffer, P25_PDU_UNCONFIRMED_LENGTH_BYTES); + } } else { LogError(LOG_P25, "unknown FMT value in PDU, fmt = $%02X", m_fmt); diff --git a/src/common/p25/data/DataBlock.h b/src/common/p25/data/DataBlock.h index 303e8b0b..31589779 100644 --- a/src/common/p25/data/DataBlock.h +++ b/src/common/p25/data/DataBlock.h @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2018-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2018-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -49,14 +49,16 @@ namespace p25 * @brief Decodes P25 PDU data block. * @param[in] data Buffer containing a PDU data block to decode. * @param header P25 PDU data header. + * @param noTrellis Flag indicating not to perform Trellis encoding. * @returns bool True, if PDU data block decoded, otherwise false. */ - bool decode(const uint8_t* data, const DataHeader& header); + bool decode(const uint8_t* data, const DataHeader& header, bool noTrellis = false); /** * @brief Encodes a P25 PDU data block. * @param[out] data Buffer to encode a PDU data block. + * @param noTrellis Flag indicating not to perform Trellis encoding. */ - void encode(uint8_t* data); + void encode(uint8_t* data, bool noTrellis = false); /** * @brief Sets the data format. diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index 8c8a7243..cc408462 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -978,6 +978,7 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) storeConvertedRxPDU(pduHeader, m_rxCall->pduUserData); } break; + case DFSIFrameType::MOT_PDU_SINGLE_CONF: { m_rxCall->resetCallData(); @@ -2586,7 +2587,125 @@ void ModemV24::convertFromAirV24(uint8_t* data, uint32_t length) break; case DUID::PDU: - break; + { + uint8_t buffer[P25_PDU_FRAME_LENGTH_BYTES]; + ::memset(buffer, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); + + uint32_t bits = P25Utils::decode(data + 2U, buffer, 0U, P25_PDU_FRAME_LENGTH_BITS); + + uint8_t rawPDU[P25_PDU_FRAME_LENGTH_BYTES]; + ::memset(rawPDU, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); + uint32_t pduBitCnt = Utils::getBits(buffer, rawPDU, 0U, bits); + + uint32_t offset = P25_PREAMBLE_LENGTH_BITS + P25_PDU_FEC_LENGTH_BITS; + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(rawPDU, buffer, P25_PREAMBLE_LENGTH_BITS, P25_PDU_FEC_LENGTH_BITS); + data::DataHeader dataHeader = data::DataHeader(); + bool ret = dataHeader.decode(buffer); + if (!ret) { + LogWarning(LOG_MODEM, P25_PDU_STR ", unfixable RF 1/2 rate header data"); + Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); + return; + } + + if (m_debug) { + ::LogDebugEx(LOG_MODEM, "ModemV24::convertFromAirV24()", "PDU OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", + dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), + dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(), dataHeader.getSynchronize(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), + dataHeader.getHeaderOffset(), dataHeader.getLLId()); + } + + // make sure we don't get a PDU with more blocks then we support + if (dataHeader.getBlocksToFollow() >= P25_MAX_PDU_BLOCKS) { + ::LogDebugEx(LOG_MODEM, "ModemV24::convertFromAirV24()", "PDU OSP, too many PDU blocks to process, %u > %u", dataHeader.getBlocksToFollow(), P25_MAX_PDU_BLOCKS); + return; + } + + uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); + uint32_t bitLength = ((blocksToFollow + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; + if (pduBitCnt >= bitLength) { + // process all blocks in the data stream + // if the primary header has a header offset ensure data if offset by that amount + if (dataHeader.getHeaderOffset() > 0U) { + offset += dataHeader.getHeaderOffset() * 8; + } + + data::DataBlock* dataBlocks = new data::DataBlock[P25_MAX_PDU_BLOCKS]; + + // decode data blocks + for (uint32_t i = 0U; i < blocksToFollow; i++) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(rawPDU, buffer, offset, P25_PDU_FEC_LENGTH_BITS); + bool ret = dataBlocks[i].decode(buffer, dataHeader); + if (ret) { + // if we are getting unconfirmed or confirmed blocks, and if we've reached the total number of blocks + // set this block as the last block for full packet CRC + if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) || (dataHeader.getFormat() == PDUFormatType::UNCONFIRMED)) { + if ((i + 1U) == blocksToFollow) { + dataBlocks[i].setLastBlock(true); + } + } + + if (m_debug) { + ::LogDebugEx(LOG_MODEM, "ModemV24::convertFromAirV24()", "PDU OSP, block %u, fmt = $%02X, lastBlock = %u", + (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? dataBlocks[i].getSerialNo() : i, dataBlocks[i].getFormat(), + dataBlocks[i].getLastBlock()); + } + } + + offset += P25_PDU_FEC_LENGTH_BITS; + } + + if (blocksToFollow <= 3U) { + DECLARE_UINT8_ARRAY(pduBuf, ((blocksToFollow + 1U) * (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES) + 1U); + uint32_t pduLen = (blocksToFollow + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES); + + pduBuf[0U] = (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? DFSIFrameType::MOT_PDU_SINGLE_CONF : DFSIFrameType::MOT_PDU_SINGLE_UNCONF; + + dataHeader.encode(pduBuf + 1U, true); + for (uint32_t i = 0U; i < blocksToFollow; i++) { + dataBlocks[i].encode(pduBuf + 1U + ((i + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES)), true); + } + + MotStartOfStream start = MotStartOfStream(); + start.setOpcode(m_rtrt ? MotStartStreamOpcode::TRANSMIT : MotStartStreamOpcode::RECEIVE); + start.setParam1(DSFI_MOT_ICW_PARM_PAYLOAD); + start.setArgument1(MotStreamPayload::DATA); + + // create buffer for bytes and encode + uint8_t startBuf[DFSI_MOT_START_LEN]; + ::memset(startBuf, 0x00U, DFSI_MOT_START_LEN); + start.encode(startBuf); + + if (m_trace) + Utils::dump(1U, "ModemV24::convertFromAirV24(), PDU StartOfStream", startBuf, DFSI_MOT_START_LEN); + + queueP25Frame(startBuf, DFSI_MOT_START_LEN, STT_NON_IMBE_NO_JITTER); + + if (m_trace) + Utils::dump(1U, "ModemV24::convertFromAirV24(), MotPDUFrame", pduBuf, pduLen); + + queueP25Frame(pduBuf, pduLen, STT_NON_IMBE_NO_JITTER); + + MotStartOfStream end = MotStartOfStream(); + end.setOpcode(m_rtrt ? MotStartStreamOpcode::TRANSMIT : MotStartStreamOpcode::RECEIVE); + end.setParam1(DFSI_MOT_ICW_PARM_STOP); + end.setArgument1(MotStreamPayload::DATA); + + // create buffer and encode + uint8_t endBuf[DFSI_MOT_START_LEN]; + ::memset(endBuf, 0x00U, DFSI_MOT_START_LEN); + end.encode(endBuf); + + queueP25Frame(endBuf, DFSI_MOT_START_LEN, STT_NON_IMBE_NO_JITTER); + queueP25Frame(endBuf, DFSI_MOT_START_LEN, STT_NON_IMBE_NO_JITTER); + } + else { + // TODO: support for more then 3 blocks + } + } + } + break; case DUID::TSDU: { diff --git a/src/host/modem/ModemV24.h b/src/host/modem/ModemV24.h index fbd02484..8c0feb55 100644 --- a/src/host/modem/ModemV24.h +++ b/src/host/modem/ModemV24.h @@ -84,6 +84,7 @@ namespace modem pduUserData(nullptr), dataHeader(), dataCall(false), + pduUserDataOffset(0U), errors(0U) { MI = new uint8_t[P25DEF::MI_LENGTH_BYTES]; @@ -162,7 +163,9 @@ namespace modem if (pduUserData != nullptr) ::memset(pduUserData, 0x00U, P25DEF::P25_MAX_PDU_BLOCKS * P25DEF::P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); dataHeader.reset(); + dataCall = false; + pduUserDataOffset = 0U; errors = 0U; } @@ -258,6 +261,10 @@ namespace modem * @brief Flag indicating the current call is a data call. */ bool dataCall; + /** + * @brief Offset index when populating the user data buffer. + */ + uint32_t pduUserDataOffset; /** * @brief Total errors for a given call sequence. From ee1d535487e37e1c75a53f35de3225c539fa60c2 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 22 Sep 2025 22:14:51 -0400 Subject: [PATCH 018/200] minor alterations to bridge UDP audio logic; set better default for bridge udpJitter buffer; --- configs/bridge-config.example.yml | 2 +- src/bridge/HostBridge.cpp | 29 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/configs/bridge-config.example.yml b/configs/bridge-config.example.yml index 436b5e1b..5d360cb3 100644 --- a/configs/bridge-config.example.yml +++ b/configs/bridge-config.example.yml @@ -89,7 +89,7 @@ network: # Jitter Buffer Length (in ms). # (This is only applied if utilizing inter frame delay, otherwise packet timing is assumed to be # properly handled by the source.) - udpJitter: 200 + udpJitter: 180 # Flag indicating the UDP audio will be padded with silence during hang time before end of call. udpHangSilence: true diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 75b04c3f..751aba81 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -3259,30 +3259,29 @@ void* HostBridge::threadUDPAudioProcess(void* arg) std::lock_guard lock(m_audioMutex); - int smpIdx = 0; - short samples[AUDIO_SAMPLES_LENGTH]; + uint8_t pcm[AUDIO_SAMPLES_LENGTH_BYTES]; + ::memset(pcm, 0x00U, AUDIO_SAMPLES_LENGTH_BYTES); + ::memcpy(pcm, req->pcm, AUDIO_SAMPLES_LENGTH_BYTES); + if (bridge->m_udpUseULaw) { + int smpIdx = 0; + short samples[AUDIO_SAMPLES_LENGTH]; + if (bridge->m_trace) - Utils::dump(1U, "HostBridge()::threadUDPAudioProcess(), uLaw Audio", req->pcm, AUDIO_SAMPLES_LENGTH * 2U); + Utils::dump(1U, "HostBridge()::threadUDPAudioProcess(), uLaw Audio", pcm, AUDIO_SAMPLES_LENGTH * 2U); for (uint32_t pcmIdx = 0; pcmIdx < AUDIO_SAMPLES_LENGTH; pcmIdx++) { - samples[smpIdx] = AnalogAudio::decodeMuLaw(req->pcm[pcmIdx]); + samples[smpIdx] = AnalogAudio::decodeMuLaw(pcm[pcmIdx]); smpIdx++; } int pcmIdx = 0; for (uint32_t smpIdx = 0; smpIdx < AUDIO_SAMPLES_LENGTH; smpIdx++) { - req->pcm[pcmIdx + 0] = (uint8_t)(samples[smpIdx] & 0xFF); - req->pcm[pcmIdx + 1] = (uint8_t)((samples[smpIdx] >> 8) & 0xFF); + pcm[pcmIdx + 0] = (uint8_t)(samples[smpIdx] & 0xFF); + pcm[pcmIdx + 1] = (uint8_t)((samples[smpIdx] >> 8) & 0xFF); pcmIdx += 2; } } - else { - for (int pcmIdx = 0; pcmIdx < req->pcmLength; pcmIdx += 2) { - samples[smpIdx] = (short)((req->pcm[pcmIdx + 1] << 8) + req->pcm[pcmIdx + 0]); - smpIdx++; - } - } bridge->m_trafficFromUDP = true; @@ -3329,13 +3328,13 @@ void* HostBridge::threadUDPAudioProcess(void* arg) switch (bridge->m_txMode) { case TX_MODE_DMR: - bridge->encodeDMRAudioFrame(req->pcm, bridge->m_udpSrcId); + bridge->encodeDMRAudioFrame(pcm, bridge->m_udpSrcId); break; case TX_MODE_P25: - bridge->encodeP25AudioFrame(req->pcm, bridge->m_udpSrcId); + bridge->encodeP25AudioFrame(pcm, bridge->m_udpSrcId); break; case TX_MODE_ANALOG: - bridge->encodeAnalogAudioFrame(req->pcm, bridge->m_udpSrcId); + bridge->encodeAnalogAudioFrame(pcm, bridge->m_udpSrcId); break; } } From 784051cf98ac5bc2a8260268104e639c18c4ca2b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 23 Sep 2025 13:56:31 -0400 Subject: [PATCH 019/200] re-engineer entirely how source untimed raw PCM frames over UDP are handled and timed; refactor how udp end of call is handled; --- configs/bridge-config.example.yml | 16 +- src/bridge/HostBridge.cpp | 378 ++++++++++++++++++------------ src/bridge/HostBridge.h | 14 +- 3 files changed, 230 insertions(+), 178 deletions(-) diff --git a/configs/bridge-config.example.yml b/configs/bridge-config.example.yml index 5d360cb3..a48d0085 100644 --- a/configs/bridge-config.example.yml +++ b/configs/bridge-config.example.yml @@ -80,19 +80,9 @@ network: # Flag indicating UDP audio should follow the USRP format. udpUsrp: false - # Delay in-between UDP audio frames (in ms). - # (Some applications will send RTP/PCM audio too fast, requiring a delay in-between packets to - # be added for appropriate IMBE audio pacing. For most cases a 20ms delay will properly pace - # audio frames. If set to 0, no frame pacing or jitter buffer will be applied and audio will be - # encoded as fast as it is received.) - udpInterFrameDelay: 0 - # Jitter Buffer Length (in ms). - # (This is only applied if utilizing inter frame delay, otherwise packet timing is assumed to be - # properly handled by the source.) - udpJitter: 180 - - # Flag indicating the UDP audio will be padded with silence during hang time before end of call. - udpHangSilence: true + # Flag indicating UDP audio frame timing will be performed at the bridge. + # (This allows the sending source to send audio as fast as it wants.) + udpFrameTiming: false # Traffic Encryption tek: diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 751aba81..1b15baa8 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -175,10 +175,8 @@ HostBridge::HostBridge(const std::string& confFile) : m_udpUseULaw(false), m_udpRTPFrames(false), m_udpUsrp(false), - m_udpInterFrameDelay(0U), - m_udpJitter(200U), - m_udpSilenceDuringHang(true), - m_lastUdpFrameTime(0U), + m_udpFrameTiming(false), + m_udpFrameCnt(0U), m_tekAlgoId(P25DEF::ALGO_UNENCRYPT), m_tekKeyId(0U), m_requestedTek(false), @@ -200,8 +198,6 @@ HostBridge::HostBridge(const std::string& confFile) : m_voxSampleLevel(30.0f), m_dropTimeMS(180U), m_localDropTime(1000U, 0U, 180U), - m_udpCallClock(1000U, 0U, 160U), - m_udpHangTime(1000U, 0U, 180U), m_udpDropTime(1000U, 0U, 180U), m_detectAnalogMDC1200(false), m_preambleLeaderTone(false), @@ -247,6 +243,7 @@ HostBridge::HostBridge(const std::string& confFile) : m_txStreamId(0U), m_detectedSampleCnt(0U), m_dumpSampleLevel(false), + m_mtNoSleep(false), m_running(false), m_trace(false), m_debug(false), @@ -539,7 +536,9 @@ int HostBridge::run() if (m_localAudio) { if (!Thread::runAsThread(this, threadAudioProcess)) return EXIT_FAILURE; + } + if (m_localAudio) { // start audio device result = ma_device_start(&m_maDevice); if (result != MA_SUCCESS) { @@ -599,7 +598,7 @@ int HostBridge::run() if (m_udpAudio && m_udpAudioSocket != nullptr) processUDPAudio(); - if (ms < 2U) + if (ms < 2U && !m_mtNoSleep) Thread::sleep(1U); } @@ -910,16 +909,15 @@ bool HostBridge::readParams() yaml::Node networkConf = m_conf["network"]; m_udpAudio = networkConf["udpAudio"].as(false); - bool udpSilenceDuringHang = networkConf["udpHangSilence"].as(true); switch (m_txMode) { case TX_MODE_DMR: break; case TX_MODE_P25: { - if (m_udpAudio && udpSilenceDuringHang && m_dropTimeMS < 360U) { - ::LogWarning(LOG_HOST, "When using UDP silence during hang time, the minimum allowable drop time is 360ms."); - m_dropTimeMS = 360U; // drop time for UDP is minimum 360ms when using silence during hang time + if (m_udpAudio) { + ::LogWarning(LOG_HOST, "When using UDP audio, the drop time is fixed to 360ms. (1 P25 audio superframe.)"); + m_dropTimeMS = 360U; } } break; @@ -930,10 +928,6 @@ bool HostBridge::readParams() m_localDropTime = Timer(1000U, 0U, m_dropTimeMS); m_udpDropTime = Timer(1000U, 0U, m_dropTimeMS); - // bryanb: UDP drop timer cannot be less then 180ms - if (m_dropTimeMS > 180U) - m_udpDropTime = Timer(1000U, 0U, m_dropTimeMS); - m_detectAnalogMDC1200 = systemConf["detectAnalogMDC1200"].as(false); m_preambleLeaderTone = systemConf["preambleLeaderTone"].as(false); @@ -1012,9 +1006,7 @@ bool HostBridge::createNetwork() m_udpReceiveAddress = networkConf["udpReceiveAddress"].as(); m_udpUseULaw = networkConf["udpUseULaw"].as(false); m_udpUsrp = networkConf["udpUsrp"].as(false); - m_udpInterFrameDelay = (uint8_t)networkConf["udpInterFrameDelay"].as(0U); - m_udpJitter = (uint16_t)networkConf["udpJitter"].as(200U); - m_udpSilenceDuringHang = networkConf["udpHangSilence"].as(true); + m_udpFrameTiming = networkConf["udpFrameTiming"].as(false); if (m_udpUsrp) { m_udpMetadata = false; // USRP disables metadata due to USRP always having metadata @@ -1176,9 +1168,7 @@ bool HostBridge::createNetwork() LogInfo(" UDP Audio RTP Framed: %s", m_udpRTPFrames ? "yes" : "no"); } LogInfo(" UDP Audio USRP: %s", m_udpUsrp ? "yes" : "no"); - LogInfo(" UDP Inter Audio Frame Delay: %ums", m_udpInterFrameDelay); - LogInfo(" UDP Jitter: %ums", m_udpJitter); - LogInfo(" UDP Silence During Hangtime: %s", m_udpSilenceDuringHang ? "yes" : "no"); + LogInfo(" UDP Frame Timing: %s", m_udpFrameTiming ? "yes" : "no"); } LogInfo(" Traffic Encrypted: %s", tekEnable ? "yes" : "no"); @@ -1266,6 +1256,8 @@ void HostBridge::processUDPAudio() } if (length > 0) { + m_mtNoSleep = true; // make main thread run as fast as possible + if (m_trace) Utils::dump(1U, "HostBridge()::processUDPAudio(), Audio Network Packet", buffer, length); @@ -1321,40 +1313,19 @@ void HostBridge::processUDPAudio() req->pcmLength = pcmLength; - req->srcId = m_srcId; - req->dstId = m_dstId; - - uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - if (m_udpInterFrameDelay > 0U) { - uint64_t pktTime = 0U; - - // if this is our first message, timestamp is just now + the jitter buffer offset in ms - if (m_lastUdpFrameTime == 0U) { - pktTime = now + m_udpJitter; - } - else { - // if the last message occurred longer than our jitter buffer delay, we restart the sequence and calculate the same as above - if ((int64_t)(now - m_lastUdpFrameTime) > m_udpJitter) { - pktTime = now + m_udpJitter; - } - // otherwise, we time out messages as required by the message type - else { - pktTime = m_lastUdpFrameTime + 20U; - } - } - - req->pktRxTime = pktTime; - m_lastUdpFrameTime = pktTime; - } else { - req->pktRxTime = now; - } - if (m_udpMetadata) { req->srcId = GET_UINT32(buffer, pcmLength + 8U); } + else { + req->srcId = m_srcId; + } + req->dstId = m_dstId; m_udpPackets.push_back(req); } + else { + m_mtNoSleep = false; // restore main thread sleeping if no pending packets + } } /* Helper to process an In-Call Control message. */ @@ -2536,6 +2507,10 @@ void HostBridge::encodeP25AudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_ m_p25SeqNo++; m_p25N++; + + // if N is >17 reset sequence + if (m_p25N > 17) + m_p25N = 0; } /* Helper to process analog network traffic. */ @@ -2903,18 +2878,16 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) return; } - LogMessage(LOG_HOST, "%s, call end, srcId = %u, dstId = %u", trafficType.c_str(), srcId, dstId); - m_audioDetect = false; m_localDropTime.stop(); m_udpDropTime.stop(); - m_udpHangTime.stop(); - m_udpCallClock.stop(); if (!m_callInProgress) { switch (m_txMode) { case TX_MODE_DMR: { + padSilenceAudio(srcId, dstId); + DMRDEF::DataType::E dataType = DMRDEF::DataType::VOICE_SYNC; if (m_dmrN == 0) dataType = DMRDEF::DataType::VOICE_SYNC; @@ -2941,6 +2914,8 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) break; case TX_MODE_P25: { + padSilenceAudio(srcId, dstId); + p25::lc::LC lc = p25::lc::LC(); lc.setLCO(P25DEF::LCO::GROUP); lc.setDstId(dstId); @@ -2978,13 +2953,15 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) } } + LogMessage(LOG_HOST, "%s, call end, srcId = %u, dstId = %u", trafficType.c_str(), srcId, dstId); + m_srcIdOverride = 0; m_txStreamId = 0; m_udpSrcId = 0; m_udpDstId = 0; - m_lastUdpFrameTime = 0U; m_trafficFromUDP = false; + m_udpFrameCnt = 0U; m_dmrSeqNo = 0U; m_dmrN = 0U; @@ -3207,38 +3184,69 @@ void* HostBridge::threadUDPAudioProcess(void* arg) ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE + StopWatch stopWatch; + stopWatch.start(); + + ulong64_t lastFrameTime = 0U; + Timer frameTimeout = Timer(1000U, 0U, 22U); + while (!g_killed) { if (!bridge->m_running) { Thread::sleep(1U); continue; } + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + + frameTimeout.clock(ms); + if (frameTimeout.isRunning() && frameTimeout.hasExpired()) { + frameTimeout.stop(); + bridge->padSilenceAudio(bridge->m_udpSrcId, bridge->m_udpDstId); + } + if (bridge->m_udpPackets.empty()) Thread::sleep(1U); else { NetPacketRequest* req = bridge->m_udpPackets[0]; - if (req != nullptr) { - if (bridge->m_udpInterFrameDelay > 0U) { - uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + + // are we timing UDP audio frame release? + if (bridge->m_udpFrameTiming) { + if (lastFrameTime == 0U) + lastFrameTime = now; + else { + // IMBEs must go out at 20ms intervals + if (lastFrameTime + 20U > now) + continue; - if (req->pktRxTime > now) { - Thread::sleep(1U); - continue; + lastFrameTime = now; } } - bridge->m_udpPackets.pop_front(); + if (bridge->m_debug) + LogDebugEx(LOG_HOST, "HostBridge::threadUDPAudioProcess()", "now = %llu, lastUdpFrameTime = %llu, audioDetect = %u, callInProgress = %u, p25N = %u, dmrN = %u, analogN = %u, frameCnt = %u", + now, lastFrameTime, bridge->m_audioDetect, bridge->m_callInProgress, bridge->m_p25N, bridge->m_dmrN, bridge->m_analogN, bridge->m_udpFrameCnt); - bridge->m_udpDstId = bridge->m_dstId; + bridge->m_udpPackets.pop_front(); + bridge->m_udpDropTime.start(); + frameTimeout.start(); + bool forceCallStart = false; + uint32_t txStreamId = bridge->m_txStreamId; if (bridge->m_udpMetadata) { if (bridge->m_overrideSrcIdFromUDP) { - if (req->srcId != 0U) { + if (req->srcId != 0U && bridge->m_udpSrcId != 0U) { // if the UDP source ID now doesn't match the current call ID, reset call states if (bridge->m_resetCallForSourceIdChange && (req->srcId != bridge->m_udpSrcId)) { + LogMessage(LOG_HOST, "%s, call switch over, old srcId = %u, new srcId = %u", UDP_CALL, bridge->m_udpSrcId, req->srcId); bridge->callEnd(bridge->m_udpSrcId, bridge->m_dstId); - bridge->m_udpDstId = bridge->m_dstId; + + if (bridge->m_udpDropTime.isRunning()) + bridge->m_udpDropTime.start(); + + forceCallStart = true; } bridge->m_udpSrcId = req->srcId; @@ -3247,6 +3255,10 @@ void* HostBridge::threadUDPAudioProcess(void* arg) if (bridge->m_udpSrcId == 0U) { bridge->m_udpSrcId = req->srcId; } + + if (bridge->m_udpSrcId == 0U) { + bridge->m_udpSrcId = bridge->m_srcId; + } } } else { @@ -3257,19 +3269,55 @@ void* HostBridge::threadUDPAudioProcess(void* arg) bridge->m_udpSrcId = bridge->m_srcId; } - std::lock_guard lock(m_audioMutex); + bridge->m_udpDstId = bridge->m_dstId; + // force start a call if one isn't already in progress + if ((!bridge->m_audioDetect && !bridge->m_callInProgress) || forceCallStart) { + bridge->m_audioDetect = true; + if (bridge->m_txStreamId == 0U) { + bridge->m_txStreamId = 1U; // prevent further false starts -- this isn't the right way to handle this... + if (forceCallStart) + bridge->m_txStreamId = txStreamId; + + LogMessage(LOG_HOST, "%s, call start, srcId = %u, dstId = %u", UDP_CALL, bridge->m_udpSrcId, bridge->m_udpDstId); + if (bridge->m_grantDemand) { + switch (bridge->m_txMode) { + case TX_MODE_P25: + { + p25::lc::LC lc = p25::lc::LC(); + lc.setLCO(P25DEF::LCO::GROUP); + lc.setDstId(bridge->m_udpDstId); + lc.setSrcId(bridge->m_udpSrcId); + + p25::data::LowSpeedData lsd = p25::data::LowSpeedData(); + + uint8_t controlByte = network::NET_CTRL_GRANT_DEMAND; + if (bridge->m_tekAlgoId != P25DEF::ALGO_UNENCRYPT) + controlByte |= network::NET_CTRL_GRANT_ENCRYPT; + controlByte |= network::NET_CTRL_SWITCH_OVER; + bridge->m_network->writeP25TDU(lc, lsd, controlByte); + } + break; + } + } + } + + bridge->m_udpDropTime.stop(); + if (!bridge->m_udpDropTime.isRunning()) + bridge->m_udpDropTime.start(); + } + + std::lock_guard lock(m_audioMutex); uint8_t pcm[AUDIO_SAMPLES_LENGTH_BYTES]; ::memset(pcm, 0x00U, AUDIO_SAMPLES_LENGTH_BYTES); ::memcpy(pcm, req->pcm, AUDIO_SAMPLES_LENGTH_BYTES); if (bridge->m_udpUseULaw) { - int smpIdx = 0; - short samples[AUDIO_SAMPLES_LENGTH]; - if (bridge->m_trace) Utils::dump(1U, "HostBridge()::threadUDPAudioProcess(), uLaw Audio", pcm, AUDIO_SAMPLES_LENGTH * 2U); + int smpIdx = 0; + short samples[AUDIO_SAMPLES_LENGTH]; for (uint32_t pcmIdx = 0; pcmIdx < AUDIO_SAMPLES_LENGTH; pcmIdx++) { samples[smpIdx] = AnalogAudio::decodeMuLaw(pcm[pcmIdx]); smpIdx++; @@ -3285,46 +3333,9 @@ void* HostBridge::threadUDPAudioProcess(void* arg) bridge->m_trafficFromUDP = true; - // force start a call if one isn't already in progress - if (!bridge->m_audioDetect && !bridge->m_callInProgress) { - bridge->m_audioDetect = true; - if (bridge->m_txStreamId == 0U) { - bridge->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, bridge->m_udpSrcId, bridge->m_udpDstId); - if (bridge->m_grantDemand) { - switch (bridge->m_txMode) { - case TX_MODE_P25: - { - p25::lc::LC lc = p25::lc::LC(); - lc.setLCO(P25DEF::LCO::GROUP); - lc.setDstId(bridge->m_udpDstId); - lc.setSrcId(bridge->m_udpSrcId); - - p25::data::LowSpeedData lsd = p25::data::LowSpeedData(); - - uint8_t controlByte = network::NET_CTRL_GRANT_DEMAND; - if (bridge->m_tekAlgoId != P25DEF::ALGO_UNENCRYPT) - controlByte |= network::NET_CTRL_GRANT_ENCRYPT; - controlByte |= network::NET_CTRL_SWITCH_OVER; - bridge->m_network->writeP25TDU(lc, lsd, controlByte); - } - break; - } - } - } - - bridge->m_udpCallClock.stop(); - bridge->m_udpDropTime.stop(); - - if (!bridge->m_udpDropTime.isRunning()) - bridge->m_udpDropTime.start(); - bridge->m_udpCallClock.start(); - } - - // If audio detection is active and no call is in progress, encode and transmit the audio + // if audio detection is active and no call is in progress, encode and transmit the audio if (bridge->m_audioDetect && !bridge->m_callInProgress) { bridge->m_udpDropTime.start(); - bridge->m_udpCallClock.start(); switch (bridge->m_txMode) { case TX_MODE_DMR: @@ -3339,19 +3350,16 @@ void* HostBridge::threadUDPAudioProcess(void* arg) } } + bridge->m_udpFrameCnt++; + delete[] req->pcm; delete req; - - if (bridge->m_udpInterFrameDelay > 0U) { - Thread::sleep(bridge->m_udpInterFrameDelay); - } - else { - Thread::sleep(1U); - } } else { bridge->m_udpPackets.pop_front(); - Thread::sleep(1U); } + + if (!bridge->m_callInProgress) + Thread::sleep(1U); } } @@ -3484,7 +3492,7 @@ void HostBridge::resetWithNullAudio(uint8_t* data, bool encrypted) /* */ -void HostBridge::callEndSilence(uint32_t srcId, uint32_t dstId) +void HostBridge::padSilenceAudio(uint32_t srcId, uint32_t dstId) { switch (m_txMode) { case TX_MODE_DMR: @@ -3527,7 +3535,7 @@ void HostBridge::callEndSilence(uint32_t srcId, uint32_t dstId) emb.encode(data); } - LogMessage(LOG_HOST, DMR_DT_VOICE ", silence srcId = %u, dstId = %u, slot = %u, seqNo = %u", srcId, dstId, m_slot, m_dmrN); + LogMessage(LOG_HOST, DMR_DT_VOICE ", audio (silence), srcId = %u, dstId = %u, slot = %u, seqNo = %u", srcId, dstId, m_slot, m_dmrN); // generate DMR network frame NetData dmrData; @@ -3556,6 +3564,99 @@ void HostBridge::callEndSilence(uint32_t srcId, uint32_t dstId) using namespace p25::data; // fill the LDU buffers appropriately + if (m_p25N > 0U) { + // LDU1 + if (m_p25N >= 0U && m_p25N < 9U) { + LogWarning(LOG_HOST, "incomplete audio frame, padding %u audio sequences with silence", 8U - m_p25N); + + for (uint8_t n = m_p25N; n < 9U; n++) { + switch (n) { + case 0: + ::memcpy(m_netLDU1 + 10U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 1: + ::memcpy(m_netLDU1 + 26U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 2: + ::memcpy(m_netLDU1 + 55U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 3: + ::memcpy(m_netLDU1 + 80U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 4: + ::memcpy(m_netLDU1 + 105U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 5: + ::memcpy(m_netLDU1 + 130U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 6: + ::memcpy(m_netLDU1 + 155U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 7: + ::memcpy(m_netLDU1 + 180U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 8: + ::memcpy(m_netLDU1 + 204U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + } + } + + m_p25N = 8U; + } + + // LDU2 + if (m_p25N >= 9U && m_p25N < 17U) { + LogWarning(LOG_HOST, "incomplete audio frame, padding %u audio sequences with silence", 17U - m_p25N); + + for (uint8_t n = m_p25N; n < 18U; n++) { + switch (n) { + case 9: + ::memcpy(m_netLDU2 + 10U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 10: + ::memcpy(m_netLDU2 + 26U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 11: + ::memcpy(m_netLDU2 + 55U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 12: + ::memcpy(m_netLDU2 + 80U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 13: + ::memcpy(m_netLDU2 + 105U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 14: + ::memcpy(m_netLDU2 + 130U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 15: + ::memcpy(m_netLDU2 + 155U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 16: + ::memcpy(m_netLDU2 + 180U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + case 17: + ::memcpy(m_netLDU2 + 204U, P25DEF::NULL_IMBE, RAW_IMBE_LENGTH_BYTES); + break; + } + } + + m_p25N = 17U; + } + } + else { + // LDU1 + if (m_p25N >= 0U && m_p25N < 9U) { + resetWithNullAudio(m_netLDU1, false); + m_p25N = 8U; + } + + // LDU2 + if (m_p25N >= 9U && m_p25N < 17U) { + resetWithNullAudio(m_netLDU2, false); + m_p25N = 17U; + } + } + switch (m_p25N) { // LDU1 case 0: @@ -3581,16 +3682,16 @@ void HostBridge::callEndSilence(uint32_t srcId, uint32_t dstId) LowSpeedData lsd = LowSpeedData(); // send P25 LDU1 - if (m_p25N == 0U) { - LogMessage(LOG_HOST, P25_LDU1_STR " silence audio, srcId = %u, dstId = %u", srcId, dstId); + if (m_p25N == 8U) { + LogMessage(LOG_HOST, P25_LDU1_STR " audio (silence padded), srcId = %u, dstId = %u", srcId, dstId); m_network->writeP25LDU1(lc, lsd, m_netLDU1, FrameType::DATA_UNIT); - m_p25N++; + m_p25N = 9U; break; } // send P25 LDU2 - if (m_p25N == 1U) { - LogMessage(LOG_HOST, P25_LDU2_STR " silence audio, algo = $%02X, kid = $%04X", ALGO_UNENCRYPT, 0U); + if (m_p25N == 17U) { + LogMessage(LOG_HOST, P25_LDU2_STR " audio (silence padded), algo = $%02X, kid = $%04X", ALGO_UNENCRYPT, 0U); m_network->writeP25LDU2(lc, lsd, m_netLDU2); m_p25N = 0U; break; @@ -3667,42 +3768,7 @@ void* HostBridge::threadCallWatchdog(void* arg) srcId = bridge->m_udpSrcId; dstId = bridge->m_udpDstId; - if (bridge->m_udpSilenceDuringHang) { - if (bridge->m_udpCallClock.isRunning()) - bridge->m_udpCallClock.clock(ms); - - if (bridge->m_udpCallClock.isRunning() && bridge->m_udpCallClock.hasExpired()) { - bridge->m_udpCallClock.stop(); - bridge->m_udpHangTime.start(); - - bridge->m_dmrN = 0U; - bridge->m_p25N = 0U; - bridge->m_analogN = 0U; - } - - if (bridge->m_udpHangTime.isRunning()) - bridge->m_udpHangTime.clock(ms); - - if (bridge->m_udpHangTime.isRunning() && bridge->m_udpHangTime.hasExpired()) { - bridge->callEndSilence(srcId, dstId); - bridge->m_udpHangTime.start(); - } - } - if (bridge->m_udpDropTime.isRunning() && bridge->m_udpDropTime.hasExpired()) { - if (bridge->m_udpSilenceDuringHang) { - bridge->m_udpHangTime.stop(); - switch (bridge->m_txMode) { - case TX_MODE_DMR: - // TODO: send DMR silence - break; - case TX_MODE_P25: - if (bridge->m_p25N > 0U) - bridge->callEndSilence(srcId, dstId); - break; - } - } - bridge->callEnd(srcId, dstId); } } diff --git a/src/bridge/HostBridge.h b/src/bridge/HostBridge.h index dbd6485f..5694fe77 100644 --- a/src/bridge/HostBridge.h +++ b/src/bridge/HostBridge.h @@ -108,8 +108,6 @@ struct NetPacketRequest { int pcmLength = 0U; //! Length of PCM data buffer uint8_t* pcm = nullptr; //! Raw PCM buffer - - uint64_t pktRxTime; //! Packet receive time }; // --------------------------------------------------------------------------- @@ -159,10 +157,8 @@ class HOST_SW_API HostBridge { bool m_udpUseULaw; bool m_udpRTPFrames; bool m_udpUsrp; - uint8_t m_udpInterFrameDelay; - uint16_t m_udpJitter; - bool m_udpSilenceDuringHang; - uint64_t m_lastUdpFrameTime; + bool m_udpFrameTiming; + uint32_t m_udpFrameCnt; uint8_t m_tekAlgoId; uint16_t m_tekKeyId; @@ -190,8 +186,6 @@ class HOST_SW_API HostBridge { float m_voxSampleLevel; uint16_t m_dropTimeMS; Timer m_localDropTime; - Timer m_udpCallClock; - Timer m_udpHangTime; Timer m_udpDropTime; bool m_detectAnalogMDC1200; @@ -255,6 +249,8 @@ class HOST_SW_API HostBridge { uint8_t m_detectedSampleCnt; bool m_dumpSampleLevel; + bool m_mtNoSleep; + bool m_running; bool m_trace; bool m_debug; @@ -565,7 +561,7 @@ class HOST_SW_API HostBridge { * @param srcId * @param dstId */ - void callEndSilence(uint32_t srcId, uint32_t dstId); + void padSilenceAudio(uint32_t srcId, uint32_t dstId); /** * @brief Entry point to call watchdog handler thread. From 88208aa702c37fe0bbdaf1476f29328a461b1f15 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 23 Sep 2025 14:24:53 -0400 Subject: [PATCH 020/200] add support to resize the recv and send buffers on the raw UDP socket; adjust the recv buffer on bridge to use 131K system buffer for the socket (this allows us to hold ~394 frames worth of *raw* PCM + metadata in the socket's internal buffer in the system kernel; --- src/bridge/HostBridge.cpp | 7 +++ src/common/network/udp/Socket.cpp | 96 ++++++++++++++++++++++++++++++- src/common/network/udp/Socket.h | 15 ++++- 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 1b15baa8..22a843f5 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -1230,6 +1230,13 @@ bool HostBridge::createNetwork() if (m_udpAudio) { m_udpAudioSocket = new Socket(m_udpReceiveAddress, m_udpReceivePort); m_udpAudioSocket->open(); + + /* + ** bryanb: resize the system UDP socket buffer used for receiving audio frames to 131K, this should hold + * ~394 raw audio frames before filling + */ + if (!m_udpAudioSocket->recvBufSize(131072)) + LogWarning(LOG_HOST, "failed to resize UDP audio socket buffer size to 131K"); } return true; diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index 8ec1f2cf..e80e7df4 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2006-2016,2020 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" @@ -161,6 +161,100 @@ bool Socket::open(const uint32_t af, const std::string& address, const uint16_t return true; } +/* Sets the socket receive buffer size. */ + +bool Socket::recvBufSize(ssize_t bufSize) +{ + int optVal = -1; + socklen_t optValLen = sizeof(optVal); + + // get original buffer size + if (::getsockopt(m_fd, SOL_SOCKET, SO_RCVBUF, (char*)&optVal, &optValLen) == -1) { +#if defined(_WIN32) + LogError(LOG_NET, "Cannot get the receive buffer size, err: %lu", ::GetLastError()); +#else + LogError(LOG_NET, "Cannot get the receive buffer size, err: %d (%s)", errno, strerror(errno)); +#endif // _WIN32 + return false; + } + + int recvBufSize = optVal; + + // resize buffer + if (::setsockopt(m_fd, SOL_SOCKET, SO_RCVBUF, (char*)& bufSize, sizeof(bufSize)) == -1) { +#if defined(_WIN32) + LogError(LOG_NET, "Cannot resize the receive buffer size, err: %lu", ::GetLastError()); +#else + LogError(LOG_NET, "Cannot resize the receive buffer size, err: %d (%s)", errno, strerror(errno)); +#endif // _WIN32 + return false; + } + + // get the buffer size after alteration + if (::getsockopt(m_fd, SOL_SOCKET, SO_RCVBUF, (char*)&optVal, &optValLen) == -1) { +#if defined(_WIN32) + LogError(LOG_NET, "Cannot get the receive buffer size, err: %lu", ::GetLastError()); +#else + LogError(LOG_NET, "Cannot get the receive buffer size, err: %d (%s)", errno, strerror(errno)); +#endif // _WIN32 + return false; + } + + if (optVal == bufSize) + return true; + else + LogError(LOG_NET, "failed to resize socket recv buffer, %u != %u", recvBufSize, optVal); + + return false; +} + +/* Sets the socket send buffer size. */ + +bool Socket::sendBufSize(ssize_t bufSize) +{ + int optVal = -1; + socklen_t optValLen = sizeof(optVal); + + // get original buffer size + if (::getsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optValLen) == -1) { +#if defined(_WIN32) + LogError(LOG_NET, "Cannot get the send buffer size, err: %lu", ::GetLastError()); +#else + LogError(LOG_NET, "Cannot get the send buffer size, err: %d (%s)", errno, strerror(errno)); +#endif // _WIN32 + return false; + } + + int recvBufSize = optVal; + + // resize buffer + if (::setsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, (char*)&bufSize, sizeof(bufSize)) == -1) { +#if defined(_WIN32) + LogError(LOG_NET, "Cannot resize the send buffer size, err: %lu", ::GetLastError()); +#else + LogError(LOG_NET, "Cannot resize the send buffer size, err: %d (%s)", errno, strerror(errno)); +#endif // _WIN32 + return false; + } + + // get the buffer size after alteration + if (::getsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optValLen) == -1) { +#if defined(_WIN32) + LogError(LOG_NET, "Cannot get the send buffer size, err: %lu", ::GetLastError()); +#else + LogError(LOG_NET, "Cannot get the send buffer size, err: %d (%s)", errno, strerror(errno)); +#endif // _WIN32 + return false; + } + + if (optVal == bufSize) + return true; + else + LogError(LOG_NET, "failed to resize socket send buffer, %u != %u", recvBufSize, optVal); + + return false; +} + /* Closes the UDP socket connection. */ void Socket::close() diff --git a/src/common/network/udp/Socket.h b/src/common/network/udp/Socket.h index 739f8820..e5258510 100644 --- a/src/common/network/udp/Socket.h +++ b/src/common/network/udp/Socket.h @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2006-2016,2020 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -222,6 +222,19 @@ namespace network */ bool open(const uint32_t af, const std::string& address, const uint16_t port) noexcept; + /** + * @brief Sets the socket receive buffer size. + * @param bufSize Buffer size to set. + * @returns bool True, if buffer size set, otherwise false. + */ + bool recvBufSize(ssize_t bufSize); + /** + * @brief Sets the socket send buffer size. + * @param bufSize Buffer size to set. + * @returns bool True, if buffer size set, otherwise false. + */ + bool sendBufSize(ssize_t bufSize); + /** * @brief Closes the UDP socket connection. */ From 7f55b5a75690eac80334b8c1b08d529777211150 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 23 Sep 2025 14:59:23 -0400 Subject: [PATCH 021/200] set socket buffer sizes to larger values then the default; --- src/common/network/Network.cpp | 1 + src/common/network/udp/Socket.cpp | 42 +++++++++++-------------------- src/fne/network/DiagNetwork.cpp | 2 ++ src/fne/network/FNENetwork.cpp | 2 ++ 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index fee31014..c8ca8cc6 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -200,6 +200,7 @@ void Network::clock(uint32_t ms) bool ret = m_socket->open(m_addr.ss_family); if (ret) { + m_socket->recvBufSize(2097152U); // 2M recv buffer ret = writeLogin(); if (!ret) { m_retryTimer.start(); diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index e80e7df4..bf248c27 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -168,18 +168,6 @@ bool Socket::recvBufSize(ssize_t bufSize) int optVal = -1; socklen_t optValLen = sizeof(optVal); - // get original buffer size - if (::getsockopt(m_fd, SOL_SOCKET, SO_RCVBUF, (char*)&optVal, &optValLen) == -1) { -#if defined(_WIN32) - LogError(LOG_NET, "Cannot get the receive buffer size, err: %lu", ::GetLastError()); -#else - LogError(LOG_NET, "Cannot get the receive buffer size, err: %d (%s)", errno, strerror(errno)); -#endif // _WIN32 - return false; - } - - int recvBufSize = optVal; - // resize buffer if (::setsockopt(m_fd, SOL_SOCKET, SO_RCVBUF, (char*)& bufSize, sizeof(bufSize)) == -1) { #if defined(_WIN32) @@ -200,10 +188,15 @@ bool Socket::recvBufSize(ssize_t bufSize) return false; } - if (optVal == bufSize) + /* + ** bryanb: this check may seem strange, but on Linux the kernel doubles the + ** requested buffer size for its own overhead, so we just need to ensure + ** that the returned buffer size is at least what we requested + */ + if (optVal >= bufSize) return true; else - LogError(LOG_NET, "failed to resize socket recv buffer, %u != %u", recvBufSize, optVal); + LogError(LOG_NET, "failed to resize socket recv buffer, %u != %u", optVal, bufSize); return false; } @@ -215,18 +208,6 @@ bool Socket::sendBufSize(ssize_t bufSize) int optVal = -1; socklen_t optValLen = sizeof(optVal); - // get original buffer size - if (::getsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optValLen) == -1) { -#if defined(_WIN32) - LogError(LOG_NET, "Cannot get the send buffer size, err: %lu", ::GetLastError()); -#else - LogError(LOG_NET, "Cannot get the send buffer size, err: %d (%s)", errno, strerror(errno)); -#endif // _WIN32 - return false; - } - - int recvBufSize = optVal; - // resize buffer if (::setsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, (char*)&bufSize, sizeof(bufSize)) == -1) { #if defined(_WIN32) @@ -247,10 +228,15 @@ bool Socket::sendBufSize(ssize_t bufSize) return false; } - if (optVal == bufSize) + /* + ** bryanb: this check may seem strange, but on Linux the kernel doubles the + ** requested buffer size for its own overhead, so we just need to ensure + ** that the returned buffer size is at least what we requested + */ + if (optVal >= bufSize) return true; else - LogError(LOG_NET, "failed to resize socket send buffer, %u != %u", recvBufSize, optVal); + LogError(LOG_NET, "failed to resize socket send buffer, %u != %u", optVal, bufSize); return false; } diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index fb5e260e..b7ebf025 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -129,6 +129,8 @@ bool DiagNetwork::open() bool ret = m_socket->open(); if (!ret) { + m_socket->recvBufSize(2097152U); // 2M recv buffer + m_socket->sendBufSize(2097152U); // 2M recv buffer m_status = NET_STAT_INVALID; } diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 74fdcde5..ca4d3f6f 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -537,6 +537,8 @@ bool FNENetwork::open() bool ret = m_socket->open(); if (!ret) { + m_socket->recvBufSize(2097152U); // 2M recv buffer + m_socket->sendBufSize(2097152U); // 2M recv buffer m_status = NET_STAT_INVALID; } From bf36dd0fd87387077dcd88f567e44540af958478 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 23 Sep 2025 15:09:37 -0400 Subject: [PATCH 022/200] annotate Linux limiting the maximum send and recv socket buffer sizes --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 5e202475..c75ee734 100644 --- a/README.md +++ b/README.md @@ -361,6 +361,9 @@ counts of up to 100,000+ calls on a x86_64 Server with 8GB RAM and 8-core proces - If you have old configuration files, missing comments or new parameters, there is a tool provided in the "tools" directory of the project called `config_annotator.py` this is a Python CLI tool designed to compare an existing configuration file against the example configuration file and recomment and add missing parameters (along with removing illegal/invalid parameters). It is recommended to backup your existing configuration file before running this tool on it. *This tool is only designed for the `dvmhost` configuration file, and no other configuration file!* +- By default Linux may restrict the maximum size of the receive and send buffers used by the kernel for network traffic. Please check the limits with `sudo sysctl net.core.rmem_max` and `sudo sysctl net.core.wmem_max`, these should be at least 2MB (2097152), while DVM will operate in lower +limits, you will see startup errors similar to: "failed to resize socket recv buffer, XXXXXX != 2097152", or "failed to resize socket send buffer, XXXXXX != 2097152". + ## Security Warnings It is highly recommended that the REST API interface not be exposed directly to the internet. If such exposure is wanted/needed, it is highly recommended to proxy the dvmhost REST API through a modern web server (like nginx for example) rather then directly exposing dvmhost's REST API port. From 963af4bf8b89cd8eb8acc927bcf875bdaf59f096 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 23 Sep 2025 15:11:49 -0400 Subject: [PATCH 023/200] update dvmhost submodule; --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c75ee734..2b806c3d 100644 --- a/README.md +++ b/README.md @@ -362,7 +362,7 @@ counts of up to 100,000+ calls on a x86_64 Server with 8GB RAM and 8-core proces - If you have old configuration files, missing comments or new parameters, there is a tool provided in the "tools" directory of the project called `config_annotator.py` this is a Python CLI tool designed to compare an existing configuration file against the example configuration file and recomment and add missing parameters (along with removing illegal/invalid parameters). It is recommended to backup your existing configuration file before running this tool on it. *This tool is only designed for the `dvmhost` configuration file, and no other configuration file!* - By default Linux may restrict the maximum size of the receive and send buffers used by the kernel for network traffic. Please check the limits with `sudo sysctl net.core.rmem_max` and `sudo sysctl net.core.wmem_max`, these should be at least 2MB (2097152), while DVM will operate in lower -limits, you will see startup errors similar to: "failed to resize socket recv buffer, XXXXXX != 2097152", or "failed to resize socket send buffer, XXXXXX != 2097152". +limits, you will see startup errors similar to: "failed to resize socket recv buffer, XXXXXX != 2097152", or "failed to resize socket send buffer, XXXXXX != 2097152". See the documentation for your specific distribution of Linux to adjust these parameters. ## Security Warnings From 8f634ebd96018835c3beb79276dfa66894ba0457 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 23 Sep 2025 15:13:32 -0400 Subject: [PATCH 024/200] use a 2M buffer for bridge UDP audio; --- src/bridge/HostBridge.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 22a843f5..e0a0a10a 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -1232,11 +1232,11 @@ bool HostBridge::createNetwork() m_udpAudioSocket->open(); /* - ** bryanb: resize the system UDP socket buffer used for receiving audio frames to 131K, this should hold - * ~394 raw audio frames before filling + ** bryanb: resize the system UDP socket buffer used for receiving audio frames to 2M, this should hold + * ~6300 raw audio frames before filling */ - if (!m_udpAudioSocket->recvBufSize(131072)) - LogWarning(LOG_HOST, "failed to resize UDP audio socket buffer size to 131K"); + if (!m_udpAudioSocket->recvBufSize(2097152U)) + LogWarning(LOG_HOST, "failed to resize UDP audio socket buffer size to 2M"); } return true; From 4f3279691faf19e85fe3e6dbf1a50a8d9164e31c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 23 Sep 2025 15:16:05 -0400 Subject: [PATCH 025/200] display the socket buffer resize as warnings and not errors; --- src/common/network/udp/Socket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index bf248c27..1608a492 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -196,7 +196,7 @@ bool Socket::recvBufSize(ssize_t bufSize) if (optVal >= bufSize) return true; else - LogError(LOG_NET, "failed to resize socket recv buffer, %u != %u", optVal, bufSize); + LogWarning(LOG_NET, "Could not resize socket recv buffer, %u != %u. This is suboptimal and may result in lost packets.", optVal, bufSize); return false; } @@ -236,7 +236,7 @@ bool Socket::sendBufSize(ssize_t bufSize) if (optVal >= bufSize) return true; else - LogError(LOG_NET, "failed to resize socket send buffer, %u != %u", optVal, bufSize); + LogWarning(LOG_NET, "Could not resize socket send buffer, %u != %u. This is suboptimal and may result in lost packets.", optVal, bufSize); return false; } From 889192da762b7cc88427e2eac95b355daba11448 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 25 Sep 2025 09:07:11 -0400 Subject: [PATCH 026/200] reduce UDP recv/send buffer size to a lower reasonable value of 512K; --- src/bridge/HostBridge.cpp | 2 +- src/common/network/Network.cpp | 2 +- src/fne/network/DiagNetwork.cpp | 4 ++-- src/fne/network/FNENetwork.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index e0a0a10a..b20125f5 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -1235,7 +1235,7 @@ bool HostBridge::createNetwork() ** bryanb: resize the system UDP socket buffer used for receiving audio frames to 2M, this should hold * ~6300 raw audio frames before filling */ - if (!m_udpAudioSocket->recvBufSize(2097152U)) + if (!m_udpAudioSocket->recvBufSize(2097152U)) // 2M recv buffer LogWarning(LOG_HOST, "failed to resize UDP audio socket buffer size to 2M"); } diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index c8ca8cc6..5f421df0 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -200,7 +200,7 @@ void Network::clock(uint32_t ms) bool ret = m_socket->open(m_addr.ss_family); if (ret) { - m_socket->recvBufSize(2097152U); // 2M recv buffer + m_socket->recvBufSize(262144U); // 256K recv buffer ret = writeLogin(); if (!ret) { m_retryTimer.start(); diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index b7ebf025..029c968f 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -129,8 +129,8 @@ bool DiagNetwork::open() bool ret = m_socket->open(); if (!ret) { - m_socket->recvBufSize(2097152U); // 2M recv buffer - m_socket->sendBufSize(2097152U); // 2M recv buffer + m_socket->recvBufSize(524288U); // 512K recv buffer + m_socket->sendBufSize(524288U); // 512K send buffer m_status = NET_STAT_INVALID; } diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index ca4d3f6f..054f1427 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -537,8 +537,8 @@ bool FNENetwork::open() bool ret = m_socket->open(); if (!ret) { - m_socket->recvBufSize(2097152U); // 2M recv buffer - m_socket->sendBufSize(2097152U); // 2M recv buffer + m_socket->recvBufSize(524288U); // 512K recv buffer + m_socket->sendBufSize(524288U); // 512K send buffer m_status = NET_STAT_INVALID; } From 2399e5043fca3e2b7df3c0ee3c9929bf3819360f Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 25 Sep 2025 09:09:19 -0400 Subject: [PATCH 027/200] update README.md; --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2b806c3d..94e8966c 100644 --- a/README.md +++ b/README.md @@ -361,8 +361,8 @@ counts of up to 100,000+ calls on a x86_64 Server with 8GB RAM and 8-core proces - If you have old configuration files, missing comments or new parameters, there is a tool provided in the "tools" directory of the project called `config_annotator.py` this is a Python CLI tool designed to compare an existing configuration file against the example configuration file and recomment and add missing parameters (along with removing illegal/invalid parameters). It is recommended to backup your existing configuration file before running this tool on it. *This tool is only designed for the `dvmhost` configuration file, and no other configuration file!* -- By default Linux may restrict the maximum size of the receive and send buffers used by the kernel for network traffic. Please check the limits with `sudo sysctl net.core.rmem_max` and `sudo sysctl net.core.wmem_max`, these should be at least 2MB (2097152), while DVM will operate in lower -limits, you will see startup errors similar to: "failed to resize socket recv buffer, XXXXXX != 2097152", or "failed to resize socket send buffer, XXXXXX != 2097152". See the documentation for your specific distribution of Linux to adjust these parameters. +- By default Linux may restrict the maximum size of the receive and send buffers used by the kernel for network traffic. Please check the limits with `sudo sysctl net.core.rmem_max` and `sudo sysctl net.core.wmem_max`, these should be at least 512K (524288), while DVM will operate in lower +limits, you will see startup errors similar to: "Could not resize socket recv buffer, XXXXXX != 524288", or "Could not resize socket send buffer, XXXXXX != 524288". See the documentation for your specific distribution of Linux to adjust these parameters. ## Security Warnings From 0ac2bbd23277bbb11472cd2d56e4a00e56b6babf Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 25 Sep 2025 09:39:33 -0400 Subject: [PATCH 028/200] add more class copy safety; --- src/common/network/NetRPC.h | 4 ++++ src/common/network/PacketBuffer.h | 4 ++++ src/common/network/viface/VIFace.h | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/common/network/NetRPC.h b/src/common/network/NetRPC.h index 2b36d0e9..d93f74ae 100644 --- a/src/common/network/NetRPC.h +++ b/src/common/network/NetRPC.h @@ -70,6 +70,10 @@ namespace network UNHANDLED_REQUEST = 402, //! Unhandled Request 402 } status; + auto operator=(NetRPC&) -> NetRPC& = delete; + auto operator=(NetRPC&&) -> NetRPC& = delete; + NetRPC(NetRPC&) = delete; + /** * @brief Initializes a new instance of the NetRPC class. * @param address Network Hostname/IP address to connect to. diff --git a/src/common/network/PacketBuffer.h b/src/common/network/PacketBuffer.h index c26f2a21..37d03667 100644 --- a/src/common/network/PacketBuffer.h +++ b/src/common/network/PacketBuffer.h @@ -42,6 +42,10 @@ namespace network */ class HOST_SW_API PacketBuffer { public: + auto operator=(PacketBuffer&) -> PacketBuffer& = delete; + auto operator=(PacketBuffer&&) -> PacketBuffer& = delete; + PacketBuffer(PacketBuffer&) = delete; + /** * @brief Initializes a new instance of the PacketBuffer class. * @param compression Flag indicating whether packet data should be compressed automatically. diff --git a/src/common/network/viface/VIFace.h b/src/common/network/viface/VIFace.h index ff304465..5fe835c2 100644 --- a/src/common/network/viface/VIFace.h +++ b/src/common/network/viface/VIFace.h @@ -54,6 +54,10 @@ namespace network */ class HOST_SW_API VIFace { public: + auto operator=(VIFace&) -> VIFace& = delete; + auto operator=(VIFace&&) -> VIFace& = delete; + VIFace(VIFace&) = delete; + /** * @brief Initializes a new instance of the VIFace class. * @param name Name of the virtual interface. The placeholder %d can be used and a number will be assigned to it. From 55e63e1f9bd67bf222c0ae2a3d36842492e60273 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 25 Sep 2025 09:43:40 -0400 Subject: [PATCH 029/200] correct missing parens; --- src/host/modem/ModemV24.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index cc408462..cf907e14 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -2657,7 +2657,7 @@ void ModemV24::convertFromAirV24(uint8_t* data, uint32_t length) } if (blocksToFollow <= 3U) { - DECLARE_UINT8_ARRAY(pduBuf, ((blocksToFollow + 1U) * (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES) + 1U); + DECLARE_UINT8_ARRAY(pduBuf, ((blocksToFollow + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES)) + 1U); uint32_t pduLen = (blocksToFollow + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES); pduBuf[0U] = (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? DFSIFrameType::MOT_PDU_SINGLE_CONF : DFSIFrameType::MOT_PDU_SINGLE_UNCONF; From 1975d4f2832435d19e6f395f56fe5336782ad35a Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 25 Sep 2025 10:13:09 -0400 Subject: [PATCH 030/200] correct loop indexing; remove unused variable; --- src/vocoder/MBEEncoder.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vocoder/MBEEncoder.cpp b/src/vocoder/MBEEncoder.cpp index 3bf86e0b..de5152ff 100644 --- a/src/vocoder/MBEEncoder.cpp +++ b/src/vocoder/MBEEncoder.cpp @@ -593,7 +593,6 @@ void MBEEncoder::encodeBits(uint8_t* bits, uint8_t* codeword) assert(bits != nullptr); assert(codeword != nullptr); - int32_t errs = 0; float samples[160U]; ::memset(samples, 0x00U, 160U * sizeof(float)); @@ -702,7 +701,7 @@ void MBEEncoder::encode(int16_t* samples, uint8_t* codeword) uint8_t rawAmbe[9U]; ::memset(rawAmbe, 0x00U, 9U); - for (int i = 0; i < 9; ++i) { + for (int i = 0; i < 7; ++i) { for (int j = 0; j < 8; ++j) { rawAmbe[i] |= (bits[(i * 8) + j] << (7 - j)); } From 0a8c990ce5ed39fbdf77ba9a2a7d1044442124d3 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 25 Sep 2025 10:19:52 -0400 Subject: [PATCH 031/200] deprecate unused DIU flag (this isn't what this byte meant in the first place); --- configs/config.example.yml | 4 +--- src/host/Host.Config.cpp | 4 +--- src/host/modem/ModemV24.cpp | 3 +-- src/host/modem/ModemV24.h | 3 +-- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/configs/config.example.yml b/configs/config.example.yml index 14359fc3..13f8d29d 100644 --- a/configs/config.example.yml +++ b/configs/config.example.yml @@ -640,10 +640,8 @@ system: # V.24 Modem Configuration # dfsi: - # RT/RT flag enabled (0x02) or disabled (0x04) + # RT/RT flag. rtrt: true - # Use the DIU source flag (0x00) instead of the quantar source flag (0x02) - diu: true # Jitter buffer length in ms jitter: 200 # Timer which will reset local/remote call flags if frames aren't received longer than this time in ms diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp index 3c4bb617..c0aee3c7 100644 --- a/src/host/Host.Config.cpp +++ b/src/host/Host.Config.cpp @@ -511,7 +511,6 @@ bool Host::createModem() yaml::Node dfsiParams = modemConf["dfsi"]; bool rtrt = dfsiParams["rtrt"].as(true); - bool diu = dfsiParams["diu"].as(true); uint16_t jitter = dfsiParams["jitter"].as(200U); uint16_t dfsiCallTimeout = dfsiParams["callTimeout"].as(200U); bool useFSCForUDP = dfsiParams["fsc"].as(false); @@ -632,7 +631,6 @@ bool Host::createModem() if (modemMode == MODEM_MODE_DFSI) { m_isModemDFSI = true; LogInfo(" DFSI RT/RT: %s", rtrt ? "yes" : "no"); - LogInfo(" DFSI DIU Flag: %s", diu ? "yes" : "no"); LogInfo(" DFSI Jitter Size: %u ms", jitter); if (g_remoteModemMode) { LogInfo(" DFSI Use FSC: %s", useFSCForUDP ? "yes" : "no"); @@ -719,7 +717,7 @@ bool Host::createModem() } if (m_isModemDFSI) { - m_modem = new ModemV24(modemPort, m_duplex, m_p25QueueSizeBytes, m_p25QueueSizeBytes, rtrt, diu, jitter, + m_modem = new ModemV24(modemPort, m_duplex, m_p25QueueSizeBytes, m_p25QueueSizeBytes, rtrt, jitter, dumpModemStatus, trace, debug); ((ModemV24*)m_modem)->setCallTimeout(dfsiCallTimeout); ((ModemV24*)m_modem)->setTIAFormat(dfsiTIAMode); diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index cf907e14..0bd4a970 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -40,11 +40,10 @@ using namespace p25::dfsi::frames; /* Initializes a new instance of the ModemV24 class. */ ModemV24::ModemV24(port::IModemPort* port, bool duplex, uint32_t p25QueueSize, uint32_t p25TxQueueSize, - bool rtrt, bool diu, uint16_t jitter, bool dumpModemStatus, bool trace, bool debug) : + bool rtrt, uint16_t jitter, bool dumpModemStatus, bool trace, bool debug) : Modem(port, duplex, false, false, false, false, false, 80U, 7U, 8U, 1U, p25QueueSize, 1U, false, false, dumpModemStatus, trace, debug), m_rtrt(rtrt), - m_diu(diu), m_superFrameCnt(0U), m_audio(), m_nid(nullptr), diff --git a/src/host/modem/ModemV24.h b/src/host/modem/ModemV24.h index 8c0feb55..9a251f16 100644 --- a/src/host/modem/ModemV24.h +++ b/src/host/modem/ModemV24.h @@ -498,7 +498,7 @@ namespace modem * @param debug Flag indicating whether air interface modem debug is enabled. */ ModemV24(port::IModemPort* port, bool duplex, uint32_t p25QueueSize, uint32_t p25TxQueueSize, - bool rtrt, bool diu, uint16_t jitter, bool dumpModemStatus, bool trace, bool debug); + bool rtrt, uint16_t jitter, bool dumpModemStatus, bool trace, bool debug); /** * @brief Finalizes a instance of the ModemV24 class. */ @@ -554,7 +554,6 @@ namespace modem private: bool m_rtrt; - bool m_diu; uint8_t m_superFrameCnt; From dffe262923810007f6a15cd39db3a6837bfed917 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 30 Sep 2025 14:53:02 -0400 Subject: [PATCH 032/200] add call start marker to main application log; --- src/host/dmr/packet/Data.cpp | 2 ++ src/host/dmr/packet/Voice.cpp | 5 +++++ src/host/nxdn/packet/Data.cpp | 2 ++ src/host/nxdn/packet/Voice.cpp | 4 ++++ src/host/p25/packet/Data.cpp | 2 ++ src/host/p25/packet/Voice.cpp | 2 ++ 6 files changed, 17 insertions(+) diff --git a/src/host/dmr/packet/Data.cpp b/src/host/dmr/packet/Data.cpp index 22bc1d49..870338f3 100644 --- a/src/host/dmr/packet/Data.cpp +++ b/src/host/dmr/packet/Data.cpp @@ -245,6 +245,7 @@ bool Data::process(uint8_t* data, uint32_t len) } ::ActivityLog("DMR", true, "Slot %u RF data header from %u to %s%u, %u blocks", m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId, m_slot->m_rfFrames); + LogMessage(LOG_RF, "DMR Data Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); ::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U); m_pduDataOffset = 0U; @@ -492,6 +493,7 @@ void Data::processNetwork(const data::NetData& dmrData) ::ActivityLog("DMR", false, "Slot %u network data header from %u to %s%u, %u blocks", m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId, m_slot->m_netFrames); + LogMessage(LOG_NET, "DMR Data Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); ::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U); m_pduDataOffset = 0U; diff --git a/src/host/dmr/packet/Voice.cpp b/src/host/dmr/packet/Voice.cpp index ca574e96..c59c0e02 100644 --- a/src/host/dmr/packet/Voice.cpp +++ b/src/host/dmr/packet/Voice.cpp @@ -215,6 +215,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } ::ActivityLog("DMR", true, "Slot %u RF %svoice header from %u to %s%u", m_slot->m_slotNo, encrypted ? "encrypted " : "", srcId, flco == FLCO::GROUP ? "TG " : "", dstId); + LogMessage(LOG_RF, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); return true; } else if (dataType == DataType::VOICE_PI_HEADER) { @@ -656,6 +657,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } ::ActivityLog("DMR", true, "Slot %u RF late entry from %u to %s%u", m_slot->m_slotNo, srcId, flco == FLCO::GROUP ? "TG " : "", dstId); + LogMessage(LOG_RF, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); return true; } } @@ -772,6 +774,7 @@ void Voice::processNetwork(const data::NetData& dmrData) } ::ActivityLog("DMR", false, "Slot %u network voice header from %u to %s%u", m_slot->m_slotNo, srcId, flco == FLCO::GROUP ? "TG " : "", dstId); + LogMessage(LOG_NET, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); } else if (dataType == DataType::VOICE_PI_HEADER) { if (m_slot->m_netState != RS_NET_AUDIO) { @@ -839,6 +842,7 @@ void Voice::processNetwork(const data::NetData& dmrData) ::ActivityLog("DMR", false, "Slot %u network late entry from %u to %s%u", m_slot->m_slotNo, srcId, m_slot->m_netLC->getFLCO() == FLCO::GROUP ? "TG " : "", dstId); + LogMessage(LOG_NET, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); } lc::FullLC fullLC; @@ -951,6 +955,7 @@ void Voice::processNetwork(const data::NetData& dmrData) ::ActivityLog("DMR", false, "Slot %u network late entry from %u to %s%u", m_slot->m_slotNo, srcId, m_slot->m_netLC->getFLCO() == FLCO::GROUP ? "TG " : "", dstId); + LogMessage(LOG_NET, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); } if (m_slot->m_netState == RS_NET_AUDIO) { diff --git a/src/host/nxdn/packet/Data.cpp b/src/host/nxdn/packet/Data.cpp index 0d432a6c..9198b564 100644 --- a/src/host/nxdn/packet/Data.cpp +++ b/src/host/nxdn/packet/Data.cpp @@ -152,6 +152,7 @@ bool Data::process(ChOption::E option, uint8_t* data, uint32_t len) } ::ActivityLog("NXDN", true, "RF data transmission from %u to %s%u", srcId, group ? "TG " : "", dstId); + LogMessage(LOG_RF, "NXDN Data Call, srcId = %u, dstId = %u", srcId, dstId); m_nxdn->m_rfLC = lc; m_nxdn->m_voice->m_rfFrames = 0U; @@ -320,6 +321,7 @@ bool Data::processNetwork(ChOption::E option, lc::RTCH& netLC, uint8_t* data, ui } ::ActivityLog("NXDN", false, "network data transmission from %u to %s%u", srcId, group ? "TG " : "", dstId); + LogMessage(LOG_NET, "NXDN Data Call, srcId = %u, dstId = %u", srcId, dstId); m_nxdn->m_netLC = lc; m_nxdn->m_voice->m_netFrames = 0U; diff --git a/src/host/nxdn/packet/Voice.cpp b/src/host/nxdn/packet/Voice.cpp index 755ca1c7..79a88b77 100644 --- a/src/host/nxdn/packet/Voice.cpp +++ b/src/host/nxdn/packet/Voice.cpp @@ -253,6 +253,7 @@ bool Voice::process(FuncChannelType::E fct, ChOption::E option, uint8_t* data, u } ::ActivityLog("NXDN", true, "RF %svoice transmission from %u to %s%u", encrypted ? "encrypted " : "", srcId, group ? "TG " : "", dstId); + LogMessage(LOG_RF, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); } return true; @@ -404,6 +405,7 @@ bool Voice::process(FuncChannelType::E fct, ChOption::E option, uint8_t* data, u } ::ActivityLog("NXDN", true, "RF %slate entry from %u to %s%u", encrypted ? "encrypted ": "", srcId, group ? "TG " : "", dstId); + LogMessage(LOG_RF, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); // create a dummy start message uint8_t start[NXDN_FRAME_LENGTH_BYTES + 2U]; @@ -759,6 +761,7 @@ bool Voice::processNetwork(FuncChannelType::E fct, ChOption::E option, lc::RTCH& } ::ActivityLog("NXDN", false, "network %svoice transmission from %u to %s%u", encrypted ? "encrypted " : "", srcId, group ? "TG " : "", dstId); + LogMessage(LOG_NET, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); } return true; @@ -895,6 +898,7 @@ bool Voice::processNetwork(FuncChannelType::E fct, ChOption::E option, lc::RTCH& } ::ActivityLog("NXDN", false, "network %slate entry from %u to %s%u", encrypted ? "encrypted ": "", srcId, group ? "TG " : "", dstId); + LogMessage(LOG_NET, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); // create a dummy start message uint8_t start[NXDN_FRAME_LENGTH_BYTES + 2U]; diff --git a/src/host/p25/packet/Data.cpp b/src/host/p25/packet/Data.cpp index 915679ba..a11b2052 100644 --- a/src/host/p25/packet/Data.cpp +++ b/src/host/p25/packet/Data.cpp @@ -463,6 +463,7 @@ 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) { ::ActivityLog("P25", true, "RF data transmission from %u to %u, %u blocks", srcId, dstId, m_rfDataHeader.getBlocksToFollow()); + LogMessage(LOG_RF, "P25 Data Call, srcId = %u, dstId = %u", srcId, dstId); if (m_verbose) { LogMessage(LOG_RF, P25_PDU_STR ", repeating PDU, llId = %u", (m_rfExtendedAddress) ? m_rfDataHeader.getSrcLLId() : m_rfDataHeader.getLLId()); @@ -770,6 +771,7 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) break; default: ::ActivityLog("P25", false, "Net data transmission from %u to %u, %u blocks", srcId, dstId, m_netDataHeader.getBlocksToFollow()); + LogMessage(LOG_NET, "P25 Data Call, srcId = %u, dstId = %u", srcId, dstId); if (m_verbose) { LogMessage(LOG_NET, P25_PDU_STR ", transmitting network PDU, llId = %u", (m_netExtendedAddress) ? m_netDataHeader.getSrcLLId() : m_netDataHeader.getLLId()); diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index ef1e32db..f227d843 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -374,6 +374,7 @@ bool Voice::process(uint8_t* data, uint32_t len) m_lastRejectId = 0U; ::ActivityLog("P25", true, "RF %svoice transmission from %u to %s%u", encrypted ? "encrypted ": "", srcId, group ? "TG " : "", dstId); + LogMessage(LOG_RF, "P25 Voice Call, srcId = %u, dstId = %u", srcId, dstId); uint8_t serviceOptions = (m_rfLC.getEmergency() ? 0x80U : 0x00U) + // Emergency Flag (m_rfLC.getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag @@ -1835,6 +1836,7 @@ void Voice::writeNet_LDU1() m_p25->writeRF_Preamble(); ::ActivityLog("P25", false, "network %svoice transmission from %u to %s%u", m_netLC.getEncrypted() ? "encrypted " : "", srcId, group ? "TG " : "", dstId); + LogMessage(LOG_NET, "P25 Voice Call, srcId = %u, dstId = %u", srcId, dstId); // conventional registration or DVRS support? if (((m_p25->m_enableControl && !m_p25->m_dedicatedControl) || m_p25->m_voiceOnControl) && !m_p25->m_disableNetworkGrant) { From 32e0b977712dd10639201c2bea83c6ebe9244cba Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 30 Sep 2025 23:10:36 -0400 Subject: [PATCH 033/200] remove SIP classes (this will be done by a different team, and done differently); begin refactoring Log and ActivityLog implementations into C++-type functions to do away with the C-style va_args functions for log message handling; correct -Wstringop-trunction warnings from BaseNetwork.cpp (we hide the warning for these because we are intentionally copying these strings without the nul terminator); --- src/bridge/ActivityLog.cpp | 41 +-- src/bridge/ActivityLog.h | 59 ++- src/common/Log.cpp | 145 +------- src/common/Log.h | 237 +++++++++++- src/common/Utils.cpp | 10 +- src/common/network/BaseNetwork.cpp | 23 +- src/common/network/sip/RequestDispatcher.h | 219 ----------- src/common/network/sip/SIPHeaders.h | 149 -------- src/common/network/sip/SIPLexer.cpp | 402 --------------------- src/common/network/sip/SIPLexer.h | 192 ---------- src/common/network/sip/SIPPayload.cpp | 220 ----------- src/common/network/sip/SIPPayload.h | 147 -------- src/fne/ActivityLog.cpp | 20 +- src/fne/ActivityLog.h | 39 +- src/fne/network/DiagNetwork.cpp | 2 +- src/host/ActivityLog.cpp | 65 +--- src/host/ActivityLog.h | 66 +++- src/host/setup/SetupMainWnd.h | 2 +- src/monitor/MonitorMainWnd.h | 2 +- src/patch/ActivityLog.cpp | 39 +- src/patch/ActivityLog.h | 57 ++- src/peered/PeerEdMainWnd.h | 2 +- src/sysview/HostWS.cpp | 2 +- src/sysview/SysViewMainWnd.h | 2 +- src/sysview/network/PeerNetwork.cpp | 2 +- src/tged/TGEdMainWnd.h | 2 +- 26 files changed, 492 insertions(+), 1654 deletions(-) delete mode 100644 src/common/network/sip/RequestDispatcher.h delete mode 100644 src/common/network/sip/SIPHeaders.h delete mode 100644 src/common/network/sip/SIPLexer.cpp delete mode 100644 src/common/network/sip/SIPLexer.h delete mode 100644 src/common/network/sip/SIPPayload.cpp delete mode 100644 src/common/network/sip/SIPPayload.h diff --git a/src/bridge/ActivityLog.cpp b/src/bridge/ActivityLog.cpp index 8e4e5715..61c61b8c 100644 --- a/src/bridge/ActivityLog.cpp +++ b/src/bridge/ActivityLog.cpp @@ -4,19 +4,13 @@ * 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 + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ #include "ActivityLog.h" #include "common/network/BaseNetwork.h" #include "common/Log.h" // for CurrentLogFileLevel() and LogGetNetwork() -#if defined(_WIN32) -#include "common/Clock.h" -#else -#include -#endif // defined(_WIN32) - #if defined(CATCH2_TEST_COMPILATION) #include #endif @@ -106,50 +100,23 @@ void ActivityLogFinalise() /* Writes a new entry to the activity log. */ -void ActivityLog(const char* msg, ...) +void log_internal::ActivityLogInternal(const std::string& log) { #if defined(CATCH2_TEST_COMPILATION) return; #endif - assert(msg != nullptr); - - char buffer[ACT_LOG_BUFFER_LEN]; - time_t now; - ::time(&now); - struct tm* tm = ::localtime(&now); - - struct timeval nowMillis; - ::gettimeofday(&nowMillis, NULL); - - ::sprintf(buffer, "A: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); - - va_list vl, vl_len; - va_start(vl, msg); - va_copy(vl_len, vl); - - size_t len = ::vsnprintf(nullptr, 0U, msg, vl_len); - ::vsnprintf(buffer + ::strlen(buffer), len + 1U, msg, vl); - - va_end(vl_len); - va_end(vl); - bool ret = ::ActivityLogOpen(); if (!ret) return; - if (LogGetNetwork() != nullptr) { - network::BaseNetwork* network = (network::BaseNetwork*)LogGetNetwork();; - network->writeActLog(buffer); - } - if (CurrentLogFileLevel() == 0U) return; - ::fprintf(m_actFpLog, "%s\n", buffer); + ::fprintf(m_actFpLog, "%s\n", log.c_str()); ::fflush(m_actFpLog); if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) { - ::fprintf(stdout, "%s" EOL, buffer); + ::fprintf(stdout, "%s" EOL, log.c_str()); ::fflush(stdout); } } diff --git a/src/bridge/ActivityLog.h b/src/bridge/ActivityLog.h index 736d14d1..9b341fa1 100644 --- a/src/bridge/ActivityLog.h +++ b/src/bridge/ActivityLog.h @@ -4,7 +4,7 @@ * 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 + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -18,12 +18,28 @@ #include "Defines.h" +#if defined(_WIN32) +#include "common/Clock.h" +#else +#include +#endif // defined(_WIN32) + #include // --------------------------------------------------------------------------- // Global Functions // --------------------------------------------------------------------------- +namespace log_internal +{ + /** + * @brief Writes a new entry to the diagnostics log. + * @param level Log level for entry. + * @param log Fully formatted log message. + */ + extern HOST_SW_API void ActivityLogInternal(const std::string& log); +} // namespace log_internal + /** * @brief Initializes the activity log. * @param filePath File path for the log file. @@ -34,12 +50,45 @@ extern HOST_SW_API bool ActivityLogInitialise(const std::string& filePath, const * @brief Finalizes the activity log. */ extern HOST_SW_API void ActivityLogFinalise(); + /** - * @brief Writes a new entry to the activity log. - * @param msg String format. + * @brief Writes a new entry to the diagnostics log. + * @param fmt String format. * - * This is a variable argument function. + * This is a variable argument function. This shouldn't be called directly, utilize the LogXXXX macros above, instead. */ -extern HOST_SW_API void ActivityLog(const char* msg, ...); +template +HOST_SW_API void ActivityLog(const std::string& fmt, Args... args) +{ + using namespace log_internal; + + int size_s = std::snprintf(nullptr, 0, fmt.c_str(), args...) + 1; // Extra space for '\0' + if (size_s <= 0) { + throw std::runtime_error("Error during formatting."); + } + + int prefixLen = 0; + char prefixBuf[256]; + + time_t now; + ::time(&now); + struct tm* tm = ::localtime(&now); + + struct timeval nowMillis; + ::gettimeofday(&nowMillis, NULL); + + prefixLen = ::sprintf(prefixBuf, "A: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); + + auto size = static_cast(size_s); + auto buf = std::make_unique(size); + + std::snprintf(buf.get(), size, fmt.c_str(), args ...); + + std::string prefix = std::string(prefixBuf, prefixBuf + prefixLen); + std::string msg = std::string(buf.get(), buf.get() + size - 1); + + ActivityLogInternal(std::string(prefix + msg)); +} #endif // __ACTIVITY_LOG_H__ diff --git a/src/common/Log.cpp b/src/common/Log.cpp index 10a835a1..89d4580f 100644 --- a/src/common/Log.cpp +++ b/src/common/Log.cpp @@ -11,21 +11,17 @@ #include "Log.h" #include "network/BaseNetwork.h" -#if defined(_WIN32) -#include "Clock.h" -#else -#include -#include -#endif // defined(_WIN32) - #if defined(CATCH2_TEST_COMPILATION) #include #endif +#if !defined(_WIN32) +#include +#endif // defined(_WIN32) + #include #include #include -#include #include #include #include @@ -60,8 +56,6 @@ static struct tm m_tm; static std::ostream m_outStream { std::cerr.rdbuf() }; -static char LEVELS[] = " DMIWEF"; - // --------------------------------------------------------------------------- // Global Functions // --------------------------------------------------------------------------- @@ -140,13 +134,6 @@ static bool LogOpen() } } -/* Internal helper to set an output stream to direct logging to. */ - -void __InternalOutputStream(std::ostream& stream) -{ - m_outStream.rdbuf(stream.rdbuf()); -} - /* Gets the instance of the Network class to transfer the activity log with. */ void* LogGetNetwork() @@ -202,128 +189,30 @@ void LogFinalise() #endif // !defined(_WIN32) } -/* Writes a new entry to the diagnostics log. */ +/* Internal helper to set an output stream to direct logging to. */ -void Log(uint32_t level, const char *module, const char* file, const int lineNo, const char* func, const char* fmt, ...) +void log_internal::SetInternalOutputStream(std::ostream& stream) { - assert(fmt != nullptr); -#if defined(CATCH2_TEST_COMPILATION) - g_disableTimeDisplay = true; -#endif - char buffer[LOG_BUFFER_LEN]; - if (!g_disableTimeDisplay && !g_useSyslog) { - time_t now; - ::time(&now); - struct tm* tm = ::localtime(&now); - - struct timeval nowMillis; - ::gettimeofday(&nowMillis, NULL); - - if (module != nullptr) { - // level 1 is DEBUG - if (level == 1U) { - // if we have a file and line number -- add that to the log entry - if (file != nullptr && lineNo > 0) { - // if we have a function name add that to the log entry - if (func != nullptr) { - ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s)[%s:%u][%s] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module, file, lineNo, func); - } - else { - ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s)[%s:%u] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module, file, lineNo); - } - } else { - ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s) ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module); - } - } else { - ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s) ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module); - } - } - else { - // level 1 is DEBUG - if (level == 1U) { - // if we have a file and line number -- add that to the log entry - if (file != nullptr && lineNo > 0) { - // if we have a function name add that to the log entry - if (func != nullptr) { - ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu [%s:%u][%s] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, file, lineNo, func); - } - else { - ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu [%s:%u] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, file, lineNo); - } - } else { - ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); - } - } else { - ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); - } - } - } - else { - if (module != nullptr) { - // level 1 is DEBUG - if (level == 1U) { - // if we have a file and line number -- add that to the log entry - if (file != nullptr && lineNo > 0) { - // if we have a function name add that to the log entry - if (func != nullptr) { - ::sprintf(buffer, "%c: (%s)[%s:%u][%s] ", LEVELS[level], module, file, lineNo, func); - } - else { - ::sprintf(buffer, "%c: (%s)[%s:%u] ", LEVELS[level], module, file, lineNo); - } - } - else { - ::sprintf(buffer, "%c: (%s) ", LEVELS[level], module); - } - } else { - ::sprintf(buffer, "%c: (%s) ", LEVELS[level], module); - } - } - else { - if (level >= 9999U) { - ::sprintf(buffer, "U: "); - } - else { - // if we have a file and line number -- add that to the log entry - if (file != nullptr && lineNo > 0) { - // if we have a function name add that to the log entry - if (func != nullptr) { - ::sprintf(buffer, "%c: [%s:%u][%s] ", LEVELS[level], file, lineNo, func); - } - else { - ::sprintf(buffer, "%c: [%s:%u] ", LEVELS[level], file, lineNo); - } - } - else { - ::sprintf(buffer, "%c: ", LEVELS[level]); - } - } - } - } - - va_list vl, vl_len; - va_start(vl, fmt); - va_copy(vl_len, vl); - - size_t len = ::vsnprintf(nullptr, 0U, fmt, vl_len); - ::vsnprintf(buffer + ::strlen(buffer), len + 1U, fmt, vl); + m_outStream.rdbuf(stream.rdbuf()); +} - va_end(vl_len); - va_end(vl); +/* Writes a new entry to the diagnostics log. */ +void log_internal::LogInternal(uint32_t level, const std::string& log) +{ if (m_outStream && g_logDisplayLevel == 0U) { - m_outStream << buffer << std::endl; + m_outStream << log << std::endl; } if (m_network != nullptr && !g_disableNetworkLog) { // don't transfer debug data... if (level > 1U) { - m_network->writeDiagLog(buffer); + m_network->writeDiagLog(log.c_str()); } } #if defined(CATCH2_TEST_COMPILATION) - UNSCOPED_INFO(buffer); + UNSCOPED_INFO(log.c_str()); return; #endif @@ -334,7 +223,7 @@ void Log(uint32_t level, const char *module, const char* file, const int lineNo, return; if (m_fpLog != nullptr) { - ::fprintf(m_fpLog, "%s\n", buffer); + ::fprintf(m_fpLog, "%s\n", log.c_str()); ::fflush(m_fpLog); } } else { @@ -363,13 +252,13 @@ void Log(uint32_t level, const char *module, const char* file, const int lineNo, break; } - syslog(syslogLevel, "%s", buffer); + syslog(syslogLevel, "%s", log.c_str()); #endif // !defined(_WIN32) } } if (!g_useSyslog && level >= g_logDisplayLevel && g_logDisplayLevel != 0U) { - ::fprintf(stdout, "%s" EOL, buffer); + ::fprintf(stdout, "%s" EOL, log.c_str()); ::fflush(stdout); } diff --git a/src/common/Log.h b/src/common/Log.h index 2ee85a2f..7450d4e4 100644 --- a/src/common/Log.h +++ b/src/common/Log.h @@ -22,7 +22,13 @@ #define __LOG_H__ #include "common/Defines.h" +#if defined(_WIN32) +#include "common/Clock.h" +#else +#include +#endif // defined(_WIN32) +#include #include /** @@ -38,7 +44,6 @@ #define LOG_HOST "HOST" #define LOG_REST "RESTAPI" -#define LOG_SIP "SIP" #define LOG_MODEM "MODEM" #define LOG_RF "RF" #define LOG_NET "NET" @@ -63,7 +68,7 @@ * * This is a variable argument function. */ -#define LogDebug(_module, fmt, ...) Log(1U, _module, __FILE__, __LINE__, nullptr, fmt, ##__VA_ARGS__) +#define LogDebug(_module, fmt, ...) Log(1U, {_module, __FILE__, __LINE__, nullptr}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a debug log entry. * @param _module Name of module generating log entry. @@ -72,7 +77,7 @@ * * This is a variable argument function. */ -#define LogDebugEx(_module, _func, fmt, ...) Log(1U, _module, __FILE__, __LINE__, _func, fmt, ##__VA_ARGS__) +#define LogDebugEx(_module, _func, fmt, ...) Log(1U, {_module, __FILE__, __LINE__, _func}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a message log entry. * @param _module Name of module generating log entry. @@ -80,7 +85,7 @@ * * This is a variable argument function. */ -#define LogMessage(_module, fmt, ...) Log(2U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) +#define LogMessage(_module, fmt, ...) Log(2U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a informational log entry. * @param _module Name of module generating log entry. @@ -89,7 +94,7 @@ * This is a variable argument function. LogInfo() does not use a module * name when creating a log entry. */ -#define LogInfo(fmt, ...) Log(3U, nullptr, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) +#define LogInfo(fmt, ...) Log(3U, {nullptr, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a informational log entry with module name. * @param _module Name of module generating log entry. @@ -97,7 +102,7 @@ * * This is a variable argument function. */ -#define LogInfoEx(_module, fmt, ...) Log(3U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) +#define LogInfoEx(_module, fmt, ...) Log(3U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a warning log entry. * @param _module Name of module generating log entry. @@ -105,7 +110,7 @@ * * This is a variable argument function. */ -#define LogWarning(_module, fmt, ...) Log(4U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) +#define LogWarning(_module, fmt, ...) Log(4U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a error log entry. * @param _module Name of module generating log entry. @@ -113,7 +118,7 @@ * * This is a variable argument function. */ -#define LogError(_module, fmt, ...) Log(5U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) +#define LogError(_module, fmt, ...) Log(5U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a fatal log entry. * @param _module Name of module generating log entry. @@ -121,7 +126,61 @@ * * This is a variable argument function. */ -#define LogFatal(_module, fmt, ...) Log(6U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) +#define LogFatal(_module, fmt, ...) Log(6U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) + +namespace log_internal +{ + constexpr static char LOG_LEVELS[] = " DMIWEF"; + + // --------------------------------------------------------------------------- + // Structure Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents a source code location. + * @ingroup logger + */ + struct SourceLocation { + public: + /** + * @brief Initializes a new instance of the SourceLocation class. + */ + constexpr SourceLocation() = default; + /** + * @brief Initializes a new instance of the SourceLocation class. + * @param module Application module. + * @param filename Source code filename. + * @param line Line number in source code file. + * @param func Function name within source code. + */ + constexpr SourceLocation(const char* module, const char* filename, int line, const char* func) : + module(module), + filename(filename), + line(line), + funcname(func) + { + /* stub */ + } + + public: + const char* module = nullptr; + const char* filename = nullptr; + int line = 0; + const char* funcname = nullptr; + }; + + /** + * @brief Internal helper to set an output stream to direct logging to. + * @param stream + */ + extern HOST_SW_API void SetInternalOutputStream(std::ostream& stream); + /** + * @brief Writes a new entry to the diagnostics log. + * @param level Log level for entry. + * @param log Fully formatted log message. + */ + extern HOST_SW_API void LogInternal(uint32_t level, const std::string& log); +} // namespace log_internal // --------------------------------------------------------------------------- // Externs @@ -147,11 +206,6 @@ extern bool g_disableNetworkLog; // --------------------------------------------------------------------------- // Global Functions // --------------------------------------------------------------------------- -/** - * @brief Internal helper to set an output stream to direct logging to. - * @param stream - */ -extern HOST_SW_API void __InternalOutputStream(std::ostream& stream); /** * @brief Helper to get the current log file level. @@ -191,23 +245,166 @@ extern HOST_SW_API void LogSetNetwork(void* network); * @param syslog Flag indicating whether or not logs will be sent to syslog. * @returns */ -extern HOST_SW_API bool LogInitialise(const std::string& filePath, const std::string& fileRoot, uint32_t fileLevel, uint32_t displayLevel, bool disableTimeDisplay = false, bool useSyslog = false); +extern HOST_SW_API bool LogInitialise(const std::string& filePath, const std::string& fileRoot, + uint32_t fileLevel, uint32_t displayLevel, bool disableTimeDisplay = false, bool useSyslog = false); /** * @brief Finalizes the diagnostics log. */ extern HOST_SW_API void LogFinalise(); + /** * @brief Writes a new entry to the diagnostics log. * @param level Log level for entry. - * @param module Name of module generating log entry. - * @param file Name of source code file generating log entry. - * @param line Line number in source code file generating log entry. - * @param func Name of function generating log entry. + * @param sourceLog Source code location information. * @param fmt String format. * * This is a variable argument function. This shouldn't be called directly, utilize the LogXXXX macros above, instead. */ -extern HOST_SW_API void Log(uint32_t level, const char* module, const char* file, const int lineNo, const char* func, const char* fmt, ...); +template +HOST_SW_API void Log(uint32_t level, log_internal::SourceLocation sourceLoc, const std::string& fmt, Args... args) +{ + using namespace log_internal; + + int size_s = std::snprintf(nullptr, 0, fmt.c_str(), args...) + 1; // Extra space for '\0' + if (size_s <= 0) { + throw std::runtime_error("Error during formatting."); + } + +#if defined(CATCH2_TEST_COMPILATION) + g_disableTimeDisplay = true; +#endif + int prefixLen = 0; + char prefixBuf[256]; + + if (!g_disableTimeDisplay && !g_useSyslog) { + time_t now; + ::time(&now); + struct tm* tm = ::localtime(&now); + + struct timeval nowMillis; + ::gettimeofday(&nowMillis, NULL); + + if (level > 7U) + level = 3U; // default this sort of log message to INFO + + if (sourceLoc.module != nullptr) { + // level 1 is DEBUG + if (level == 1U) { + // if we have a file and line number -- add that to the log entry + if (sourceLoc.filename != nullptr && sourceLoc.line > 0) { + // if we have a function name add that to the log entry + if (sourceLoc.funcname != nullptr) { + prefixLen = ::sprintf(prefixBuf, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s)[%s:%u][%s] ", LOG_LEVELS[level], + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, + sourceLoc.module, sourceLoc.filename, sourceLoc.line, sourceLoc.funcname); + } + else { + prefixLen = ::sprintf(prefixBuf, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s)[%s:%u] ", LOG_LEVELS[level], + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, + sourceLoc.module, sourceLoc.filename, sourceLoc.line); + } + } else { + prefixLen = ::sprintf(prefixBuf, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s) ", LOG_LEVELS[level], + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, + sourceLoc.module); + } + } else { + prefixLen = ::sprintf(prefixBuf, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s) ", LOG_LEVELS[level], + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, + sourceLoc.module); + } + } + else { + // level 1 is DEBUG + if (level == 1U) { + // if we have a file and line number -- add that to the log entry + if (sourceLoc.filename != nullptr && sourceLoc.line > 0) { + // if we have a function name add that to the log entry + if (sourceLoc.funcname != nullptr) { + prefixLen = ::sprintf(prefixBuf, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu [%s:%u][%s] ", LOG_LEVELS[level], + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, + sourceLoc.filename, sourceLoc.line, sourceLoc.funcname); + } + else { + prefixLen = ::sprintf(prefixBuf, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu [%s:%u] ", LOG_LEVELS[level], + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, + sourceLoc.filename, sourceLoc.line); + } + } else { + prefixLen = ::sprintf(prefixBuf, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LOG_LEVELS[level], tm->tm_year + 1900, + tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); + } + } else { + prefixLen = ::sprintf(prefixBuf, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LOG_LEVELS[level], + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); + } + } + } + else { + if (sourceLoc.module != nullptr) { + if (level > 7U) + level = 3U; // default this sort of log message to INFO + + // level 1 is DEBUG + if (level == 1U) { + // if we have a file and line number -- add that to the log entry + if (sourceLoc.filename != nullptr && sourceLoc.line > 0) { + // if we have a function name add that to the log entry + if (sourceLoc.funcname != nullptr) { + prefixLen = ::sprintf(prefixBuf, "%c: (%s)[%s:%u][%s] ", LOG_LEVELS[level], + sourceLoc.module, sourceLoc.filename, sourceLoc.line, sourceLoc.funcname); + } + else { + prefixLen = ::sprintf(prefixBuf, "%c: (%s)[%s:%u] ", LOG_LEVELS[level], + sourceLoc.module, sourceLoc.filename, sourceLoc.line); + } + } + else { + prefixLen = ::sprintf(prefixBuf, "%c: (%s) ", LOG_LEVELS[level], + sourceLoc.module); + } + } else { + prefixLen = ::sprintf(prefixBuf, "%c: (%s) ", LOG_LEVELS[level], + sourceLoc.module); + } + } + else { + if (level >= 9999U) { + prefixLen = ::sprintf(prefixBuf, "U: "); + } + else { + if (level > 7U) + level = 3U; // default this sort of log message to INFO + + // if we have a file and line number -- add that to the log entry + if (sourceLoc.filename != nullptr && sourceLoc.line > 0) { + // if we have a function name add that to the log entry + if (sourceLoc.funcname != nullptr) { + prefixLen = ::sprintf(prefixBuf, "%c: [%s:%u][%s] ", LOG_LEVELS[level], + sourceLoc.filename, sourceLoc.line, sourceLoc.funcname); + } + else { + prefixLen = ::sprintf(prefixBuf, "%c: [%s:%u] ", LOG_LEVELS[level], + sourceLoc.filename, sourceLoc.line); + } + } + else { + prefixLen = ::sprintf(prefixBuf, "%c: ", LOG_LEVELS[level]); + } + } + } + } + + auto size = static_cast(size_s); + auto buf = std::make_unique(size); + + std::snprintf(buf.get(), size, fmt.c_str(), args ...); + + std::string prefix = std::string(prefixBuf, prefixBuf + prefixLen); + std::string msg = std::string(buf.get(), buf.get() + size - 1); + + LogInternal(level, std::string(prefix + msg)); +} /** @} */ #endif // __LOG_H__ diff --git a/src/common/Utils.cpp b/src/common/Utils.cpp index 12ac2e8d..4fa66dce 100644 --- a/src/common/Utils.cpp +++ b/src/common/Utils.cpp @@ -72,7 +72,7 @@ void Utils::dump(int level, const std::string& title, const uint8_t* data, uint3 { assert(data != nullptr); - ::Log(level, "DUMP", nullptr, 0, nullptr, "%s (len %u)", title.c_str(), length); + ::Log(level, {"DUMP", nullptr, 0, nullptr}, "%s (len %u)", title.c_str(), length); uint32_t offset = 0U; @@ -103,7 +103,7 @@ void Utils::dump(int level, const std::string& title, const uint8_t* data, uint3 output += '*'; #endif - ::Log(level, "DUMP", nullptr, 0, nullptr, "%04X: %s", offset, output.c_str()); + ::Log(level, {"DUMP", nullptr, 0, nullptr}, "%04X: %s", offset, output.c_str()); offset += 16U; @@ -143,7 +143,7 @@ void Utils::symbols(const std::string& title, const uint8_t* data, uint32_t leng { assert(data != nullptr); - ::Log(2U, "SYMBOLS", nullptr, 0, nullptr, "%s (len %u)", title.c_str(), length); + ::Log(2U, {"SYMBOLS", nullptr, 0, nullptr}, "%s (len %u)", title.c_str(), length); uint32_t offset = 0U; uint32_t count = 0U; @@ -158,7 +158,7 @@ void Utils::symbols(const std::string& title, const uint8_t* data, uint32_t leng microslotHeader += temp; } - ::Log(2U, "SYMBOLS", nullptr, 0, nullptr, "MCR: %s", microslotHeader.c_str()); + ::Log(2U, {"SYMBOLS", nullptr, 0, nullptr}, "MCR: %s", microslotHeader.c_str()); uint32_t bufLen = length; while (bufLen > 0U) { @@ -188,7 +188,7 @@ void Utils::symbols(const std::string& title, const uint8_t* data, uint32_t leng symOffset += 9; } - ::Log(2U, "SYMBOLS", nullptr, 0, nullptr, "%03u: %s", count, output.c_str()); + ::Log(2U, {"SYMBOLS", nullptr, 0, nullptr}, "%03u: %s", count, output.c_str()); offset += 18U; count += 2U; diff --git a/src/common/network/BaseNetwork.cpp b/src/common/network/BaseNetwork.cpp index b3f6e229..d112cd2c 100644 --- a/src/common/network/BaseNetwork.cpp +++ b/src/common/network/BaseNetwork.cpp @@ -163,8 +163,15 @@ bool BaseNetwork::writeActLog(const char* message) char buffer[DATA_PACKET_LENGTH]; uint32_t len = ::strlen(message); +#if !defined(_WIN32) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif ::strncpy(buffer + 11U, message, len); - +#if !defined(_WIN32) +#pragma GCC diagnostic pop +#endif + return writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_ACTIVITY }, (uint8_t*)buffer, (uint32_t)len + 11U, RTP_END_OF_CALL_SEQ, 0U, false, m_useAlternatePortForDiagnostics); } @@ -184,7 +191,14 @@ bool BaseNetwork::writeDiagLog(const char* message) char buffer[DATA_PACKET_LENGTH]; uint32_t len = ::strlen(message); +#if !defined(_WIN32) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif ::strncpy(buffer + 11U, message, len); +#if !defined(_WIN32) +#pragma GCC diagnostic pop +#endif return writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_DIAG }, (uint8_t*)buffer, (uint32_t)len + 11U, RTP_END_OF_CALL_SEQ, 0U, false, m_useAlternatePortForDiagnostics); @@ -209,7 +223,14 @@ bool BaseNetwork::writePeerStatus(json::object obj) char buffer[DATA_PACKET_LENGTH]; uint32_t len = ::strlen(json.c_str()); +#if !defined(_WIN32) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif ::strncpy(buffer + 11U, json.c_str(), len); +#if !defined(_WIN32) +#pragma GCC diagnostic pop +#endif return writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_STATUS }, (uint8_t*)buffer, (uint32_t)len + 11U, RTP_END_OF_CALL_SEQ, 0U, false, m_useAlternatePortForDiagnostics); diff --git a/src/common/network/sip/RequestDispatcher.h b/src/common/network/sip/RequestDispatcher.h deleted file mode 100644 index 522b5c80..00000000 --- a/src/common/network/sip/RequestDispatcher.h +++ /dev/null @@ -1,219 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - Common Library - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL - * - */ -/** - * @defgroup sip Session Initiation Protocol Services - * @brief Implementation for Session Initiation Protocol services. - * @ingroup network_core - * - * @file RequestDispatcher.h - * @ingroup sip - */ -#if !defined(__SIP__DISPATCHER_H__) -#define __SIP__DISPATCHER_H__ - -#include "common/Defines.h" -#include "common/network/sip/SIPPayload.h" -#include "common/Log.h" - -#include -#include -#include -#include -#include - -namespace network -{ - namespace sip - { - // --------------------------------------------------------------------------- - // Structure Declaration - // --------------------------------------------------------------------------- - - /** - * @brief Structure representing a request handler. - * @ingroup rest - */ - template - struct RequestHandler { - typedef std::function RequestHandlerType; - - /** - * @brief Initializes a new instance of the RequestHandler structure. - */ - explicit RequestHandler() { /* stub */} - - /** - * @brief Handler for INVITE requests. - * @param handler INVITE request handler. - * @return RequestHandler* Instance of a RequestHandler. - */ - RequestHandler& invite(RequestHandlerType handler) { - m_handlers[SIP_INVITE] = handler; - return *this; - } - /** - * @brief Handler for ACK requests. - * @param handler ACK request handler. - * @return RequestHandler* Instance of a RequestHandler. - */ - RequestHandler& ack(RequestHandlerType handler) { - m_handlers[SIP_ACK] = handler; - return *this; - } - /** - * @brief Handler for BYE requests. - * @param handler BYE request handler. - * @return RequestHandler* Instance of a RequestHandler. - */ - RequestHandler& bye(RequestHandlerType handler) { - m_handlers[SIP_BYE] = handler; - return *this; - } - /** - * @brief Handler for CANCEL requests. - * @param handler CANCEL request handler. - * @return RequestHandler* Instance of a RequestHandler. - */ - RequestHandler& cancel(RequestHandlerType handler) { - m_handlers[SIP_CANCEL] = handler; - return *this; - } - /** - * @brief Handler for REGISTER requests. - * @param handler REGISTER request handler. - * @return RequestHandler* Instance of a RequestHandler. - */ - RequestHandler& registerReq(RequestHandlerType handler) { - m_handlers[SIP_REGISTER] = handler; - return *this; - } - /** - * @brief Handler for OPTIONS requests. - * @param handler OPTIONS request handler. - * @return RequestHandler* Instance of a RequestHandler. - */ - RequestHandler& options(RequestHandlerType handler) { - m_handlers[SIP_OPTIONS] = handler; - return *this; - } - /** - * @brief Handler for MESSAGE requests. - * @param handler MESSAGE request handler. - * @return RequestHandler* Instance of a RequestHandler. - */ - RequestHandler& message(RequestHandlerType handler) { - m_handlers[SIP_MESSAGE] = handler; - return *this; - } - - /** - * @brief Helper to handle the actual request. - * @param request HTTP request. - * @param reply HTTP reply. - */ - void handleRequest(const Request& request, Reply& reply) { - // dispatching to handler - auto& handler = m_handlers[request.method]; - if (handler) { - handler(request, reply); - } - } - - private: - std::map m_handlers; - }; - - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements SIP request dispatching. - * @tparam Request SIP request. - * @tparam Reply SIP reply. - */ - template - class SIPRequestDispatcher { - typedef RequestHandler HandlerType; - public: - /** - * @brief Initializes a new instance of the SIPRequestDispatcher class. - */ - SIPRequestDispatcher() : m_handlers(), m_debug(false) { /* stub */ } - /** - * @brief Initializes a new instance of the SIPRequestDispatcher class. - * @param debug Flag indicating whether or not verbose logging should be enabled. - */ - SIPRequestDispatcher(bool debug) : m_handlers(), m_debug(debug) { /* stub */ } - - /** - * @brief Helper to set a request handler. - * @returns HandlerType Instance of a request handler. - */ - HandlerType& handler() - { - HandlerTypePtr& p = m_handlers; - return *p; - } - - /** - * @brief Helper to handle SIP request. - * @param request SIP request. - * @param reply SIP reply. - */ - void handleRequest(const Request& request, Reply& reply) - { - m_handlers.handleRequest(request, reply); - return; - } - - private: - typedef std::shared_ptr HandlerTypePtr; - HandlerType m_handlers; - - bool m_debug; - }; - - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements a generic debug request dispatcher. - * @tparam Request SIP request. - * @tparam Reply SIP reply. - */ - template - class SIPDebugRequestDispatcher { - public: - /** - * @brief Initializes a new instance of the SIPDebugRequestDispatcher class. - */ - SIPDebugRequestDispatcher() { /* stub */ } - - /** - * @brief Helper to handle SIP request. - * @param request SIP request. - * @param reply SIP reply. - */ - void handleRequest(const Request& request, Reply& reply) - { - for (auto header : request.headers.headers()) - ::LogDebugEx(LOG_SIP, "SIPDebugRequestDispatcher::handleRequest()", "header = %s, value = %s", header.name.c_str(), header.value.c_str()); - - ::LogDebugEx(LOG_SIP, "SIPDebugRequestDispatcher::handleRequest()", "content = %s", request.content.c_str()); - } - }; - - typedef SIPRequestDispatcher DefaultSIPRequestDispatcher; - } // namespace sip -} // namespace network - -#endif // __SIP__DISPATCHER_H__ diff --git a/src/common/network/sip/SIPHeaders.h b/src/common/network/sip/SIPHeaders.h deleted file mode 100644 index df1b43df..00000000 --- a/src/common/network/sip/SIPHeaders.h +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file SIPClient.h - * @ingroup sip - */ -#if !defined(__SIP__SIP_HEADERS_H__) -#define __SIP__SIP_HEADERS_H__ - -#include "common/Defines.h" -#include "common/Log.h" -#include "common/Utils.h" - -#include -#include - -namespace network -{ - namespace sip - { - // --------------------------------------------------------------------------- - // Class Prototypes - // --------------------------------------------------------------------------- - - struct SIPPayload; - - // --------------------------------------------------------------------------- - // Structure Declaration - // --------------------------------------------------------------------------- - - /** - * @brief Represents SIP headers. - * @ingroup sip - */ - struct SIPHeaders { - /** - * @brief Structure representing an individual SIP header. - * @ingroup sip - */ - struct Header - { - /** - * @brief Header name. - */ - std::string name; - /** - * @brief Header value. - */ - std::string value; - - /** - * @brief Initializes a new instance of the Header class. - */ - Header() : name{}, value{} { /* stub */} - /** - * @brief Initializes a new instance of the Header class - * @param n Header name. - * @param v Header value. - */ - Header(std::string n, std::string v) : name{n}, value{v} { /* stub */ } - }; - - /** - * @brief Gets the list of SIP headers. - * @returns std::vector
List of SIP headers. - */ - std::vector
headers() const { return m_headers; } - /** - * @brief Returns true if the headers are empty. - * @returns bool True, if no SIP headers are present, otherwise false. - */ - bool empty() const { return m_headers.empty(); } - /** - * @brief Returns the number of headers. - * @returns std::size_t Number of headers. - */ - std::size_t size() const { return m_headers.size(); } - /** - * @brief Clears the list of SIP headers. - */ - void clearHeaders() { m_headers = std::vector
(); } - /** - * @brief Helper to add a SIP header. - * @param name Header name. - * @param value Header value. - */ - void add(const std::string& name, const std::string& value) - { - //::LogDebugEx(LOG_SIP, "SIPHeaders::add()", "header = %s, value = %s", name.c_str(), value.c_str()); - for (auto& header : m_headers) { - if (::strtolower(header.name) == ::strtolower(name)) { - header.value = value; - return; - } - } - - m_headers.push_back(Header(name, value)); - //for (auto header : m_headers) - // ::LogDebugEx(LOG_SIP, "SIPHeaders::add()", "m_headers.header = %s, m_headers.value = %s", header.name.c_str(), header.value.c_str()); - } - /** - * @brief Helper to remove a SIP header. - * @param headerName Header name. - */ - void remove(const std::string headerName) - { - auto header = std::find_if(m_headers.begin(), m_headers.end(), [&](const Header& h) { - return ::strtolower(h.name) == ::strtolower(headerName); - }); - - if (header != m_headers.end()) { - m_headers.erase(header); - } - } - /** - * @brief Helper to find the named SIP header. - * @param headerName Header name. - * @returns std::string Value of named header (if any). - */ - std::string find(const std::string headerName) const - { - auto header = std::find_if(m_headers.begin(), m_headers.end(), [&](const Header& h) { - return ::strtolower(h.name) == ::strtolower(headerName); - }); - - if (header != m_headers.end()) { - return header->value; - } - else { - return ""; - } - } - - private: - friend struct SIPPayload; - std::vector
m_headers; - }; - } // namespace sip -} // namespace network - -#endif // __SIP__SIP_HEADERS_H__ diff --git a/src/common/network/sip/SIPLexer.cpp b/src/common/network/sip/SIPLexer.cpp deleted file mode 100644 index ff408329..00000000 --- a/src/common/network/sip/SIPLexer.cpp +++ /dev/null @@ -1,402 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL - * - */ -#include "Defines.h" -#include "network/sip/SIPLexer.h" -#include "network/sip/SIPPayload.h" -#include "Log.h" - -using namespace network::sip; - -#include - -// --------------------------------------------------------------------------- -// Public Class Members -// --------------------------------------------------------------------------- - -/* Initializes a new instance of the SIPLexer class. */ - -SIPLexer::SIPLexer(bool clientLexer) : - m_headers(), - m_clientLexer(clientLexer), - m_consumed(0U), - m_state(METHOD_START) -{ - if (m_clientLexer) { - m_state = SIP_VERSION_S; - } -} - -/* Reset to initial parser state. */ - -void SIPLexer::reset() -{ - m_state = METHOD_START; - if (m_clientLexer) { - m_state = SIP_VERSION_S; - } - - m_headers = std::vector(); -} - -// --------------------------------------------------------------------------- -// Private Class Members -// --------------------------------------------------------------------------- - -/* Handle the next character of input. */ - -SIPLexer::ResultType SIPLexer::consume(SIPPayload& req, char input) -{ - m_consumed++; - switch (m_state) - { - /* - ** HTTP Method - */ - - case METHOD_START: - if (!isChar(input) || isControl(input) || isSpecial(input)) - return BAD; - else { - m_state = METHOD; - req.method.push_back(input); - return INDETERMINATE; - } - case METHOD: - if (input == ' ') { - m_state = URI; - return INDETERMINATE; - } - else if (!isChar(input) || isControl(input) || isSpecial(input)) { - return BAD; - } - else { - req.method.push_back(input); - return INDETERMINATE; - } - - /* - ** URI - */ - - case URI: - if (input == ' ') { - m_state = SIP_VERSION_S; - return INDETERMINATE; - } - else if (isControl(input)) { - return BAD; - } - else { - req.uri.push_back(input); - return INDETERMINATE; - } - - /* - ** SIP/2.0 - ** SIP/2.0 200 OK - */ - case SIP_VERSION_S: - if (input == 'S') { - m_state = SIP_VERSION_I; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_VERSION_I: - if (input == 'I') { - m_state = SIP_VERSION_P; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_VERSION_P: - if (input == 'P') { - m_state = SIP_VERSION_SLASH; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_VERSION_SLASH: - if (input == '/') { - req.sipVersionMajor = 0; - req.sipVersionMinor = 0; - m_state = SIP_VERSION_MAJOR_START; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_VERSION_MAJOR_START: - if (isDigit(input)) { - req.sipVersionMajor = req.sipVersionMajor * 10 + input - '0'; - m_state = SIP_VERSION_MAJOR; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_VERSION_MAJOR: - if (input == '.') { - m_state = SIP_VERSION_MINOR_START; - return INDETERMINATE; - } - else if (isDigit(input)) { - req.sipVersionMajor = req.sipVersionMajor * 10 + input - '0'; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_VERSION_MINOR_START: - if (isDigit(input)) { - req.sipVersionMinor = req.sipVersionMinor * 10 + input - '0'; - m_state = SIP_VERSION_MINOR; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_VERSION_MINOR: - if (input == '\r') { - m_state = EXPECTING_NEWLINE_1; - if (m_clientLexer) { - return BAD; - } - else { - return INDETERMINATE; - } - } - else if (input == ' ') { - if (m_clientLexer) { - m_state = SIP_STATUS_1; - return INDETERMINATE; - } - else { - return BAD; - } - } - else if (isDigit(input)) { - req.sipVersionMinor = req.sipVersionMinor * 10 + input - '0'; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_STATUS_1: - if (isDigit(input)) { - m_state = SIP_STATUS_2; - m_status = m_status * 10 + input - '0'; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_STATUS_2: - if (isDigit(input)) { - m_state = SIP_STATUS_3; - m_status = m_status * 10 + input - '0'; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_STATUS_3: - if (isDigit(input)) { - m_state = SIP_STATUS_END; - m_status = m_status * 10 + input - '0'; - req.status = (SIPPayload::StatusType)m_status; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_STATUS_END: - if (input == ' ') { - m_state = SIP_STATUS_MESSAGE; - return INDETERMINATE; - } - else { - return BAD; - } - case SIP_STATUS_MESSAGE_START: - if (!isChar(input) || isControl(input) || isSpecial(input)) { - return BAD; - } - else { - m_state = SIP_STATUS_MESSAGE; - return INDETERMINATE; - } - case SIP_STATUS_MESSAGE: - if (input == '\r') { - m_state = EXPECTING_NEWLINE_1; - return INDETERMINATE; - } - else if (input == ' ') { - m_state = SIP_STATUS_MESSAGE; - return INDETERMINATE; - } - else if (!isChar(input) || isControl(input) || isSpecial(input)) { - return BAD; - } - else { - return INDETERMINATE; - } - - case EXPECTING_NEWLINE_1: - if (input == '\n') { - m_state = HEADER_LINE_START; - return INDETERMINATE; - } - else { - return BAD; - } - - /* - ** Headers - */ - - case HEADER_LINE_START: - if (input == '\r') { - m_state = EXPECTING_NEWLINE_3; - return INDETERMINATE; - } - else if (!req.headers.empty() && (input == ' ' || input == '\t')) { - m_state = HEADER_LWS; - return INDETERMINATE; - } - else if (!isChar(input) || isControl(input) || isSpecial(input)) { - return BAD; - } - else { - m_headers.push_back(LexedHeader()); - m_headers.back().name.push_back(std::tolower(input)); - m_state = HEADER_NAME; - return INDETERMINATE; - } - - case HEADER_LWS: - if (input == '\r') { - m_state = EXPECTING_NEWLINE_2; - return INDETERMINATE; - } - else if (input == ' ' || input == '\t') { - return INDETERMINATE; - } - else if (isControl(input)) { - return BAD; - } - else { - m_state = HEADER_VALUE; - m_headers.back().value.push_back(input); - return INDETERMINATE; - } - - case HEADER_NAME: - if (input == ':') { - m_state = SPACE_BEFORE_HEADER_VALUE; - return INDETERMINATE; - } - else if (!isChar(input) || isControl(input) || isSpecial(input)) { - return BAD; - } - else - { - m_headers.back().name.push_back(std::tolower(input)); - return INDETERMINATE; - } - - case SPACE_BEFORE_HEADER_VALUE: - if (input == ' ') { - m_state = HEADER_VALUE; - return INDETERMINATE; - } - else { - return BAD; - } - - case HEADER_VALUE: - if (input == '\r') { - m_state = EXPECTING_NEWLINE_2; - return INDETERMINATE; - } - else if (isControl(input)) { - return BAD; - } - else { - m_headers.back().value.push_back(input); - return INDETERMINATE; - } - - case EXPECTING_NEWLINE_2: - if (input == '\n') { - m_state = HEADER_LINE_START; - return INDETERMINATE; - } - else { - return BAD; - } - - case EXPECTING_NEWLINE_3: - if (input == '\n') { - for (auto header : m_headers) { - //::LogDebugEx(LOG_SIP, "SIPLexer::consume()", "header = %s, value = %s", header.name.c_str(), header.value.c_str()); - req.headers.add(header.name, header.value); - } - - return GOOD; - } else { - return BAD; - } - - default: - return BAD; - } -} - -/* Check if a byte is an SIP character. */ - -bool SIPLexer::isChar(int c) -{ - return c >= 0 && c <= 127; -} - -/* Check if a byte is an SIP control character. */ - -bool SIPLexer::isControl(int c) -{ - return (c >= 0 && c <= 31) || (c == 127); -} - -/* Check if a byte is an SIP special character. */ - -bool SIPLexer::isSpecial(int c) -{ - switch (c) - { - case '(': case ')': case '<': case '>': case '@': - case ',': case ';': case ':': case '\\': case '"': - case '/': case '[': case ']': case '?': case '=': - case '{': case '}': case ' ': case '\t': - return true; - default: - return false; - } -} - -/* Check if a byte is an digit. */ - -bool SIPLexer::isDigit(int c) -{ - return c >= '0' && c <= '9'; -} diff --git a/src/common/network/sip/SIPLexer.h b/src/common/network/sip/SIPLexer.h deleted file mode 100644 index ec897ebb..00000000 --- a/src/common/network/sip/SIPLexer.h +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file SIPLexer.h - * @ingroup sip - * @file SIPLexer.cpp - * @ingroup sip - */ -#if !defined(__SIP__SIP_LEXER_H__) -#define __SIP__SIP_LEXER_H__ - -#include "common/Defines.h" - -#include -#include - -namespace network -{ - namespace sip - { - // --------------------------------------------------------------------------- - // Class Prototypes - // --------------------------------------------------------------------------- - - struct SIPPayload; - - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements the lexer for incoming payloads. - */ - class SIPLexer { - public: - /** - * @brief Lexing result. - */ - enum ResultType { GOOD, BAD, INDETERMINATE, CONTINUE }; - - /** - * @brief Initializes a new instance of the SIPLexer class. - * @param clientLexer Flag indicating this lexer is used for a SIP client. - */ - SIPLexer(bool clientLexer); - - /** - * @brief Reset to initial parser state. - */ - void reset(); - - /** - * @brief Parse some data. The enum return value is good when a complete request has - * been parsed, bad if the data is invalid, indeterminate when more data is - * required. The InputIterator return value indicates how much of the input - * has been consumed. - * @tparam InputIterator - * @param payload SIP request payload. - * @param begin - * @param end - * @returns std::tuple - */ - template - std::tuple parse(SIPPayload& payload, InputIterator begin, InputIterator end) - { - while (begin != end) { - ResultType result = consume(payload, *begin++); - if (result == GOOD || result == BAD) - return std::make_tuple(result, begin); - } - return std::make_tuple(INDETERMINATE, begin); - } - - /** - * @brief Returns flag indicating whether or not characters have been consumed from the payload. - * @returns True, if characters were consumed, otherwise false. - */ - uint32_t consumed() const { return m_consumed; } - - private: - /** - * @brief Handle the next character of input. - * @param payload SIP request payload. - * @param input Character. - */ - ResultType consume(SIPPayload& payload, char input); - - /** - * @brief Check if a byte is an SIP character. - * @param c Character. - * @returns bool True, if character is an SIP character, otherwise false. - */ - static bool isChar(int c); - /** - * @brief Check if a byte is an SIP control character. - * @param c Character. - * @returns bool True, if character is an SIP control character, otherwise false. - */ - static bool isControl(int c); - /** - * @brief Check if a byte is an SIP special character. - * @param c Character. - * @returns bool True, if character is an SIP special character, otherwise false. - */ - static bool isSpecial(int c); - /** - * @brief Check if a byte is an digit. - * @param c Character. - * @returns bool True, if character is a digit, otherwise false. - */ - static bool isDigit(int c); - - /** - * @brief Structure representing lexed SIP headers. - */ - struct LexedHeader - { - /** - * @brief Header name. - */ - std::string name; - /** - * @brief Header value. - */ - std::string value; - - /** - * @brief Initializes a new instance of the LexedHeader class - */ - LexedHeader() { /* stub */ } - /** - * @brief Initializes a new instance of the LexedHeader class - * @param n Header name. - * @param v Header value. - */ - LexedHeader(const std::string& n, const std::string& v) : name(n), value(v) {} - }; - - std::vector m_headers; - uint16_t m_status; - bool m_clientLexer = false; - uint32_t m_consumed; - - /** - * @brief Lexer machine state. - */ - enum state - { - METHOD_START, //! SIP Method Start - METHOD, //! SIP Method - URI, //! SIP URI - - SIP_VERSION_S, //! SIP Version: S - SIP_VERSION_I, //! SIP Version: I - SIP_VERSION_P, //! SIP Version: P - SIP_VERSION_SLASH, //! SIP Version: / - SIP_VERSION_MAJOR_START, //! SIP Version Major Start - SIP_VERSION_MAJOR, //! SIP Version Major - SIP_VERSION_MINOR_START, //! SIP Version Minor Start - SIP_VERSION_MINOR, //! SIP Version Minor - - SIP_STATUS_1, //! Status Number 1 - SIP_STATUS_2, //! Status Number 2 - SIP_STATUS_3, //! Status Number 3 - SIP_STATUS_END, //! Status End - SIP_STATUS_MESSAGE_START, //! Status Message Start - SIP_STATUS_MESSAGE, //! Status Message End - - EXPECTING_NEWLINE_1, //! - - HEADER_LINE_START, //! Header Line Start - HEADER_LWS, //! - HEADER_NAME, //! Header Name - SPACE_BEFORE_HEADER_VALUE, //! - HEADER_VALUE, //! Header Value - - EXPECTING_NEWLINE_2, //! - EXPECTING_NEWLINE_3 //! - } m_state; - }; - } // namespace sip -} // namespace network - -#endif // __SIP__SIP_LEXER_H__ diff --git a/src/common/network/sip/SIPPayload.cpp b/src/common/network/sip/SIPPayload.cpp deleted file mode 100644 index 29e20bdc..00000000 --- a/src/common/network/sip/SIPPayload.cpp +++ /dev/null @@ -1,220 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL - * - */ -#include "Defines.h" -#include "network/sip/SIPPayload.h" -#include "Log.h" -#include "Utils.h" - -using namespace network::sip; - -#include - -namespace status_strings { - const std::string trying = "SIP/2.0 100 Trying\r\n"; - const std::string ringing = "SIP/2.0 180 Ringing\r\n"; - const std::string ok = "SIP/2.0 200 OK\r\n"; - const std::string accepted = "SIP/2.0 202 Accepted\r\n"; - const std::string no_notify = "SIP/2.0 204 No Notification\r\n"; - const std::string multiple_choices = "SIP/2.0 300 Multiple Choices\r\n"; - const std::string moved_permanently = "SIP/2.0 301 Moved Permanently\r\n"; - const std::string moved_temporarily = "SIP/2.0 302 Moved Temporarily\r\n"; - const std::string bad_request = "SIP/2.0 400 Bad Request\r\n"; - const std::string unauthorized = "SIP/2.0 401 Unauthorized\r\n"; - const std::string forbidden = "SIP/2.0 403 Forbidden\r\n"; - const std::string not_found = "SIP/2.0 404 Not Found\r\n"; - const std::string internal_server_error = "SIP/2.0 500 Internal Server Error\r\n"; - const std::string not_implemented = "SIP/2.0 501 Not Implemented\r\n"; - const std::string bad_gateway = "SIP/2.0 502 Bad Gateway\r\n"; - const std::string service_unavailable = "SIP/2.0 503 Service Unavailable\r\n"; - const std::string busy_everywhere = "SIP/2.0 600 Busy Everywhere\r\n"; - const std::string decline = "SIP/2.0 603 Decline\r\n"; - - asio::const_buffer toBuffer(SIPPayload::StatusType status) - { - switch (status) - { - case SIPPayload::TRYING: - return asio::buffer(trying); - case SIPPayload::RINGING: - return asio::buffer(ringing); - case SIPPayload::OK: - return asio::buffer(ok); - case SIPPayload::ACCEPTED: - return asio::buffer(accepted); - case SIPPayload::NO_NOTIFY: - return asio::buffer(no_notify); - case SIPPayload::MULTIPLE_CHOICES: - return asio::buffer(multiple_choices); - case SIPPayload::MOVED_PERMANENTLY: - return asio::buffer(moved_permanently); - case SIPPayload::MOVED_TEMPORARILY: - return asio::buffer(moved_temporarily); - case SIPPayload::BAD_REQUEST: - return asio::buffer(bad_request); - case SIPPayload::UNAUTHORIZED: - return asio::buffer(unauthorized); - case SIPPayload::FORBIDDEN: - return asio::buffer(forbidden); - case SIPPayload::NOT_FOUND: - return asio::buffer(not_found); - case SIPPayload::INTERNAL_SERVER_ERROR: - return asio::buffer(internal_server_error); - case SIPPayload::NOT_IMPLEMENTED: - return asio::buffer(not_implemented); - case SIPPayload::BAD_GATEWAY: - return asio::buffer(bad_gateway); - case SIPPayload::SERVICE_UNAVAILABLE: - return asio::buffer(service_unavailable); - case SIPPayload::BUSY_EVERYWHERE: - return asio::buffer(busy_everywhere); - case SIPPayload::DECLINE: - return asio::buffer(decline); - default: - return asio::buffer(internal_server_error); - } - } -} // namespace status_strings - -namespace misc_strings { - const char name_value_separator[] = { ':', ' ' }; - const char request_method_separator[] = { ' ' }; - const char crlf[] = { '\r', '\n' }; - - const char sip_default_version[] = { 'S', 'I', 'P', '/', '2', '.', '0' }; -} // namespace misc_strings - -// --------------------------------------------------------------------------- -// Public Class Members -// --------------------------------------------------------------------------- - -/* Convert the reply into a vector of buffers. The buffers do not own the underlying memory blocks, therefore the reply object must remain valid and not be changed until the write operation has completed. */ - -std::vector SIPPayload::toBuffers() -{ - std::vector buffers; - if (isClientPayload) { - // copy method and erase zero terminator - method.erase(std::find(method.begin(), method.end(), '\0'), method.end()); - - // copy URI and erase zero terminator - uri.erase(std::find(uri.begin(), uri.end(), '\0'), uri.end()); -#if DEBUG_SIP_PAYLOAD - ::LogDebugEx(LOG_SIP, "SIPPayload::toBuffers()", "method = %s, uri = %s", method.c_str(), uri.c_str()); -#endif - buffers.push_back(asio::buffer(method)); - buffers.push_back(asio::buffer(misc_strings::request_method_separator)); - buffers.push_back(asio::buffer(uri)); - buffers.push_back(asio::buffer(misc_strings::request_method_separator)); - buffers.push_back(asio::buffer(misc_strings::sip_default_version)); - buffers.push_back(asio::buffer(misc_strings::crlf)); - } - else { - buffers.push_back(status_strings::toBuffer(status)); - } - - for (std::size_t i = 0; i < headers.size(); ++i) { - SIPHeaders::Header& h = headers.m_headers[i]; -#if DEBUG_SIP_PAYLOAD - ::LogDebugEx(LOG_SIP, "SIPPayload::toBuffers()", "header = %s, value = %s", h.name.c_str(), h.value.c_str()); -#endif - - buffers.push_back(asio::buffer(h.name)); - buffers.push_back(asio::buffer(misc_strings::name_value_separator)); - buffers.push_back(asio::buffer(h.value)); - buffers.push_back(asio::buffer(misc_strings::crlf)); - } - - buffers.push_back(asio::buffer(misc_strings::crlf)); - if (content.size() > 0) - buffers.push_back(asio::buffer(content)); - -#if DEBUG_SIP_PAYLOAD - ::LogDebugEx(LOG_SIP, "SIPPayload::toBuffers()", "content = %s", content.c_str()); - for (auto buffer : buffers) - Utils::dump("SIPPayload::toBuffers(), buffer[]", (uint8_t*)buffer.data(), buffer.size()); -#endif - - return buffers; -} - -/* Prepares payload for transmission by finalizing status and content type. */ - -void SIPPayload::payload(json::object& obj, SIPPayload::StatusType s) -{ - json::value v = json::value(obj); - std::string json = std::string(v.serialize()); - payload(json, s, "application/json"); -} - -/* Prepares payload for transmission by finalizing status and content type. */ - -void SIPPayload::payload(std::string& c, SIPPayload::StatusType s, const std::string& contentType) -{ - content = c; - status = s; - ensureDefaultHeaders(contentType); -} - -// --------------------------------------------------------------------------- -// Static Members -// --------------------------------------------------------------------------- - -/* Get a status payload. */ - -SIPPayload SIPPayload::requestPayload(std::string method, std::string uri) -{ - SIPPayload rep; - rep.isClientPayload = true; - rep.method = ::strtoupper(method); - rep.uri = std::string(uri); - return rep; -} - -/* Get a status payload. */ - -SIPPayload SIPPayload::statusPayload(SIPPayload::StatusType status, const std::string& contentType) -{ - SIPPayload rep; - rep.isClientPayload = false; - rep.status = status; - rep.ensureDefaultHeaders(contentType); - - return rep; -} - - -/* Helper to attach a host TCP stream reader. */ - -void SIPPayload::attachHostHeader(const asio::ip::tcp::endpoint remoteEndpoint) -{ - headers.add("Host", std::string(remoteEndpoint.address().to_string() + ":" + std::to_string(remoteEndpoint.port()))); -} - -// --------------------------------------------------------------------------- -// Private Members -// --------------------------------------------------------------------------- - -/* Internal helper to ensure the headers are of a default for the given content type. */ - -void SIPPayload::ensureDefaultHeaders(const std::string& contentType) -{ - if (!isClientPayload) { - headers.add("Content-Type", std::string(contentType)); - headers.add("Content-Length", std::to_string(content.size())); - headers.add("Server", std::string(("DVM/" __VER__))); - } - else { - headers.add("User-Agent", std::string(("DVM/" __VER__))); - headers.add("Accept", "*/*"); - headers.add("Content-Type", std::string(contentType)); - headers.add("Content-Length", std::to_string(content.size())); - } -} diff --git a/src/common/network/sip/SIPPayload.h b/src/common/network/sip/SIPPayload.h deleted file mode 100644 index 85978e57..00000000 --- a/src/common/network/sip/SIPPayload.h +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file SIPPayload.h - * @ingroup sip - * @file SIPPayload.cpp - * @ingroup sip - */ -#if !defined(__SIP__SIP_PAYLOAD_H__) -#define __SIP__SIP_PAYLOAD_H__ - -#include "common/Defines.h" -#include "common/network/json/json.h" -#include "common/network/sip/SIPHeaders.h" - -#include -#include - -#include - -namespace network -{ - namespace sip - { - // --------------------------------------------------------------------------- - // Constants - // --------------------------------------------------------------------------- - - #define SIP_INVITE "INVITE" - #define SIP_ACK "ACK" - #define SIP_BYE "BYE" - #define SIP_CANCEL "CANCEL" - #define SIP_REGISTER "REGISTER" - #define SIP_OPTIONS "OPTIONS" - #define SIP_MESSAGE "MESSAGE" - - // --------------------------------------------------------------------------- - // Structure Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This struct implements a model of a payload to be sent to a - * SIP client/server. - * @ingroup sip - */ - struct SIPPayload { - /** - * @brief SIP Status/Response Codes - */ - enum StatusType { - TRYING = 100, //! SIP Trying 100 - RINGING = 180, //! SIP Ringing 180 - - OK = 200, //! SIP OK 200 - ACCEPTED = 202, //! SIP Accepted 202 - NO_NOTIFY = 204, //! SIP No Notification 204 - - MULTIPLE_CHOICES = 300, //! SIP Multiple Choices 300 - MOVED_PERMANENTLY = 301, //! SIP Moved Permenantly 301 - MOVED_TEMPORARILY = 302, //! SIP Moved Temporarily 302 - - BAD_REQUEST = 400, //! SIP Bad Request 400 - UNAUTHORIZED = 401, //! SIP Unauthorized 401 - FORBIDDEN = 403, //! SIP Forbidden 403 - NOT_FOUND = 404, //! SIP Not Found 404 - - INTERNAL_SERVER_ERROR = 500, //! SIP Internal Server Error 500 - NOT_IMPLEMENTED = 501, //! SIP Not Implemented 501 - BAD_GATEWAY = 502, //! SIP Bad Gateway 502 - SERVICE_UNAVAILABLE = 503, //! SIP Service Unavailable 503 - - BUSY_EVERYWHERE = 600, //! SIP Busy Everywhere 600 - DECLINE = 603, //! SIP Decline 603 - } status; - - SIPHeaders headers; - std::string content; - size_t contentLength; - - std::string method; - std::string uri; - - int sipVersionMajor; - int sipVersionMinor; - - bool isClientPayload = false; - - /** - * @brief Convert the payload into a vector of buffers. The buffers do not own the - * underlying memory blocks, therefore the payload object must remain valid and - * not be changed until the write operation has completed. - * @returns std::vector List of buffers representing the SIP payload. - */ - std::vector toBuffers(); - - /** - * @brief Prepares payload for transmission by finalizing status and content type. - * @param obj - * @param status SIP status. - */ - void payload(json::object& obj, StatusType status = OK); - /** - * @brief Prepares payload for transmission by finalizing status and content type. - * @param content - * @param status SIP status. - * @param contentType SIP content type. - */ - void payload(std::string& content, StatusType status = OK, const std::string& contentType = "application/sdp"); - - /** - * @brief Get a request payload. - * @param method SIP method. - * @param uri SIP uri. - */ - static SIPPayload requestPayload(std::string method, std::string uri); - /** - * @brief Get a status payload. - * @param status SIP status. - * @param contentType SIP content type. - */ - static SIPPayload statusPayload(StatusType status, const std::string& contentType = "application/sdp"); - - /** - * @brief Helper to attach a host TCP stream reader. - * @param remoteEndpoint Endpoint. - */ - void attachHostHeader(const asio::ip::tcp::endpoint remoteEndpoint); - - private: - /** - * @brief Internal helper to ensure the headers are of a default for the given content type. - * @param contentType SIP content type. - */ - void ensureDefaultHeaders(const std::string& contentType = "application/sdp"); - }; - } // namespace sip -} // namespace network - -#endif // __SIP__SIP_PAYLOAD_H__ diff --git a/src/fne/ActivityLog.cpp b/src/fne/ActivityLog.cpp index d7a974f4..9ab11657 100644 --- a/src/fne/ActivityLog.cpp +++ b/src/fne/ActivityLog.cpp @@ -98,25 +98,11 @@ void ActivityLogFinalise() /* Writes a new entry to the activity log. */ -void ActivityLog(const char* msg, ...) +void log_internal::ActivityLogInternal(const std::string& log) { #if defined(CATCH2_TEST_COMPILATION) return; #endif - assert(msg != nullptr); - - char buffer[ACT_LOG_BUFFER_LEN]; - - va_list vl, vl_len; - va_start(vl, msg); - va_copy(vl_len, vl); - - size_t len = ::vsnprintf(nullptr, 0U, msg, vl_len); - ::vsnprintf(buffer, len + 1U, msg, vl); - - va_end(vl_len); - va_end(vl); - bool ret = ::ActivityLogOpen(); if (!ret) return; @@ -124,11 +110,11 @@ void ActivityLog(const char* msg, ...) if (CurrentLogFileLevel() == 0U) return; - ::fprintf(m_actFpLog, "%s\n", buffer); + ::fprintf(m_actFpLog, "%s\n", log.c_str()); ::fflush(m_actFpLog); if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) { - ::fprintf(stdout, "%s" EOL, buffer); + ::fprintf(stdout, "%s" EOL, log.c_str()); ::fflush(stdout); } } diff --git a/src/fne/ActivityLog.h b/src/fne/ActivityLog.h index ca32863f..5800f76c 100644 --- a/src/fne/ActivityLog.h +++ b/src/fne/ActivityLog.h @@ -4,7 +4,7 @@ * 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 + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -24,6 +24,16 @@ // Global Functions // --------------------------------------------------------------------------- +namespace log_internal +{ + /** + * @brief Writes a new entry to the diagnostics log. + * @param level Log level for entry. + * @param log Fully formatted log message. + */ + extern HOST_SW_API void ActivityLogInternal(const std::string& log); +} // namespace log_internal + /** * @brief Initializes the activity log. * @param filePath File path for the log file. @@ -34,12 +44,31 @@ extern HOST_SW_API bool ActivityLogInitialise(const std::string& filePath, const * @brief Finalizes the activity log. */ extern HOST_SW_API void ActivityLogFinalise(); + /** - * @brief Writes a new entry to the activity log. - * @param msg String format. + * @brief Writes a new entry to the diagnostics log. + * @param fmt String format. * - * This is a variable argument function. + * This is a variable argument function. This shouldn't be called directly, utilize the LogXXXX macros above, instead. */ -extern HOST_SW_API void ActivityLog(const char* msg, ...); +template +HOST_SW_API void ActivityLog(const std::string& fmt, Args... args) +{ + using namespace log_internal; + + int size_s = std::snprintf(nullptr, 0, fmt.c_str(), args...) + 1; // Extra space for '\0' + if (size_s <= 0) { + throw std::runtime_error("Error during formatting."); + } + + auto size = static_cast(size_s); + auto buf = std::make_unique(size); + + std::snprintf(buf.get(), size, fmt.c_str(), args ...); + + std::string msg = std::string(buf.get(), buf.get() + size - 1); + + ActivityLogInternal(std::string(msg)); +} #endif // __ACTIVITY_LOG_H__ diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 029c968f..03a0602e 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -287,7 +287,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) bool currState = g_disableTimeDisplay; g_disableTimeDisplay = true; - ::Log(9999U, nullptr, nullptr, 0U, nullptr, "%.9u (%8s) %s", peerId, connection->identity().c_str(), payload.c_str()); + ::Log(9999U, {nullptr, nullptr, 0U, nullptr}, "%.9u (%8s) %s", peerId, connection->identity().c_str(), payload.c_str()); g_disableTimeDisplay = currState; // report diagnostic log to InfluxDB diff --git a/src/host/ActivityLog.cpp b/src/host/ActivityLog.cpp index 6764a4c8..4552710d 100644 --- a/src/host/ActivityLog.cpp +++ b/src/host/ActivityLog.cpp @@ -1,25 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-only -/** -* Digital Voice Modem - Modem Host Software -* GPLv2 Open Source. Use is subject to license terms. -* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -* -* @package DVM / Modem Host Software -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* -* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL -* -*/ +/* + * Digital Voice Modem - 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) 2024-2025 Bryan Biedenkapp, N2PLL + * + */ #include "ActivityLog.h" #include "common/network/BaseNetwork.h" #include "common/Log.h" // for CurrentLogFileLevel() and LogGetNetwork() -#if defined(_WIN32) -#include "common/Clock.h" -#else -#include -#endif // defined(_WIN32) - #if defined(CATCH2_TEST_COMPILATION) #include #endif @@ -29,7 +20,6 @@ #include #include #include -#include // --------------------------------------------------------------------------- // Constants @@ -110,56 +100,23 @@ void ActivityLogFinalise() /* Writes a new entry to the activity log. */ -void ActivityLog(const char *mode, const bool sourceRf, const char* msg, ...) +void log_internal::ActivityLogInternal(const std::string& log) { #if defined(CATCH2_TEST_COMPILATION) return; #endif - assert(mode != nullptr); - assert(msg != nullptr); - - char buffer[ACT_LOG_BUFFER_LEN]; - time_t now; - ::time(&now); - struct tm* tm = ::localtime(&now); - - struct timeval nowMillis; - ::gettimeofday(&nowMillis, NULL); - - if (strcmp(mode, "") == 0) { - ::sprintf(buffer, "A: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); - } - else { - ::sprintf(buffer, "A: %04d-%02d-%02d %02d:%02d:%02d.%03lu %s %s ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, mode, (sourceRf) ? "RF" : "Net"); - } - - va_list vl, vl_len; - va_start(vl, msg); - va_copy(vl_len, vl); - - size_t len = ::vsnprintf(nullptr, 0U, msg, vl_len); - ::vsnprintf(buffer + ::strlen(buffer), len + 1U, msg, vl); - - va_end(vl_len); - va_end(vl); - bool ret = ::ActivityLogOpen(); if (!ret) return; - if (LogGetNetwork() != nullptr) { - network::BaseNetwork* network = (network::BaseNetwork*)LogGetNetwork();; - network->writeActLog(buffer); - } - if (CurrentLogFileLevel() == 0U) return; - ::fprintf(m_actFpLog, "%s\n", buffer); + ::fprintf(m_actFpLog, "%s\n", log.c_str()); ::fflush(m_actFpLog); if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) { - ::fprintf(stdout, "%s" EOL, buffer); + ::fprintf(stdout, "%s" EOL, log.c_str()); ::fflush(stdout); } } diff --git a/src/host/ActivityLog.h b/src/host/ActivityLog.h index 0a4c4a0c..d3a12652 100644 --- a/src/host/ActivityLog.h +++ b/src/host/ActivityLog.h @@ -4,7 +4,7 @@ * 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 + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -18,12 +18,29 @@ #include "Defines.h" +#if defined(_WIN32) +#include "common/Clock.h" +#else +#include +#endif // defined(_WIN32) + #include +#include // --------------------------------------------------------------------------- // Global Functions // --------------------------------------------------------------------------- +namespace log_internal +{ + /** + * @brief Writes a new entry to the diagnostics log. + * @param level Log level for entry. + * @param log Fully formatted log message. + */ + extern HOST_SW_API void ActivityLogInternal(const std::string& log); +} // namespace log_internal + /** * @brief Initializes the activity log. * @param filePath File path for the log file. @@ -34,14 +51,53 @@ extern HOST_SW_API bool ActivityLogInitialise(const std::string& filePath, const * @brief Finalizes the activity log. */ extern HOST_SW_API void ActivityLogFinalise(); + /** - * @brief Writes a new entry to the activity log. + * @brief Writes a new entry to the diagnostics log. * @param mode Activity mode. * @param sourceRf Flag indicating whether or not the activity entry came from RF. - * @param msg String format. + * @param fmt String format. * - * This is a variable argument function. + * This is a variable argument function. This shouldn't be called directly, utilize the LogXXXX macros above, instead. */ -extern HOST_SW_API void ActivityLog(const char* mode, const bool sourceRf, const char* msg, ...); +template +HOST_SW_API void ActivityLog(const char* mode, const bool sourceRf, const std::string& fmt, Args... args) +{ + using namespace log_internal; + + int size_s = std::snprintf(nullptr, 0, fmt.c_str(), args...) + 1; // Extra space for '\0' + if (size_s <= 0) { + throw std::runtime_error("Error during formatting."); + } + + int prefixLen = 0; + char prefixBuf[256]; + + time_t now; + ::time(&now); + struct tm* tm = ::localtime(&now); + + struct timeval nowMillis; + ::gettimeofday(&nowMillis, NULL); + + if (strcmp(mode, "") == 0) { + prefixLen = ::sprintf(prefixBuf, "A: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); + } + else { + prefixLen = ::sprintf(prefixBuf, "A: %04d-%02d-%02d %02d:%02d:%02d.%03lu %s %s ", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, mode, (sourceRf) ? "RF" : "Net"); + } + + auto size = static_cast(size_s); + auto buf = std::make_unique(size); + + std::snprintf(buf.get(), size, fmt.c_str(), args ...); + + std::string prefix = std::string(prefixBuf, prefixBuf + prefixLen); + std::string msg = std::string(buf.get(), buf.get() + size - 1); + + ActivityLogInternal(std::string(prefix + msg)); +} #endif // __ACTIVITY_LOG_H__ diff --git a/src/host/setup/SetupMainWnd.h b/src/host/setup/SetupMainWnd.h index a06c6899..ddfe5be5 100644 --- a/src/host/setup/SetupMainWnd.h +++ b/src/host/setup/SetupMainWnd.h @@ -63,7 +63,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { explicit SetupMainWnd(HostSetup* setup, FWidget* widget = nullptr) : FWidget{widget}, m_setup(setup) { - __InternalOutputStream(m_logWnd); + log_internal::SetInternalOutputStream(m_logWnd); m_statusWnd.hide(); resetBERWnd(); diff --git a/src/monitor/MonitorMainWnd.h b/src/monitor/MonitorMainWnd.h index d72a0ef0..72d990fa 100644 --- a/src/monitor/MonitorMainWnd.h +++ b/src/monitor/MonitorMainWnd.h @@ -58,7 +58,7 @@ class HOST_SW_API MonitorMainWnd final : public finalcut::FWidget { */ explicit MonitorMainWnd(FWidget* widget = nullptr) : FWidget{widget} { - __InternalOutputStream(m_logWnd); + log_internal::SetInternalOutputStream(m_logWnd); // file menu m_quitItem.addAccelerator(FKey::Meta_x); // Meta/Alt + X diff --git a/src/patch/ActivityLog.cpp b/src/patch/ActivityLog.cpp index 2680141f..060c4a4c 100644 --- a/src/patch/ActivityLog.cpp +++ b/src/patch/ActivityLog.cpp @@ -11,12 +11,6 @@ #include "common/network/BaseNetwork.h" #include "common/Log.h" // for CurrentLogFileLevel() and LogGetNetwork() -#if defined(_WIN32) -#include "common/Clock.h" -#else -#include -#endif // defined(_WIN32) - #if defined(CATCH2_TEST_COMPILATION) #include #endif @@ -106,50 +100,23 @@ void ActivityLogFinalise() /* Writes a new entry to the activity log. */ -void ActivityLog(const char* msg, ...) +void log_internal::ActivityLogInternal(const std::string& log) { #if defined(CATCH2_TEST_COMPILATION) return; #endif - assert(msg != nullptr); - - char buffer[ACT_LOG_BUFFER_LEN]; - time_t now; - ::time(&now); - struct tm* tm = ::localtime(&now); - - struct timeval nowMillis; - ::gettimeofday(&nowMillis, NULL); - - ::sprintf(buffer, "A: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); - - va_list vl, vl_len; - va_start(vl, msg); - va_copy(vl_len, vl); - - size_t len = ::vsnprintf(nullptr, 0U, msg, vl_len); - ::vsnprintf(buffer + ::strlen(buffer), len + 1U, msg, vl); - - va_end(vl_len); - va_end(vl); - bool ret = ::ActivityLogOpen(); if (!ret) return; - if (LogGetNetwork() != nullptr) { - network::BaseNetwork* network = (network::BaseNetwork*)LogGetNetwork();; - network->writeActLog(buffer); - } - if (CurrentLogFileLevel() == 0U) return; - ::fprintf(m_actFpLog, "%s\n", buffer); + ::fprintf(m_actFpLog, "%s\n", log.c_str()); ::fflush(m_actFpLog); if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) { - ::fprintf(stdout, "%s" EOL, buffer); + ::fprintf(stdout, "%s" EOL, log.c_str()); ::fflush(stdout); } } diff --git a/src/patch/ActivityLog.h b/src/patch/ActivityLog.h index 8f9ffec8..b577a732 100644 --- a/src/patch/ActivityLog.h +++ b/src/patch/ActivityLog.h @@ -18,12 +18,28 @@ #include "Defines.h" +#if defined(_WIN32) +#include "common/Clock.h" +#else +#include +#endif // defined(_WIN32) + #include // --------------------------------------------------------------------------- // Global Functions // --------------------------------------------------------------------------- +namespace log_internal +{ + /** + * @brief Writes a new entry to the diagnostics log. + * @param level Log level for entry. + * @param log Fully formatted log message. + */ + extern HOST_SW_API void ActivityLogInternal(const std::string& log); +} // namespace log_internal + /** * @brief Initializes the activity log. * @param filePath File path for the log file. @@ -34,12 +50,45 @@ extern HOST_SW_API bool ActivityLogInitialise(const std::string& filePath, const * @brief Finalizes the activity log. */ extern HOST_SW_API void ActivityLogFinalise(); + /** - * @brief Writes a new entry to the activity log. - * @param msg String format. + * @brief Writes a new entry to the diagnostics log. + * @param fmt String format. * - * This is a variable argument function. + * This is a variable argument function. This shouldn't be called directly, utilize the LogXXXX macros above, instead. */ -extern HOST_SW_API void ActivityLog(const char* msg, ...); +template +HOST_SW_API void ActivityLog(const std::string& fmt, Args... args) +{ + using namespace log_internal; + + int size_s = std::snprintf(nullptr, 0, fmt.c_str(), args...) + 1; // Extra space for '\0' + if (size_s <= 0) { + throw std::runtime_error("Error during formatting."); + } + + int prefixLen = 0; + char prefixBuf[256]; + + time_t now; + ::time(&now); + struct tm* tm = ::localtime(&now); + + struct timeval nowMillis; + ::gettimeofday(&nowMillis, NULL); + + prefixLen = ::sprintf(prefixBuf, "A: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); + + auto size = static_cast(size_s); + auto buf = std::make_unique(size); + + std::snprintf(buf.get(), size, fmt.c_str(), args ...); + + std::string prefix = std::string(prefixBuf, prefixBuf + prefixLen); + std::string msg = std::string(buf.get(), buf.get() + size - 1); + + ActivityLogInternal(std::string(prefix + msg)); +} #endif // __ACTIVITY_LOG_H__ diff --git a/src/peered/PeerEdMainWnd.h b/src/peered/PeerEdMainWnd.h index 90b49e73..7aa11d94 100644 --- a/src/peered/PeerEdMainWnd.h +++ b/src/peered/PeerEdMainWnd.h @@ -60,7 +60,7 @@ class HOST_SW_API PeerEdMainWnd final : public finalcut::FWidget { */ explicit PeerEdMainWnd(FWidget* widget = nullptr) : FWidget{widget} { - __InternalOutputStream(m_logWnd); + log_internal::SetInternalOutputStream(m_logWnd); // file menu m_fileMenuSeparator1.setSeparator(); diff --git a/src/sysview/HostWS.cpp b/src/sysview/HostWS.cpp index 7efcaeee..d5bb75e6 100644 --- a/src/sysview/HostWS.cpp +++ b/src/sysview/HostWS.cpp @@ -260,7 +260,7 @@ int HostWS::run() g_logDisplayLevel = 0U; std::ostringstream logOutput; - __InternalOutputStream(logOutput); + log_internal::SetInternalOutputStream(logOutput); Timer peerListUpdate(1000U, 10U); peerListUpdate.start(); diff --git a/src/sysview/SysViewMainWnd.h b/src/sysview/SysViewMainWnd.h index cc420b19..db5aadce 100644 --- a/src/sysview/SysViewMainWnd.h +++ b/src/sysview/SysViewMainWnd.h @@ -64,7 +64,7 @@ class HOST_SW_API SysViewMainWnd final : public finalcut::FWidget { */ explicit SysViewMainWnd(FWidget* widget = nullptr) : FWidget{widget} { - __InternalOutputStream(m_logWnd); + log_internal::SetInternalOutputStream(m_logWnd); // file menu m_statusMenu.addCallback("clicked", this, [&]() { diff --git a/src/sysview/network/PeerNetwork.cpp b/src/sysview/network/PeerNetwork.cpp index 5235c4a8..7ebe2a58 100644 --- a/src/sysview/network/PeerNetwork.cpp +++ b/src/sysview/network/PeerNetwork.cpp @@ -77,7 +77,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco if (it != g_peerIdentityNameMap.end()) identity = g_peerIdentityNameMap[peerId]; - ::Log(9999U, nullptr, "%.9u (%8s) %s", peerId, identity.c_str(), payload.c_str()); + ::Log(9999U, {nullptr, nullptr, 0U, nullptr}, "%.9u (%8s) %s", peerId, identity.c_str(), payload.c_str()); g_disableTimeDisplay = currState; } break; diff --git a/src/tged/TGEdMainWnd.h b/src/tged/TGEdMainWnd.h index 62b2ba44..6ed6f293 100644 --- a/src/tged/TGEdMainWnd.h +++ b/src/tged/TGEdMainWnd.h @@ -61,7 +61,7 @@ class HOST_SW_API TGEdMainWnd final : public finalcut::FWidget { */ explicit TGEdMainWnd(FWidget* widget = nullptr) : FWidget{widget} { - __InternalOutputStream(m_logWnd); + log_internal::SetInternalOutputStream(m_logWnd); // file menu m_fileMenuSeparator1.setSeparator(); From 270e4315f515020b80ac3db4c4068ef6d079f20b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 30 Sep 2025 23:16:11 -0400 Subject: [PATCH 034/200] whoops accidentally blew away activity log transmission to the FNE, fix that...; --- src/bridge/ActivityLog.cpp | 5 +++++ src/host/ActivityLog.cpp | 5 +++++ src/patch/ActivityLog.cpp | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/src/bridge/ActivityLog.cpp b/src/bridge/ActivityLog.cpp index 61c61b8c..506a279e 100644 --- a/src/bridge/ActivityLog.cpp +++ b/src/bridge/ActivityLog.cpp @@ -112,6 +112,11 @@ void log_internal::ActivityLogInternal(const std::string& log) if (CurrentLogFileLevel() == 0U) return; + if (LogGetNetwork() != nullptr) { + network::BaseNetwork* network = (network::BaseNetwork*)LogGetNetwork(); + network->writeActLog(log.c_str()); + } + ::fprintf(m_actFpLog, "%s\n", log.c_str()); ::fflush(m_actFpLog); diff --git a/src/host/ActivityLog.cpp b/src/host/ActivityLog.cpp index 4552710d..5b4a5223 100644 --- a/src/host/ActivityLog.cpp +++ b/src/host/ActivityLog.cpp @@ -112,6 +112,11 @@ void log_internal::ActivityLogInternal(const std::string& log) if (CurrentLogFileLevel() == 0U) return; + if (LogGetNetwork() != nullptr) { + network::BaseNetwork* network = (network::BaseNetwork*)LogGetNetwork(); + network->writeActLog(log.c_str()); + } + ::fprintf(m_actFpLog, "%s\n", log.c_str()); ::fflush(m_actFpLog); diff --git a/src/patch/ActivityLog.cpp b/src/patch/ActivityLog.cpp index 060c4a4c..0ee90191 100644 --- a/src/patch/ActivityLog.cpp +++ b/src/patch/ActivityLog.cpp @@ -112,6 +112,11 @@ void log_internal::ActivityLogInternal(const std::string& log) if (CurrentLogFileLevel() == 0U) return; + if (LogGetNetwork() != nullptr) { + network::BaseNetwork* network = (network::BaseNetwork*)LogGetNetwork(); + network->writeActLog(log.c_str()); + } + ::fprintf(m_actFpLog, "%s\n", log.c_str()); ::fflush(m_actFpLog); From 9d8d4fdcad83e14631ed2c1c873693ee161fa777 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 1 Oct 2025 09:52:10 -0400 Subject: [PATCH 035/200] add better concurrency protection to AffiliationLookup; fix issues when getting the granted source ID from a destination ID by properly ensuring the mapping table has an entry first; add srcId to the release grant callback (callback should never call AffiliationLookup gets as it can deadlock, so adding this parameter gives the source ID to the callback so that it doesn't have to do the lookup); --- src/common/lookups/AffiliationLookup.cpp | 86 +++++++++++++++++-- src/common/lookups/AffiliationLookup.h | 13 ++- src/host/dmr/Slot.cpp | 2 +- src/host/dmr/lookups/DMRAffiliationLookup.cpp | 19 +++- src/host/nxdn/Control.cpp | 2 +- src/host/p25/Control.cpp | 2 +- 6 files changed, 111 insertions(+), 13 deletions(-) diff --git a/src/common/lookups/AffiliationLookup.cpp b/src/common/lookups/AffiliationLookup.cpp index d0b53033..f01c0eb3 100644 --- a/src/common/lookups/AffiliationLookup.cpp +++ b/src/common/lookups/AffiliationLookup.cpp @@ -67,6 +67,8 @@ void AffiliationLookup::unitReg(uint32_t srcId) return; } + __lock(); + m_unitRegTable.push_back(srcId); m_unitRegTimers[srcId] = Timer(1000U, UNIT_REG_TIMEOUT); @@ -76,6 +78,8 @@ void AffiliationLookup::unitReg(uint32_t srcId) LogMessage(LOG_HOST, "%s, unit registration, srcId = %u", m_name.c_str(), srcId); } + + __unlock(); } /* Helper to group unaffiliate a source ID. */ @@ -88,13 +92,15 @@ bool AffiliationLookup::unitDereg(uint32_t srcId, bool automatic) return false; } + groupUnaff(srcId); + + __lock(); + if (m_verbose) { LogMessage(LOG_HOST, "%s, unit deregistration, srcId = %u", m_name.c_str(), srcId); } - groupUnaff(srcId); - m_unitRegTimers[srcId].stop(); // remove dynamic unit registration table entry @@ -113,6 +119,8 @@ bool AffiliationLookup::unitDereg(uint32_t srcId, bool automatic) } } + __unlock(); + return ret; } @@ -124,6 +132,8 @@ void AffiliationLookup::touchUnitReg(uint32_t srcId) return; } + __spinlock(); + if (isUnitReg(srcId)) { m_unitRegTimers[srcId].start(); } @@ -137,6 +147,8 @@ uint32_t AffiliationLookup::unitRegTimeout(uint32_t srcId) return 0U; } + __spinlock(); + if (isUnitReg(srcId)) { return m_unitRegTimers[srcId].getTimeout(); } @@ -152,6 +164,8 @@ uint32_t AffiliationLookup::unitRegTimer(uint32_t srcId) return 0U; } + __spinlock(); + if (isUnitReg(srcId)) { return m_unitRegTimers[srcId].getTimer(); } @@ -163,6 +177,8 @@ uint32_t AffiliationLookup::unitRegTimer(uint32_t srcId) bool AffiliationLookup::isUnitReg(uint32_t srcId) const { + __spinlock(); + // lookup dynamic unit registration table entry m_unitRegTable.lock(false); if (std::find(m_unitRegTable.begin(), m_unitRegTable.end(), srcId) != m_unitRegTable.end()) { @@ -179,9 +195,11 @@ bool AffiliationLookup::isUnitReg(uint32_t srcId) const void AffiliationLookup::clearUnitReg() { + __lock(); std::vector srcToRel = std::vector(); LogWarning(LOG_HOST, "%s, releasing all unit registrations", m_name.c_str()); m_unitRegTable.clear(); + __unlock(); } /* Helper to group affiliate a source ID. */ @@ -189,6 +207,8 @@ void AffiliationLookup::clearUnitReg() void AffiliationLookup::groupAff(uint32_t srcId, uint32_t dstId) { if (!isGroupAff(srcId, dstId)) { + __lock(); + // update dynamic affiliation table m_grpAffTable[srcId] = dstId; @@ -196,6 +216,8 @@ void AffiliationLookup::groupAff(uint32_t srcId, uint32_t dstId) LogMessage(LOG_HOST, "%s, group affiliation, srcId = %u, dstId = %u", m_name.c_str(), srcId, dstId); } + + __unlock(); } } @@ -203,6 +225,8 @@ void AffiliationLookup::groupAff(uint32_t srcId, uint32_t dstId) bool AffiliationLookup::groupUnaff(uint32_t srcId) { + __lock(); + // lookup dynamic affiliation table entry if (m_grpAffTable.find(srcId) != m_grpAffTable.end()) { uint32_t tblDstId = m_grpAffTable.at(srcId); @@ -211,6 +235,7 @@ bool AffiliationLookup::groupUnaff(uint32_t srcId) m_name.c_str(), srcId, tblDstId); } } else { + __unlock(); return false; } @@ -219,9 +244,11 @@ bool AffiliationLookup::groupUnaff(uint32_t srcId) uint32_t entry = m_grpAffTable.at(srcId); // this value will get discarded (void)entry; // but some variants of C++ mark the unordered_map<>::at as nodiscard m_grpAffTable.erase(srcId); + __unlock(); return true; } catch (...) { + __unlock(); return false; } } @@ -230,6 +257,8 @@ bool AffiliationLookup::groupUnaff(uint32_t srcId) bool AffiliationLookup::hasGroupAff(uint32_t dstId) const { + __spinlock(); + // lookup dynamic affiliation table entry m_grpAffTable.lock(false); for (auto entry : m_grpAffTable) { @@ -247,6 +276,8 @@ bool AffiliationLookup::hasGroupAff(uint32_t dstId) const bool AffiliationLookup::isGroupAff(uint32_t srcId, uint32_t dstId) const { + __spinlock(); + // lookup dynamic affiliation table entry m_grpAffTable.lock(false); if (m_grpAffTable.find(srcId) != m_grpAffTable.end()) { @@ -288,10 +319,14 @@ std::vector AffiliationLookup::clearGroupAff(uint32_t dstId, bool rele } } + __lock(); + for (auto srcId : srcToRel) { m_grpAffTable.erase(srcId); } + __unlock(); + return srcToRel; } @@ -312,6 +347,8 @@ bool AffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTi return false; } + __lock(); + m_grantChTable[dstId] = chNo; m_grantSrcIdTable[dstId] = srcId; m_rfGrantChCnt++; @@ -327,6 +364,8 @@ bool AffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTi m_name.c_str(), chNo, dstId, srcId, grp); } + __unlock(); + return true; } @@ -338,6 +377,8 @@ void AffiliationLookup::touchGrant(uint32_t dstId) return; } + __spinlock(); + if (isGranted(dstId)) { m_grantTimers[dstId].start(); } @@ -373,6 +414,7 @@ bool AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) if (isGranted(dstId)) { uint32_t chNo = m_grantChTable.at(dstId); + uint32_t srcId = getGrantedSrcId(dstId); if (m_verbose) { LogMessage(LOG_HOST, "%s, releasing channel grant, chNo = %u, dstId = %u", @@ -380,9 +422,11 @@ bool AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) } if (m_releaseGrant != nullptr) { - m_releaseGrant(chNo, dstId, 0U); + m_releaseGrant(chNo, srcId, dstId, 0U); } + __lock(); + m_grantChTable.erase(dstId); m_grantSrcIdTable.erase(dstId); m_uuGrantedTable.erase(dstId); @@ -398,6 +442,8 @@ bool AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) m_grantTimers[dstId].stop(); + __unlock(); + return true; } @@ -412,6 +458,8 @@ bool AffiliationLookup::isChBusy(uint32_t chNo) const return false; } + __spinlock(); + // lookup dynamic channel grant table entry m_grantChTable.lock(false); for (auto entry : m_grantChTable) { @@ -433,6 +481,8 @@ bool AffiliationLookup::isGranted(uint32_t dstId) const return false; } + __spinlock(); + // lookup dynamic channel grant table entry m_grantChTable.lock(false); for (auto entry : m_grantChTable) { @@ -457,6 +507,8 @@ bool AffiliationLookup::isGroup(uint32_t dstId) const return true; } + __spinlock(); + // lookup U-U grant flag table entry m_uuGrantedTable.lock(false); for (auto entry : m_uuGrantedTable) { @@ -481,6 +533,8 @@ bool AffiliationLookup::isNetGranted(uint32_t dstId) const return false; } + __spinlock(); + // lookup net granted flag table entry m_netGrantedTable.lock(false); for (auto entry : m_netGrantedTable) { @@ -505,8 +559,17 @@ uint32_t AffiliationLookup::getGrantedCh(uint32_t dstId) return 0U; } + __spinlock(); + if (isGranted(dstId)) { - return m_grantChTable[dstId]; + // lookup dynamic channel grant table entry + m_grantChTable.lock(false); + auto it = m_grantChTable.find(dstId); + if (it != m_grantChTable.end()) { + m_grantChTable.unlock(); + return m_grantChTable[dstId]; + } + m_grantChTable.unlock(); } return 0U; @@ -516,6 +579,8 @@ uint32_t AffiliationLookup::getGrantedCh(uint32_t dstId) uint32_t AffiliationLookup::getGrantedDstByCh(uint32_t chNo) { + __spinlock(); + // lookup dynamic channel grant table entry m_grantChTable.lock(false); for (auto entry : m_grantChTable) { @@ -537,6 +602,8 @@ uint32_t AffiliationLookup::getGrantedBySrcId(uint32_t srcId) return 0U; } + __spinlock(); + // lookup dynamic channel grant source table entry m_grantSrcIdTable.lock(false); for (auto entry : m_grantSrcIdTable) { @@ -558,8 +625,17 @@ uint32_t AffiliationLookup::getGrantedSrcId(uint32_t dstId) return 0U; } + __spinlock(); + if (isGranted(dstId)) { - return m_grantSrcIdTable[dstId]; + // lookup dynamic channel grant source table entry + m_grantSrcIdTable.lock(false); + auto it = m_grantSrcIdTable.find(dstId); + if (it != m_grantSrcIdTable.end()) { + m_grantSrcIdTable.unlock(); + return m_grantSrcIdTable[dstId]; + } + m_grantSrcIdTable.unlock(); } return 0U; diff --git a/src/common/lookups/AffiliationLookup.h b/src/common/lookups/AffiliationLookup.h index 23893914..f14cbd9a 100644 --- a/src/common/lookups/AffiliationLookup.h +++ b/src/common/lookups/AffiliationLookup.h @@ -21,6 +21,7 @@ #define __AFFILIATION_LOOKUP_H__ #include "common/Defines.h" +#include "common/concurrent/concurrent_lock.h" #include "common/concurrent/vector.h" #include "common/concurrent/unordered_map.h" #include "common/lookups/ChannelLookup.h" @@ -41,7 +42,7 @@ namespace lookups * and group affiliation information. * @ingroup lookups_aff */ - class HOST_SW_API AffiliationLookup { + class HOST_SW_API AffiliationLookup : public concurrent::concurrent_lock { public: /** * @brief Initializes a new instance of the AffiliationLookup class. @@ -265,11 +266,15 @@ namespace lookups /** * @brief Helper to set the release grant callback. + * @note Do not call AffiliationLookup get functions from within this callback, deadlock protection + * is not guaranteed. * @param callback Relase grant function callback. */ - void setReleaseGrantCallback(std::function&& callback) { m_releaseGrant = callback; } + void setReleaseGrantCallback(std::function&& callback) { m_releaseGrant = callback; } /** * @brief Helper to set the unit deregistration callback. + * @note Do not call AffiliationLookup get functions from within this callback, deadlock protection + * is not guaranteed. * @param callback Unit deregistration function callback. */ void setUnitDeregCallback(std::function&& callback) { m_unitDereg = callback; } @@ -287,8 +292,8 @@ namespace lookups concurrent::unordered_map m_netGrantedTable; concurrent::unordered_map m_grantTimers; - // chNo dstId slot - std::function m_releaseGrant; + // chNo srcId dstId slot + std::function m_releaseGrant; // srcId auto std::function m_unitDereg; diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index 1a8dedbd..af5b9c38 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -1026,7 +1026,7 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s m_affiliations = new dmr::lookups::DMRAffiliationLookup(chLookup, verbose); // set the grant release callback - m_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { + m_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t srcId, uint32_t dstId, uint8_t slot) { Slot* tscc = m_dmr->getTSCCSlot(); if (tscc != nullptr) { if (chNo == tscc->m_channelNo) { diff --git a/src/host/dmr/lookups/DMRAffiliationLookup.cpp b/src/host/dmr/lookups/DMRAffiliationLookup.cpp index 6a03b862..4204ccf6 100644 --- a/src/host/dmr/lookups/DMRAffiliationLookup.cpp +++ b/src/host/dmr/lookups/DMRAffiliationLookup.cpp @@ -61,6 +61,8 @@ bool DMRAffiliationLookup::grantChSlot(uint32_t dstId, uint32_t srcId, uint8_t s return false; } + __lock(); + if (getAvailableSlotForChannel(chNo) == 0U || chNo == m_tsccChNo) { m_chLookup->removeRFCh(chNo); } @@ -81,6 +83,8 @@ bool DMRAffiliationLookup::grantChSlot(uint32_t dstId, uint32_t srcId, uint8_t s m_name.c_str(), chNo, slot, dstId, grp); } + __unlock(); + return true; } @@ -112,6 +116,7 @@ bool DMRAffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) if (isGranted(dstId)) { uint32_t chNo = m_grantChTable.at(dstId); + uint32_t srcId = getGrantedSrcId(dstId); std::tuple slotData = m_grantChSlotTable.at(dstId); uint8_t slot = std::get<1>(slotData); @@ -121,9 +126,11 @@ bool DMRAffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) } if (m_releaseGrant != nullptr) { - m_releaseGrant(chNo, dstId, slot); + m_releaseGrant(chNo, srcId, dstId, slot); } + __lock(); + m_grantChTable.erase(dstId); m_grantSrcIdTable.erase(dstId); m_grantChSlotTable.erase(dstId); @@ -140,6 +147,8 @@ bool DMRAffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) m_grantTimers[dstId].stop(); + __unlock(); + return true; } @@ -154,6 +163,8 @@ bool DMRAffiliationLookup::isChBusy(uint32_t chNo) const return false; } + __spinlock(); + // lookup dynamic channel grant table entry for (auto grantEntry : m_grantChTable) { if (grantEntry.second == chNo) { @@ -187,6 +198,8 @@ uint8_t DMRAffiliationLookup::getGrantedSlot(uint32_t dstId) const return 0U; } + __spinlock(); + // lookup dynamic channel grant table entry for (auto entry : m_grantChSlotTable) { if (entry.first == dstId) { @@ -219,6 +232,8 @@ uint32_t DMRAffiliationLookup::getAvailableChannelForSlot(uint8_t slot) const return 0U; } + __spinlock(); + uint32_t chNo = 0U; for (auto entry : m_chLookup->rfChDataTable()) { if (entry.second.chNo() == m_tsccChNo && slot == m_tsccSlot) { @@ -260,6 +275,8 @@ uint8_t DMRAffiliationLookup::getAvailableSlotForChannel(uint32_t chNo) const return 0U; } + __spinlock(); + uint8_t slot = 1U; // lookup dynamic channel slot grant table entry diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index 5d4f2192..fd257b0a 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -278,7 +278,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw m_affiliations->setDisableUnitRegTimeout(disableUnitRegTimeout); // set the grant release callback - m_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { + m_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t srcId, uint32_t dstId, uint8_t slot) { // callback REST API to clear TG permit for the granted TG on the specified voice channel if (m_authoritative && m_supervisor) { ::lookups::VoiceChData voiceChData = m_affiliations->rfCh()->getRFChData(chNo); diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index edbefed4..64c84a81 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -452,7 +452,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw m_affiliations->setDisableUnitRegTimeout(disableUnitRegTimeout); // set the grant release callback - m_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { + m_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t srcId, uint32_t dstId, uint8_t slot) { // callback REST API to clear TG permit for the granted TG on the specified voice channel if (m_authoritative && m_supervisor) { ::lookups::VoiceChData voiceChData = m_affiliations->rfCh()->getRFChData(chNo); From 5d75babf099dc9c6c832803b90ed4380d8ada947 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 1 Oct 2025 13:12:21 -0400 Subject: [PATCH 036/200] remove __spinlock() from touchGrant() and isGranted(); --- src/common/lookups/AffiliationLookup.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/common/lookups/AffiliationLookup.cpp b/src/common/lookups/AffiliationLookup.cpp index f01c0eb3..9463f86c 100644 --- a/src/common/lookups/AffiliationLookup.cpp +++ b/src/common/lookups/AffiliationLookup.cpp @@ -377,11 +377,17 @@ void AffiliationLookup::touchGrant(uint32_t dstId) return; } - __spinlock(); + /* + ** bryanb: this doesn't __spinlock(), this is dangerous but necessary + ** otherwise an affiliations lookup lock can cause audio cuts if this is + ** used in an audio processing chain + */ + m_grantTimers.lock(false); if (isGranted(dstId)) { m_grantTimers[dstId].start(); } + m_grantTimers.unlock(); } /* Helper to release the channel grant for the destination ID. */ @@ -481,7 +487,11 @@ bool AffiliationLookup::isGranted(uint32_t dstId) const return false; } - __spinlock(); + /* + ** bryanb: this doesn't __spinlock(), this is dangerous but necessary + ** otherwise an affiliations lookup lock can cause audio cuts if this is + ** used in an audio processing chain + */ // lookup dynamic channel grant table entry m_grantChTable.lock(false); From b8f228fb6e368aba813e4d70e3ed43d7530c3114 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 1 Oct 2025 21:27:27 -0400 Subject: [PATCH 037/200] correct bad ordering of log message for call source switching; --- src/fne/network/callhandler/TagDMRData.cpp | 4 ++-- src/fne/network/callhandler/TagNXDNData.cpp | 4 ++-- src/fne/network/callhandler/TagP25Data.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 4d995a8e..55b3cf91 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -243,10 +243,10 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver && status.slotNo == slotNo) { - status.streamId = streamId; - status.srcId = srcId; LogMessage(LOG_NET, "DMR, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, external = %u", peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, external); + status.streamId = streamId; + status.srcId = srcId; } if (status.srcId != 0U && status.srcId != srcId) { diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 61149508..0adeb6f0 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -281,10 +281,10 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver) { - status.streamId = streamId; - status.srcId = srcId; LogMessage(LOG_NET, "NXDN, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + status.streamId = streamId; + status.srcId = srcId; } if (status.srcId != 0U && status.srcId != srcId) { diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index c32fe8b1..3726e69c 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -286,10 +286,10 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver) { - status.streamId = streamId; - status.srcId = srcId; LogMessage(LOG_NET, "P25, Call Source Switched, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + status.streamId = streamId; + status.srcId = srcId; } if (status.srcId != 0U && status.srcId != srcId) { From 0646ba52a800a63628d52ae3aa24f7c7dfb88982 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 1 Oct 2025 22:54:55 -0400 Subject: [PATCH 038/200] remove blockTrafficTo; implement promiscuous hub mode for FNE (this mode allows the FNE to pass any and all traffic transparently); --- README.md | 4 +- configs/fne-config.example.yml | 17 +-------- src/fne/FNEMain.cpp | 8 ++++ src/fne/FNEMain.h | 3 ++ src/fne/HostFNE.cpp | 22 ++++------- src/fne/network/DiagNetwork.cpp | 4 +- src/fne/network/FNENetwork.cpp | 20 +++++----- src/fne/network/FNENetwork.h | 8 ++-- src/fne/network/PeerNetwork.cpp | 26 +------------ src/fne/network/PeerNetwork.h | 18 --------- src/fne/network/callhandler/TagAnalogData.cpp | 13 ++----- src/fne/network/callhandler/TagDMRData.cpp | 38 ++++++++++++------- src/fne/network/callhandler/TagNXDNData.cpp | 38 ++++++++++++------- src/fne/network/callhandler/TagP25Data.cpp | 38 ++++++++++++------- .../callhandler/packetdata/DMRPacketData.cpp | 13 ++----- .../callhandler/packetdata/P25PacketData.cpp | 5 --- 16 files changed, 122 insertions(+), 153 deletions(-) diff --git a/README.md b/README.md index 94e8966c..9d2213a4 100644 --- a/README.md +++ b/README.md @@ -254,12 +254,14 @@ usage: ./dvmhost [-vhdf] [--syslog] [--setup] [--cal][--boot] [-c ] +usage: ./dvmhost/dvmfne [-vhf][-p][--syslog][-c ] -v show version information -h show this screen -f foreground mode + -p promiscuous hub mode + --syslog force logging to syslog -c specifies the configuration file to use diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index f4831673..be70f498 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -180,7 +180,7 @@ master: time: 30 # -# External Peers +# FNE Neighbor Peers # peers: - name: EXAMPLEPEER @@ -197,21 +197,6 @@ peers: # Network Peer ID peerId: 9000990 - # List of peer IDs to block traffic to for this peer. - # The purpose of the blockTrafficTo peer ID list is to prevent traffic sourced from a listed peer ID from - # being resent/repeated to this peer. This usually *should* not needed to be configured, and is usually used - # on complex system configurations where traffic loops are possible due to duplicated or redundant peer - # connections. - # - # For example: If we have FNEs: A, B and C, where both B and C are connected to A, and B is also connected to - # C. On FNE B we would have blockTrafficTo entries for each external peer block listing the peer block peer ID's - # for external peer Cs ID on external peer A's entry, and external peer As ID on external peer Cs entry. - # - # Additionally, depending on configured talkgroup rules and other criteria, it may be necessary to also have - # FNE Bs peer ID on FNE Cs peer block entry for FNE A. - # - blockTrafficTo: [] - # Flag indicating whether or not peer endpoint networking is encrypted. encrypted: false # AES-256 32-byte Preshared Key diff --git a/src/fne/FNEMain.cpp b/src/fne/FNEMain.cpp index 5b61eacc..32ed5d46 100644 --- a/src/fne/FNEMain.cpp +++ b/src/fne/FNEMain.cpp @@ -49,6 +49,8 @@ std::string g_lockFile = std::string(DEFAULT_LOCK_FILE); bool g_foreground = false; bool g_killed = false; +bool g_promiscuousHub = false; + uint8_t* g_gitHashBytes = nullptr; // --------------------------------------------------------------------------- @@ -98,6 +100,7 @@ void usage(const char* message, const char* arg) ::fprintf(stdout, "usage: %s [-vhf]" + "[-p]" "[--syslog]" "[-c ]" "\n\n" @@ -105,6 +108,8 @@ void usage(const char* message, const char* arg) " -h show this screen\n" " -f foreground mode\n" "\n" + " -p promiscuous hub\n" + "\n" " --syslog force logging to syslog\n" "\n" " -c specifies the configuration file to use\n" @@ -137,6 +142,9 @@ int checkArgs(int argc, char* argv[]) else if (IS("-f")) { g_foreground = true; } + else if (IS("-p")) { + g_promiscuousHub = true; + } else if (IS("--syslog")) { g_useSyslog = true; } diff --git a/src/fne/FNEMain.h b/src/fne/FNEMain.h index b677d775..9d279160 100644 --- a/src/fne/FNEMain.h +++ b/src/fne/FNEMain.h @@ -38,6 +38,9 @@ extern bool g_foreground; /** @brief (Global) Flag indicating the FNE should stop immediately. */ extern bool g_killed; +/** @brief (Global) Flag indicating the FNE is a promiscuous hub, and will pass all TGs and RIDs. */ +extern bool g_promiscuousHub; + extern uint8_t* g_gitHashBytes; /** diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 632e5a26..a1a52537 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -372,7 +372,14 @@ bool HostFNE::readParams() LogWarning(LOG_HOST, "It is *not* recommended to disable the \"allowActivityTransfer\" option."); } + if (g_promiscuousHub) { + LogWarning(LOG_HOST, "This FNE was started as a promiscuous hub! In this mode, the FNE will not apply any TG or RID ACL rules and will transparently pass *all* traffic."); + LogWarning(LOG_HOST, "Promiscuous Hub is intended as a loop breaking measure *only*!"); + } + LogInfo("General Parameters"); + if (g_promiscuousHub) + LogInfo(" !! Promiscuous Hub: yes"); LogInfo(" Peer Ping Time: %us", m_pingTime); LogInfo(" Maximum Missed Pings: %u", m_maxMissedPings); LogInfo(" ACL Rule Update Time: %u mins", m_updateLookupTime); @@ -864,21 +871,6 @@ bool HostFNE::createPeerNetworks() network->setNXDNCallback(std::bind(&HostFNE::processPeerNXDN, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); network->setAnalogCallback(std::bind(&HostFNE::processPeerAnalog, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); - /* - ** Block Traffic To Peers - */ - - yaml::Node& blockTrafficTo = peerConf["blockTrafficTo"]; - if (blockTrafficTo.size() > 0U) { - for (size_t i = 0; i < blockTrafficTo.size(); i++) { - uint32_t peerId = (uint32_t)::strtoul(blockTrafficTo[i].as("0").c_str(), NULL, 10); - if (peerId != 0U) { - ::LogInfoEx(LOG_HOST, "Peer ID %u Blocks Traffic To PEER %u", id, peerId); - network->addBlockedTrafficPeer(peerId); - } - } - } - network->enable(enabled); if (enabled) { bool ret = network->open(); diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 03a0602e..c281a627 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -195,7 +195,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_peers.find(req->rtpHeader.getSSRC()) != network->m_peers.end()) { FNEPeerConnection* connection = network->m_peers[req->rtpHeader.getSSRC()]; if (connection != nullptr) { - if (connection->isExternalPeer() && connection->isPeerLink()) { + if (connection->isExternalFNEPeer() && connection->isPeerLink()) { validPeerId = true; pktPeerId = req->rtpHeader.getSSRC(); } @@ -376,7 +376,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); // validate peer (simple validation really) - if (connection->connected() && connection->address() == ip && connection->isExternalPeer() && + if (connection->connected() && connection->address() == ip && connection->isExternalFNEPeer() && connection->isPeerLink()) { DECLARE_UINT8_ARRAY(rawPayload, req->length); ::memcpy(rawPayload, req->buffer, req->length); diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 054f1427..1834a562 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -363,7 +363,7 @@ void FNENetwork::clock(uint32_t ms) FNEPeerConnection* connection = peer.second; if (connection != nullptr) { uint64_t dt = 0U; - if (connection->isExternalPeer() || connection->isPeerLink()) + if (connection->isExternalFNEPeer() || connection->isPeerLink()) dt = connection->lastPing() + ((m_host->m_pingTime * 1000) * (m_host->m_maxMissedPings * 2U)); else dt = connection->lastPing() + ((m_host->m_pingTime * 1000) * m_host->m_maxMissedPings); @@ -376,9 +376,9 @@ void FNENetwork::clock(uint32_t ms) connection->connected(false); connection->connectionState(NET_STAT_INVALID); - // if the connection was an external peer or a peer link -- be noisy about a possible + // if the connection was an external FNE neighbor peer or a peer link -- be noisy about a possible // netsplit - if (connection->isExternalPeer() || connection->isPeerLink()) { + if (connection->isExternalFNEPeer() || connection->isPeerLink()) { for (uint8_t i = 0U; i < 3U; i++) LogWarning(LOG_NET, "PEER %u (%s) downstream netsplit, dt = %u, now = %u", id, connection->identity().c_str(), dt, now); @@ -1020,12 +1020,12 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogInfoEx(LOG_NET, "PEER %u reports identity [%8s]", peerId, identity.c_str()); } - // is the peer reporting it is an external peer? + // is the peer reporting it is an external FNE neighbor peer? if (peerConfig["externalPeer"].is()) { bool external = peerConfig["externalPeer"].get(); - connection->isExternalPeer(external); + connection->isExternalFNEPeer(external); if (external) - LogInfoEx(LOG_NET, "PEER %u reports external peer", peerId); + LogInfoEx(LOG_NET, "PEER %u reports external FNE neighbor peer", peerId); // check if the peer is participating in peer link lookups::PeerId peerEntry = network->m_peerListLookup->find(req->peerId); @@ -1856,7 +1856,7 @@ void FNENetwork::taskACLUpdate(ACLUpdateRequest* req) // if the connection is an external peer, and peer is participating in peer link, // send the peer proper configuration data - if (connection->isExternalPeer() && connection->isPeerLink()) { + if (connection->isExternalFNEPeer() && connection->isPeerLink()) { LogInfoEx(LOG_NET, "PEER %u (%s) sending Peer-Link ACL list updates", req->peerId, peerIdentity.c_str()); network->writeWhitelistRIDs(req->peerId, aclStreamId, true); @@ -2338,9 +2338,9 @@ bool FNENetwork::writePeer(uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePai if (m_maskOutboundPeerID) ssrc = m_peerId; // mask the source SSRC to our own peer ID else { - if ((connection->isExternalPeer() && !connection->isPeerLink()) && m_maskOutboundPeerIDForNonPL) { - // if the peer is an external peer, and not a Peer-Link peer, we need to send the packet - // to the external peer with our peer ID as the source instead of the originating peer + if ((connection->isExternalFNEPeer() && !connection->isPeerLink()) && m_maskOutboundPeerIDForNonPL) { + // if the peer is an external FNE neighbor peer, and not a Peer-Link peer, we need to send the packet + // to the external FNE neighbor peer with our peer ID as the source instead of the originating peer // because we have routed it ssrc = m_peerId; } diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index d3ae0b1e..13223ee1 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -122,7 +122,7 @@ namespace network m_pingsReceived(0U), m_lastPing(0U), m_missedACLUpdates(0U), - m_isExternalPeer(false), + m_isExternalFNEPeer(false), m_isConventionalPeer(false), m_isSysView(false), m_isPeerLink(false), @@ -151,7 +151,7 @@ namespace network m_pingsReceived(0U), m_lastPing(0U), m_missedACLUpdates(0U), - m_isExternalPeer(false), + m_isExternalFNEPeer(false), m_isConventionalPeer(false), m_isSysView(false), m_isPeerLink(false), @@ -342,9 +342,9 @@ namespace network DECLARE_PROPERTY_PLAIN(uint32_t, missedACLUpdates); /** - * @brief Flag indicating this connection is from an external peer. + * @brief Flag indicating this connection is from an external neighbor FNE peer. */ - DECLARE_PROPERTY_PLAIN(bool, isExternalPeer); + DECLARE_PROPERTY_PLAIN(bool, isExternalFNEPeer); /** * @brief Flag indicating this connection is from an conventional peer. */ diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index e3070e68..dc4ec26c 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -41,7 +41,6 @@ PeerNetwork::PeerNetwork(const std::string& address, uint16_t port, uint16_t loc bool duplex, bool debug, bool dmr, bool p25, bool nxdn, bool analog, bool slot1, bool slot2, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup) : Network(address, port, localPort, peerId, password, duplex, debug, dmr, p25, nxdn, analog, slot1, slot2, allowActivityTransfer, allowDiagnosticTransfer, updateLookup, saveLookup), m_attachedKeyRSPHandler(false), - m_blockTrafficToTable(), m_dmrCallback(nullptr), m_p25Callback(nullptr), m_nxdnCallback(nullptr), @@ -104,29 +103,6 @@ void PeerNetwork::close() Network::close(); } -/* Checks if the passed peer ID is blocked from sending to this peer. */ - -bool PeerNetwork::checkBlockedPeer(uint32_t peerId) -{ - if (!m_enabled) - return false; - - if (m_blockTrafficToTable.empty()) - return false; - - if (std::find(m_blockTrafficToTable.begin(), m_blockTrafficToTable.end(), peerId) != m_blockTrafficToTable.end()) { - if (m_debug) { - ::LogDebugEx(LOG_HOST, "PeerNetwork::checkBlockedPeer()", "PEER %u peerId = %u, blocking traffic", m_peerId, peerId); - } - return true; - } - - if (m_debug) { - ::LogDebugEx(LOG_HOST, "PeerNetwork::checkBlockedPeer()", "PEER %u peerId = %u, passing traffic", m_peerId, peerId); - } - return false; -} - /* Writes a complete update of this CFNE's active peer list to the network. */ bool PeerNetwork::writePeerLinkPeers(json::array* peerList) @@ -436,7 +412,7 @@ bool PeerNetwork::writeConfig() // Flags bool external = true; - config["externalPeer"].set(external); // External Peer Marker + config["externalPeer"].set(external); // External FNE Neighbor Peer Marker config["software"].set(std::string(software)); // Software ID diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index 28c9d700..c4fdf184 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -124,22 +124,6 @@ namespace network */ void setAnalogCallback(std::function&& callback) { m_analogCallback = callback; } - /** - * @brief Gets the blocked traffic peer ID table. - * @returns std::vector List of peer IDs this peer network cannot send traffic to. - */ - std::vector blockTrafficTo() const { return m_blockTrafficToTable; } - /** - * @brief Adds an entry to the blocked traffic peer ID table. - * @param peerId Peer ID to add to the blocked traffic table. - */ - void addBlockedTrafficPeer(uint32_t peerId) { m_blockTrafficToTable.push_back(peerId); } - /** - * @brief Checks if the passed peer ID is blocked from sending to this peer. - * @returns bool True, if blocked peer table is cleared, otherwise false. - */ - bool checkBlockedPeer(uint32_t peerId); - /** * @brief Writes a complete update of this CFNE's active peer list to the network. * @param peerList List of active peers. @@ -166,8 +150,6 @@ namespace network DECLARE_PROPERTY(bool, attachedKeyRSPHandler, AttachedKeyRSPHandler); protected: - std::vector m_blockTrafficToTable; - /** * @brief DMR Protocol Callback. * (This is called when the master sends a DMR packet.) diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 415708a1..c0d81c38 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -285,18 +285,13 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee continue; } - // is this peer ignored? - if (!isPeerPermitted(dstPeerId, analogData, streamId, true)) { - continue; - } - - // check if the source peer is blocked from sending to this peer - if (peer.second->checkBlockedPeer(peerId)) { + // skip peer if it isn't enabled + if (!peer.second->isEnabled()) { continue; } - // skip peer if it isn't enabled - if (!peer.second->isEnabled()) { + // is this peer ignored? + if (!isPeerPermitted(dstPeerId, analogData, streamId, true)) { continue; } diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 55b3cf91..1de1c20f 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -19,6 +19,7 @@ #include "network/FNENetwork.h" #include "network/callhandler/TagDMRData.h" #include "HostFNE.h" +#include "FNEMain.h" using namespace system_clock; using namespace network; @@ -368,7 +369,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (peerId != peer.first) { FNEPeerConnection* conn = peer.second; if (conn != nullptr) { - if (conn->isExternalPeer()) { + if (conn->isExternalFNEPeer()) { continue; } } @@ -384,7 +385,11 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } } - // repeat traffic to the connected peers + /* + ** MASTER TRAFFIC + */ + + // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; for (auto peer : m_network->m_peers) { @@ -399,7 +404,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // is this peer an external peer? bool external = false; if (conn != nullptr) { - external = conn->isExternalPeer(); + external = conn->isExternalFNEPeer(); } // is this a private call? @@ -456,7 +461,11 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId return true; } - // repeat traffic to external peers + /* + ** PEER TRAFFIC (e.g. networks this FNE is peered to) + */ + + // repeat traffic to master nodes we have connected to as a peer if (m_network->m_host->m_peerNetworks.size() > 0U && !tg.config().parrot()) { for (auto peer : m_network->m_host->m_peerNetworks) { uint32_t dstPeerId = peer.second->getPeerId(); @@ -469,18 +478,13 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId continue; } - // is this peer ignored? - if (!isPeerPermitted(dstPeerId, dmrData, streamId, true)) { - continue; - } - - // check if the source peer is blocked from sending to this peer - if (peer.second->checkBlockedPeer(peerId)) { + // skip peer if it isn't enabled + if (!peer.second->isEnabled()) { continue; } - // skip peer if it isn't enabled - if (!peer.second->isEnabled()) { + // is this peer ignored? + if (!isPeerPermitted(dstPeerId, dmrData, streamId, true)) { continue; } @@ -785,6 +789,10 @@ bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::NetDat bool TagDMRData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t streamId, bool external) { + // promiscuous hub mode performs no ACL checking and will pass all traffic + if (g_promiscuousHub) + return true; + if (data.getFLCO() == FLCO::PRIVATE) { if (m_network->m_disallowU2U) return false; @@ -889,6 +897,10 @@ bool TagDMRData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t bool TagDMRData::validate(uint32_t peerId, data::NetData& data, lc::CSBK* csbk, uint32_t streamId) { + // promiscuous hub mode performs no ACL checking and will pass all traffic + if (g_promiscuousHub) + return true; + // is the source ID a blacklisted ID? bool rejectUnknownBadCall = false; lookups::RadioId rid = m_network->m_ridLookup->find(data.getSrcId()); diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 0adeb6f0..28fa8958 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -23,6 +23,7 @@ #include "network/FNENetwork.h" #include "network/callhandler/TagNXDNData.h" #include "HostFNE.h" +#include "FNEMain.h" using namespace system_clock; using namespace network; @@ -398,7 +399,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI if (peerId != peer.first) { FNEPeerConnection* conn = peer.second; if (conn != nullptr) { - if (conn->isExternalPeer()) { + if (conn->isExternalFNEPeer()) { continue; } } @@ -414,7 +415,11 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI } } - // repeat traffic to the connected peers + /* + ** MASTER TRAFFIC + */ + + // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; for (auto peer : m_network->m_peers) { @@ -429,7 +434,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // is this peer an external peer? bool external = false; if (conn != nullptr) { - external = conn->isExternalPeer(); + external = conn->isExternalFNEPeer(); } // is this a private call? @@ -486,7 +491,11 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI return true; } - // repeat traffic to external peers + /* + ** PEER TRAFFIC (e.g. networks this FNE is peered to) + */ + + // repeat traffic to master nodes we have connected to as a peer if (m_network->m_host->m_peerNetworks.size() > 0U && !tg.config().parrot()) { for (auto peer : m_network->m_host->m_peerNetworks) { uint32_t dstPeerId = peer.second->getPeerId(); @@ -499,18 +508,13 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI continue; } - // is this peer ignored? - if (!isPeerPermitted(dstPeerId, lc, messageType, streamId, true)) { - continue; - } - - // check if the source peer is blocked from sending to this peer - if (peer.second->checkBlockedPeer(peerId)) { + // skip peer if it isn't enabled + if (!peer.second->isEnabled()) { continue; } - // skip peer if it isn't enabled - if (!peer.second->isEnabled()) { + // is this peer ignored? + if (!isPeerPermitted(dstPeerId, lc, messageType, streamId, true)) { continue; } @@ -677,6 +681,10 @@ bool TagNXDNData::peerRewrite(uint32_t peerId, uint32_t& dstId, bool outbound) bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, uint32_t streamId, bool external) { + // promiscuous hub mode performs no ACL checking and will pass all traffic + if (g_promiscuousHub) + return true; + if (!lc.getGroup()) { if (m_network->m_disallowU2U) return false; @@ -781,6 +789,10 @@ bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t message bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, uint32_t streamId) { + // promiscuous hub mode performs no ACL checking and will pass all traffic + if (g_promiscuousHub) + return true; + // is the source ID a blacklisted ID? bool rejectUnknownBadCall = false; lookups::RadioId rid = m_network->m_ridLookup->find(lc.getSrcId()); diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 3726e69c..1717f9bc 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -18,6 +18,7 @@ #include "network/FNENetwork.h" #include "network/callhandler/TagP25Data.h" #include "HostFNE.h" +#include "FNEMain.h" using namespace system_clock; using namespace network; @@ -422,7 +423,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (peerId != peer.first) { FNEPeerConnection* conn = peer.second; if (conn != nullptr) { - if (conn->isExternalPeer()) { + if (conn->isExternalFNEPeer()) { continue; } } @@ -438,7 +439,11 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } } - // repeat traffic to the connected peers + /* + ** MASTER TRAFFIC + */ + + // repeat traffic to nodes connected to us as peers if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; for (auto peer : m_network->m_peers) { @@ -453,7 +458,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // is this peer an external peer? bool external = false; if (conn != nullptr) { - external = conn->isExternalPeer(); + external = conn->isExternalFNEPeer(); } // is this a private call? @@ -515,7 +520,11 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId return true; } - // repeat traffic to external peers + /* + ** PEER TRAFFIC (e.g. networks this FNE is peered to) + */ + + // repeat traffic to master nodes we have connected to as a peer if (m_network->m_host->m_peerNetworks.size() > 0U && !tg.config().parrot()) { for (auto peer : m_network->m_host->m_peerNetworks) { uint32_t dstPeerId = peer.second->getPeerId(); @@ -528,18 +537,13 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId continue; } - // is this peer ignored? - if (!isPeerPermitted(dstPeerId, control, duid, streamId, true)) { - continue; - } - - // check if the source peer is blocked from sending to this peer - if (peer.second->checkBlockedPeer(peerId)) { + // skip peer if it isn't enabled + if (!peer.second->isEnabled()) { continue; } - // skip peer if it isn't enabled - if (!peer.second->isEnabled()) { + // is this peer ignored? + if (!isPeerPermitted(dstPeerId, control, duid, streamId, true)) { continue; } @@ -1085,6 +1089,10 @@ bool TagP25Data::processTSDUToExternal(uint8_t* buffer, uint32_t srcPeerId, uint bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, DUID::E duid, uint32_t streamId, bool external) { + // promiscuous hub mode performs no ACL checking and will pass all traffic + if (g_promiscuousHub) + return true; + if (control.getLCO() == LCO::PRIVATE) { if (m_network->m_disallowU2U) return false; @@ -1240,6 +1248,10 @@ bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, DUID::E duid, bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const p25::lc::TSBK* tsbk, uint32_t streamId) { + // promiscuous hub mode performs no ACL checking and will pass all traffic + if (g_promiscuousHub) + return true; + bool skipRidCheck = false; if ((control.getMFId() == MFG_MOT && control.getSrcId() == 0U) || control.getSrcId() > WUID_FNE) { skipRidCheck = true; diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp index 89aaa829..3a226f54 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp @@ -305,18 +305,13 @@ void DMRPacketData::dispatchToFNE(uint32_t peerId, dmr::data::NetData& dmrData, // don't try to repeat traffic to the source peer...if this traffic // is coming from a external peer if (dstPeerId != peerId) { - // is this peer ignored? - if (!m_tag->isPeerPermitted(dstPeerId, dmrData, streamId, true)) { - continue; - } - - // check if the source peer is blocked from sending to this peer - if (peer.second->checkBlockedPeer(peerId)) { + // skip peer if it isn't enabled + if (!peer.second->isEnabled()) { continue; } - // skip peer if it isn't enabled - if (!peer.second->isEnabled()) { + // is this peer ignored? + if (!m_tag->isPeerPermitted(dstPeerId, dmrData, streamId, true)) { continue; } diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index eec1766f..f0cd19d4 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -815,11 +815,6 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) // don't try to repeat traffic to the source peer...if this traffic // is coming from a external peer if (dstPeerId != peerId) { - // check if the source peer is blocked from sending to this peer - if (peer.second->checkBlockedPeer(peerId)) { - continue; - } - // skip peer if it isn't enabled if (!peer.second->isEnabled()) { continue; From 6995c25bf82145c454f71ab26fa1f25ef22c06da Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 1 Oct 2025 22:58:21 -0400 Subject: [PATCH 039/200] fix typo; --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d2213a4..5ab8e42c 100644 --- a/README.md +++ b/README.md @@ -254,7 +254,7 @@ usage: ./dvmhost [-vhdf] [--syslog] [--setup] [--cal][--boot] [-c ] +usage: ./dvmfne [-vhf][-p][--syslog][-c ] -v show version information -h show this screen From 5df7bd356d556029d3939a929658ade4813d4e3a Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 2 Oct 2025 16:10:19 -0400 Subject: [PATCH 040/200] for the purposes of my OCD fix incorrect one-liner Doxygen documentation (its //!< and not just //!); --- src/bridge/HostBridge.h | 8 +- src/common/Defines.h | 26 +- src/common/Thread.h | 4 +- src/common/analog/AnalogDefines.h | 10 +- src/common/concurrent/concurrent_lock.h | 4 +- src/common/dmr/DMRDefines.h | 254 +++---- src/common/lookups/AdjSiteMapLookup.h | 4 +- src/common/lookups/PeerListLookup.h | 4 +- src/common/lookups/RadioIdLookup.h | 4 +- src/common/lookups/TalkgroupRulesLookup.h | 4 +- src/common/network/BaseNetwork.h | 48 +- src/common/network/NetRPC.h | 8 +- src/common/network/Network.h | 30 +- src/common/network/RTPFNEHeader.h | 100 +-- src/common/network/rest/http/HTTPLexer.h | 62 +- src/common/network/rest/http/HTTPPayload.h | 32 +- src/common/network/udp/Socket.h | 12 +- src/common/network/viface/VIFace.h | 4 +- src/common/nxdn/NXDNDefines.h | 264 ++++---- src/common/p25/P25Defines.h | 632 +++++++++--------- src/common/p25/dfsi/DFSIDefines.h | 122 ++-- src/common/p25/dfsi/frames/FrameDefines.h | 56 +- src/fne/HostFNE.h | 4 +- src/fne/network/FNENetwork.h | 26 +- src/fne/network/P25OTARService.h | 8 +- src/fne/network/PeerNetwork.h | 16 +- .../callhandler/packetdata/P25PacketData.h | 16 +- src/host/dmr/Slot.h | 8 +- src/host/modem/Modem.h | 196 +++--- src/host/modem/ModemV24.h | 8 +- src/host/modem/port/specialized/V24UDPPort.h | 10 +- src/vocoder/MBEDecoder.h | 4 +- src/vocoder/MBEEncoder.h | 4 +- 33 files changed, 996 insertions(+), 996 deletions(-) diff --git a/src/bridge/HostBridge.h b/src/bridge/HostBridge.h index 5694fe77..4897f1bd 100644 --- a/src/bridge/HostBridge.h +++ b/src/bridge/HostBridge.h @@ -103,11 +103,11 @@ void mdcPacketDetected(int frameCount, mdc_u8_t op, mdc_u8_t arg, mdc_u16_t unit * @ingroup bridge */ struct NetPacketRequest { - uint32_t srcId; - uint32_t dstId; + uint32_t srcId; //!< Source Address + uint32_t dstId; //!< Destination Address - int pcmLength = 0U; //! Length of PCM data buffer - uint8_t* pcm = nullptr; //! Raw PCM buffer + int pcmLength = 0U; //!< Length of PCM data buffer + uint8_t* pcm = nullptr; //!< Raw PCM buffer }; // --------------------------------------------------------------------------- diff --git a/src/common/Defines.h b/src/common/Defines.h index 47f21ddd..eca5a11c 100644 --- a/src/common/Defines.h +++ b/src/common/Defines.h @@ -168,32 +168,32 @@ const uint32_t RPC_DEFAULT_PORT = 9890; * @brief Operational Host States */ enum HOST_STATE { - FNE_STATE = 240U, //! FNE (only used by dvmfne) + FNE_STATE = 240U, //!< FNE (only used by dvmfne) - HOST_STATE_LOCKOUT = 250U, //! Lockout (dvmhost traffic lockout state) - HOST_STATE_ERROR = 254U, //! Error (dvmhost error state) - HOST_STATE_QUIT = 255U, //! Quit (dvmhost quit state) + HOST_STATE_LOCKOUT = 250U, //!< Lockout (dvmhost traffic lockout state) + HOST_STATE_ERROR = 254U, //!< Error (dvmhost error state) + HOST_STATE_QUIT = 255U, //!< Quit (dvmhost quit state) }; /** * @brief Operational RF States */ enum RPT_RF_STATE { - RS_RF_LISTENING, //! Modem Listening - RS_RF_LATE_ENTRY, //! Traffic Late Entry - RS_RF_AUDIO, //! Audio - RS_RF_DATA, //! Data - RS_RF_REJECTED, //! Traffic Rejected - RS_RF_INVALID //! Traffic Invalid + RS_RF_LISTENING, //!< Modem Listening + RS_RF_LATE_ENTRY, //!< Traffic Late Entry + RS_RF_AUDIO, //!< Audio + RS_RF_DATA, //!< Data + RS_RF_REJECTED, //!< Traffic Rejected + RS_RF_INVALID //!< Traffic Invalid }; /** * @brief Operational Network States */ enum RPT_NET_STATE { - RS_NET_IDLE, //! Idle - RS_NET_AUDIO, //! Audio - RS_NET_DATA //! Data + RS_NET_IDLE, //!< Idle + RS_NET_AUDIO, //!< Audio + RS_NET_DATA //!< Data }; const uint8_t UDP_COMPRESS_NONE = 0x00U; diff --git a/src/common/Thread.h b/src/common/Thread.h index 7232e012..b6d43726 100644 --- a/src/common/Thread.h +++ b/src/common/Thread.h @@ -47,8 +47,8 @@ typedef HANDLE pthread_t; * @ingroup common */ struct thread_t { - void* obj; //! Object that created this thread. - pthread_t thread; //! Thread Handle. + void* obj; //!< Object that created this thread. + pthread_t thread; //!< Thread Handle. }; // --------------------------------------------------------------------------- diff --git a/src/common/analog/AnalogDefines.h b/src/common/analog/AnalogDefines.h index ee604b16..6fb6a773 100644 --- a/src/common/analog/AnalogDefines.h +++ b/src/common/analog/AnalogDefines.h @@ -37,17 +37,17 @@ namespace analog * @{ */ - const uint32_t AUDIO_SAMPLES_LENGTH = 160U; //! Sample size for 20ms of 16-bit audio at 8kHz. - const uint32_t AUDIO_SAMPLES_LENGTH_BYTES = 320U; //! Sample size for 20ms of 16-bit audio at 8kHz in bytes. + const uint32_t AUDIO_SAMPLES_LENGTH = 160U; //!< Sample size for 20ms of 16-bit audio at 8kHz. + const uint32_t AUDIO_SAMPLES_LENGTH_BYTES = 320U; //!< Sample size for 20ms of 16-bit audio at 8kHz in bytes. /** @} */ /** @brief Audio Frame Type(s) */ namespace AudioFrameType { /** @brief Audio Frame Type(s) */ enum E : uint8_t { - VOICE_START = 0x00U, //! Voice Start Frame - VOICE = 0x01U, //! Voice Continuation Frame - TERMINATOR = 0x02U, //! Voice End Frame / Call Terminator + VOICE_START = 0x00U, //!< Voice Start Frame + VOICE = 0x01U, //!< Voice Continuation Frame + TERMINATOR = 0x02U, //!< Voice End Frame / Call Terminator }; } diff --git a/src/common/concurrent/concurrent_lock.h b/src/common/concurrent/concurrent_lock.h index 0352d3a2..a302c947 100644 --- a/src/common/concurrent/concurrent_lock.h +++ b/src/common/concurrent/concurrent_lock.h @@ -62,8 +62,8 @@ namespace concurrent void spinlock() const { __spinlock(); } protected: - mutable std::mutex m_mutex; //! Mutex used for change locking. - mutable bool m_locked = false; //! Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. + mutable std::mutex m_mutex; //!< Mutex used for change locking. + mutable bool m_locked = false; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. /** * @brief Lock the object. diff --git a/src/common/dmr/DMRDefines.h b/src/common/dmr/DMRDefines.h index 2277faa2..0fc996b2 100644 --- a/src/common/dmr/DMRDefines.h +++ b/src/common/dmr/DMRDefines.h @@ -147,16 +147,16 @@ namespace dmr const uint16_t DMR_LOGICAL_CH_ABSOLUTE = 0xFFFU; - const uint32_t WUID_SUPLI = 0xFFFEC4U; //! Supplementary Data Service Working Unit ID - const uint32_t WUID_SDMI = 0xFFFEC5U; //! UDT Short Data Service Working Unit ID - const uint32_t WUID_REGI = 0xFFFEC6U; //! Registration Working Unit ID - const uint32_t WUID_STUNI = 0xFFFECCU; //! MS Stun/Revive Identifier - const uint32_t WUID_AUTHI = 0xFFFECDU; //! Authentication Working Unit ID - const uint32_t WUID_KILLI = 0xFFFECFU; //! MS Kill Identifier - const uint32_t WUID_TATTSI = 0xFFFED7U; //! Talkgroup Subscription/Attachement Service Working Unit ID - const uint32_t WUID_ALLL = 0xFFFFFDU; //! All-call Site-wide Working Unit ID - const uint32_t WUID_ALLZ = 0xFFFFFEU; //! All-call System-wide Working Unit ID - const uint32_t WUID_ALL = 0xFFFFFFU; //! All-call Network-wide Working Unit ID + const uint32_t WUID_SUPLI = 0xFFFEC4U; //!< Supplementary Data Service Working Unit ID + const uint32_t WUID_SDMI = 0xFFFEC5U; //!< UDT Short Data Service Working Unit ID + const uint32_t WUID_REGI = 0xFFFEC6U; //!< Registration Working Unit ID + const uint32_t WUID_STUNI = 0xFFFECCU; //!< MS Stun/Revive Identifier + const uint32_t WUID_AUTHI = 0xFFFECDU; //!< Authentication Working Unit ID + const uint32_t WUID_KILLI = 0xFFFECFU; //!< MS Kill Identifier + const uint32_t WUID_TATTSI = 0xFFFED7U; //!< Talkgroup Subscription/Attachement Service Working Unit ID + const uint32_t WUID_ALLL = 0xFFFFFDU; //!< All-call Site-wide Working Unit ID + const uint32_t WUID_ALLZ = 0xFFFFFEU; //!< All-call System-wide Working Unit ID + const uint32_t WUID_ALL = 0xFFFFFFU; //!< All-call Network-wide Working Unit ID const uint32_t NO_HEADERS_SIMPLEX = 8U; const uint32_t NO_HEADERS_DUPLEX = 3U; @@ -167,13 +167,13 @@ namespace dmr namespace DPF { /** @brief Data Packet Format */ enum E : uint8_t { - UDT = 0x00U, //! Unified Data Transport Header - RESPONSE = 0x01U, //! Response Data Header - UNCONFIRMED_DATA = 0x02U, //! Unconfirmed Data Header - CONFIRMED_DATA = 0x03U, //! Confirmed Data Header - DEFINED_SHORT = 0x0DU, //! Defined Short Data Header - DEFINED_RAW = 0x0EU, //! Defined Raw Data Header - PROPRIETARY = 0x0FU, //! Proprietary + UDT = 0x00U, //!< Unified Data Transport Header + RESPONSE = 0x01U, //!< Response Data Header + UNCONFIRMED_DATA = 0x02U, //!< Unconfirmed Data Header + CONFIRMED_DATA = 0x03U, //!< Confirmed Data Header + DEFINED_SHORT = 0x0DU, //!< Defined Short Data Header + DEFINED_RAW = 0x0EU, //!< Defined Raw Data Header + PROPRIETARY = 0x0FU, //!< Proprietary }; }; @@ -181,9 +181,9 @@ namespace dmr namespace PDUResponseClass { /** @brief Data Response Class */ enum : uint8_t { - ACK = 0x00U, //! Acknowledge - NACK = 0x01U, //! Negative Acknowledge - ACK_RETRY = 0x02U //! Acknowlege Retry + ACK = 0x00U, //!< Acknowledge + NACK = 0x01U, //!< Negative Acknowledge + ACK_RETRY = 0x02U //!< Acknowlege Retry }; }; @@ -191,12 +191,12 @@ namespace dmr namespace PDUResponseType { /** @brief Data Response Type */ enum : uint8_t { - ACK = 0x01U, //! Acknowledge + ACK = 0x01U, //!< Acknowledge - NACK_ILLEGAL = 0x00U, //! Illegal Format - NACK_PACKET_CRC = 0x01U, //! Packet CRC - NACK_MEMORY_FULL = 0x02U, //! Memory Full - NACK_UNDELIVERABLE = 0x04U //! Undeliverable + NACK_ILLEGAL = 0x00U, //!< Illegal Format + NACK_PACKET_CRC = 0x01U, //!< Packet CRC + NACK_MEMORY_FULL = 0x02U, //!< Memory Full + NACK_UNDELIVERABLE = 0x04U //!< Undeliverable }; }; @@ -239,10 +239,10 @@ namespace dmr namespace SLCO { /** @brief Short-Link Control Opcode(s) */ enum E : uint8_t { - NONE = 0x00U, //! NULL + NONE = 0x00U, //!< NULL ACT = 0x01U, //! - TSCC = 0x02U, //! TSCC - PAYLOAD = 0x03U //! Payload + TSCC = 0x02U, //!< TSCC + PAYLOAD = 0x03U //!< Payload }; } @@ -250,8 +250,8 @@ namespace dmr namespace FLCO { /** @brief Full-Link Control Opcode(s) */ enum E : uint8_t { - GROUP = 0x00U, //! GRP VCH USER - Group Voice Channel User - PRIVATE = 0x03U, //! UU VCH USER - Unit-to-Unit Voice Channel User + GROUP = 0x00U, //!< GRP VCH USER - Group Voice Channel User + PRIVATE = 0x03U, //!< UU VCH USER - Unit-to-Unit Voice Channel User TALKER_ALIAS_HEADER = 0x04U, //! TALKER_ALIAS_BLOCK1 = 0x05U, //! @@ -266,12 +266,12 @@ namespace dmr namespace ExtendedFunctions { /** @brief FID_MOT Extended Functions. */ enum : uint16_t { - CHECK = 0x0000U, //! Radio Check - UNINHIBIT = 0x007EU, //! Radio Uninhibit - INHIBIT = 0x007FU, //! Radio Inhibit - CHECK_ACK = 0x0080U, //! Radio Check Ack - UNINHIBIT_ACK = 0x00FEU, //! Radio Uninhibit Ack - INHIBIT_ACK = 0x00FFU //! Radio Inhibit Ack + CHECK = 0x0000U, //!< Radio Check + UNINHIBIT = 0x007EU, //!< Radio Uninhibit + INHIBIT = 0x007FU, //!< Radio Inhibit + CHECK_ACK = 0x0080U, //!< Radio Check Ack + UNINHIBIT_ACK = 0x00FEU, //!< Radio Uninhibit Ack + INHIBIT_ACK = 0x00FFU //!< Radio Inhibit Ack }; }; @@ -279,30 +279,30 @@ namespace dmr namespace DataType { /** @brief Data Type(s) */ enum E : uint8_t { - VOICE_PI_HEADER = 0x00U, //! Voice with Privacy Indicator Header - VOICE_LC_HEADER = 0x01U, //! Voice with Link Control Header + VOICE_PI_HEADER = 0x00U, //!< Voice with Privacy Indicator Header + VOICE_LC_HEADER = 0x01U, //!< Voice with Link Control Header - TERMINATOR_WITH_LC = 0x02U, //! Terminator with Link Control + TERMINATOR_WITH_LC = 0x02U, //!< Terminator with Link Control - CSBK = 0x03U, //! CSBK + CSBK = 0x03U, //!< CSBK - MBC_HEADER = 0x04U, //! Multi-Block Control Header - MBC_DATA = 0x05U, //! Multi-Block Control Data + MBC_HEADER = 0x04U, //!< Multi-Block Control Header + MBC_DATA = 0x05U, //!< Multi-Block Control Data - DATA_HEADER = 0x06U, //! Data Header - RATE_12_DATA = 0x07U, //! 1/2 Rate Data - RATE_34_DATA = 0x08U, //! 3/4 Rate Data + DATA_HEADER = 0x06U, //!< Data Header + RATE_12_DATA = 0x07U, //!< 1/2 Rate Data + RATE_34_DATA = 0x08U, //!< 3/4 Rate Data - IDLE = 0x09U, //! Idle + IDLE = 0x09U, //!< Idle - RATE_1_DATA = 0x0AU, //! Rate 1 Data + RATE_1_DATA = 0x0AU, //!< Rate 1 Data /* ** Internal Data Type(s) */ - VOICE_SYNC = 0xF0U, //! Internal - Voice Sync - VOICE = 0xF1U //! Internal - Voice + VOICE_SYNC = 0xF0U, //!< Internal - Voice Sync + VOICE = 0xF1U //!< Internal - Voice }; } @@ -321,10 +321,10 @@ namespace dmr namespace SiteModel { /** @brief Site Models */ enum E : uint8_t { - SM_TINY = 0x00U, //! Tiny - SM_SMALL = 0x01U, //! Small - SM_LARGE = 0x02U, //! Large - SM_HUGE = 0x03U //! Huge + SM_TINY = 0x00U, //!< Tiny + SM_SMALL = 0x01U, //!< Small + SM_LARGE = 0x02U, //!< Large + SM_HUGE = 0x03U //!< Huge }; } @@ -337,13 +337,13 @@ namespace dmr namespace TalkerID { /** @brief Talker ID */ enum : uint8_t { - NONE = 0x00U, //! No Talker ID + NONE = 0x00U, //!< No Talker ID - HEADER = 0x01U, //! Talker ID Header + HEADER = 0x01U, //!< Talker ID Header - BLOCK1 = 0x02U, //! Talker ID Block 1 - BLOCK2 = 0x04U, //! Talker ID Block 2 - BLOCK3 = 0x08U //! Talker ID Block 3 + BLOCK1 = 0x02U, //!< Talker ID Block 1 + BLOCK2 = 0x04U, //!< Talker ID Block 2 + BLOCK3 = 0x08U //!< Talker ID Block 3 }; } @@ -351,34 +351,34 @@ namespace dmr namespace ReasonCode { /** @brief Reason Code(s) */ enum : uint8_t { - TS_ACK_RSN_MSG = 0x60U, //! TS - Message Accepted - TS_ACK_RSN_REG = 0x62U, //! TS - Registration Accepted - TS_ACK_RSN_AUTH_RESP = 0x64U, //! TS - Authentication Challenge Response - TS_ACK_RSN_REG_SUB_ATTACH = 0x65U, //! TS - Registration Response with subscription - MS_ACK_RSN_MSG = 0x44U, //! MS - Message Accepted - MS_ACK_RSN_AUTH_RESP = 0x48U, //! MS - Authentication Challenge Response - - TS_DENY_RSN_SYS_UNSUPPORTED_SVC = 0x20U,//! System Unsupported Service - TS_DENY_RSN_PERM_USER_REFUSED = 0x21U, //! User Permenantly Refused - TS_DENY_RSN_TEMP_USER_REFUSED = 0x22U, //! User Temporarily Refused - TS_DENY_RSN_TRSN_SYS_REFUSED = 0x23U, //! System Refused - TS_DENY_RSN_TGT_NOT_REG = 0x24U, //! Target Not Registered - TS_DENY_RSN_TGT_UNAVAILABLE = 0x25U, //! Target Unavailable - TS_DENY_RSN_SYS_BUSY = 0x27U, //! System Busy - TS_DENY_RSN_SYS_NOT_READY = 0x28U, //! System Not Ready - TS_DENY_RSN_CALL_CNCL_REFUSED = 0x29U, //! Call Cancel Refused - TS_DENY_RSN_REG_REFUSED = 0x2AU, //! Registration Refused - TS_DENY_RSN_REG_DENIED = 0x2BU, //! Registration Denied - TS_DENY_RSN_MS_NOT_REG = 0x2DU, //! MS Not Registered - TS_DENY_RSN_TGT_BUSY = 0x2EU, //! Target Busy - TS_DENY_RSN_TGT_GROUP_NOT_VALID = 0x2FU,//! Group Not Valid - - TS_QUEUED_RSN_NO_RESOURCE = 0xA0U, //! No Resources Available - TS_QUEUED_RSN_SYS_BUSY = 0xA1U, //! System Busy - - TS_WAIT_RSN = 0xE0U, //! Wait - - MS_DENY_RSN_UNSUPPORTED_SVC = 0x00U, //! Service Unsupported + TS_ACK_RSN_MSG = 0x60U, //!< TS - Message Accepted + TS_ACK_RSN_REG = 0x62U, //!< TS - Registration Accepted + TS_ACK_RSN_AUTH_RESP = 0x64U, //!< TS - Authentication Challenge Response + TS_ACK_RSN_REG_SUB_ATTACH = 0x65U, //!< TS - Registration Response with subscription + MS_ACK_RSN_MSG = 0x44U, //!< MS - Message Accepted + MS_ACK_RSN_AUTH_RESP = 0x48U, //!< MS - Authentication Challenge Response + + TS_DENY_RSN_SYS_UNSUPPORTED_SVC = 0x20U,//!< System Unsupported Service + TS_DENY_RSN_PERM_USER_REFUSED = 0x21U, //!< User Permenantly Refused + TS_DENY_RSN_TEMP_USER_REFUSED = 0x22U, //!< User Temporarily Refused + TS_DENY_RSN_TRSN_SYS_REFUSED = 0x23U, //!< System Refused + TS_DENY_RSN_TGT_NOT_REG = 0x24U, //!< Target Not Registered + TS_DENY_RSN_TGT_UNAVAILABLE = 0x25U, //!< Target Unavailable + TS_DENY_RSN_SYS_BUSY = 0x27U, //!< System Busy + TS_DENY_RSN_SYS_NOT_READY = 0x28U, //!< System Not Ready + TS_DENY_RSN_CALL_CNCL_REFUSED = 0x29U, //!< Call Cancel Refused + TS_DENY_RSN_REG_REFUSED = 0x2AU, //!< Registration Refused + TS_DENY_RSN_REG_DENIED = 0x2BU, //!< Registration Denied + TS_DENY_RSN_MS_NOT_REG = 0x2DU, //!< MS Not Registered + TS_DENY_RSN_TGT_BUSY = 0x2EU, //!< Target Busy + TS_DENY_RSN_TGT_GROUP_NOT_VALID = 0x2FU,//!< Group Not Valid + + TS_QUEUED_RSN_NO_RESOURCE = 0xA0U, //!< No Resources Available + TS_QUEUED_RSN_SYS_BUSY = 0xA1U, //!< System Busy + + TS_WAIT_RSN = 0xE0U, //!< Wait + + MS_DENY_RSN_UNSUPPORTED_SVC = 0x00U, //!< Service Unsupported }; } @@ -386,19 +386,19 @@ namespace dmr namespace ServiceKind { /** @brief Random Access Service Kind */ enum : uint8_t { - IND_VOICE_CALL = 0x00U, //! Individual Voice Call - GRP_VOICE_CALL = 0x01U, //! Group Voice Call - IND_DATA_CALL = 0x02U, //! Individual Data Call - GRP_DATA_CALL = 0x03U, //! Group Data Call - IND_UDT_DATA_CALL = 0x04U, //! Individual UDT Short Data Call - GRP_UDT_DATA_CALL = 0x05U, //! Group UDT Short Data Call - UDT_SHORT_POLL = 0x06U, //! UDT Short Data Polling Service - STATUS_TRANSPORT = 0x07U, //! Status Transport Service - CALL_DIVERSION = 0x08U, //! Call Diversion Service - CALL_ANSWER = 0x09U, //! Call Answer Service - SUPPLEMENTARY_SVC = 0x0DU, //! Supplementary Service - REG_SVC = 0x0EU, //! Registration Service - CANCEL_CALL = 0x0FU //! Cancel Call Service + IND_VOICE_CALL = 0x00U, //!< Individual Voice Call + GRP_VOICE_CALL = 0x01U, //!< Group Voice Call + IND_DATA_CALL = 0x02U, //!< Individual Data Call + GRP_DATA_CALL = 0x03U, //!< Group Data Call + IND_UDT_DATA_CALL = 0x04U, //!< Individual UDT Short Data Call + GRP_UDT_DATA_CALL = 0x05U, //!< Group UDT Short Data Call + UDT_SHORT_POLL = 0x06U, //!< UDT Short Data Polling Service + STATUS_TRANSPORT = 0x07U, //!< Status Transport Service + CALL_DIVERSION = 0x08U, //!< Call Diversion Service + CALL_ANSWER = 0x09U, //!< Call Answer Service + SUPPLEMENTARY_SVC = 0x0DU, //!< Supplementary Service + REG_SVC = 0x0EU, //!< Registration Service + CANCEL_CALL = 0x0FU //!< Cancel Call Service }; } @@ -406,14 +406,14 @@ namespace dmr namespace BroadcastAnncType { /** @brief Broadcast Announcement Type(s) */ enum : uint8_t { - ANN_WD_TSCC = 0x00U, //! Announce-Withdraw TSCC Channel - CALL_TIMER_PARMS = 0x01U, //! Specify Call Timer Parameters - VOTE_NOW = 0x02U, //! Vote Now Advice - LOCAL_TIME = 0x03U, //! Broadcast Local Time - MASS_REG = 0x04U, //! Mass Registration - CHAN_FREQ = 0x05U, //! Logical Channel/Frequency - ADJ_SITE = 0x06U, //! Adjacent Site Information - SITE_PARMS = 0x07U //! General Site Parameters + ANN_WD_TSCC = 0x00U, //!< Announce-Withdraw TSCC Channel + CALL_TIMER_PARMS = 0x01U, //!< Specify Call Timer Parameters + VOTE_NOW = 0x02U, //!< Vote Now Advice + LOCAL_TIME = 0x03U, //!< Broadcast Local Time + MASS_REG = 0x04U, //!< Mass Registration + CHAN_FREQ = 0x05U, //!< Logical Channel/Frequency + ADJ_SITE = 0x06U, //!< Adjacent Site Information + SITE_PARMS = 0x07U //!< General Site Parameters }; } @@ -423,28 +423,28 @@ namespace dmr enum : uint8_t { // CSBK ISP/OSP Shared Opcode(s) NONE = 0x00U, //! - UU_V_REQ = 0x04U, //! UU VCH REQ - Unit-to-Unit Voice Channel Request - UU_ANS_RSP = 0x05U, //! UU ANS RSP - Unit-to-Unit Answer Response - CTCSBK = 0x07U, //! CT CSBK - Channel Timing CSBK - ALOHA = 0x19U, //! ALOHA - Aloha PDU for Random Access - AHOY = 0x1CU, //! AHOY - Enquiry from TSCC - RAND = 0x1FU, //! (ETSI) RAND - Random Access / (DMRA) CALL ALRT - Call Alert - ACK_RSP = 0x20U, //! ACK RSP - Acknowledge Response - EXT_FNCT = 0x24U, //! (DMRA) EXT FNCT - Extended Function - NACK_RSP = 0x26U, //! NACK RSP - Negative Acknowledgement Response - BROADCAST = 0x28U, //! BCAST - Announcement PDU - MAINT = 0x2AU, //! MAINT - Call Maintainence PDU - P_CLEAR = 0x2EU, //! P_CLEAR - Payload Channel Clear - PV_GRANT = 0x30U, //! PV_GRANT - Private Voice Channel Grant - TV_GRANT = 0x31U, //! TV_GRANT - Talkgroup Voice Channel Grant - BTV_GRANT = 0x32U, //! BTV_GRANT - Broadcast Talkgroup Voice Channel Grant - PD_GRANT = 0x33U, //! PD_GRANT - Private Data Channel Grant - TD_GRANT = 0x34U, //! TD_GRANT - Talkgroup Data Channel Grant - BSDWNACT = 0x38U, //! BS DWN ACT - BS Outbound Activation - PRECCSBK = 0x3DU, //! PRE CSBK - Preamble CSBK + UU_V_REQ = 0x04U, //!< UU VCH REQ - Unit-to-Unit Voice Channel Request + UU_ANS_RSP = 0x05U, //!< UU ANS RSP - Unit-to-Unit Answer Response + CTCSBK = 0x07U, //!< CT CSBK - Channel Timing CSBK + ALOHA = 0x19U, //!< ALOHA - Aloha PDU for Random Access + AHOY = 0x1CU, //!< AHOY - Enquiry from TSCC + RAND = 0x1FU, //!< (ETSI) RAND - Random Access / (DMRA) CALL ALRT - Call Alert + ACK_RSP = 0x20U, //!< ACK RSP - Acknowledge Response + EXT_FNCT = 0x24U, //!< (DMRA) EXT FNCT - Extended Function + NACK_RSP = 0x26U, //!< NACK RSP - Negative Acknowledgement Response + BROADCAST = 0x28U, //!< BCAST - Announcement PDU + MAINT = 0x2AU, //!< MAINT - Call Maintainence PDU + P_CLEAR = 0x2EU, //!< P_CLEAR - Payload Channel Clear + PV_GRANT = 0x30U, //!< PV_GRANT - Private Voice Channel Grant + TV_GRANT = 0x31U, //!< TV_GRANT - Talkgroup Voice Channel Grant + BTV_GRANT = 0x32U, //!< BTV_GRANT - Broadcast Talkgroup Voice Channel Grant + PD_GRANT = 0x33U, //!< PD_GRANT - Private Data Channel Grant + TD_GRANT = 0x34U, //!< TD_GRANT - Talkgroup Data Channel Grant + BSDWNACT = 0x38U, //!< BS DWN ACT - BS Outbound Activation + PRECCSBK = 0x3DU, //!< PRE CSBK - Preamble CSBK // CSBK DVM Outbound Signalling Packet (OSP) Opcode(s) - DVM_GIT_HASH = 0x3FU //! + DVM_GIT_HASH = 0x3FU //!< }; } diff --git a/src/common/lookups/AdjSiteMapLookup.h b/src/common/lookups/AdjSiteMapLookup.h index ca196f6c..7cbe8a6b 100644 --- a/src/common/lookups/AdjSiteMapLookup.h +++ b/src/common/lookups/AdjSiteMapLookup.h @@ -233,8 +233,8 @@ namespace lookups bool m_stop; - static std::mutex m_mutex; //! Mutex used for change locking. - static bool m_locked; //! Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. + static std::mutex m_mutex; //!< Mutex used for change locking. + static bool m_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. /** * @brief Loads the table from the passed lookup table file. diff --git a/src/common/lookups/PeerListLookup.h b/src/common/lookups/PeerListLookup.h index 5b7dd15b..65f54917 100644 --- a/src/common/lookups/PeerListLookup.h +++ b/src/common/lookups/PeerListLookup.h @@ -241,8 +241,8 @@ namespace lookups bool save() override; private: - static std::mutex m_mutex; //! Mutex used for change locking. - static bool m_locked; //! Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. + static std::mutex m_mutex; //!< Mutex used for change locking. + static bool m_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. }; } // namespace lookups diff --git a/src/common/lookups/RadioIdLookup.h b/src/common/lookups/RadioIdLookup.h index 2e65400a..167f41a2 100644 --- a/src/common/lookups/RadioIdLookup.h +++ b/src/common/lookups/RadioIdLookup.h @@ -208,8 +208,8 @@ namespace lookups bool save() override; private: - static std::mutex m_mutex; //! Mutex used for change locking. - static bool m_locked; //! Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. + static std::mutex m_mutex; //!< Mutex used for change locking. + static bool m_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. }; } // namespace lookups diff --git a/src/common/lookups/TalkgroupRulesLookup.h b/src/common/lookups/TalkgroupRulesLookup.h index 0b52b4a7..9cae89d0 100644 --- a/src/common/lookups/TalkgroupRulesLookup.h +++ b/src/common/lookups/TalkgroupRulesLookup.h @@ -643,8 +643,8 @@ namespace lookups bool m_acl; bool m_stop; - static std::mutex m_mutex; //! Mutex used for change locking. - static bool m_locked; //! Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. + static std::mutex m_mutex; //!< Mutex used for change locking. + static bool m_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. /** * @brief Loads the table from the passed lookup table file. diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index 79cdb8f3..2f2a8df4 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -99,19 +99,19 @@ namespace network */ enum NET_CONN_STATUS { // Common States - NET_STAT_WAITING_CONNECT, //! Waiting for Connection - NET_STAT_WAITING_LOGIN, //! Waiting for Login - NET_STAT_WAITING_AUTHORISATION, //! Waiting for Authorization - NET_STAT_WAITING_CONFIG, //! Waiting for Configuration - NET_STAT_RUNNING, //! Peer Running + NET_STAT_WAITING_CONNECT, //!< Waiting for Connection + NET_STAT_WAITING_LOGIN, //!< Waiting for Login + NET_STAT_WAITING_AUTHORISATION, //!< Waiting for Authorization + NET_STAT_WAITING_CONFIG, //!< Waiting for Configuration + NET_STAT_RUNNING, //!< Peer Running // Master States - NET_STAT_RPTL_RECEIVED, //! Login Received - NET_STAT_CHALLENGE_SENT, //! Authentication Challenge Sent + NET_STAT_RPTL_RECEIVED, //!< Login Received + NET_STAT_CHALLENGE_SENT, //!< Authentication Challenge Sent - NET_STAT_MST_RUNNING, //! Master Running + NET_STAT_MST_RUNNING, //!< Master Running - NET_STAT_INVALID = 0x7FFFFFF //! Invalid + NET_STAT_INVALID = 0x7FFFFFF //!< Invalid }; /** @@ -119,20 +119,20 @@ namespace network * @ingroup network_core */ enum NET_CONN_NAK_REASON { - NET_CONN_NAK_GENERAL_FAILURE, //! General Failure + NET_CONN_NAK_GENERAL_FAILURE, //!< General Failure - NET_CONN_NAK_MODE_NOT_ENABLED, //! Mode Not Enabled - NET_CONN_NAK_ILLEGAL_PACKET, //! Illegal Packet + NET_CONN_NAK_MODE_NOT_ENABLED, //!< Mode Not Enabled + NET_CONN_NAK_ILLEGAL_PACKET, //!< Illegal Packet - NET_CONN_NAK_FNE_UNAUTHORIZED, //! FNE Unauthorized - NET_CONN_NAK_BAD_CONN_STATE, //! Bad Connection State - NET_CONN_NAK_INVALID_CONFIG_DATA, //! Invalid Configuration Data - NET_CONN_NAK_PEER_RESET, //! Peer Reset - NET_CONN_NAK_PEER_ACL, //! Peer ACL + NET_CONN_NAK_FNE_UNAUTHORIZED, //!< FNE Unauthorized + NET_CONN_NAK_BAD_CONN_STATE, //!< Bad Connection State + NET_CONN_NAK_INVALID_CONFIG_DATA, //!< Invalid Configuration Data + NET_CONN_NAK_PEER_RESET, //!< Peer Reset + NET_CONN_NAK_PEER_ACL, //!< Peer ACL - NET_CONN_NAK_FNE_MAX_CONN, //! FNE Maximum Connections + NET_CONN_NAK_FNE_MAX_CONN, //!< FNE Maximum Connections - NET_CONN_NAK_INVALID = 0xFFFF //! Invalid + NET_CONN_NAK_INVALID = 0xFFFF //!< Invalid }; /** @@ -141,11 +141,11 @@ namespace network * @ingroup network_core */ enum CONTROL_BYTE { - NET_CTRL_GRANT_DEMAND = 0x80U, //! Grant Demand - NET_CTRL_GRANT_DENIAL = 0x40U, //! Grant Denial - NET_CTRL_SWITCH_OVER = 0x20U, //! Call Source RID Switch Over - NET_CTRL_GRANT_ENCRYPT = 0x08U, //! Grant Encrypt - NET_CTRL_U2U = 0x01U, //! Unit-to-Unit + NET_CTRL_GRANT_DEMAND = 0x80U, //!< Grant Demand + NET_CTRL_GRANT_DENIAL = 0x40U, //!< Grant Denial + NET_CTRL_SWITCH_OVER = 0x20U, //!< Call Source RID Switch Over + NET_CTRL_GRANT_ENCRYPT = 0x08U, //!< Grant Encrypt + NET_CTRL_U2U = 0x01U, //!< Unit-to-Unit }; // --------------------------------------------------------------------------- diff --git a/src/common/network/NetRPC.h b/src/common/network/NetRPC.h index d93f74ae..53d3f112 100644 --- a/src/common/network/NetRPC.h +++ b/src/common/network/NetRPC.h @@ -63,11 +63,11 @@ namespace network * @brief Status/Response Codes */ enum StatusType { - OK = 200, //! OK 200 + OK = 200, //!< OK 200 - BAD_REQUEST = 400, //! Bad Request 400 - INVALID_ARGS = 401, //! Invalid Arguments 401 - UNHANDLED_REQUEST = 402, //! Unhandled Request 402 + BAD_REQUEST = 400, //!< Bad Request 400 + INVALID_ARGS = 401, //!< Invalid Arguments 401 + UNHANDLED_REQUEST = 402, //!< Unhandled Request 402 } status; auto operator=(NetRPC&) -> NetRPC& = delete; diff --git a/src/common/network/Network.h b/src/common/network/Network.h index 740bfbc0..f2b4e95d 100644 --- a/src/common/network/Network.h +++ b/src/common/network/Network.h @@ -40,33 +40,33 @@ namespace network */ struct PeerMetadata { /** @name Identity and Frequency */ - std::string identity; //! Peer Identity - uint32_t rxFrequency; //! Peer Rx Frequency - uint32_t txFrequency; //! Peer Tx Frequency + std::string identity; //!< Peer Identity + uint32_t rxFrequency; //!< Peer Rx Frequency + uint32_t txFrequency; //!< Peer Tx Frequency /** @} */ /** @name System Info */ - uint32_t power; //! Peer Tx Power (W) - float latitude; //! Location Latitude (decmial notation) - float longitude; //! Location Longitude (decmial notation) - int height; //! Height (M) - std::string location; //! Textual Location + uint32_t power; //!< Peer Tx Power (W) + float latitude; //!< Location Latitude (decmial notation) + float longitude; //!< Location Longitude (decmial notation) + int height; //!< Height (M) + std::string location; //!< Textual Location /** @} */ /** @name Channel Data */ - float txOffsetMhz; //! Tx Offset (MHz) - float chBandwidthKhz; //! Channel Bandwidth (kHz) - uint8_t channelId; //! Channel ID - uint32_t channelNo; //! Channel Number + float txOffsetMhz; //!< Tx Offset (MHz) + float chBandwidthKhz; //!< Channel Bandwidth (kHz) + uint8_t channelId; //!< Channel ID + uint32_t channelNo; //!< Channel Number /** @} */ /** @name RCON */ - std::string restApiPassword; //! REST API Password - uint16_t restApiPort; //! REST API Port + std::string restApiPassword; //!< REST API Password + uint16_t restApiPort; //!< REST API Port /** @} */ /** @name Flags */ - bool isConventional; //! Flag indicating peer is a conventional peer. + bool isConventional; //!< Flag indicating peer is a conventional peer. /** @} */ }; diff --git a/src/common/network/RTPFNEHeader.h b/src/common/network/RTPFNEHeader.h index 8719f8dc..0edb2f65 100644 --- a/src/common/network/RTPFNEHeader.h +++ b/src/common/network/RTPFNEHeader.h @@ -44,35 +44,35 @@ namespace network */ namespace NET_FUNC { enum ENUM : uint8_t { - ILLEGAL = 0xFFU, //! Illegal Function + ILLEGAL = 0xFFU, //!< Illegal Function - PROTOCOL = 0x00U, //! Digital Protocol Function + PROTOCOL = 0x00U, //!< Digital Protocol Function - MASTER = 0x01U, //! Network Master Function + MASTER = 0x01U, //!< Network Master Function - RPTL = 0x60U, //! Repeater Login - RPTK = 0x61U, //! Repeater Authorisation - RPTC = 0x62U, //! Repeater Configuration + RPTL = 0x60U, //!< Repeater Login + RPTK = 0x61U, //!< Repeater Authorisation + RPTC = 0x62U, //!< Repeater Configuration - RPT_DISC = 0x70U, //! Repeater Disconnect - MST_DISC = 0x71U, //! Master Disconnect + RPT_DISC = 0x70U, //!< Repeater Disconnect + MST_DISC = 0x71U, //!< Master Disconnect - PING = 0x74U, //! Ping - PONG = 0x75U, //! Pong + PING = 0x74U, //!< Ping + PONG = 0x75U, //!< Pong - GRANT_REQ = 0x7AU, //! Grant Request - INCALL_CTRL = 0x7BU, //! In-Call Control - KEY_REQ = 0x7CU, //! Encryption Key Request - KEY_RSP = 0x7DU, //! Encryption Key Response + GRANT_REQ = 0x7AU, //!< Grant Request + INCALL_CTRL = 0x7BU, //!< In-Call Control + KEY_REQ = 0x7CU, //!< Encryption Key Request + KEY_RSP = 0x7DU, //!< Encryption Key Response - ACK = 0x7EU, //! Packet Acknowledge - NAK = 0x7FU, //! Packet Negative Acknowledge + ACK = 0x7EU, //!< Packet Acknowledge + NAK = 0x7FU, //!< Packet Negative Acknowledge - TRANSFER = 0x90U, //! Network Transfer Function + TRANSFER = 0x90U, //!< Network Transfer Function - ANNOUNCE = 0x91U, //! Network Announce Function + ANNOUNCE = 0x91U, //!< Network Announce Function - PEER_LINK = 0x92U //! FNE Peer-Link Function + PEER_LINK = 0x92U //!< FNE Peer-Link Function }; }; @@ -82,34 +82,34 @@ namespace network */ namespace NET_SUBFUNC { enum ENUM : uint8_t { - NOP = 0xFFU, //! No Operation Sub-Function - - PROTOCOL_SUBFUNC_DMR = 0x00U, //! DMR - PROTOCOL_SUBFUNC_P25 = 0x01U, //! P25 - PROTOCOL_SUBFUNC_NXDN = 0x02U, //! NXDN - PROTOCOL_SUBFUNC_ANALOG = 0x0FU, //! Analog - - MASTER_SUBFUNC_WL_RID = 0x00U, //! Whitelist RIDs - MASTER_SUBFUNC_BL_RID = 0x01U, //! Blacklist RIDs - MASTER_SUBFUNC_ACTIVE_TGS = 0x02U, //! Active TGIDs - MASTER_SUBFUNC_DEACTIVE_TGS = 0x03U, //! Deactive TGIDs - - TRANSFER_SUBFUNC_ACTIVITY = 0x01U, //! Activity Log Transfer - TRANSFER_SUBFUNC_DIAG = 0x02U, //! Diagnostic Log Transfer - TRANSFER_SUBFUNC_STATUS = 0x03U, //! Status Transfer - - ANNC_SUBFUNC_GRP_AFFIL = 0x00U, //! Announce Group Affiliation - ANNC_SUBFUNC_UNIT_REG = 0x01U, //! Announce Unit Registration - ANNC_SUBFUNC_UNIT_DEREG = 0x02U, //! Announce Unit Deregistration - ANNC_SUBFUNC_GRP_UNAFFIL = 0x03U, //! Announce Group Affiliation Removal - ANNC_SUBFUNC_AFFILS = 0x90U, //! Update All Affiliations - ANNC_SUBFUNC_SITE_VC = 0x9AU, //! Announce Site VCs - - PL_TALKGROUP_LIST = 0x00U, //! FNE Peer-Link Talkgroup Transfer - PL_RID_LIST = 0x01U, //! FNE Peer-Link Radio ID Transfer - PL_PEER_LIST = 0x02U, //! FNE Peer-Link Peer List Transfer - - PL_ACT_PEER_LIST = 0xA2U, //! FNE Peer-Link Active Peer List Transfer + NOP = 0xFFU, //!< No Operation Sub-Function + + PROTOCOL_SUBFUNC_DMR = 0x00U, //!< DMR + PROTOCOL_SUBFUNC_P25 = 0x01U, //!< P25 + PROTOCOL_SUBFUNC_NXDN = 0x02U, //!< NXDN + PROTOCOL_SUBFUNC_ANALOG = 0x0FU, //!< Analog + + MASTER_SUBFUNC_WL_RID = 0x00U, //!< Whitelist RIDs + MASTER_SUBFUNC_BL_RID = 0x01U, //!< Blacklist RIDs + MASTER_SUBFUNC_ACTIVE_TGS = 0x02U, //!< Active TGIDs + MASTER_SUBFUNC_DEACTIVE_TGS = 0x03U, //!< Deactive TGIDs + + TRANSFER_SUBFUNC_ACTIVITY = 0x01U, //!< Activity Log Transfer + TRANSFER_SUBFUNC_DIAG = 0x02U, //!< Diagnostic Log Transfer + TRANSFER_SUBFUNC_STATUS = 0x03U, //!< Status Transfer + + ANNC_SUBFUNC_GRP_AFFIL = 0x00U, //!< Announce Group Affiliation + ANNC_SUBFUNC_UNIT_REG = 0x01U, //!< Announce Unit Registration + ANNC_SUBFUNC_UNIT_DEREG = 0x02U, //!< Announce Unit Deregistration + ANNC_SUBFUNC_GRP_UNAFFIL = 0x03U, //!< Announce Group Affiliation Removal + ANNC_SUBFUNC_AFFILS = 0x90U, //!< Update All Affiliations + ANNC_SUBFUNC_SITE_VC = 0x9AU, //!< Announce Site VCs + + PL_TALKGROUP_LIST = 0x00U, //!< FNE Peer-Link Talkgroup Transfer + PL_RID_LIST = 0x01U, //!< FNE Peer-Link Radio ID Transfer + PL_PEER_LIST = 0x02U, //!< FNE Peer-Link Peer List Transfer + + PL_ACT_PEER_LIST = 0xA2U, //!< FNE Peer-Link Active Peer List Transfer }; }; @@ -119,10 +119,10 @@ namespace network */ namespace NET_ICC { enum ENUM : uint8_t { - NOP = 0xFFU, //! No Operation Sub-Function + NOP = 0xFFU, //!< No Operation Sub-Function - BUSY_DENY = 0x00U, //! Busy Deny - REJECT_TRAFFIC = 0x01U, //! Reject Active Traffic + BUSY_DENY = 0x00U, //!< Busy Deny + REJECT_TRAFFIC = 0x01U, //!< Reject Active Traffic }; }; diff --git a/src/common/network/rest/http/HTTPLexer.h b/src/common/network/rest/http/HTTPLexer.h index 736af7fc..c884d46f 100644 --- a/src/common/network/rest/http/HTTPLexer.h +++ b/src/common/network/rest/http/HTTPLexer.h @@ -156,37 +156,37 @@ namespace network */ enum state { - METHOD_START, //! HTTP Method Start - METHOD, //! HTTP Method - URI, //! HTTP URI - - HTTP_VERSION_H, //! HTTP Version: H - HTTP_VERSION_T_1, //! HTTP Version: T - HTTP_VERSION_T_2, //! HTTP Version: T - HTTP_VERSION_P, //! HTTP Version: P - HTTP_VERSION_SLASH, //! HTTP Version: / - HTTP_VERSION_MAJOR_START, //! HTTP Version Major Start - HTTP_VERSION_MAJOR, //! HTTP Version Major - HTTP_VERSION_MINOR_START, //! HTTP Version Minor Start - HTTP_VERSION_MINOR, //! HTTP Version Minor - - HTTP_STATUS_1, //! Status Number 1 - HTTP_STATUS_2, //! Status Number 2 - HTTP_STATUS_3, //! Status Number 3 - HTTP_STATUS_END, //! Status End - HTTP_STATUS_MESSAGE_START, //! Status Message Start - HTTP_STATUS_MESSAGE, //! Status Message End - - EXPECTING_NEWLINE_1, //! - - HEADER_LINE_START, //! Header Line Start - HEADER_LWS, //! - HEADER_NAME, //! Header Name - SPACE_BEFORE_HEADER_VALUE, //! - HEADER_VALUE, //! Header Value - - EXPECTING_NEWLINE_2, //! - EXPECTING_NEWLINE_3 //! + METHOD_START, //!< HTTP Method Start + METHOD, //!< HTTP Method + URI, //!< HTTP URI + + HTTP_VERSION_H, //!< HTTP Version: H + HTTP_VERSION_T_1, //!< HTTP Version: T + HTTP_VERSION_T_2, //!< HTTP Version: T + HTTP_VERSION_P, //!< HTTP Version: P + HTTP_VERSION_SLASH, //!< HTTP Version: / + HTTP_VERSION_MAJOR_START, //!< HTTP Version Major Start + HTTP_VERSION_MAJOR, //!< HTTP Version Major + HTTP_VERSION_MINOR_START, //!< HTTP Version Minor Start + HTTP_VERSION_MINOR, //!< HTTP Version Minor + + HTTP_STATUS_1, //!< Status Number 1 + HTTP_STATUS_2, //!< Status Number 2 + HTTP_STATUS_3, //!< Status Number 3 + HTTP_STATUS_END, //!< Status End + HTTP_STATUS_MESSAGE_START, //!< Status Message Start + HTTP_STATUS_MESSAGE, //!< Status Message End + + EXPECTING_NEWLINE_1, //!< + + HEADER_LINE_START, //!< Header Line Start + HEADER_LWS, //!< + HEADER_NAME, //!< Header Name + SPACE_BEFORE_HEADER_VALUE, //!< + HEADER_VALUE, //!< Header Value + + EXPECTING_NEWLINE_2, //!< + EXPECTING_NEWLINE_3 //!< } m_state; }; } // namespace http diff --git a/src/common/network/rest/http/HTTPPayload.h b/src/common/network/rest/http/HTTPPayload.h index 91e686ce..d35c989b 100644 --- a/src/common/network/rest/http/HTTPPayload.h +++ b/src/common/network/rest/http/HTTPPayload.h @@ -56,25 +56,25 @@ namespace network * @brief HTTP Status/Response Codes */ enum StatusType { - OK = 200, //! HTTP OK 200 - CREATED = 201, //! HTTP Created 201 - ACCEPTED = 202, //! HTTP Accepted 202 - NO_CONTENT = 204, //! HTTP No Content 204 + OK = 200, //!< HTTP OK 200 + CREATED = 201, //!< HTTP Created 201 + ACCEPTED = 202, //!< HTTP Accepted 202 + NO_CONTENT = 204, //!< HTTP No Content 204 - MULTIPLE_CHOICES = 300, //! HTTP Multiple Choices 300 - MOVED_PERMANENTLY = 301, //! HTTP Moved Permenantly 301 - MOVED_TEMPORARILY = 302, //! HTTP Moved Temporarily 302 - NOT_MODIFIED = 304, //! HTTP Not Modified 304 + MULTIPLE_CHOICES = 300, //!< HTTP Multiple Choices 300 + MOVED_PERMANENTLY = 301, //!< HTTP Moved Permenantly 301 + MOVED_TEMPORARILY = 302, //!< HTTP Moved Temporarily 302 + NOT_MODIFIED = 304, //!< HTTP Not Modified 304 - BAD_REQUEST = 400, //! HTTP Bad Request 400 - UNAUTHORIZED = 401, //! HTTP Unauthorized 401 - FORBIDDEN = 403, //! HTTP Forbidden 403 - NOT_FOUND = 404, //! HTTP Not Found 404 + BAD_REQUEST = 400, //!< HTTP Bad Request 400 + UNAUTHORIZED = 401, //!< HTTP Unauthorized 401 + FORBIDDEN = 403, //!< HTTP Forbidden 403 + NOT_FOUND = 404, //!< HTTP Not Found 404 - INTERNAL_SERVER_ERROR = 500, //! HTTP Internal Server Error 500 - NOT_IMPLEMENTED = 501, //! HTTP Not Implemented 501 - BAD_GATEWAY = 502, //! HTTP Bad Gateway 502 - SERVICE_UNAVAILABLE = 503 //! HTTP Service Unavailable 503 + INTERNAL_SERVER_ERROR = 500, //!< HTTP Internal Server Error 500 + NOT_IMPLEMENTED = 501, //!< HTTP Not Implemented 501 + BAD_GATEWAY = 502, //!< HTTP Bad Gateway 502 + SERVICE_UNAVAILABLE = 503 //!< HTTP Service Unavailable 503 } status; HTTPHeaders headers; diff --git a/src/common/network/udp/Socket.h b/src/common/network/udp/Socket.h index e5258510..261f5779 100644 --- a/src/common/network/udp/Socket.h +++ b/src/common/network/udp/Socket.h @@ -51,8 +51,8 @@ * @ingroup udp_socket */ enum IPMATCHTYPE { - IMT_ADDRESS_AND_PORT, //! Address and Port - IMT_ADDRESS_ONLY //! Address Only + IMT_ADDRESS_AND_PORT, //!< Address and Port + IMT_ADDRESS_ONLY //!< Address Only }; #if defined(_WIN32) @@ -160,11 +160,11 @@ namespace network * @ingroup udp_socket */ struct UDPDatagram { - uint8_t* buffer; //! Message Buffer - size_t length; //! Length of Message Buffer + uint8_t* buffer; //!< Message Buffer + size_t length; //!< Length of Message Buffer - sockaddr_storage address; //! Address and Port - uint32_t addrLen; //! Length of address structure + sockaddr_storage address; //!< Address and Port + uint32_t addrLen; //!< Length of address structure }; /** @brief Vector of buffers that contain a full frames */ diff --git a/src/common/network/viface/VIFace.h b/src/common/network/viface/VIFace.h index 5fe835c2..804cf376 100644 --- a/src/common/network/viface/VIFace.h +++ b/src/common/network/viface/VIFace.h @@ -39,8 +39,8 @@ namespace network */ struct viface_queues { - int rxFd; //! Receive Packet File Descriptor - int txFd; //! Transmit Packet File Descriptor + int rxFd; //!< Receive Packet File Descriptor + int txFd; //!< Transmit Packet File Descriptor }; // --------------------------------------------------------------------------- diff --git a/src/common/nxdn/NXDNDefines.h b/src/common/nxdn/NXDNDefines.h index a9e4c363..255ebc67 100644 --- a/src/common/nxdn/NXDNDefines.h +++ b/src/common/nxdn/NXDNDefines.h @@ -133,10 +133,10 @@ namespace nxdn namespace RFChannelType { /** @brief Link Information Channel - RF Channel Type */ enum E : uint8_t { - RCCH = 0U, //! Control Channel - RTCH = 1U, //! Traffic Channel - RDCH = 2U, //! Data Channel - RTCH_C = 3U //! Composite Control/Traffic Channel + RCCH = 0U, //!< Control Channel + RTCH = 1U, //!< Traffic Channel + RDCH = 2U, //!< Data Channel + RTCH_C = 3U //!< Composite Control/Traffic Channel }; } @@ -145,15 +145,15 @@ namespace nxdn /** @brief Link Information Channel - Functional Channel Type */ enum E : uint8_t { // Common Access Channel - CAC_OUTBOUND = 0U, //! Common Access Channel - Outbound - CAC_INBOUND_LONG = 1U, //! Common Access Channel - Inbound Long - CAC_INBOUND_SHORT = 3U, //! Common Access Channel - Inbound Short + CAC_OUTBOUND = 0U, //!< Common Access Channel - Outbound + CAC_INBOUND_LONG = 1U, //!< Common Access Channel - Inbound Long + CAC_INBOUND_SHORT = 3U, //!< Common Access Channel - Inbound Short // Slow Associated Control Channel / User Specific Channel - USC_SACCH_NS = 0U, //! Slow Access Control Channel - Non-Superframe - USC_UDCH = 1U, //! - USC_SACCH_SS = 2U, //! Slow Access Control Channel - Superframe - USC_SACCH_SS_IDLE = 3U //! Slow Access Control Channel - Sueprframe/Idle + USC_SACCH_NS = 0U, //!< Slow Access Control Channel - Non-Superframe + USC_UDCH = 1U, //!< + USC_SACCH_SS = 2U, //!< Slow Access Control Channel - Superframe + USC_SACCH_SS_IDLE = 3U //!< Slow Access Control Channel - Sueprframe/Idle }; } @@ -161,14 +161,14 @@ namespace nxdn namespace ChOption { /** @brief Link Information Channel - Channel Options */ enum E : uint8_t { - DATA_NORMAL = 0U, //! Normal RCCH Data (Mandatory Data) - DATA_IDLE = 1U, //! Idle RCCH Data (Data that does not need to be Rx) - DATA_COMMON = 2U, //! Common RCCH Data (Optional Data) - - STEAL_NONE = 3U, //! No Stealing - STEAL_FACCH1_2 = 2U, //! 2 VCHs second half of FACCH1 - STEAL_FACCH1_1 = 1U, //! 2 VCHs first half of FACCH1 - STEAL_FACCH = 0U //! FACCH + DATA_NORMAL = 0U, //!< Normal RCCH Data (Mandatory Data) + DATA_IDLE = 1U, //!< Idle RCCH Data (Data that does not need to be Rx) + DATA_COMMON = 2U, //!< Common RCCH Data (Optional Data) + + STEAL_NONE = 3U, //!< No Stealing + STEAL_FACCH1_2 = 2U, //!< 2 VCHs second half of FACCH1 + STEAL_FACCH1_1 = 1U, //!< 2 VCHs first half of FACCH1 + STEAL_FACCH = 0U //!< FACCH }; } @@ -176,31 +176,31 @@ namespace nxdn namespace ChStructure { /** @brief Common Access Channel - Structure */ enum E : uint8_t { - SR_RCCH_SINGLE = 0x00U, //! Single Non-Header RCCH Message - SR_RCCH_DUAL = 0x01U, //! Dual Non-Header RCCH Message - SR_RCCH_HEAD_SINGLE = 0x02U, //! Single Header RCCH Message - SR_RCCH_HEAD_DUAL = 0x03U, //! Dual Header RCCH Message + SR_RCCH_SINGLE = 0x00U, //!< Single Non-Header RCCH Message + SR_RCCH_DUAL = 0x01U, //!< Dual Non-Header RCCH Message + SR_RCCH_HEAD_SINGLE = 0x02U, //!< Single Header RCCH Message + SR_RCCH_HEAD_DUAL = 0x03U, //!< Dual Header RCCH Message - SR_SINGLE = 0U, //! SACCH Single (same as SR_4_4, kept for clarity) + SR_SINGLE = 0U, //!< SACCH Single (same as SR_4_4, kept for clarity) - SR_4_4 = 0U, //! 4/4 SACCH Single/Last - SR_3_4 = 1U, //! 3/4 SACCH - SR_2_4 = 2U, //! 2/4 SACCH - SR_1_4 = 3U //! 1/4 SACCH Header + SR_4_4 = 0U, //!< 4/4 SACCH Single/Last + SR_3_4 = 1U, //!< 3/4 SACCH + SR_2_4 = 2U, //!< 2/4 SACCH + SR_1_4 = 3U //!< 1/4 SACCH Header }; } /** @name Encryption Algorithms */ - const uint8_t CIPHER_TYPE_NONE = 0x00U; //! Unencrypted + const uint8_t CIPHER_TYPE_NONE = 0x00U; //!< Unencrypted /** @} */ /** @brief Location Category */ namespace LocationCategory { /** @brief Location Category */ enum E : uint8_t { - GLOBAL = 0x00U, //! Global - LOCAL = 0x01U, //! Local - REGIONAL = 0x02U //! Regional + GLOBAL = 0x00U, //!< Global + LOCAL = 0x01U, //!< Local + REGIONAL = 0x02U //!< Regional }; } @@ -209,9 +209,9 @@ namespace nxdn /** @brief Data Response Class */ enum : uint8_t { - ACK = 0x00U, //! Acknowledge + ACK = 0x00U, //!< Acknowledge ACK_S = 0x01U, //! - NACK = 0x03U //! Negative Acknowledge + NACK = 0x03U //!< Negative Acknowledge }; } @@ -219,42 +219,42 @@ namespace nxdn namespace CauseResponse { /** @brief Cause Responses */ enum : uint8_t { - RSRC_NOT_AVAIL_NETWORK = 0x51U, //! Network Resource Not Available - RSRC_NOT_AVAIL_TEMP = 0x52U, //! Resource Temporarily Not Available - RSRC_NOT_AVAIL_QUEUED = 0x53U, //! Resource Queued Not Available - SVC_UNAVAILABLE = 0x60U, //! Service Unavailable - PROC_ERROR = 0x70U, //! Procedure Error - Lack of packet data - PROC_ERROR_UNDEF = 0x71U, //! Procedure Error - Invalid packet data - - MM_REG_ACCEPTED = 0x01U, //! Registration Accepted - MM_LOC_ACPT_GRP_FAIL = 0x04U, //! Location Accepted / Group Failed - MM_LOC_ACPT_GRP_REFUSE = 0x05U, //! Location Accepted / Group Refused - MM_REG_FAILED = 0x06U, //! Registration Failed - MM_REG_REFUSED = 0x08U, //! Registration Refused - - VD_ACCEPTED = 0x10U, //! Voice Accepted - VD_GRP_NOT_PERM = 0x11U, //! Voice Group Not Permitted - VD_REQ_UNIT_NOT_PERM = 0x12U, //! Requesting Unit Not Permitted - VD_TGT_UNIT_NOT_PERM = 0x13U, //! Target Unit Not Permitted - VD_REQ_UNIT_NOT_REG = 0x1CU, //! Requesting Unit Not Registered - VD_QUE_CHN_RESOURCE_NOT_AVAIL = 0x30U, //! Channel resources unavailable - VD_QUE_TGT_UNIT_BUSY = 0x38U, //! Target Unit Busy - VD_QUE_GRP_BUSY = 0x39U, //! Group Busy - - SS_ACK_R = 0x01U, //! Data Response - ACK Rx Success - SS_ACK_S = 0x02U, //! Data Response - ACK Tx Success - SS_NACK = 0x08U, //! Data Response - NACK (Request Full Retry) - SS_ACCEPTED = 0x10U, //! Data Accepted - SS_GRP_NOT_PERM = 0x11U, //! Data Group Not Permitted - SS_REQ_UNIT_NOT_PERM = 0x12U, //! Requesting Unit Not Permitted - SS_TGT_UNIT_NOT_PERM = 0x13U, //! Target Unit Not Permitted - SS_REQ_UNIT_NOT_REG = 0x1CU, //! Requesting Unit Not Registered - - DREQ_USER = 0x10U, //! Disconnect by User Request - DREQ_OTHER = 0x1FU, //! Other Disconnect Request - - DISC_USER = 0x10U, //! Disconnect by User - DISC_OTHER = 0x1FU //! Other Disconnect + RSRC_NOT_AVAIL_NETWORK = 0x51U, //!< Network Resource Not Available + RSRC_NOT_AVAIL_TEMP = 0x52U, //!< Resource Temporarily Not Available + RSRC_NOT_AVAIL_QUEUED = 0x53U, //!< Resource Queued Not Available + SVC_UNAVAILABLE = 0x60U, //!< Service Unavailable + PROC_ERROR = 0x70U, //!< Procedure Error - Lack of packet data + PROC_ERROR_UNDEF = 0x71U, //!< Procedure Error - Invalid packet data + + MM_REG_ACCEPTED = 0x01U, //!< Registration Accepted + MM_LOC_ACPT_GRP_FAIL = 0x04U, //!< Location Accepted / Group Failed + MM_LOC_ACPT_GRP_REFUSE = 0x05U, //!< Location Accepted / Group Refused + MM_REG_FAILED = 0x06U, //!< Registration Failed + MM_REG_REFUSED = 0x08U, //!< Registration Refused + + VD_ACCEPTED = 0x10U, //!< Voice Accepted + VD_GRP_NOT_PERM = 0x11U, //!< Voice Group Not Permitted + VD_REQ_UNIT_NOT_PERM = 0x12U, //!< Requesting Unit Not Permitted + VD_TGT_UNIT_NOT_PERM = 0x13U, //!< Target Unit Not Permitted + VD_REQ_UNIT_NOT_REG = 0x1CU, //!< Requesting Unit Not Registered + VD_QUE_CHN_RESOURCE_NOT_AVAIL = 0x30U, //!< Channel resources unavailable + VD_QUE_TGT_UNIT_BUSY = 0x38U, //!< Target Unit Busy + VD_QUE_GRP_BUSY = 0x39U, //!< Group Busy + + SS_ACK_R = 0x01U, //!< Data Response - ACK Rx Success + SS_ACK_S = 0x02U, //!< Data Response - ACK Tx Success + SS_NACK = 0x08U, //!< Data Response - NACK (Request Full Retry) + SS_ACCEPTED = 0x10U, //!< Data Accepted + SS_GRP_NOT_PERM = 0x11U, //!< Data Group Not Permitted + SS_REQ_UNIT_NOT_PERM = 0x12U, //!< Requesting Unit Not Permitted + SS_TGT_UNIT_NOT_PERM = 0x13U, //!< Target Unit Not Permitted + SS_REQ_UNIT_NOT_REG = 0x1CU, //!< Requesting Unit Not Registered + + DREQ_USER = 0x10U, //!< Disconnect by User Request + DREQ_OTHER = 0x1FU, //!< Other Disconnect Request + + DISC_USER = 0x10U, //!< Disconnect by User + DISC_OTHER = 0x1FU //!< Other Disconnect }; } @@ -262,14 +262,14 @@ namespace nxdn namespace SiteInformation1 { /** @brief Site Information 1 (SIF1) */ enum : uint8_t { - DATA_CALL_SVC = 0x01U, //! Data Call Service - VOICE_CALL_SVC = 0x02U, //! Voice Call Service - COMPOSITE_CONTROL = 0x04U, //! Composite Control Channel - AUTH_SVC = 0x08U, //! Authentication Service - GRP_REG_SVC = 0x10U, //! Group Registration Service - LOC_REG_SVC = 0x20U, //! Location Registration Service - MULTI_SYSTEM_SVC = 0x40U, //! Multi-System Service - MULTI_SITE_SVC = 0x80U //! Multi-Site Service + DATA_CALL_SVC = 0x01U, //!< Data Call Service + VOICE_CALL_SVC = 0x02U, //!< Voice Call Service + COMPOSITE_CONTROL = 0x04U, //!< Composite Control Channel + AUTH_SVC = 0x08U, //!< Authentication Service + GRP_REG_SVC = 0x10U, //!< Group Registration Service + LOC_REG_SVC = 0x20U, //!< Location Registration Service + MULTI_SYSTEM_SVC = 0x40U, //!< Multi-System Service + MULTI_SITE_SVC = 0x80U //!< Multi-Site Service }; } @@ -277,10 +277,10 @@ namespace nxdn namespace SiteInformation2 { /** @brief Site Information 2 (SIF2) */ enum : uint8_t { - IP_NETWORK = 0x10U, //! IP Networked - PSTN_NETWORK = 0x20U, //! PSTN Networked - STATUS_CALL_REM_CTRL = 0x40U, //! Status Call & Remote Control Service - SHORT_DATA_CALL_SVC = 0x80U //! Short Data Call Service + IP_NETWORK = 0x10U, //!< IP Networked + PSTN_NETWORK = 0x20U, //!< PSTN Networked + STATUS_CALL_REM_CTRL = 0x40U, //!< Status Call & Remote Control Service + SHORT_DATA_CALL_SVC = 0x80U //!< Short Data Call Service }; } @@ -288,10 +288,10 @@ namespace nxdn namespace RemoteControlCommand { /** @brief Remote Control Commands */ enum : uint8_t { - STUN = 0x00U, //! Stun - REVIVE = 0x01U, //! Revive - KILL = 0x02U, //! Kill - REMOTE_MONITOR = 0x04U //! Remote Monitor + STUN = 0x00U, //!< Stun + REVIVE = 0x01U, //!< Revive + KILL = 0x02U, //!< Kill + REMOTE_MONITOR = 0x04U //!< Remote Monitor }; } @@ -299,9 +299,9 @@ namespace nxdn namespace ChAccessStep { /** @brief Channel Access - Step */ enum E : uint8_t { - SYS_DEFINED = 0x00U, //! System Defined - ONEDOT25K = 0x02U, //! 1.25khz Step - THREEDOT125K = 0x03U //! 3.125khz Step + SYS_DEFINED = 0x00U, //!< System Defined + ONEDOT25K = 0x02U, //!< 1.25khz Step + THREEDOT125K = 0x03U //!< 3.125khz Step }; } @@ -309,11 +309,11 @@ namespace nxdn namespace ChAccessBase { /** @brief Channel Access - Base */ enum E : uint8_t { - FREQ_100 = 0x01U, //! 100mhz Base - FREQ_330 = 0x02U, //! 330mhz Base - FREQ_400 = 0x03U, //! 400mhz Base - FREQ_750 = 0x04U, //! 750mhz Base - FREQ_SYS_DEFINED = 0x07U //! System Defined + FREQ_100 = 0x01U, //!< 100mhz Base + FREQ_330 = 0x02U, //!< 330mhz Base + FREQ_400 = 0x03U, //!< 400mhz Base + FREQ_750 = 0x04U, //!< 750mhz Base + FREQ_SYS_DEFINED = 0x07U //!< System Defined }; } @@ -321,12 +321,12 @@ namespace nxdn namespace CallType { /** @brief Call Types */ enum : uint8_t { - BROADCAST = 0x00U, //! Broadcast - CONFERENCE = 0x01U, //! Conference - UNSPECIFIED = 0x02U, //! Unspecified - INDIVIDUAL = 0x04U, //! Individual - INTERCONNECT = 0x06U, //! Interconnect - SPEED_DIAL = 0x07U //! Speed Dial + BROADCAST = 0x00U, //!< Broadcast + CONFERENCE = 0x01U, //!< Conference + UNSPECIFIED = 0x02U, //!< Unspecified + INDIVIDUAL = 0x04U, //!< Individual + INTERCONNECT = 0x06U, //!< Interconnect + SPEED_DIAL = 0x07U //!< Speed Dial }; } @@ -334,9 +334,9 @@ namespace nxdn namespace TransmissionMode { /** @brief Transmission Mode */ enum : uint8_t { - MODE_4800 = 0x00U, //! 4800-baud - MODE_9600 = 0x02U, //! 9600-baud - MODE_9600_EFR = 0x03U //! 9600-baud; should never be used on data calls + MODE_4800 = 0x00U, //!< 4800-baud + MODE_9600 = 0x02U, //!< 9600-baud + MODE_9600_EFR = 0x03U //!< 9600-baud; should never be used on data calls }; } @@ -345,39 +345,39 @@ namespace nxdn /** @brief Message Types */ enum : uint8_t { // Common Message Types - IDLE = 0x10U, //! IDLE - Idle - DISC = 0x11U, //! DISC - Disconnect - DST_ID_INFO = 0x17U, //! DST_ID_INFO - Digital Station ID - SRV_INFO = 0x19U, //! SRV_INFO - Service Information - CCH_INFO = 0x1AU, //! CCH_INFO - Control Channel Information - ADJ_SITE_INFO = 0x1BU, //! ADJ_SITE_INFO - Adjacent Site Information - REM_CON_REQ = 0x34U, //! REM_CON_REQ - Remote Control Request - REM_CON_RESP = 0x35U, //! REM_CON_RESP - Remote Control Response + IDLE = 0x10U, //!< IDLE - Idle + DISC = 0x11U, //!< DISC - Disconnect + DST_ID_INFO = 0x17U, //!< DST_ID_INFO - Digital Station ID + SRV_INFO = 0x19U, //!< SRV_INFO - Service Information + CCH_INFO = 0x1AU, //!< CCH_INFO - Control Channel Information + ADJ_SITE_INFO = 0x1BU, //!< ADJ_SITE_INFO - Adjacent Site Information + REM_CON_REQ = 0x34U, //!< REM_CON_REQ - Remote Control Request + REM_CON_RESP = 0x35U, //!< REM_CON_RESP - Remote Control Response // Traffic Channel Message Types - RTCH_VCALL = 0x01U, //! VCALL - Voice Call - RTCH_VCALL_IV = 0x03U, //! VCALL_IV - Voice Call Initialization Vector - RTCH_TX_REL_EX = 0x07U, //! TX_REL_EX - Transmission Release Extension - RTCH_TX_REL = 0x08U, //! TX_REL - Transmission Release - RTCH_DCALL_HDR = 0x09U, //! DCALL - Data Call (Header) - RTCH_DCALL_DATA = 0x0BU, //! DCALL - Data Call (User Data Format) - RTCH_DCALL_ACK = 0x0CU, //! DCALL_ACK - Data Call Acknowledge - RTCH_HEAD_DLY = 0x0FU, //! HEAD_DLY - Header Delay - RTCH_SDCALL_REQ_HDR = 0x38U, //! SDCALL_REQ - Short Data Call Request (Header) - RTCH_SDCALL_REQ_DATA = 0x39U, //! SDCALL_REQ - Short Data Call Request (User Data Format) - RTCH_SDCALL_IV = 0x3AU, //! SDCALL_IV - Short Data Call Initialization Vector - RTCH_SDCALL_RESP = 0x3BU, //! SDCALL_RESP - Short Data Call Response + RTCH_VCALL = 0x01U, //!< VCALL - Voice Call + RTCH_VCALL_IV = 0x03U, //!< VCALL_IV - Voice Call Initialization Vector + RTCH_TX_REL_EX = 0x07U, //!< TX_REL_EX - Transmission Release Extension + RTCH_TX_REL = 0x08U, //!< TX_REL - Transmission Release + RTCH_DCALL_HDR = 0x09U, //!< DCALL - Data Call (Header) + RTCH_DCALL_DATA = 0x0BU, //!< DCALL - Data Call (User Data Format) + RTCH_DCALL_ACK = 0x0CU, //!< DCALL_ACK - Data Call Acknowledge + RTCH_HEAD_DLY = 0x0FU, //!< HEAD_DLY - Header Delay + RTCH_SDCALL_REQ_HDR = 0x38U, //!< SDCALL_REQ - Short Data Call Request (Header) + RTCH_SDCALL_REQ_DATA = 0x39U, //!< SDCALL_REQ - Short Data Call Request (User Data Format) + RTCH_SDCALL_IV = 0x3AU, //!< SDCALL_IV - Short Data Call Initialization Vector + RTCH_SDCALL_RESP = 0x3BU, //!< SDCALL_RESP - Short Data Call Response // Control Channel Message Types - RCCH_VCALL_CONN = 0x03U, //! VCALL_CONN - Voice Call Connection Request (ISP) / Voice Call Connection Response (OSP) - RCCH_VCALL_ASSGN = 0x04U, //! VCALL_ASSGN - Voice Call Assignment - RCCH_DCALL_ASSGN = 0x14U, //! DCALL_ASSGN - Data Call Assignment - RCCH_SITE_INFO = 0x18U, //! SITE_INFO - Site Information - RCCH_REG = 0x20U, //! REG - Registration Request (ISP) / Registration Response (OSP) - RCCH_REG_C = 0x22U, //! REG_C - Registration Clear Request (ISP) / Registration Clear Response (OSP) - RCCH_REG_COMM = 0x23U, //! REG_COMM - Registration Command - RCCH_GRP_REG = 0x24U, //! GRP_REG - Group Registration Request (ISP) / Group Registration Response (OSP) - RCCH_PROP_FORM = 0x3FU //! PROP_FORM - Proprietary Form + RCCH_VCALL_CONN = 0x03U, //!< VCALL_CONN - Voice Call Connection Request (ISP) / Voice Call Connection Response (OSP) + RCCH_VCALL_ASSGN = 0x04U, //!< VCALL_ASSGN - Voice Call Assignment + RCCH_DCALL_ASSGN = 0x14U, //!< DCALL_ASSGN - Data Call Assignment + RCCH_SITE_INFO = 0x18U, //!< SITE_INFO - Site Information + RCCH_REG = 0x20U, //!< REG - Registration Request (ISP) / Registration Response (OSP) + RCCH_REG_C = 0x22U, //!< REG_C - Registration Clear Request (ISP) / Registration Clear Response (OSP) + RCCH_REG_COMM = 0x23U, //!< REG_COMM - Registration Command + RCCH_GRP_REG = 0x24U, //!< GRP_REG - Group Registration Request (ISP) / Group Registration Response (OSP) + RCCH_PROP_FORM = 0x3FU //!< PROP_FORM - Proprietary Form }; } diff --git a/src/common/p25/P25Defines.h b/src/common/p25/P25Defines.h index 7c3ac5fa..22dba8e7 100644 --- a/src/common/p25/P25Defines.h +++ b/src/common/p25/P25Defines.h @@ -199,12 +199,12 @@ namespace p25 namespace ServiceClass { /** @brief Station Service Classes */ enum : uint8_t { - INVALID = 0x00U, //! Invalid Service Class - COMPOSITE = 0x01U, //! Composite Control Channel - DATA = 0x10U, //! Data - VOICE = 0x20U, //! Voice - REG = 0x40U, //! Registration - AUTH = 0x80U //! Authentication + INVALID = 0x00U, //!< Invalid Service Class + COMPOSITE = 0x01U, //!< Composite Control Channel + DATA = 0x10U, //!< Data + VOICE = 0x20U, //!< Voice + REG = 0x40U, //!< Registration + AUTH = 0x80U //!< Authentication }; } @@ -212,23 +212,23 @@ namespace p25 namespace SystemService { /** @brief System Service Types */ enum : uint32_t { - NET_ACTIVE = 0x0200000U, //! Network Active - GROUP_VOICE = 0x0080000U, //! Group Voice - IND_VOICE = 0x0040000U, //! Individual Voice - PSTN_UNIT_VOICE = 0x0020000U, //! PSTN Unit Voice - UNIT_PSTN_VOICE = 0x0010000U, //! Unit PSTN Voice - GROUP_DATA = 0x0004000U, //! Group Data - IND_DATA = 0x0002000U, //! Individual Data - UNIT_REG = 0x0000800U, //! Unit Registration - GROUP_AFF = 0x0000400U, //! Group Affiliation - GROUP_AFF_Q = 0x0000200U, //! Group Affiliation Query - USER_STS = 0x0000040U, //! User Status - USER_MSG = 0x0000020U, //! User Message - UNIT_STS = 0x0000010U, //! Unit Status - USER_STS_Q = 0x0000008U, //! User Status Query - UNIT_STS_Q = 0x0000004U, //! Unit Status Query - CALL_ALRT = 0x0000002U, //! Call Alert - EMERGENCY = 0x0000001U //! Emergency + NET_ACTIVE = 0x0200000U, //!< Network Active + GROUP_VOICE = 0x0080000U, //!< Group Voice + IND_VOICE = 0x0040000U, //!< Individual Voice + PSTN_UNIT_VOICE = 0x0020000U, //!< PSTN Unit Voice + UNIT_PSTN_VOICE = 0x0010000U, //!< Unit PSTN Voice + GROUP_DATA = 0x0004000U, //!< Group Data + IND_DATA = 0x0002000U, //!< Individual Data + UNIT_REG = 0x0000800U, //!< Unit Registration + GROUP_AFF = 0x0000400U, //!< Group Affiliation + GROUP_AFF_Q = 0x0000200U, //!< Group Affiliation Query + USER_STS = 0x0000040U, //!< User Status + USER_MSG = 0x0000020U, //!< User Message + UNIT_STS = 0x0000010U, //!< Unit Status + USER_STS_Q = 0x0000008U, //!< User Status Query + UNIT_STS_Q = 0x0000004U, //!< Unit Status Query + CALL_ALRT = 0x0000002U, //!< Call Alert + EMERGENCY = 0x0000001U //!< Emergency }; } @@ -245,10 +245,10 @@ namespace p25 namespace CFVA { /** @brief Conventional/Failur/Valid/Networked Flags */ enum : uint8_t { - CONV = 0x08U, //! Conventional - FAILURE = 0x04U, //! Failure - VALID = 0x02U, //! Valid - NETWORK = 0x01U //! Networked + CONV = 0x08U, //!< Conventional + FAILURE = 0x04U, //!< Failure + VALID = 0x02U, //!< Valid + NETWORK = 0x01U //!< Networked }; } @@ -257,15 +257,15 @@ namespace p25 /** @brief Response Codes */ enum : uint8_t { // General Codes - ACCEPT = 0x00U, //! Accept - FAIL = 0x01U, //! Fail - DENY = 0x02U, //! Deny - REFUSED = 0x03U, //! Refused + ACCEPT = 0x00U, //!< Accept + FAIL = 0x01U, //!< Fail + DENY = 0x02U, //!< Deny + REFUSED = 0x03U, //!< Refused // Answer Codes - ANS_PROCEED = 0x20U, //! Proceed - ANS_DENY = 0x21U, //! Deny - ANS_WAIT = 0x22U //! Wait + ANS_PROCEED = 0x20U, //!< Proceed + ANS_DENY = 0x21U, //!< Deny + ANS_WAIT = 0x22U //!< Wait }; } @@ -273,9 +273,9 @@ namespace p25 namespace CancelService { /** @brief Cancel Service Codes */ enum : uint8_t { - NONE = 0x00U, //! None - TERM_QUE = 0x10U, //! Terminate Queued - TERM_RSRC_ASSIGN = 0x20U //! Terminate Resource Assigned + NONE = 0x00U, //!< None + TERM_QUE = 0x10U, //!< Terminate Queued + TERM_RSRC_ASSIGN = 0x20U //!< Terminate Resource Assigned }; } @@ -284,35 +284,35 @@ namespace p25 /** @brief Reason Codes */ enum : uint8_t { // Denial Codes - DENY_REQ_UNIT_NOT_VALID = 0x10U, //! Requesting Unit Not Valid - DENY_REQ_UNIT_NOT_AUTH = 0x11U, //! Requesting Unit Not Authenticated + DENY_REQ_UNIT_NOT_VALID = 0x10U, //!< Requesting Unit Not Valid + DENY_REQ_UNIT_NOT_AUTH = 0x11U, //!< Requesting Unit Not Authenticated - DENY_TGT_UNIT_NOT_VALID = 0x20U, //! Target Unit Not Vaild - DENY_TGT_UNIT_NOT_AUTH = 0x21U, //! Target Unit Not Authenticated - DENY_SU_FAILED_AUTH = 0x22U, //! Subscriber Failed Authentication - DENY_TGT_UNIT_REFUSED = 0x2FU, //! Target Unit Refused + DENY_TGT_UNIT_NOT_VALID = 0x20U, //!< Target Unit Not Vaild + DENY_TGT_UNIT_NOT_AUTH = 0x21U, //!< Target Unit Not Authenticated + DENY_SU_FAILED_AUTH = 0x22U, //!< Subscriber Failed Authentication + DENY_TGT_UNIT_REFUSED = 0x2FU, //!< Target Unit Refused - DENY_TGT_GROUP_NOT_VALID = 0x30U, //! Target Group Not Valid - DENY_TGT_GROUP_NOT_AUTH = 0x31U, //! Target Group Not Authenticated + DENY_TGT_GROUP_NOT_VALID = 0x30U, //!< Target Group Not Valid + DENY_TGT_GROUP_NOT_AUTH = 0x31U, //!< Target Group Not Authenticated - DENY_NO_NET_RSRC_AVAIL = 0x53U, //! No Network Resources Available - DENY_NO_RF_RSRC_AVAIL = 0x54U, //! No RF Resources Available - DENY_SVC_IN_USE = 0x55U, //! Service In Use + DENY_NO_NET_RSRC_AVAIL = 0x53U, //!< No Network Resources Available + DENY_NO_RF_RSRC_AVAIL = 0x54U, //!< No RF Resources Available + DENY_SVC_IN_USE = 0x55U, //!< Service In Use - DENY_SITE_ACCESS_DENIAL = 0x60U, //! Site Access Denial + DENY_SITE_ACCESS_DENIAL = 0x60U, //!< Site Access Denial - DENY_PTT_COLLIDE = 0x67U, //! Push-to-Talk Collision - DENY_PTT_BONK = 0x77U, //! Push-to-Talk Denial/Bonk + DENY_PTT_COLLIDE = 0x67U, //!< Push-to-Talk Collision + DENY_PTT_BONK = 0x77U, //!< Push-to-Talk Denial/Bonk - DENY_SYS_UNSUPPORTED_SVC = 0xFFU, //! Service Unsupported + DENY_SYS_UNSUPPORTED_SVC = 0xFFU, //!< Service Unsupported // Queue Codes - QUE_REQ_ACTIVE_SERVICE = 0x10U, //! Requested Service Active - QUE_TGT_ACTIVE_SERVICE = 0x20U, //! Target Service Active + QUE_REQ_ACTIVE_SERVICE = 0x10U, //!< Requested Service Active + QUE_TGT_ACTIVE_SERVICE = 0x20U, //!< Target Service Active - QUE_TGT_UNIT_QUEUED = 0x2FU, //! Target Unit Queued + QUE_TGT_UNIT_QUEUED = 0x2FU, //!< Target Unit Queued - QUE_CHN_RESOURCE_NOT_AVAIL = 0x40U //! Channel Resource Not Available + QUE_CHN_RESOURCE_NOT_AVAIL = 0x40U //!< Channel Resource Not Available }; } @@ -320,22 +320,22 @@ namespace p25 namespace ExtendedFunctions { /** @brief Extended Function Opcodes */ enum : uint16_t { - CHECK = 0x0000U, //! Radio Check - UNINHIBIT = 0x007EU, //! Radio Uninhibit - INHIBIT = 0x007FU, //! Radio Inhibit - CHECK_ACK = 0x0080U, //! Radio Check Ack - UNINHIBIT_ACK = 0x00FEU, //! Radio Uninhibit Ack - INHIBIT_ACK = 0x00FFU, //! Radio Inhibit Ack - - DYN_REGRP_REQ = 0x0200U, //! MFID $90 (Motorola) Dynamic Regroup IR - DYN_REGRP_CANCEL = 0x0201U, //! MFID $90 (Motorola) Dynamic Regroup IR Cancellation - DYN_REGRP_LOCK = 0x0202U, //! MFID $90 (Motorola) Lock Selector - DYN_REGRP_UNLOCK = 0x0203U, //! MFID $90 (Motorola) Unlock Selector - - DYN_REGRP_REQ_ACK = 0x0280U, //! MFID $90 (Motorola) Dynamic Regroup IR Ack - DYN_REGRP_CANCEL_ACK = 0x0281U, //! MFID $90 (Motorola) Dynamic Regroup IR Cancellation Ack - DYN_REGRP_LOCK_ACK = 0x0282U, //! MFID $90 (Motorola) Lock Selector Ack - DYN_REGRP_UNLOCK_ACK = 0x0283U, //! MFID $90 (Motorola) Unlock Selector Ack + CHECK = 0x0000U, //!< Radio Check + UNINHIBIT = 0x007EU, //!< Radio Uninhibit + INHIBIT = 0x007FU, //!< Radio Inhibit + CHECK_ACK = 0x0080U, //!< Radio Check Ack + UNINHIBIT_ACK = 0x00FEU, //!< Radio Uninhibit Ack + INHIBIT_ACK = 0x00FFU, //!< Radio Inhibit Ack + + DYN_REGRP_REQ = 0x0200U, //!< MFID $90 (Motorola) Dynamic Regroup IR + DYN_REGRP_CANCEL = 0x0201U, //!< MFID $90 (Motorola) Dynamic Regroup IR Cancellation + DYN_REGRP_LOCK = 0x0202U, //!< MFID $90 (Motorola) Lock Selector + DYN_REGRP_UNLOCK = 0x0203U, //!< MFID $90 (Motorola) Unlock Selector + + DYN_REGRP_REQ_ACK = 0x0280U, //!< MFID $90 (Motorola) Dynamic Regroup IR Ack + DYN_REGRP_CANCEL_ACK = 0x0281U, //!< MFID $90 (Motorola) Dynamic Regroup IR Cancellation Ack + DYN_REGRP_LOCK_ACK = 0x0282U, //!< MFID $90 (Motorola) Lock Selector Ack + DYN_REGRP_UNLOCK_ACK = 0x0283U, //!< MFID $90 (Motorola) Unlock Selector Ack }; } @@ -343,10 +343,10 @@ namespace p25 namespace FrameType { /** @brief DVM Network Frame Types */ enum E : uint8_t { - HDU_VALID = 0x01U, //! HDU Valid - HDU_LATE_ENTRY = 0x02U, //! HDU Late Entry - TERMINATOR = 0x03U, //! TDU/TDULC Terminator - DATA_UNIT = 0x00U //! Standard Data Unit + HDU_VALID = 0x01U, //!< HDU Valid + HDU_LATE_ENTRY = 0x02U, //!< HDU Late Entry + TERMINATOR = 0x03U, //!< TDU/TDULC Terminator + DATA_UNIT = 0x00U //!< Standard Data Unit }; } @@ -354,10 +354,10 @@ namespace p25 namespace PDUFormatType { /** @brief Data Format Type */ enum : uint8_t { - RSP = 0x03U, //! Response - UNCONFIRMED = 0x15U, //! Unconfirmed PDU - CONFIRMED = 0x16U, //! Confirmed PDU - AMBT = 0x17U //! Alternate Multi Block Trunking + RSP = 0x03U, //!< Response + UNCONFIRMED = 0x15U, //!< Unconfirmed PDU + CONFIRMED = 0x16U, //!< Confirmed PDU + AMBT = 0x17U //!< Alternate Multi Block Trunking }; } @@ -365,23 +365,23 @@ namespace p25 namespace PDUSAP { /** @brief Service Access Point */ enum : uint8_t { - USER_DATA = 0x00U, //! User Data - ENC_USER_DATA = 0x01U, //! Encrypted User Data + USER_DATA = 0x00U, //!< User Data + ENC_USER_DATA = 0x01U, //!< Encrypted User Data - PACKET_DATA = 0x04U, //! Packet Data + PACKET_DATA = 0x04U, //!< Packet Data - ARP = 0x05U, //! ARP + ARP = 0x05U, //!< ARP - SNDCP_CTRL_DATA = 0x06U, //! SNDCP Control Data + SNDCP_CTRL_DATA = 0x06U, //!< SNDCP Control Data - EXT_ADDR = 0x1FU, //! Extended Addressing + EXT_ADDR = 0x1FU, //!< Extended Addressing - CONV_DATA_REG = 0x20U, //! Registration + CONV_DATA_REG = 0x20U, //!< Registration - UNENC_KMM = 0x28U, //! Unencrypted KMM - ENC_KMM = 0x29U, //! Encrypted KMM + UNENC_KMM = 0x28U, //!< Unencrypted KMM + ENC_KMM = 0x29U, //!< Encrypted KMM - TRUNK_CTRL = 0x3DU //! Trunking Control + TRUNK_CTRL = 0x3DU //!< Trunking Control }; } @@ -389,9 +389,9 @@ namespace p25 namespace PDUAckClass { /** @brief Acknowledgement Class */ enum : uint8_t { - ACK = 0x00U, //! Acknowledge - NACK = 0x01U, //! Negative Acknowledge - ACK_RETRY = 0x02U //! Acknowledge Retry + ACK = 0x00U, //!< Acknowledge + NACK = 0x01U, //!< Negative Acknowledge + ACK_RETRY = 0x02U //!< Acknowledge Retry }; } @@ -399,17 +399,17 @@ namespace p25 namespace PDUAckType { /** @brief Acknowledgement Type */ enum : uint8_t { - RETRY = 0x00U, //! Retry + RETRY = 0x00U, //!< Retry - ACK = 0x01U, //! Acknowledge + ACK = 0x01U, //!< Acknowledge - NACK_ILLEGAL = 0x00U, //! Illegal Format - NACK_PACKET_CRC = 0x01U, //! Packet CRC - NACK_MEMORY_FULL = 0x02U, //! Memory Full - NACK_SEQ = 0x03U, //! Out of logical sequence FSN - NACK_UNDELIVERABLE = 0x04U, //! Undeliverable - NACK_OUT_OF_SEQ = 0x05U, //! Out of sequence, N(S) != V(R) or V(R) + 1 - NACK_INVL_USER = 0x06U //! Invalid User disallowed by the system + NACK_ILLEGAL = 0x00U, //!< Illegal Format + NACK_PACKET_CRC = 0x01U, //!< Packet CRC + NACK_MEMORY_FULL = 0x02U, //!< Memory Full + NACK_SEQ = 0x03U, //!< Out of logical sequence FSN + NACK_UNDELIVERABLE = 0x04U, //!< Undeliverable + NACK_OUT_OF_SEQ = 0x05U, //!< Out of sequence, N(S) != V(R) or V(R) + 1 + NACK_INVL_USER = 0x06U //!< Invalid User disallowed by the system }; } @@ -417,105 +417,105 @@ namespace p25 namespace PDURegType { /** @brief Registration Type */ enum : uint8_t { - CONNECT = 0x00U, //! Connect - DISCONNECT = 0x01U, //! Disconnect - ACCEPT = 0x04U, //! Accept - DENY = 0x05U //! Deny + CONNECT = 0x00U, //!< Connect + DISCONNECT = 0x01U, //!< Disconnect + ACCEPT = 0x04U, //!< Accept + DENY = 0x05U //!< Deny }; } /** @brief KMM Message Type */ namespace KMM_MessageType { enum : uint8_t { - NULL_CMD = 0x00U, //! Null + NULL_CMD = 0x00U, //!< Null - CHANGE_RSI_CMD = 0x03U, //! Change RSI Command - CHANGE_RSI_RSP = 0x04U, //! Change RSI Response - CHANGEOVER_CMD = 0x05U, //! Changeover Command - CHANGEOVER_RSP = 0x06U, //! Changeover Response + CHANGE_RSI_CMD = 0x03U, //!< Change RSI Command + CHANGE_RSI_RSP = 0x04U, //!< Change RSI Response + CHANGEOVER_CMD = 0x05U, //!< Changeover Command + CHANGEOVER_RSP = 0x06U, //!< Changeover Response - HELLO = 0x0CU, //! Hello + HELLO = 0x0CU, //!< Hello - INVENTORY_CMD = 0x0DU, //! Inventory Command - INVENTORY_RSP = 0x0EU, //! Inventory Response + INVENTORY_CMD = 0x0DU, //!< Inventory Command + INVENTORY_RSP = 0x0EU, //!< Inventory Response - MODIFY_KEY_CMD = 0x13U, //! Modify Key Command + MODIFY_KEY_CMD = 0x13U, //!< Modify Key Command - NAK = 0x16U, //! Negative Ack - NO_SERVICE = 0x17U, //! No Service + NAK = 0x16U, //!< Negative Ack + NO_SERVICE = 0x17U, //!< No Service - ZEROIZE_CMD = 0x21U, //! Zeroize Command - ZEROIZE_RSP = 0x22U, //! Zeroize Response + ZEROIZE_CMD = 0x21U, //!< Zeroize Command + ZEROIZE_RSP = 0x22U, //!< Zeroize Response - DEREG_CMD = 0x23U, //! SU Deregistration Command - DEREG_RSP = 0x24U, //! SU Deregistration Response - REG_CMD = 0x25U, //! SU Registration Command - REG_RSP = 0x26U, //! SU Registration Response + DEREG_CMD = 0x23U, //!< SU Deregistration Command + DEREG_RSP = 0x24U, //!< SU Deregistration Response + REG_CMD = 0x25U, //!< SU Registration Command + REG_RSP = 0x26U, //!< SU Registration Response }; } /** @brief KMM Response Kind */ namespace KMM_ResponseKind { enum : uint8_t { - NONE = 0x00U, //! Response Kind 1 (None) - DELAYED = 0x01U, //! Response Kind 2 (Delayed) - IMMEDIATE = 0x02U, //! Response Kind 3 (Immediate) + NONE = 0x00U, //!< Response Kind 1 (None) + DELAYED = 0x01U, //!< Response Kind 2 (Delayed) + IMMEDIATE = 0x02U, //!< Response Kind 3 (Immediate) }; } /** @brief KMM Message Authentication */ namespace KMM_MAC { enum : uint8_t { - NO_MAC = 0x00U, //! No Message Authentication - ENH_MAC = 0x02U, //! Enhanced Message Authentication - DES_MAC = 0x03U, //! DES Message Authentication + NO_MAC = 0x00U, //!< No Message Authentication + ENH_MAC = 0x02U, //!< Enhanced Message Authentication + DES_MAC = 0x03U, //!< DES Message Authentication }; } /** @brief KMM Inventory Type */ namespace KMM_InventoryType { enum : uint8_t { - NULL_INVENTORY = 0x00U, //! Null + NULL_INVENTORY = 0x00U, //!< Null - LIST_ACTIVE_KEYSET_IDS = 0x01U, //! List Active Keyset IDs - LIST_INACTIVE_KEYSET_IDS = 0x02U, //! List Inactive Keyset IDs - LIST_ACTIVE_KEY_IDS = 0x03U, //! List Active Key IDs - LIST_INACTIVE_KEY_IDS = 0x04U, //! List Inactive Key IDs + LIST_ACTIVE_KEYSET_IDS = 0x01U, //!< List Active Keyset IDs + LIST_INACTIVE_KEYSET_IDS = 0x02U, //!< List Inactive Keyset IDs + LIST_ACTIVE_KEY_IDS = 0x03U, //!< List Active Key IDs + LIST_INACTIVE_KEY_IDS = 0x04U, //!< List Inactive Key IDs }; } /** @brief KMM Hello Flag */ namespace KMM_HelloFlag { enum : uint8_t { - IDENT_ONLY = 0x00U, //! KMF or SU Identification Only + IDENT_ONLY = 0x00U, //!< KMF or SU Identification Only - REKEY_REQUEST_UKEK = 0x01U, //! Rekey Request (UKEK Exists) - REKEY_REQUEST_NO_UKEK = 0x02U, //! Rekey Request (UKEK does not exist) + REKEY_REQUEST_UKEK = 0x01U, //!< Rekey Request (UKEK Exists) + REKEY_REQUEST_NO_UKEK = 0x02U, //!< Rekey Request (UKEK does not exist) }; } /** @brief KMM Status */ namespace KMM_Status { enum : uint8_t { - CMD_PERFORMED = 0x00U, //! Command Performed - CMD_NOT_PERFORMED = 0x01U, //! Command Was Not Performed + CMD_PERFORMED = 0x00U, //!< Command Performed + CMD_NOT_PERFORMED = 0x01U, //!< Command Was Not Performed - ITEM_NOT_EXIST = 0x02U, //! Item does not exist - INVALID_MSG_ID = 0x03U, //! Invalid Message ID - INVALID_MAC = 0x04U, //! Invalid Message Authentication Code + ITEM_NOT_EXIST = 0x02U, //!< Item does not exist + INVALID_MSG_ID = 0x03U, //!< Invalid Message ID + INVALID_MAC = 0x04U, //!< Invalid Message Authentication Code - OUT_OF_MEMORY = 0x05U, //! Out of Memory - FAILED_TO_DECRYPT = 0x06U, //! Failed to decrypt message + OUT_OF_MEMORY = 0x05U, //!< Out of Memory + FAILED_TO_DECRYPT = 0x06U, //!< Failed to decrypt message - INVALID_MSG_NUMBER = 0x07U, //! Invalid Message Number - INVALID_KID = 0x08U, //! Invalid Key ID - INVALID_ALGID = 0x09U, //! Invalid Algorithm ID - INVALID_MFID = 0x0AU, //! Invalid Manufacturer ID + INVALID_MSG_NUMBER = 0x07U, //!< Invalid Message Number + INVALID_KID = 0x08U, //!< Invalid Key ID + INVALID_ALGID = 0x09U, //!< Invalid Algorithm ID + INVALID_MFID = 0x0AU, //!< Invalid Manufacturer ID - MI_ALL_ZERO = 0x0CU, //! Message Indicator All Zero - KEY_FAIL = 0x0DU, //! Key Identified by Alg/KID is Erased + MI_ALL_ZERO = 0x0CU, //!< Message Indicator All Zero + KEY_FAIL = 0x0DU, //!< Key Identified by Alg/KID is Erased - UNKNOWN = 0xFFU, //! Unknown + UNKNOWN = 0xFFU, //!< Unknown }; } @@ -545,13 +545,13 @@ namespace p25 namespace SNDCP_PDUType { /** @brief SNDCP PDU Message Type */ enum : uint8_t { - ACT_TDS_CTX = 0x00U, //! Context Activation Request (ISP) / Context Activation Accept (OSP) + ACT_TDS_CTX = 0x00U, //!< Context Activation Request (ISP) / Context Activation Accept (OSP) - DEACT_TDS_CTX_REQ = 0x02U, //! Deactivate Context Request - ACT_TDS_CTX_REJECT = 0x03U, //! Activate Context Reject + DEACT_TDS_CTX_REQ = 0x02U, //!< Deactivate Context Request + ACT_TDS_CTX_REJECT = 0x03U, //!< Activate Context Reject - RF_UNCONFIRMED = 0x04U, //! Data Unconfirmed - RF_CONFIRMED = 0x05U //! Data Confirmed + RF_UNCONFIRMED = 0x04U, //!< Data Unconfirmed + RF_CONFIRMED = 0x05U //!< Data Confirmed }; } @@ -559,13 +559,13 @@ namespace p25 namespace SNDCPState { /** @brief SNDCP Activation TDS States */ enum E : uint8_t { - IDLE = 0U, //! Idle - Waiting for SU Registration - READY_S = 1U, //! Ready* - Waiting for SU Activation - STANDBY = 2U, //! Standby - SU Activated - READY = 3U, //! Ready - SU Activated and Rx/Tx Data - CLOSED = 4U, //! Closed - SU not yet Registered or Deregistered + IDLE = 0U, //!< Idle - Waiting for SU Registration + READY_S = 1U, //!< Ready* - Waiting for SU Activation + STANDBY = 2U, //!< Standby - SU Activated + READY = 3U, //!< Ready - SU Activated and Rx/Tx Data + CLOSED = 4U, //!< Closed - SU not yet Registered or Deregistered - ILLEGAL = 255U //! Illegal/Unknown + ILLEGAL = 255U //!< Illegal/Unknown }; } @@ -573,9 +573,9 @@ namespace p25 namespace SNDCPNAT { /** @brief SNDCP Network Address Type */ enum : uint8_t { - IPV4_STATIC_ADDR = 0U, //! IPv4 Static Address - IPV4_DYN_ADDR = 1U, //! IPv4 Dynamic Address - IPV4_NO_ADDRESS = 15U //! No Address + IPV4_STATIC_ADDR = 0U, //!< IPv4 Static Address + IPV4_DYN_ADDR = 1U, //!< IPv4 Dynamic Address + IPV4_NO_ADDRESS = 15U //!< No Address }; } @@ -583,12 +583,12 @@ namespace p25 namespace SNDCP_DSUT { /** @brief SNDCP Data Subscriber Unit Type */ enum : uint8_t { - TRUNKED_DATA_ONLY = 0U, //! Trunked Data Only - ALTERNATING_TRUNKED_DATA_VOICE = 1U, //! Alternating Trunked Voice & Data - CONV_DATA_ONLY = 2U, //! Conventional Data Only - ALTERNATING_CONV_DATA_VOICE = 3U, //! Alternating Conventional Voice & Data - TRUNKED_CONV_DATA_ONLY = 4U, //! Trunked and Conventional Data Only - ALT_T_AND_C_DATA_VOICE = 5U //! Alternating Trunked and Conventional Voice & Data + TRUNKED_DATA_ONLY = 0U, //!< Trunked Data Only + ALTERNATING_TRUNKED_DATA_VOICE = 1U, //!< Alternating Trunked Voice & Data + CONV_DATA_ONLY = 2U, //!< Conventional Data Only + ALTERNATING_CONV_DATA_VOICE = 3U, //!< Alternating Conventional Voice & Data + TRUNKED_CONV_DATA_ONLY = 4U, //!< Trunked and Conventional Data Only + ALT_T_AND_C_DATA_VOICE = 5U //!< Alternating Trunked and Conventional Voice & Data }; } @@ -596,22 +596,22 @@ namespace p25 namespace SNDCPReadyTimer { /** @brief SNDCP Ready Timer */ enum : uint8_t { - NOT_ALLOWED = 0U, //! Not Allowed - ONE_SECOND = 1U, //! 1 Second - TWO_SECONDS = 2U, //! 2 Seconds - FOUR_SECONDS = 3U, //! 4 Seconds - SIX_SECONDS = 4U, //! 6 Seconds - EIGHT_SECONDS = 5U, //! 8 Seconds - TEN_SECONDS = 6U, //! 10 Seconds - FIFTEEN_SECONDS = 7U, //! 15 Seconds - TWENTY_SECONDS = 8U, //! 20 Seconds - TWENTYFIVE_SECONDS = 9U, //! 25 Seconds - THIRTY_SECONDS = 10U, //! 30 Seconds - SIXTY_SECONDS = 11U, //! 60 Seconds - ONE_TWENTY_SECONDS = 12U, //! 120 Seconds - ONE_EIGHT_SECONDS = 13U, //! 180 Seconds - THREE_HUNDRED_SECONDS = 14U, //! 300 Seconds - ALWAYS = 15U //! Always + NOT_ALLOWED = 0U, //!< Not Allowed + ONE_SECOND = 1U, //!< 1 Second + TWO_SECONDS = 2U, //!< 2 Seconds + FOUR_SECONDS = 3U, //!< 4 Seconds + SIX_SECONDS = 4U, //!< 6 Seconds + EIGHT_SECONDS = 5U, //!< 8 Seconds + TEN_SECONDS = 6U, //!< 10 Seconds + FIFTEEN_SECONDS = 7U, //!< 15 Seconds + TWENTY_SECONDS = 8U, //!< 20 Seconds + TWENTYFIVE_SECONDS = 9U, //!< 25 Seconds + THIRTY_SECONDS = 10U, //!< 30 Seconds + SIXTY_SECONDS = 11U, //!< 60 Seconds + ONE_TWENTY_SECONDS = 12U, //!< 120 Seconds + ONE_EIGHT_SECONDS = 13U, //!< 180 Seconds + THREE_HUNDRED_SECONDS = 14U, //!< 300 Seconds + ALWAYS = 15U //!< Always }; } @@ -619,22 +619,22 @@ namespace p25 namespace SNDCPStandbyTimer { /** @brief SNDCP Standby Timer */ enum : uint8_t { - NOT_ALLOWED = 0U, //! Not Allowed - TEN_SECONDS = 1U, //! 10 Seconds - THIRTY_SECONDS = 2U, //! 30 Seconds - ONE_MINUTE = 3U, //! 1 Minute - FIVE_MINUTES = 4U, //! 5 Minutes - TEN_MINUTES = 5U, //! 10 Minutes - THIRTY_MINUTES = 6U, //! 30 Minutes - ONE_HOUR = 7U, //! 1 Hour - TWO_HOURS = 8U, //! 2 Hours - FOUR_HOURS = 9U, //! 4 Hours - EIGHT_HOURS = 10U, //! 8 Hours - TWELVE_HOURS = 11U, //! 12 Hours - TWENTY_FOUR_HOURS = 12U, //! 24 Hours - FORTY_EIGHT_HOURS = 13U, //! 48 Hours - SEVENTY_TWO_HOURS = 14U, //! 72 Hours - ALWAYS = 15U //! Always + NOT_ALLOWED = 0U, //!< Not Allowed + TEN_SECONDS = 1U, //!< 10 Seconds + THIRTY_SECONDS = 2U, //!< 30 Seconds + ONE_MINUTE = 3U, //!< 1 Minute + FIVE_MINUTES = 4U, //!< 5 Minutes + TEN_MINUTES = 5U, //!< 10 Minutes + THIRTY_MINUTES = 6U, //!< 30 Minutes + ONE_HOUR = 7U, //!< 1 Hour + TWO_HOURS = 8U, //!< 2 Hours + FOUR_HOURS = 9U, //!< 4 Hours + EIGHT_HOURS = 10U, //!< 8 Hours + TWELVE_HOURS = 11U, //!< 12 Hours + TWENTY_FOUR_HOURS = 12U, //!< 24 Hours + FORTY_EIGHT_HOURS = 13U, //!< 48 Hours + SEVENTY_TWO_HOURS = 14U, //!< 72 Hours + ALWAYS = 15U //!< Always }; } @@ -642,22 +642,22 @@ namespace p25 namespace SNDCPRejectReason { /** @brief SNDCP Reject Reasons */ enum : uint8_t { - ANY_REASON = 0U, //! Any Reason - SU_NOT_PROVISIONED = 1U, //! Subscriber Not Provisioned - SU_DSUT_NOT_SUPPORTED = 2U, //! Subscriber Data Unit Type Not Supported - MAX_TDS_CTX_EXCEEDED = 3U, //! Maximum Number of TDS Contexts Exceeded - SNDCP_VER_NOT_SUPPORTED = 4U, //! SNDCP Version Not Supported - PDS_NOT_SUPPORTED_SITE = 5U, //! Packet Data Service Not Supported on Site - PDS_NOT_SUPPORTED_SYSTEM = 6U, //! Packet Data Service Not Supported on System - - STATIC_IP_NOT_CORRECT = 7U, //! Static IP Address Not Correct - STATIC_IP_ALLOCATION_UNSUPPORTED = 8U, //! Static IP Address Allocation Unsupported - STATIC_IP_IN_USE = 9U, //! Static IP In Use - - IPV4_NOT_SUPPORTED = 10U, //! IPv4 Not Supported - - DYN_IP_POOL_EMPTY = 11U, //! Dynamic IP Address Pool Empty - DYN_IP_ALLOCATION_UNSUPPORTED = 12U //! Dynamic IP Address Allocation Unsupported + ANY_REASON = 0U, //!< Any Reason + SU_NOT_PROVISIONED = 1U, //!< Subscriber Not Provisioned + SU_DSUT_NOT_SUPPORTED = 2U, //!< Subscriber Data Unit Type Not Supported + MAX_TDS_CTX_EXCEEDED = 3U, //!< Maximum Number of TDS Contexts Exceeded + SNDCP_VER_NOT_SUPPORTED = 4U, //!< SNDCP Version Not Supported + PDS_NOT_SUPPORTED_SITE = 5U, //!< Packet Data Service Not Supported on Site + PDS_NOT_SUPPORTED_SYSTEM = 6U, //!< Packet Data Service Not Supported on System + + STATIC_IP_NOT_CORRECT = 7U, //!< Static IP Address Not Correct + STATIC_IP_ALLOCATION_UNSUPPORTED = 8U, //!< Static IP Address Allocation Unsupported + STATIC_IP_IN_USE = 9U, //!< Static IP In Use + + IPV4_NOT_SUPPORTED = 10U, //!< IPv4 Not Supported + + DYN_IP_POOL_EMPTY = 11U, //!< Dynamic IP Address Pool Empty + DYN_IP_ALLOCATION_UNSUPPORTED = 12U //!< Dynamic IP Address Allocation Unsupported }; } @@ -665,8 +665,8 @@ namespace p25 namespace SNDCPDeactivationType { /** @brief SNDCP Deactivation Types */ enum : uint8_t { - DEACT_ALL = 0U, //! Deactivate all NSAPIs - DEACT_THIS_PDU = 1U //! Deactivate NSAPI in this PDU + DEACT_ALL = 0U, //!< Deactivate all NSAPIs + DEACT_THIS_PDU = 1U //!< Deactivate NSAPI in this PDU }; } @@ -679,38 +679,38 @@ namespace p25 namespace LCO { /** @brief LDUx/TDULC Link Control Opcode(s) */ enum : uint8_t { - GROUP = 0x00U, //! GRP VCH USER - Group Voice Channel User - GROUP_UPDT = 0x02U, //! GRP VCH UPDT - Group Voice Channel Update - PRIVATE = 0x03U, //! UU VCH USER - Unit-to-Unit Voice Channel User - UU_ANS_REQ = 0x05U, //! UU ANS REQ - Unit to Unit Answer Request - TEL_INT_VCH_USER = 0x06U, //! TEL INT VCH USER - Telephone Interconnect Voice Channel User / MOT GPS DATA - Motorola In-Band GPS Data - TEL_INT_ANS_RQST = 0x07U, //! TEL INT ANS RQST - Telephone Interconnect Answer Request - EXPLICIT_SOURCE_ID = 0x09U, //! EXPLICIT SOURCE ID - Explicit Source ID - PRIVATE_EXT = 0x0AU, //! UU VCH USER EXT - Unit-to-Unit Voice Channel User Extended - CALL_TERM = 0x0FU, //! CALL TERM - Call Termination or Cancellation - IDEN_UP = 0x18U, //! IDEN UP - Channel Identifier Update - SYS_SRV_BCAST = 0x20U, //! SYS SRV BCAST - System Service Broadcast - ADJ_STS_BCAST = 0x22U, //! ADJ STS BCAST - Adjacent Site Status Broadcast - RFSS_STS_BCAST = 0x23U, //! RFSS STS BCAST - RFSS Status Broadcast - NET_STS_BCAST = 0x24U, //! NET STS BCAST - Network Status Broadcast - CONV_FALLBACK = 0x2AU, //! CONV FALLBACK - Conventional Fallback + GROUP = 0x00U, //!< GRP VCH USER - Group Voice Channel User + GROUP_UPDT = 0x02U, //!< GRP VCH UPDT - Group Voice Channel Update + PRIVATE = 0x03U, //!< UU VCH USER - Unit-to-Unit Voice Channel User + UU_ANS_REQ = 0x05U, //!< UU ANS REQ - Unit to Unit Answer Request + TEL_INT_VCH_USER = 0x06U, //!< TEL INT VCH USER - Telephone Interconnect Voice Channel User / MOT GPS DATA - Motorola In-Band GPS Data + TEL_INT_ANS_RQST = 0x07U, //!< TEL INT ANS RQST - Telephone Interconnect Answer Request + EXPLICIT_SOURCE_ID = 0x09U, //!< EXPLICIT SOURCE ID - Explicit Source ID + PRIVATE_EXT = 0x0AU, //!< UU VCH USER EXT - Unit-to-Unit Voice Channel User Extended + CALL_TERM = 0x0FU, //!< CALL TERM - Call Termination or Cancellation + IDEN_UP = 0x18U, //!< IDEN UP - Channel Identifier Update + SYS_SRV_BCAST = 0x20U, //!< SYS SRV BCAST - System Service Broadcast + ADJ_STS_BCAST = 0x22U, //!< ADJ STS BCAST - Adjacent Site Status Broadcast + RFSS_STS_BCAST = 0x23U, //!< RFSS STS BCAST - RFSS Status Broadcast + NET_STS_BCAST = 0x24U, //!< NET STS BCAST - Network Status Broadcast + CONV_FALLBACK = 0x2AU, //!< CONV FALLBACK - Conventional Fallback // LDUx/TDULC Motorola Link Control Opcode(s) - FAILSOFT = 0x02U, //! FAILSOFT - Failsoft + FAILSOFT = 0x02U, //!< FAILSOFT - Failsoft - MOT_PTT_LOC_HEADER = 0x29U, //! MOT PTT LOC HEADER - Motorola PTT Location Header - MOT_PTT_LOC_PAYLOAD = 0x2AU, //! MOT PTT LOC PAYLOAD - Motorola PTT Location Payload + MOT_PTT_LOC_HEADER = 0x29U, //!< MOT PTT LOC HEADER - Motorola PTT Location Header + MOT_PTT_LOC_PAYLOAD = 0x2AU, //!< MOT PTT LOC PAYLOAD - Motorola PTT Location Payload // LDUx/TDULC Harris Link Control Opcode(s) - HARRIS_PTT_PA_ODD = 0x2AU, //! HARRIS PTT PA ODD - Harris PTT Position and Altitude Odd - HARRIS_PTT_PB_ODD = 0x2BU, //! HARRIS PTT PB ODD - Harris PTT Position and Bearing Odd - HARRIS_PTT_PA_EVEN = 0x2CU, //! HARRIS PTT PA EVEN - Harris PTT Position and Altitude Even - HARRIS_PTT_PB_EVEN = 0x2DU, //! HARRIS PTT PB EVEN - Harris PTT Position and Bearing Even - - HARRIS_USER_ALIAS_PA_ODD = 0x32U, //! HARRIS USER ALIAS PA ODD - Harris User Alias Position and Altitude Odd - HARRIS_USER_ALIAS_PB_ODD = 0x33U, //! HARRIS USER ALIAS PB ODD - Harris User Alias Position and Bearing Odd - HARRIS_USER_ALIAS_PA_EVEN = 0x34U, //! HARRIS USER ALIAS PA EVEN - Harris User Alias Position and Altitude Even - HARRIS_USER_ALIAS_PB_EVEN = 0x35U, //! HARRIS USER ALIAS PB EVEN - Harris User Alias Position and Bearing Even + HARRIS_PTT_PA_ODD = 0x2AU, //!< HARRIS PTT PA ODD - Harris PTT Position and Altitude Odd + HARRIS_PTT_PB_ODD = 0x2BU, //!< HARRIS PTT PB ODD - Harris PTT Position and Bearing Odd + HARRIS_PTT_PA_EVEN = 0x2CU, //!< HARRIS PTT PA EVEN - Harris PTT Position and Altitude Even + HARRIS_PTT_PB_EVEN = 0x2DU, //!< HARRIS PTT PB EVEN - Harris PTT Position and Bearing Even + + HARRIS_USER_ALIAS_PA_ODD = 0x32U, //!< HARRIS USER ALIAS PA ODD - Harris User Alias Position and Altitude Odd + HARRIS_USER_ALIAS_PB_ODD = 0x33U, //!< HARRIS USER ALIAS PB ODD - Harris User Alias Position and Bearing Odd + HARRIS_USER_ALIAS_PA_EVEN = 0x34U, //!< HARRIS USER ALIAS PA EVEN - Harris User Alias Position and Altitude Even + HARRIS_USER_ALIAS_PB_EVEN = 0x35U, //!< HARRIS USER ALIAS PB EVEN - Harris User Alias Position and Bearing Even }; } @@ -719,73 +719,73 @@ namespace p25 /** @brief TSBK Control Opcode(s) */ enum : uint8_t { // TSBK ISP/OSP Shared Opcode(s) - IOSP_GRP_VCH = 0x00U, //! GRP VCH REQ - Group Voice Channel Request (ISP), GRP VCH GRANT - Group Voice Channel Grant (OSP) - IOSP_UU_VCH = 0x04U, //! UU VCH REQ - Unit-to-Unit Voice Channel Request (ISP), UU VCH GRANT - Unit-to-Unit Voice Channel Grant (OSP) - IOSP_UU_ANS = 0x05U, //! UU ANS RSP - Unit-to-Unit Answer Response (ISP), UU ANS REQ - Unit-to-Unit Answer Request (OSP) - IOSP_TELE_INT_DIAL = 0x08U, //! TELE INT DIAL REQ - Telephone Interconnect Request - Explicit (ISP), TELE INT DIAL GRANT - Telephone Interconnect Grant (OSP) - IOSP_TELE_INT_ANS = 0x0AU, //! TELE INT ANS RSP - Telephone Interconnect Answer Response (ISP), TELE INT ANS REQ - Telephone Interconnect Answer Request (OSP) - IOSP_STS_UPDT = 0x18U, //! STS UPDT REQ - Status Update Request (ISP), STS UPDT - Status Update (OSP) - IOSP_STS_Q = 0x1AU, //! STS Q REQ - Status Query Request (ISP), STS Q - Status Query (OSP) - IOSP_MSG_UPDT = 0x1CU, //! MSG UPDT REQ - Message Update Request (ISP), MSG UPDT - Message Update (OSP) - IOSP_RAD_MON = 0x1DU, //! RAD MON REQ - Radio Unit Monitor Request (ISP), RAD MON CMD - Radio Monitor Command (OSP) - IOSP_RAD_MON_ENH = 0x1EU, //! RAD MON ENH REQ - Radio Unit Monitor Enhanced Request (ISP), RAD MON ENH CMD - Radio Unit Monitor Enhanced Command (OSP) - IOSP_CALL_ALRT = 0x1FU, //! CALL ALRT REQ - Call Alert Request (ISP), CALL ALRT - Call Alert (OSP) - IOSP_ACK_RSP = 0x20U, //! ACK RSP U - Acknowledge Response - Unit (ISP), ACK RSP FNE - Acknowledge Response - FNE (OSP) - IOSP_EXT_FNCT = 0x24U, //! EXT FNCT RSP - Extended Function Response (ISP), EXT FNCT CMD - Extended Function Command (OSP) - IOSP_GRP_AFF = 0x28U, //! GRP AFF REQ - Group Affiliation Request (ISP), GRP AFF RSP - Group Affiliation Response (OSP) - IOSP_U_REG = 0x2CU, //! U REG REQ - Unit Registration Request (ISP), U REG RSP - Unit Registration Response (OSP) + IOSP_GRP_VCH = 0x00U, //!< GRP VCH REQ - Group Voice Channel Request (ISP), GRP VCH GRANT - Group Voice Channel Grant (OSP) + IOSP_UU_VCH = 0x04U, //!< UU VCH REQ - Unit-to-Unit Voice Channel Request (ISP), UU VCH GRANT - Unit-to-Unit Voice Channel Grant (OSP) + IOSP_UU_ANS = 0x05U, //!< UU ANS RSP - Unit-to-Unit Answer Response (ISP), UU ANS REQ - Unit-to-Unit Answer Request (OSP) + IOSP_TELE_INT_DIAL = 0x08U, //!< TELE INT DIAL REQ - Telephone Interconnect Request - Explicit (ISP), TELE INT DIAL GRANT - Telephone Interconnect Grant (OSP) + IOSP_TELE_INT_ANS = 0x0AU, //!< TELE INT ANS RSP - Telephone Interconnect Answer Response (ISP), TELE INT ANS REQ - Telephone Interconnect Answer Request (OSP) + IOSP_STS_UPDT = 0x18U, //!< STS UPDT REQ - Status Update Request (ISP), STS UPDT - Status Update (OSP) + IOSP_STS_Q = 0x1AU, //!< STS Q REQ - Status Query Request (ISP), STS Q - Status Query (OSP) + IOSP_MSG_UPDT = 0x1CU, //!< MSG UPDT REQ - Message Update Request (ISP), MSG UPDT - Message Update (OSP) + IOSP_RAD_MON = 0x1DU, //!< RAD MON REQ - Radio Unit Monitor Request (ISP), RAD MON CMD - Radio Monitor Command (OSP) + IOSP_RAD_MON_ENH = 0x1EU, //!< RAD MON ENH REQ - Radio Unit Monitor Enhanced Request (ISP), RAD MON ENH CMD - Radio Unit Monitor Enhanced Command (OSP) + IOSP_CALL_ALRT = 0x1FU, //!< CALL ALRT REQ - Call Alert Request (ISP), CALL ALRT - Call Alert (OSP) + IOSP_ACK_RSP = 0x20U, //!< ACK RSP U - Acknowledge Response - Unit (ISP), ACK RSP FNE - Acknowledge Response - FNE (OSP) + IOSP_EXT_FNCT = 0x24U, //!< EXT FNCT RSP - Extended Function Response (ISP), EXT FNCT CMD - Extended Function Command (OSP) + IOSP_GRP_AFF = 0x28U, //!< GRP AFF REQ - Group Affiliation Request (ISP), GRP AFF RSP - Group Affiliation Response (OSP) + IOSP_U_REG = 0x2CU, //!< U REG REQ - Unit Registration Request (ISP), U REG RSP - Unit Registration Response (OSP) // TSBK Inbound Signalling Packet (ISP) Opcode(s) - ISP_TELE_INT_PSTN_REQ = 0x09U, //! TELE INT PSTN REQ - Telephone Interconnect Request - Implicit - ISP_SNDCP_CH_REQ = 0x12U, //! SNDCP CH REQ - SNDCP Data Channel Request - ISP_SNDCP_REC_REQ = 0x14U, //! SNDCP REC REQ - SNDCP Reconnect Request - ISP_STS_Q_RSP = 0x19U, //! STS Q RSP - Status Query Response - ISP_STS_Q_REQ = 0x1CU, //! STS Q REQ - Status Query Request - ISP_CAN_SRV_REQ = 0x23U, //! CAN SRV REQ - Cancel Service Request - ISP_EMERG_ALRM_REQ = 0x27U, //! EMERG ALRM REQ - Emergency Alarm Request - ISP_GRP_AFF_Q_RSP = 0x29U, //! GRP AFF Q RSP - Group Affiliation Query Response - ISP_U_DEREG_REQ = 0x2BU, //! U DE REG REQ - Unit De-Registration Request - ISP_LOC_REG_REQ = 0x2DU, //! LOC REG REQ - Location Registration Request - ISP_AUTH_RESP = 0x38U, //! AUTH RESP - Authentication Response - ISP_AUTH_RESP_M = 0x39U, //! AUTH RESP M - Authentication Response Mutual - ISP_AUTH_FNE_RST = 0x3AU, //! AUTH FNE RST - Authentication FNE Result - ISP_AUTH_SU_DMD = 0x3BU, //! AUTH SU DMD - Authentication SU Demand + ISP_TELE_INT_PSTN_REQ = 0x09U, //!< TELE INT PSTN REQ - Telephone Interconnect Request - Implicit + ISP_SNDCP_CH_REQ = 0x12U, //!< SNDCP CH REQ - SNDCP Data Channel Request + ISP_SNDCP_REC_REQ = 0x14U, //!< SNDCP REC REQ - SNDCP Reconnect Request + ISP_STS_Q_RSP = 0x19U, //!< STS Q RSP - Status Query Response + ISP_STS_Q_REQ = 0x1CU, //!< STS Q REQ - Status Query Request + ISP_CAN_SRV_REQ = 0x23U, //!< CAN SRV REQ - Cancel Service Request + ISP_EMERG_ALRM_REQ = 0x27U, //!< EMERG ALRM REQ - Emergency Alarm Request + ISP_GRP_AFF_Q_RSP = 0x29U, //!< GRP AFF Q RSP - Group Affiliation Query Response + ISP_U_DEREG_REQ = 0x2BU, //!< U DE REG REQ - Unit De-Registration Request + ISP_LOC_REG_REQ = 0x2DU, //!< LOC REG REQ - Location Registration Request + ISP_AUTH_RESP = 0x38U, //!< AUTH RESP - Authentication Response + ISP_AUTH_RESP_M = 0x39U, //!< AUTH RESP M - Authentication Response Mutual + ISP_AUTH_FNE_RST = 0x3AU, //!< AUTH FNE RST - Authentication FNE Result + ISP_AUTH_SU_DMD = 0x3BU, //!< AUTH SU DMD - Authentication SU Demand // TSBK Outbound Signalling Packet (OSP) Opcode(s) - OSP_GRP_VCH_GRANT_UPD = 0x02U, //! GRP VCH GRANT UPD - Group Voice Channel Grant Update - OSP_UU_VCH_GRANT_UPD = 0x06U, //! UU VCH GRANT UPD - Unit-to-Unit Voice Channel Grant Update - OSP_SNDCP_CH_GNT = 0x14U, //! SNDCP CH GNT - SNDCP Data Channel Grant - OSP_SNDCP_CH_ANN = 0x16U, //! SNDCP CH ANN - SNDCP Data Channel Announcement - OSP_STS_Q = 0x1AU, //! STS Q - Status Query - OSP_QUE_RSP = 0x21U, //! QUE RSP - Queued Response - OSP_DENY_RSP = 0x27U, //! DENY RSP - Deny Response - OSP_SCCB_EXP = 0x29U, //! SCCB - Secondary Control Channel Broadcast - Explicit - OSP_GRP_AFF_Q = 0x2AU, //! GRP AFF Q - Group Affiliation Query - OSP_LOC_REG_RSP = 0x2BU, //! LOC REG RSP - Location Registration Response - OSP_U_REG_CMD = 0x2DU, //! U REG CMD - Unit Registration Command - OSP_U_DEREG_ACK = 0x2FU, //! U DE REG ACK - Unit De-Registration Acknowledge - OSP_SYNC_BCAST = 0x30U, //! SYNC BCAST - Synchronization Broadcast - OSP_AUTH_DMD = 0x31U, //! AUTH DMD - Authentication Demand - OSP_AUTH_FNE_RESP = 0x32U, //! AUTH FNE RESP - Authentication FNE Response - OSP_IDEN_UP_VU = 0x34U, //! IDEN UP VU - Channel Identifier Update for VHF/UHF Bands - OSP_TIME_DATE_ANN = 0x35U, //! TIME DATE ANN - Time and Date Announcement - OSP_SYS_SRV_BCAST = 0x38U, //! SYS SRV BCAST - System Service Broadcast - OSP_SCCB = 0x39U, //! SCCB - Secondary Control Channel Broadcast - OSP_RFSS_STS_BCAST = 0x3AU, //! RFSS STS BCAST - RFSS Status Broadcast - OSP_NET_STS_BCAST = 0x3BU, //! NET STS BCAST - Network Status Broadcast - OSP_ADJ_STS_BCAST = 0x3CU, //! ADJ STS BCAST - Adjacent Site Status Broadcast - OSP_IDEN_UP = 0x3DU, //! IDEN UP - Channel Identifier Update + OSP_GRP_VCH_GRANT_UPD = 0x02U, //!< GRP VCH GRANT UPD - Group Voice Channel Grant Update + OSP_UU_VCH_GRANT_UPD = 0x06U, //!< UU VCH GRANT UPD - Unit-to-Unit Voice Channel Grant Update + OSP_SNDCP_CH_GNT = 0x14U, //!< SNDCP CH GNT - SNDCP Data Channel Grant + OSP_SNDCP_CH_ANN = 0x16U, //!< SNDCP CH ANN - SNDCP Data Channel Announcement + OSP_STS_Q = 0x1AU, //!< STS Q - Status Query + OSP_QUE_RSP = 0x21U, //!< QUE RSP - Queued Response + OSP_DENY_RSP = 0x27U, //!< DENY RSP - Deny Response + OSP_SCCB_EXP = 0x29U, //!< SCCB - Secondary Control Channel Broadcast - Explicit + OSP_GRP_AFF_Q = 0x2AU, //!< GRP AFF Q - Group Affiliation Query + OSP_LOC_REG_RSP = 0x2BU, //!< LOC REG RSP - Location Registration Response + OSP_U_REG_CMD = 0x2DU, //!< U REG CMD - Unit Registration Command + OSP_U_DEREG_ACK = 0x2FU, //!< U DE REG ACK - Unit De-Registration Acknowledge + OSP_SYNC_BCAST = 0x30U, //!< SYNC BCAST - Synchronization Broadcast + OSP_AUTH_DMD = 0x31U, //!< AUTH DMD - Authentication Demand + OSP_AUTH_FNE_RESP = 0x32U, //!< AUTH FNE RESP - Authentication FNE Response + OSP_IDEN_UP_VU = 0x34U, //!< IDEN UP VU - Channel Identifier Update for VHF/UHF Bands + OSP_TIME_DATE_ANN = 0x35U, //!< TIME DATE ANN - Time and Date Announcement + OSP_SYS_SRV_BCAST = 0x38U, //!< SYS SRV BCAST - System Service Broadcast + OSP_SCCB = 0x39U, //!< SCCB - Secondary Control Channel Broadcast + OSP_RFSS_STS_BCAST = 0x3AU, //!< RFSS STS BCAST - RFSS Status Broadcast + OSP_NET_STS_BCAST = 0x3BU, //!< NET STS BCAST - Network Status Broadcast + OSP_ADJ_STS_BCAST = 0x3CU, //!< ADJ STS BCAST - Adjacent Site Status Broadcast + OSP_IDEN_UP = 0x3DU, //!< IDEN UP - Channel Identifier Update // TSBK Motorola Outbound Signalling Packet (OSP) Opcode(s) - OSP_MOT_GRG_ADD = 0x00U, //! MOT GRG ADD - Motorola / Group Regroup Add (Patch Supergroup) - OSP_MOT_GRG_DEL = 0x01U, //! MOT GRG DEL - Motorola / Group Regroup Delete (Unpatch Supergroup) - OSP_MOT_GRG_VCH_GRANT = 0x02U, //! MOT GRG GROUP VCH GRANT / Group Regroup Voice Channel Grant - OSP_MOT_GRG_VCH_UPD = 0x03U, //! MOT GRG GROUP VCH GRANT UPD / Group Regroup Voice Channel Grant Update - OSP_MOT_CC_BSI = 0x0BU, //! MOT CC BSI - Motorola / Control Channel Base Station Identifier - OSP_MOT_PSH_CCH = 0x0EU, //! MOT PSH CCH - Motorola / Planned Control Channel Shutdown + OSP_MOT_GRG_ADD = 0x00U, //!< MOT GRG ADD - Motorola / Group Regroup Add (Patch Supergroup) + OSP_MOT_GRG_DEL = 0x01U, //!< MOT GRG DEL - Motorola / Group Regroup Delete (Unpatch Supergroup) + OSP_MOT_GRG_VCH_GRANT = 0x02U, //!< MOT GRG GROUP VCH GRANT / Group Regroup Voice Channel Grant + OSP_MOT_GRG_VCH_UPD = 0x03U, //!< MOT GRG GROUP VCH GRANT UPD / Group Regroup Voice Channel Grant Update + OSP_MOT_CC_BSI = 0x0BU, //!< MOT CC BSI - Motorola / Control Channel Base Station Identifier + OSP_MOT_PSH_CCH = 0x0EU, //!< MOT PSH CCH - Motorola / Planned Control Channel Shutdown // TSBK DVM Outbound Signalling Packet (OSP) Opcode(s) - OSP_DVM_GIT_HASH = 0x3FU, //! + OSP_DVM_GIT_HASH = 0x3FU, //!< }; } @@ -793,15 +793,15 @@ namespace p25 namespace DUID { /** @brief Data Unit ID(s) */ enum E : uint8_t { - HDU = 0x00U, //! Header Data Unit - TDU = 0x03U, //! Simple Terminator Data Unit - LDU1 = 0x05U, //! Logical Link Data Unit 1 - VSELP1 = 0x06U, //! Motorola VSELP 1 - TSDU = 0x07U, //! Trunking System Data Unit - VSELP2 = 0x09U, //! Motorola VSELP 2 - LDU2 = 0x0AU, //! Logical Link Data Unit 2 - PDU = 0x0CU, //! Packet Data Unit - TDULC = 0x0FU //! Terminator Data Unit with Link Control + HDU = 0x00U, //!< Header Data Unit + TDU = 0x03U, //!< Simple Terminator Data Unit + LDU1 = 0x05U, //!< Logical Link Data Unit 1 + VSELP1 = 0x06U, //!< Motorola VSELP 1 + TSDU = 0x07U, //!< Trunking System Data Unit + VSELP2 = 0x09U, //!< Motorola VSELP 2 + LDU2 = 0x0AU, //!< Logical Link Data Unit 2 + PDU = 0x0CU, //!< Packet Data Unit + TDULC = 0x0FU //!< Terminator Data Unit with Link Control }; } diff --git a/src/common/p25/dfsi/DFSIDefines.h b/src/common/p25/dfsi/DFSIDefines.h index e8f2f80f..5cde6888 100644 --- a/src/common/p25/dfsi/DFSIDefines.h +++ b/src/common/p25/dfsi/DFSIDefines.h @@ -78,76 +78,76 @@ namespace p25 * @{ */ - const uint8_t DFSI_RTP_PAYLOAD_TYPE = 0x64U; //! - const uint8_t DFSI_RTP_MOT_PAYLOAD_TYPE = 0x5DU; //! - const uint8_t DFSI_RTP_MOT_DATA_PAYLOAD_TYPE = 0x5EU; //! + const uint8_t DFSI_RTP_PAYLOAD_TYPE = 0x64U; //!< + const uint8_t DFSI_RTP_MOT_PAYLOAD_TYPE = 0x5DU; //!< + const uint8_t DFSI_RTP_MOT_DATA_PAYLOAD_TYPE = 0x5EU; //!< - const uint8_t DFSI_RTP_SEQ_HANDSHAKE = 0x00U; //! - const uint8_t DFSI_RTP_SEQ_STARTSTOP = 0x01U; //! + const uint8_t DFSI_RTP_SEQ_HANDSHAKE = 0x00U; //!< + const uint8_t DFSI_RTP_SEQ_STARTSTOP = 0x01U; //!< - const uint8_t DFSI_MOT_ICW_FMT_TYPE3 = 0x02U; //! + const uint8_t DFSI_MOT_ICW_FMT_TYPE3 = 0x02U; //!< - const uint8_t DFSI_MOT_ICW_PARM_NOP = 0x00U; //! No Operation - const uint8_t DSFI_MOT_ICW_PARM_PAYLOAD = 0x0CU; //! Stream Payload - const uint8_t DFSI_MOT_ICW_PARM_RSSI1 = 0x1AU; //! RSSI Data - const uint8_t DFSI_MOT_ICW_PARM_RSSI2 = 0x1BU; //! RSSI Data - const uint8_t DFSI_MOT_ICW_PARM_STOP = 0x25U; //! Stop Stream - const uint8_t DFSI_MOT_ICW_TX_ADDRESS = 0x2CU; //! Tx Device Address - const uint8_t DFSI_MOT_ICW_RX_ADDRESS = 0x35U; //! Rx Device Address + const uint8_t DFSI_MOT_ICW_PARM_NOP = 0x00U; //!< No Operation + const uint8_t DSFI_MOT_ICW_PARM_PAYLOAD = 0x0CU; //!< Stream Payload + const uint8_t DFSI_MOT_ICW_PARM_RSSI1 = 0x1AU; //!< RSSI Data + const uint8_t DFSI_MOT_ICW_PARM_RSSI2 = 0x1BU; //!< RSSI Data + const uint8_t DFSI_MOT_ICW_PARM_STOP = 0x25U; //!< Stop Stream + const uint8_t DFSI_MOT_ICW_TX_ADDRESS = 0x2CU; //!< Tx Device Address + const uint8_t DFSI_MOT_ICW_RX_ADDRESS = 0x35U; //!< Rx Device Address - const uint8_t DFSI_BUSY_BITS_TALKAROUND = 0x00U; //! Talkaround - const uint8_t DFSI_BUSY_BITS_BUSY = 0x01U; //! Busy - const uint8_t DFSI_BUSY_BITS_INBOUND = 0x02U; //! Inbound - const uint8_t DFSI_BUSY_BITS_IDLE = 0x03U; //! Idle + const uint8_t DFSI_BUSY_BITS_TALKAROUND = 0x00U; //!< Talkaround + const uint8_t DFSI_BUSY_BITS_BUSY = 0x01U; //!< Busy + const uint8_t DFSI_BUSY_BITS_INBOUND = 0x02U; //!< Inbound + const uint8_t DFSI_BUSY_BITS_IDLE = 0x03U; //!< Idle /** @brief DFSI Frame Type */ namespace DFSIFrameType { /** @brief DFSI Frame Type */ enum E : uint8_t { - MOT_START_STOP = 0x00U, // Motorola/V.24 Start/Stop Stream - - MOT_VHDR_1 = 0x60U, // Motorola/V.24 Voice Header 1 - MOT_VHDR_2 = 0x61U, // Motorola/V.24 Voice Header 2 - - LDU1_VOICE1 = 0x62U, // IMBE LDU1 - Voice 1 - LDU1_VOICE2 = 0x63U, // IMBE LDU1 - Voice 2 - LDU1_VOICE3 = 0x64U, // IMBE LDU1 - Voice 3 + Link Control - LDU1_VOICE4 = 0x65U, // IMBE LDU1 - Voice 4 + Link Control - LDU1_VOICE5 = 0x66U, // IMBE LDU1 - Voice 5 + Link Control - LDU1_VOICE6 = 0x67U, // IMBE LDU1 - Voice 6 + Link Control - LDU1_VOICE7 = 0x68U, // IMBE LDU1 - Voice 7 + Link Control - LDU1_VOICE8 = 0x69U, // IMBE LDU1 - Voice 8 + Link Control - LDU1_VOICE9 = 0x6AU, // IMBE LDU1 - Voice 9 + Low Speed Data - - LDU2_VOICE10 = 0x6BU, // IMBE LDU2 - Voice 10 - LDU2_VOICE11 = 0x6CU, // IMBE LDU2 - Voice 11 - LDU2_VOICE12 = 0x6DU, // IMBE LDU2 - Voice 12 + Encryption Sync - LDU2_VOICE13 = 0x6EU, // IMBE LDU2 - Voice 13 + Encryption Sync - LDU2_VOICE14 = 0x6FU, // IMBE LDU2 - Voice 14 + Encryption Sync - LDU2_VOICE15 = 0x70U, // IMBE LDU2 - Voice 15 + Encryption Sync - LDU2_VOICE16 = 0x71U, // IMBE LDU2 - Voice 16 + Encryption Sync - LDU2_VOICE17 = 0x72U, // IMBE LDU2 - Voice 17 + Encryption Sync - LDU2_VOICE18 = 0x73U, // IMBE LDU2 - Voice 18 + Low Speed Data - - MOT_TDULC = 0x74U, // Motorola/V.24 TDULC - - MOT_PDU_UNCONF_HEADER = 0x80U, // Motorola/V.24 PDU (Unconfirmed Block Header) - MOT_PDU_UNCONF_BLOCK_1 = 0x81U, // Motorola/V.24 PDU (Unconfirmed Block 1) - MOT_PDU_UNCONF_BLOCK_2 = 0x82U, // Motorola/V.24 PDU (Unconfirmed Block 2) - MOT_PDU_UNCONF_BLOCK_3 = 0x83U, // Motorola/V.24 PDU (Unconfirmed Block 3) - MOT_PDU_UNCONF_BLOCK_4 = 0x84U, // Motorola/V.24 PDU (Unconfirmed Block 4) - MOT_PDU_UNCONF_END = 0x85U, // Motorola/V.24 PDU (Unconfirmed Block End) - MOT_PDU_SINGLE_UNCONF = 0x87U, // Motorola/V.24 PDU (Single Unconfirmed Block) - - MOD_PDU_CONF_HEADER = 0x88U, // Motorola/V.24 PDU (Confirmed Block Header) - MOT_PDU_CONF_BLOCK_1 = 0x89U, // Motorola/V.24 PDU (Confirmed Block 1) - MOT_PDU_CONF_BLOCK_2 = 0x8AU, // Motorola/V.24 PDU (Confirmed Block 2) - MOT_PDU_CONF_BLOCK_3 = 0x8BU, // Motorola/V.24 PDU (Confirmed Block 3) - MOT_PDU_CONF_BLOCK_4 = 0x8CU, // Motorola/V.24 PDU (Confirmed Block 4) - MOT_PDU_CONF_END = 0x8DU, // Motorola/V.24 PDU (Confirmed Block End) - MOT_PDU_SINGLE_CONF = 0x8FU, // Motorola/V.24 PDU (Single Confirmed Block) - - MOT_TSBK = 0xA1U // Motorola/V.24 TSBK (Single Block) + MOT_START_STOP = 0x00U, //!< Motorola/V.24 Start/Stop Stream + + MOT_VHDR_1 = 0x60U, //!< Motorola/V.24 Voice Header 1 + MOT_VHDR_2 = 0x61U, //!< Motorola/V.24 Voice Header 2 + + LDU1_VOICE1 = 0x62U, //!< IMBE LDU1 - Voice 1 + LDU1_VOICE2 = 0x63U, //!< IMBE LDU1 - Voice 2 + LDU1_VOICE3 = 0x64U, //!< IMBE LDU1 - Voice 3 + Link Control + LDU1_VOICE4 = 0x65U, //!< IMBE LDU1 - Voice 4 + Link Control + LDU1_VOICE5 = 0x66U, //!< IMBE LDU1 - Voice 5 + Link Control + LDU1_VOICE6 = 0x67U, //!< IMBE LDU1 - Voice 6 + Link Control + LDU1_VOICE7 = 0x68U, //!< IMBE LDU1 - Voice 7 + Link Control + LDU1_VOICE8 = 0x69U, //!< IMBE LDU1 - Voice 8 + Link Control + LDU1_VOICE9 = 0x6AU, //!< IMBE LDU1 - Voice 9 + Low Speed Data + + LDU2_VOICE10 = 0x6BU, //!< IMBE LDU2 - Voice 10 + LDU2_VOICE11 = 0x6CU, //!< IMBE LDU2 - Voice 11 + LDU2_VOICE12 = 0x6DU, //!< IMBE LDU2 - Voice 12 + Encryption Sync + LDU2_VOICE13 = 0x6EU, //!< IMBE LDU2 - Voice 13 + Encryption Sync + LDU2_VOICE14 = 0x6FU, //!< IMBE LDU2 - Voice 14 + Encryption Sync + LDU2_VOICE15 = 0x70U, //!< IMBE LDU2 - Voice 15 + Encryption Sync + LDU2_VOICE16 = 0x71U, //!< IMBE LDU2 - Voice 16 + Encryption Sync + LDU2_VOICE17 = 0x72U, //!< IMBE LDU2 - Voice 17 + Encryption Sync + LDU2_VOICE18 = 0x73U, //!< IMBE LDU2 - Voice 18 + Low Speed Data + + MOT_TDULC = 0x74U, //!< Motorola/V.24 TDULC + + MOT_PDU_UNCONF_HEADER = 0x80U, //!< Motorola/V.24 PDU (Unconfirmed Block Header) + MOT_PDU_UNCONF_BLOCK_1 = 0x81U, //!< Motorola/V.24 PDU (Unconfirmed Block 1) + MOT_PDU_UNCONF_BLOCK_2 = 0x82U, //!< Motorola/V.24 PDU (Unconfirmed Block 2) + MOT_PDU_UNCONF_BLOCK_3 = 0x83U, //!< Motorola/V.24 PDU (Unconfirmed Block 3) + MOT_PDU_UNCONF_BLOCK_4 = 0x84U, //!< Motorola/V.24 PDU (Unconfirmed Block 4) + MOT_PDU_UNCONF_END = 0x85U, //!< Motorola/V.24 PDU (Unconfirmed Block End) + MOT_PDU_SINGLE_UNCONF = 0x87U, //!< Motorola/V.24 PDU (Single Unconfirmed Block) + + MOD_PDU_CONF_HEADER = 0x88U, //!< Motorola/V.24 PDU (Confirmed Block Header) + MOT_PDU_CONF_BLOCK_1 = 0x89U, //!< Motorola/V.24 PDU (Confirmed Block 1) + MOT_PDU_CONF_BLOCK_2 = 0x8AU, //!< Motorola/V.24 PDU (Confirmed Block 2) + MOT_PDU_CONF_BLOCK_3 = 0x8BU, //!< Motorola/V.24 PDU (Confirmed Block 3) + MOT_PDU_CONF_BLOCK_4 = 0x8CU, //!< Motorola/V.24 PDU (Confirmed Block 4) + MOT_PDU_CONF_END = 0x8DU, //!< Motorola/V.24 PDU (Confirmed Block End) + MOT_PDU_SINGLE_CONF = 0x8FU, //!< Motorola/V.24 PDU (Single Confirmed Block) + + MOT_TSBK = 0xA1U //!< Motorola/V.24 TSBK (Single Block) }; } diff --git a/src/common/p25/dfsi/frames/FrameDefines.h b/src/common/p25/dfsi/frames/FrameDefines.h index eb8c01bf..ed958923 100644 --- a/src/common/p25/dfsi/frames/FrameDefines.h +++ b/src/common/p25/dfsi/frames/FrameDefines.h @@ -44,17 +44,17 @@ namespace p25 namespace FSCMessageType { /** @brief FSC Control Service Message.*/ enum E : uint8_t { - FSC_CONNECT = 0, //! Establish connection with FSS - FSC_HEARTBEAT = 1, //! Heartbeat/Connectivity Maintenance - FSC_ACK = 2, //! Control Service Ack. + FSC_CONNECT = 0, //!< Establish connection with FSS + FSC_HEARTBEAT = 1, //!< Heartbeat/Connectivity Maintenance + FSC_ACK = 2, //!< Control Service Ack. - FSC_SEL_CHAN = 5, //! Channel Selection + FSC_SEL_CHAN = 5, //!< Channel Selection - FSC_REPORT_SEL_MODES = 8, //! Report Selected Modes + FSC_REPORT_SEL_MODES = 8, //!< Report Selected Modes - FSC_DISCONNECT = 9, //! Detach Control Service + FSC_DISCONNECT = 9, //!< Detach Control Service - FSC_INVALID = 127, //! Invalid Control Message + FSC_INVALID = 127, //!< Invalid Control Message }; } @@ -62,14 +62,14 @@ namespace p25 namespace FSCAckResponseCode { /** @brief FSC ACK/NAK Codes. */ enum E : uint8_t { - CONTROL_ACK = 0, //! Acknowledgement. - CONTROL_NAK = 1, //! Unspecified Negative Acknowledgement. - CONTROL_NAK_CONNECTED = 2, //! Server is connected to some other host. - CONTROL_NAK_M_UNSUPP = 3, //! Unsupported Manufactuerer Message. - CONTROL_NAK_V_UNSUPP = 4, //! Unsupported Message Version. - CONTROL_NAK_F_UNSUPP = 5, //! Unsupported Function. - CONTROL_NAK_PARMS = 6, //! Bad / Unsupported Command Parameters. - CONTROL_NAK_BUSY = 7 //! FSS is currently busy with a function. + CONTROL_ACK = 0, //!< Acknowledgement. + CONTROL_NAK = 1, //!< Unspecified Negative Acknowledgement. + CONTROL_NAK_CONNECTED = 2, //!< Server is connected to some other host. + CONTROL_NAK_M_UNSUPP = 3, //!< Unsupported Manufactuerer Message. + CONTROL_NAK_V_UNSUPP = 4, //!< Unsupported Message Version. + CONTROL_NAK_F_UNSUPP = 5, //!< Unsupported Function. + CONTROL_NAK_PARMS = 6, //!< Bad / Unsupported Command Parameters. + CONTROL_NAK_BUSY = 7 //!< FSS is currently busy with a function. }; } @@ -77,17 +77,17 @@ namespace p25 namespace BlockType { /** @brief DFSI Block Types */ enum E : uint8_t { - FULL_RATE_VOICE = 0, //! Full Rate Voice + FULL_RATE_VOICE = 0, //!< Full Rate Voice - VOICE_HEADER_P1 = 6, //! Voice Header 1 - VOICE_HEADER_P2 = 7, //! Voice Header 2 + VOICE_HEADER_P1 = 6, //!< Voice Header 1 + VOICE_HEADER_P2 = 7, //!< Voice Header 2 - START_OF_STREAM = 9, //! Start of Stream - END_OF_STREAM = 10, //! End of Stream + START_OF_STREAM = 9, //!< Start of Stream + END_OF_STREAM = 10, //!< End of Stream - START_OF_STREAM_ACK = 14, //! Start of Stream Ack + START_OF_STREAM_ACK = 14, //!< Start of Stream Ack - UNDEFINED = 127 //! Undefined + UNDEFINED = 127 //!< Undefined }; } @@ -95,8 +95,8 @@ namespace p25 namespace MotStartStreamOpcode { /** @brief Motorola Start of Stream Operation */ enum E : uint8_t { - TRANSMIT = 0x02U, //! Transmit - RECEIVE = 0x04U, //! Receive + TRANSMIT = 0x02U, //!< Transmit + RECEIVE = 0x04U, //!< Receive }; } @@ -104,10 +104,10 @@ namespace p25 namespace MotStreamPayload { /** @brief Motorola Stream Payload */ enum E : uint8_t { - VOICE = 0x0BU, //! P25 Voice - DATA = 0x0CU, //! P25 Data - TERM_LC = 0x0EU, //! P25 Termination Link Control - TSBK = 0x0FU //! P25 TSBK + VOICE = 0x0BU, //!< P25 Voice + DATA = 0x0CU, //!< P25 Data + TERM_LC = 0x0EU, //!< P25 Termination Link Control + TSBK = 0x0FU //!< P25 TSBK }; } /** @} */ diff --git a/src/fne/HostFNE.h b/src/fne/HostFNE.h index 4c76c47e..3720036a 100644 --- a/src/fne/HostFNE.h +++ b/src/fne/HostFNE.h @@ -59,8 +59,8 @@ class HOST_SW_API HostFNE { * @brief Virtual Network Packet Data Digital Mode */ enum PacketDataMode { - DMR, //! Digital Mobile Radio - PROJECT25 //! Project 25 + DMR, //!< Digital Mobile Radio + PROJECT25 //!< Project 25 }; /** diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 13223ee1..bce59fac 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -68,13 +68,13 @@ namespace network * @brief DVM states. */ enum DVM_STATE { - STATE_IDLE = 0U, //! Idle + STATE_IDLE = 0U, //!< Idle // DMR - STATE_DMR = 1U, //! Digital Mobile Radio + STATE_DMR = 1U, //!< Digital Mobile Radio // Project 25 - STATE_P25 = 2U, //! Project 25 + STATE_P25 = 2U, //!< Project 25 // NXDN - STATE_NXDN = 3U, //! NXDN + STATE_NXDN = 3U, //!< NXDN }; #define INFLUXDB_ERRSTR_DISABLED_SRC_RID "disabled source RID" @@ -378,7 +378,7 @@ namespace network * @ingroup fne_network */ struct ACLUpdateRequest : thread_t { - uint32_t peerId; //! Peer ID for this request. + uint32_t peerId; //!< Peer ID for this request. }; // --------------------------------------------------------------------------- @@ -390,16 +390,16 @@ namespace network * @ingroup fne_network */ struct NetPacketRequest : thread_t { - uint32_t peerId; //! Peer ID for this request. + uint32_t peerId; //!< Peer ID for this request. - sockaddr_storage address; //! IP Address and Port. - uint32_t addrLen; //! - frame::RTPHeader rtpHeader; //! RTP Header - frame::RTPFNEHeader fneHeader; //! RTP FNE Header - int length = 0U; //! Length of raw data buffer - uint8_t* buffer = nullptr; //! Raw data buffer + sockaddr_storage address; //!< IP Address and Port. + uint32_t addrLen; //!< + frame::RTPHeader rtpHeader; //!< RTP Header + frame::RTPFNEHeader fneHeader; //!< RTP FNE Header + int length = 0U; //!< Length of raw data buffer + uint8_t* buffer = nullptr; //!< Raw data buffer - uint64_t pktRxTime; //! Packet receive time + uint64_t pktRxTime; //!< Packet receive time }; // --------------------------------------------------------------------------- diff --git a/src/fne/network/P25OTARService.h b/src/fne/network/P25OTARService.h index 49063a01..b07cd02d 100644 --- a/src/fne/network/P25OTARService.h +++ b/src/fne/network/P25OTARService.h @@ -35,10 +35,10 @@ namespace network * @ingroup fne_network */ struct OTARPacketRequest : thread_t { - sockaddr_storage address; //! IP Address and Port. - uint32_t addrLen; //! - int length = 0U; //! Length of raw data buffer - uint8_t *buffer; //! Raw data buffer + sockaddr_storage address; //!< IP Address and Port. + uint32_t addrLen; //!< + int length = 0U; //!< Length of raw data buffer + uint8_t *buffer; //!< Raw data buffer }; // --------------------------------------------------------------------------- diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index c4fdf184..34624655 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -37,17 +37,17 @@ namespace network * @ingroup fne_network */ struct PeerPacketRequest : thread_t { - uint32_t peerId; //! Peer ID for this request. - uint32_t streamId; //! Stream ID for this request. + uint32_t peerId; //!< Peer ID for this request. + uint32_t streamId; //!< Stream ID for this request. - frame::RTPHeader rtpHeader; //! RTP Header - frame::RTPFNEHeader fneHeader; //! RTP FNE Header - int length = 0U; //! Length of raw data buffer - uint8_t* buffer = nullptr; //! Raw data buffer + frame::RTPHeader rtpHeader; //!< RTP Header + frame::RTPFNEHeader fneHeader; //!< RTP FNE Header + int length = 0U; //!< Length of raw data buffer + uint8_t* buffer = nullptr; //!< Raw data buffer - network::NET_SUBFUNC::ENUM subFunc; //! Sub-function of the packet + network::NET_SUBFUNC::ENUM subFunc; //!< Sub-function of the packet - uint64_t pktRxTime; //! Packet receive time + uint64_t pktRxTime; //!< Packet receive time }; // --------------------------------------------------------------------------- diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index d10c6337..f8da4732 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -113,16 +113,16 @@ namespace network */ class QueuedDataFrame { public: - p25::data::DataHeader* header; //! Instance of a PDU data header. - uint32_t llId; //! Logical Link ID - uint32_t tgtProtoAddr; //! Target Protocol Address + p25::data::DataHeader* header; //!< Instance of a PDU data header. + uint32_t llId; //!< Logical Link ID + uint32_t tgtProtoAddr; //!< Target Protocol Address - uint8_t* userData; //! Raw data buffer - uint32_t userDataLen; //! Length of raw data buffer + uint8_t* userData; //!< Raw data buffer + uint32_t userDataLen; //!< Length of raw data buffer - uint64_t timestamp; //! Timestamp in milliseconds - uint8_t retryCnt; //! Packet Retry Counter - bool extendRetry; //! Flag indicating whether or not to extend the retry count for this packet. + uint64_t timestamp; //!< Timestamp in milliseconds + uint8_t retryCnt; //!< Packet Retry Counter + bool extendRetry; //!< Flag indicating whether or not to extend the retry count for this packet. }; concurrent::deque m_queuedFrames; diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 04e71a92..88430dee 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -482,10 +482,10 @@ namespace dmr * @brief Short LC Activity Type */ enum SLCO_ACT_TYPE { - NONE, //! None - VOICE, //! Voice - DATA, //! Data - CSBK //! CSBK + NONE, //!< Slot Activity Type - None + VOICE, //!< Slot Activity Type - Voice + DATA, //!< Slot Activity Type - Data + CSBK //!< Slot Activity Type - CSBK }; static defines::FLCO::E m_flco1; diff --git a/src/host/modem/Modem.h b/src/host/modem/Modem.h index fe1e4db9..8b085218 100644 --- a/src/host/modem/Modem.h +++ b/src/host/modem/Modem.h @@ -103,159 +103,159 @@ namespace modem * @brief Modem response types. */ enum RESP_TYPE_DVM { - RTM_OK, //! OK - RTM_TIMEOUT, //! Timeout - RTM_ERROR //! Error + RTM_OK, //!< OK + RTM_TIMEOUT, //!< Timeout + RTM_ERROR //!< Error }; /** * @brief Modem operation states. */ enum DVM_STATE { - STATE_IDLE = 0U, //! Idle + STATE_IDLE = 0U, //!< Idle // DMR - STATE_DMR = 1U, //! DMR + STATE_DMR = 1U, //!< DMR // Project 25 - STATE_P25 = 2U, //! Project 25 + STATE_P25 = 2U, //!< Project 25 // NXDN - STATE_NXDN = 3U, //! NXDN + STATE_NXDN = 3U, //!< NXDN // CW - STATE_CW = 10U, //! Continuous Wave + STATE_CW = 10U, //!< Continuous Wave // Calibration States - STATE_P25_CAL_1K = 92U, //! Project 25 Calibration 1K + STATE_P25_CAL_1K = 92U, //!< Project 25 Calibration 1K - STATE_DMR_DMO_CAL_1K = 93U, //! DMR DMO Calibration 1K - STATE_DMR_CAL_1K = 94U, //! DMR Calibration 1K - STATE_DMR_LF_CAL = 95U, //! DMR Low Frequency Calibration + STATE_DMR_DMO_CAL_1K = 93U, //!< DMR DMO Calibration 1K + STATE_DMR_CAL_1K = 94U, //!< DMR Calibration 1K + STATE_DMR_LF_CAL = 95U, //!< DMR Low Frequency Calibration - STATE_RSSI_CAL = 96U, //! RSSI Calibration + STATE_RSSI_CAL = 96U, //!< RSSI Calibration - STATE_P25_CAL = 97U, //! Project 25 Calibration - STATE_DMR_CAL = 98U, //! DMR Calibration - STATE_NXDN_CAL = 99U //! NXDN Calibration + STATE_P25_CAL = 97U, //!< Project 25 Calibration + STATE_DMR_CAL = 98U, //!< DMR Calibration + STATE_NXDN_CAL = 99U //!< NXDN Calibration }; /** * @brief Modem commands. */ enum DVM_COMMANDS { - CMD_GET_VERSION = 0x00U, //! Get Modem Version - CMD_GET_STATUS = 0x01U, //! Get Modem Status - CMD_SET_CONFIG = 0x02U, //! Set Modem Configuration - CMD_SET_MODE = 0x03U, //! Set Modem Mode - - CMD_SET_SYMLVLADJ = 0x04U, //! Set Symbol Level Adjustments - CMD_SET_RXLEVEL = 0x05U, //! Set Rx Level - CMD_SET_RFPARAMS = 0x06U, //! (Hotspot) Set RF Parameters - - CMD_CAL_DATA = 0x08U, //! Calibration Data - CMD_RSSI_DATA = 0x09U, //! RSSI Data - - CMD_SEND_CWID = 0x0AU, //! Send Continous Wave ID (Morse) - - CMD_SET_BUFFERS = 0x0FU, //! Set FIFO Buffer Lengths - - CMD_DMR_DATA1 = 0x18U, //! DMR Data Slot 1 - CMD_DMR_LOST1 = 0x19U, //! DMR Data Lost Slot 1 - CMD_DMR_DATA2 = 0x1AU, //! DMR Data Slot 2 - CMD_DMR_LOST2 = 0x1BU, //! DMR Data Lost Slot 2 - CMD_DMR_SHORTLC = 0x1CU, //! DMR Short Link Control - CMD_DMR_START = 0x1DU, //! DMR Start Transmit - CMD_DMR_ABORT = 0x1EU, //! DMR Abort - CMD_DMR_CACH_AT_CTRL = 0x1FU, //! DMR Set CACH AT Control - CMD_DMR_CLEAR1 = 0x20U, //! DMR Clear Slot 1 Buffer - CMD_DMR_CLEAR2 = 0x21U, //! DMR Clear Slot 2 Buffer - - CMD_P25_DATA = 0x31U, //! Project 25 Data - CMD_P25_LOST = 0x32U, //! Project 25 Data Lost - CMD_P25_CLEAR = 0x33U, //! Project 25 Clear Buffer - - CMD_NXDN_DATA = 0x41U, //! NXDN Data - CMD_NXDN_LOST = 0x42U, //! NXDN Data Lost - CMD_NXDN_CLEAR = 0x43U, //! NXDN Clear Buffer - - CMD_ACK = 0x70U, //! Command ACK - CMD_NAK = 0x7FU, //! Command NACK - - CMD_FLSH_READ = 0xE0U, //! Read Flash Partition - CMD_FLSH_WRITE = 0xE1U, //! Write Flash Partition - - CMD_RESET_MCU = 0xEAU, //! Soft Reboot MCU - - CMD_DEBUG1 = 0xF1U, //! - CMD_DEBUG2 = 0xF2U, //! - CMD_DEBUG3 = 0xF3U, //! - CMD_DEBUG4 = 0xF4U, //! - CMD_DEBUG5 = 0xF5U, //! - CMD_DEBUG_DUMP = 0xFAU, //! + CMD_GET_VERSION = 0x00U, //!< Get Modem Version + CMD_GET_STATUS = 0x01U, //!< Get Modem Status + CMD_SET_CONFIG = 0x02U, //!< Set Modem Configuration + CMD_SET_MODE = 0x03U, //!< Set Modem Mode + + CMD_SET_SYMLVLADJ = 0x04U, //!< Set Symbol Level Adjustments + CMD_SET_RXLEVEL = 0x05U, //!< Set Rx Level + CMD_SET_RFPARAMS = 0x06U, //!< (Hotspot) Set RF Parameters + + CMD_CAL_DATA = 0x08U, //!< Calibration Data + CMD_RSSI_DATA = 0x09U, //!< RSSI Data + + CMD_SEND_CWID = 0x0AU, //!< Send Continous Wave ID (Morse) + + CMD_SET_BUFFERS = 0x0FU, //!< Set FIFO Buffer Lengths + + CMD_DMR_DATA1 = 0x18U, //!< DMR Data Slot 1 + CMD_DMR_LOST1 = 0x19U, //!< DMR Data Lost Slot 1 + CMD_DMR_DATA2 = 0x1AU, //!< DMR Data Slot 2 + CMD_DMR_LOST2 = 0x1BU, //!< DMR Data Lost Slot 2 + CMD_DMR_SHORTLC = 0x1CU, //!< DMR Short Link Control + CMD_DMR_START = 0x1DU, //!< DMR Start Transmit + CMD_DMR_ABORT = 0x1EU, //!< DMR Abort + CMD_DMR_CACH_AT_CTRL = 0x1FU, //!< DMR Set CACH AT Control + CMD_DMR_CLEAR1 = 0x20U, //!< DMR Clear Slot 1 Buffer + CMD_DMR_CLEAR2 = 0x21U, //!< DMR Clear Slot 2 Buffer + + CMD_P25_DATA = 0x31U, //!< Project 25 Data + CMD_P25_LOST = 0x32U, //!< Project 25 Data Lost + CMD_P25_CLEAR = 0x33U, //!< Project 25 Clear Buffer + + CMD_NXDN_DATA = 0x41U, //!< NXDN Data + CMD_NXDN_LOST = 0x42U, //!< NXDN Data Lost + CMD_NXDN_CLEAR = 0x43U, //!< NXDN Clear Buffer + + CMD_ACK = 0x70U, //!< Command ACK + CMD_NAK = 0x7FU, //!< Command NACK + + CMD_FLSH_READ = 0xE0U, //!< Read Flash Partition + CMD_FLSH_WRITE = 0xE1U, //!< Write Flash Partition + + CMD_RESET_MCU = 0xEAU, //!< Soft Reboot MCU + + CMD_DEBUG1 = 0xF1U, //!< + CMD_DEBUG2 = 0xF2U, //!< + CMD_DEBUG3 = 0xF3U, //!< + CMD_DEBUG4 = 0xF4U, //!< + CMD_DEBUG5 = 0xF5U, //!< + CMD_DEBUG_DUMP = 0xFAU, //!< }; /** * @brief Modem command tags. */ enum CMD_TAGS { - TAG_HEADER = 0x00U, //! Header + TAG_HEADER = 0x00U, //!< Header - TAG_DATA = 0x01U, //! Data + TAG_DATA = 0x01U, //!< Data - TAG_LOST = 0x02U, //! Lost Data - TAG_EOT = 0x03U, //! End of Transmission + TAG_LOST = 0x02U, //!< Lost Data + TAG_EOT = 0x03U, //!< End of Transmission }; /** * @brief Modem response reason codes. */ enum CMD_REASON_CODE { - RSN_OK = 0U, //! OK - RSN_NAK = 1U, //! Negative Acknowledge + RSN_OK = 0U, //!< OK + RSN_NAK = 1U, //!< Negative Acknowledge - RSN_ILLEGAL_LENGTH = 2U, //! Illegal Length - RSN_INVALID_REQUEST = 4U, //! Invalid Request - RSN_RINGBUFF_FULL = 8U, //! Ring Buffer Full + RSN_ILLEGAL_LENGTH = 2U, //!< Illegal Length + RSN_INVALID_REQUEST = 4U, //!< Invalid Request + RSN_RINGBUFF_FULL = 8U, //!< Ring Buffer Full - RSN_INVALID_FDMA_PREAMBLE = 10U, //! Invalid FDMA Preamble Length - RSN_INVALID_MODE = 11U, //! Invalid Mode + RSN_INVALID_FDMA_PREAMBLE = 10U, //!< Invalid FDMA Preamble Length + RSN_INVALID_MODE = 11U, //!< Invalid Mode - RSN_INVALID_DMR_CC = 12U, //! Invalid DMR CC - RSN_INVALID_DMR_SLOT = 13U, //! Invalid DMR Slot - RSN_INVALID_DMR_START = 14U, //! Invaild DMR Start Transmit - RSN_INVALID_DMR_RX_DELAY = 15U, //! Invalid DMR Rx Delay + RSN_INVALID_DMR_CC = 12U, //!< Invalid DMR CC + RSN_INVALID_DMR_SLOT = 13U, //!< Invalid DMR Slot + RSN_INVALID_DMR_START = 14U, //!< Invaild DMR Start Transmit + RSN_INVALID_DMR_RX_DELAY = 15U, //!< Invalid DMR Rx Delay - RSN_INVALID_P25_CORR_COUNT = 16U, //! Invalid P25 Correlation Count + RSN_INVALID_P25_CORR_COUNT = 16U, //!< Invalid P25 Correlation Count - RSN_NO_INTERNAL_FLASH = 20U, //! No Internal Flash - RSN_FAILED_ERASE_FLASH = 21U, //! Failed to erase flash partition - RSN_FAILED_WRITE_FLASH = 22U, //! Failed to write flash partition - RSN_FLASH_WRITE_TOO_BIG = 23U, //! Data to large for flash partition + RSN_NO_INTERNAL_FLASH = 20U, //!< No Internal Flash + RSN_FAILED_ERASE_FLASH = 21U, //!< Failed to erase flash partition + RSN_FAILED_WRITE_FLASH = 22U, //!< Failed to write flash partition + RSN_FLASH_WRITE_TOO_BIG = 23U, //!< Data to large for flash partition - RSN_HS_NO_DUAL_MODE = 32U, //! (Hotspot) No Dual Mode Operation + RSN_HS_NO_DUAL_MODE = 32U, //!< (Hotspot) No Dual Mode Operation - RSN_DMR_DISABLED = 63U, //! DMR Disabled - RSN_P25_DISABLED = 64U, //! Project 25 Disabled - RSN_NXDN_DISABLED = 65U //! NXDN Disabled + RSN_DMR_DISABLED = 63U, //!< DMR Disabled + RSN_P25_DISABLED = 64U, //!< Project 25 Disabled + RSN_NXDN_DISABLED = 65U //!< NXDN Disabled }; /** * @brief Modem response state machine. */ enum RESP_STATE { - RESP_START, //! Start Handling Frame - RESP_LENGTH1, //! Frame Length 1 - RESP_LENGTH2, //! Frame Length 2 - RESP_TYPE, //! Frame Type - RESP_DATA //! Frame Data + RESP_START, //!< Start Handling Frame + RESP_LENGTH1, //!< Frame Length 1 + RESP_LENGTH2, //!< Frame Length 2 + RESP_TYPE, //!< Frame Type + RESP_DATA //!< Frame Data }; /** * @brief Hotspot gain modes. */ enum ADF_GAIN_MODE { - ADF_GAIN_AUTO = 0U, //! Automatic - ADF_GAIN_AUTO_LIN = 1U, //! Automatic (Linear) - ADF_GAIN_LOW = 2U, //! Low - ADF_GAIN_HIGH = 3U //! High + ADF_GAIN_AUTO = 0U, //!< Automatic + ADF_GAIN_AUTO_LIN = 1U, //!< Automatic (Linear) + ADF_GAIN_LOW = 2U, //!< Low + ADF_GAIN_HIGH = 3U //!< High }; const uint8_t DVM_SHORT_FRAME_START = 0xFEU; diff --git a/src/host/modem/ModemV24.h b/src/host/modem/ModemV24.h index 9a251f16..ec705da9 100644 --- a/src/host/modem/ModemV24.h +++ b/src/host/modem/ModemV24.h @@ -41,10 +41,10 @@ namespace modem * @ingroup modem */ enum SERIAL_TX_TYPE { - STT_NO_DATA, //! No Data - STT_NON_IMBE, //! Non-IMBE Data/Signalling Frame - STT_NON_IMBE_NO_JITTER, //! Non-IMBE Data/Signalling Frame with Jitter Disabled - STT_IMBE //! IMBE Voice Frame + STT_NO_DATA, //!< No Data + STT_NON_IMBE, //!< Non-IMBE Data/Signalling Frame + STT_NON_IMBE_NO_JITTER, //!< Non-IMBE Data/Signalling Frame with Jitter Disabled + STT_IMBE //!< IMBE Voice Frame }; /** @} */ diff --git a/src/host/modem/port/specialized/V24UDPPort.h b/src/host/modem/port/specialized/V24UDPPort.h index 8cbea521..6b326b58 100644 --- a/src/host/modem/port/specialized/V24UDPPort.h +++ b/src/host/modem/port/specialized/V24UDPPort.h @@ -48,11 +48,11 @@ namespace modem * @ingroup fne_network */ struct V24PacketRequest : thread_t { - sockaddr_storage address; //! IP Address and Port. - uint32_t addrLen; //! - network::frame::RTPHeader rtpHeader; //! RTP Header - int length = 0U; //! Length of raw data buffer - uint8_t *buffer; //! Raw data buffer + sockaddr_storage address; //!< IP Address and Port. + uint32_t addrLen; //!< + network::frame::RTPHeader rtpHeader; //!< RTP Header + int length = 0U; //!< Length of raw data buffer + uint8_t *buffer; //!< Raw data buffer }; // --------------------------------------------------------------------------- diff --git a/src/vocoder/MBEDecoder.h b/src/vocoder/MBEDecoder.h index 1aef85c6..e55eedef 100644 --- a/src/vocoder/MBEDecoder.h +++ b/src/vocoder/MBEDecoder.h @@ -63,8 +63,8 @@ namespace vocoder * @brief Vocoder Decoding Mode */ enum MBE_DECODER_MODE { - DECODE_DMR_AMBE, //! DMR AMBE - DECODE_88BIT_IMBE //! 88-bit IMBE (P25) + DECODE_DMR_AMBE, //!< DMR AMBE + DECODE_88BIT_IMBE //!< 88-bit IMBE (P25) }; // --------------------------------------------------------------------------- diff --git a/src/vocoder/MBEEncoder.h b/src/vocoder/MBEEncoder.h index c4fbb061..c1612b0e 100644 --- a/src/vocoder/MBEEncoder.h +++ b/src/vocoder/MBEEncoder.h @@ -33,8 +33,8 @@ namespace vocoder * @brief Vocoder Encoding Mode */ enum MBE_ENCODER_MODE { - ENCODE_DMR_AMBE, //! DMR AMBE - ENCODE_88BIT_IMBE, //! 88-bit IMBE (P25) + ENCODE_DMR_AMBE, //!< DMR AMBE + ENCODE_88BIT_IMBE, //!< 88-bit IMBE (P25) }; // --------------------------------------------------------------------------- From 2f9113582d804203988eb47070a0e0757c02713f Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 3 Oct 2025 13:40:21 -0400 Subject: [PATCH 041/200] better document peer ID and rid ACL list files; --- configs/peer_list.example.dat | 19 ++++++++++++++++++- configs/rid_acl.example.dat | 11 +++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/configs/peer_list.example.dat b/configs/peer_list.example.dat index 60cf3299..4b9b2691 100644 --- a/configs/peer_list.example.dat +++ b/configs/peer_list.example.dat @@ -1,7 +1,24 @@ # -# This file sets the valid peer IDs allowed on a FNE. +# Digital Voice Modem - Peer ID Access Control List +# +# This file sets the valid peer IDs allowed on a FNE. This file should always end with an empty line! +# +# * PEER ID [REQUIRED] - Unique ID for a peer. +# Peer IDs are valid numbers between 1 and 999999999. +# * PEER PASSWORD [REQUIRED] - Unique password for this peer to use when authenticating. +# * PEER LINK [OPTIONAL] - Flag indicating whether or not the peer connection is another FNE, and will receive +# full configuration from this FNE. When peer link is set, and the connection is +# another FNE, that FNE will receive all the talkgroups, radio ID lists, and +# peer lists from this FNE. +# * CAN REQUEST KEYS [OPTIONAL] - Flag indicating the peer connection is allowed to request encryption keys. +# If this flag is disabled (0), and the connected peer requests and encryption key +# the encryption key request will be dropped and ignored. +# * CAN ISSUE INHIBIT [OPTIONAL] - Flag indicating the peer connection is capable of transmitting inhibit packets. +# If this flag is disabled (0), and the connected peer issues an inhibit to the network +# this FNE will drop the packet and ignore it. # # Entry Format: "Peer ID,Peer Password,Peer Link (1 = Enabled / 0 = Disabled),Peer Alias (optional),Can Request Keys (1 = Enabled / 0 = Disabled),Can Issue Inhibit (1 = Enabled / 0 = Disabled)" +# Examples: #1234,,0,,1,0, #5678,MYSECUREPASSWORD,0,,0,0, #9876,MYSECUREPASSWORD,1,,0,0, diff --git a/configs/rid_acl.example.dat b/configs/rid_acl.example.dat index 97198cda..d4294133 100644 --- a/configs/rid_acl.example.dat +++ b/configs/rid_acl.example.dat @@ -1,6 +1,13 @@ # -# This file sets the valid Radio IDs allowed on a repeater. +# Digital Voice Modem - Radio ID Access Control List # -# Entry Format: "RID,Enabled (1 = Enabled / 0 = Disabled),Optional Alias,Optional IP Address," +# This file sets the valid Radio IDs allowed on a repeater. This file should always end with an empty line! +# +# * RID [REQUIRED] - Unique Radio ID. +# * ENABLED [REQUIRED] - Flag indicating whether or not this radio ID entry is enabled and valid. +# * ALIAS [OPTIONAL] - Textual string representing an alias for this radio ID entry. +# * IP ADDRESS [OPTIONAL] - IP Address assigned to this radio ID. # +# Entry Format: "RID,Enabled (1 = Enabled / 0 = Disabled),Optional Alias,Optional IP Address," +# Example: #1234,1,RID Alias,IP Address, From 2e1d60cd0c982e9a63842ce46cf89106c2b0db4b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 3 Oct 2025 22:37:55 -0400 Subject: [PATCH 042/200] add backward for stacktrace support on crash; --- CMakeLists.txt | 26 + README.md | 2 + src/CMakeLists.txt | 48 +- src/bridge/BridgeMain.cpp | 2 + src/common/CMakeLists.txt | 1 + src/common/Log.cpp | 23 + src/common/Log.h | 573 +++- src/common/backtrace/backward.h | 4539 +++++++++++++++++++++++++++++++ src/fne/FNEMain.cpp | 2 + src/host/HostMain.cpp | 2 + src/patch/PatchMain.cpp | 2 + 11 files changed, 5176 insertions(+), 44 deletions(-) create mode 100644 src/common/backtrace/backward.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d41d1f28..93490d18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,8 @@ option(CROSS_COMPILE_AARCH64 "Cross-compile for 64-bit ARM" off) option(CROSS_COMPILE_RPI_ARM "Cross-compile for (old RPi) 32-bit ARM" off) option(COMPILE_WIN32 "Compile for Win32" off) +set(ENABLE_LIBDW_SUPPORT ON) + if (COMPILE_WIN32) set(ARCH amd64) set(CMAKE_SYSTEM_PROCESSOR amd64) @@ -133,6 +135,7 @@ if (CROSS_COMPILE_RPI_ARM) # No TUI for this set(ENABLE_TUI_SUPPORT OFF) + set(ENABLE_LIBDW_SUPPORT OFF) message(CHECK_START "Enable TUI support - no; for simplicity RPI_ARM cross-compiling does not support TUI.") endif (CROSS_COMPILE_RPI_ARM) @@ -191,6 +194,7 @@ set(CMAKE_INSTALL_PREFIX "/usr/local") # # Library Inclusions # +# ASIO if (NOT ASIO_INCLUDED) option(WITH_ASIO "Manually specify the location for the ASIO library" off) if (WITH_ASIO) @@ -210,6 +214,7 @@ if (NOT ASIO_INCLUDED) endif (WITH_ASIO) endif (NOT ASIO_INCLUDED) +# WebSocket++ if (NOT DISABLE_WEBSOCKETS) if (NOT WEBSOCKETPP_INCLUDED) message("-- Cloning WebSocket++") @@ -225,6 +230,27 @@ if (NOT DISABLE_WEBSOCKETS) endif (NOT WEBSOCKETPP_INCLUDED) endif (NOT DISABLE_WEBSOCKETS) +# elfutils (libdw-dev) +if (ENABLE_LIBDW_SUPPORT) + find_path(LIBDW_INCLUDE_DIR NAMES elfutils/libdw.h elfutils/libdwfl.h HINTS /usr /usr/local PATH_SUFFIXES include) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Libdw DEFAULT_MSG LIBDW_INCLUDE_DIR) + + if (LIBDW_FOUND) + message("-- libdw (libdw-dev) found, detailed backtrace support enabled") + add_definitions(-DBACKWARD_HAS_DW=1) + set(LIBDW_LIBRARY "dw") + else() + message("-- libdw (libdw-dev) not found, simple backtrace only") + set(LIBDW_INCLUDE_DIR "") + set(LIBDW_LIBRARY "") + endif (LIBDW_FOUND) + + mark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARY) +endif (ENABLE_LIBDW_SUPPORT) + +# FinalCut if (ENABLE_TUI_SUPPORT AND NOT FC_INCLUDED) message("-- Cloning finalcut") include(FetchContent) diff --git a/README.md b/README.md index 5ab8e42c..9e193349 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ The DVM Host software requires the library dependancies below. Generally, the so Alternatively, if you download the ASIO library from the ASIO website and extract it to a location, you can specify the path to the ASIO library using: `-DWITH_ASIO=/path/to/asio`. This method is required when cross-compiling for old Raspberry Pi ARM 32 bit. +If you want detailed stacktrace output on a crash, for compilation ensure `libdw-dev` is also installed. (`apt-get install libdw-dev`). For runtime you will need the `elfutils` package to be installed. (`apt-get install elfutils`). + If cross-compiling ensure you install the appropriate libraries, for example for AARCH64/ARM64: ``` sudo dpkg --add-architecture arm64 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d3e486a4..75cb28d5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,16 +39,16 @@ endif (ENABLE_SETUP_TUI) add_executable(dvmhost ${common_INCLUDE} ${dvmhost_SRC}) if (ENABLE_SETUP_TUI) - target_link_libraries(dvmhost PRIVATE common ${OPENSSL_LIBRARIES} asio::asio finalcut Threads::Threads util) + target_link_libraries(dvmhost PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio finalcut Threads::Threads util) else () if (COMPILE_WIN32) target_sources(dvmhost PRIVATE ${dvmhost_RC}) - target_link_libraries(dvmhost PRIVATE common ${OPENSSL_LIBRARIES} asio::asio Threads::Threads) + target_link_libraries(dvmhost PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio Threads::Threads) else () - target_link_libraries(dvmhost PRIVATE common ${OPENSSL_LIBRARIES} asio::asio Threads::Threads util) + target_link_libraries(dvmhost PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio Threads::Threads util) endif (COMPILE_WIN32) endif (ENABLE_SETUP_TUI) -target_include_directories(dvmhost PRIVATE ${OPENSSL_INCLUDE_DIR} src src/host) +target_include_directories(dvmhost PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} src src/host) set(CPACK_SET_DESTDIR true) set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/local") @@ -80,8 +80,8 @@ add_executable(dvmfne ${common_INCLUDE} ${dvmfne_SRC}) if (COMPILE_WIN32) target_sources(dvmfne PRIVATE ${dvmfne_RC}) endif (COMPILE_WIN32) -target_link_libraries(dvmfne PRIVATE common ${OPENSSL_LIBRARIES} asio::asio Threads::Threads) -target_include_directories(dvmfne PRIVATE ${OPENSSL_INCLUDE_DIR} src src/fne) +target_link_libraries(dvmfne PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio Threads::Threads) +target_include_directories(dvmfne PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} src src/fne) # ## dvmmon @@ -89,8 +89,8 @@ target_include_directories(dvmfne PRIVATE ${OPENSSL_INCLUDE_DIR} src src/fne) if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) include(src/monitor/CMakeLists.txt) add_executable(dvmmon ${common_INCLUDE} ${dvmmon_SRC}) - target_link_libraries(dvmmon PRIVATE common ${OPENSSL_LIBRARIES} asio::asio finalcut Threads::Threads) - target_include_directories(dvmmon PRIVATE ${OPENSSL_INCLUDE_DIR} src src/host src/monitor) + target_link_libraries(dvmmon PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio finalcut Threads::Threads) + target_include_directories(dvmmon PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} src src/host src/monitor) endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) # @@ -99,11 +99,11 @@ endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) include(src/sysview/CMakeLists.txt) add_executable(sysview ${common_INCLUDE} ${sysView_SRC}) - target_link_libraries(sysview PRIVATE common ${OPENSSL_LIBRARIES} asio::asio finalcut Threads::Threads) + target_link_libraries(sysview PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio finalcut Threads::Threads) if (NOT DISABLE_WEBSOCKETS) - target_include_directories(sysview PRIVATE ${OPENSSL_INCLUDE_DIR} ${websocketpp_SOURCE_DIR} src src/host src/sysview) + target_include_directories(sysview PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} ${websocketpp_SOURCE_DIR} src src/host src/sysview) else () - target_include_directories(sysview PRIVATE ${OPENSSL_INCLUDE_DIR} src src/host src/sysview) + target_include_directories(sysview PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} src src/host src/sysview) endif (NOT DISABLE_WEBSOCKETS) endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) @@ -113,8 +113,8 @@ endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) include(src/tged/CMakeLists.txt) add_executable(tged ${common_INCLUDE} ${tged_SRC}) - target_link_libraries(tged PRIVATE common ${OPENSSL_LIBRARIES} asio::asio finalcut Threads::Threads) - target_include_directories(tged PRIVATE ${OPENSSL_INCLUDE_DIR} websocketpp src src/host src/tged) + target_link_libraries(tged PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio finalcut Threads::Threads) + target_include_directories(tged PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} websocketpp src src/host src/tged) endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) # @@ -123,8 +123,8 @@ endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) include(src/peered/CMakeLists.txt) add_executable(peered ${common_INCLUDE} ${peered_SRC}) - target_link_libraries(peered PRIVATE common ${OPENSSL_LIBRARIES} asio::asio finalcut Threads::Threads) - target_include_directories(peered PRIVATE ${OPENSSL_INCLUDE_DIR} websocketpp src src/host src/peered) + target_link_libraries(peered PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio finalcut Threads::Threads) + target_include_directories(peered PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} websocketpp src src/host src/peered) endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) # @@ -135,8 +135,8 @@ add_executable(dvmcmd ${common_INCLUDE} ${dvmcmd_SRC}) if (COMPILE_WIN32) target_sources(dvmcmd PRIVATE ${dvmcmd_RC}) endif (COMPILE_WIN32) -target_link_libraries(dvmcmd PRIVATE common ${OPENSSL_LIBRARIES} asio::asio Threads::Threads) -target_include_directories(dvmcmd PRIVATE ${OPENSSL_INCLUDE_DIR} src src/remote) +target_link_libraries(dvmcmd PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio Threads::Threads) +target_include_directories(dvmcmd PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} src src/remote) # ## dvmbridge @@ -145,15 +145,15 @@ include(src/bridge/CMakeLists.txt) add_executable(dvmbridge ${common_INCLUDE} ${bridge_SRC}) if (COMPILE_WIN32) target_sources(dvmbridge PRIVATE ${bridge_RC}) - target_link_libraries(dvmbridge PRIVATE common vocoder ${OPENSSL_LIBRARIES} asio::asio Threads::Threads) + target_link_libraries(dvmbridge PRIVATE common vocoder ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio Threads::Threads) else () if (ARCH STREQUAL "arm64" OR ARCH STREQUAL "armhf") - target_link_libraries(dvmbridge PRIVATE common vocoder ${OPENSSL_LIBRARIES} dl atomic asio::asio Threads::Threads) + target_link_libraries(dvmbridge PRIVATE common vocoder ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} dl atomic asio::asio Threads::Threads) else () - target_link_libraries(dvmbridge PRIVATE common vocoder ${OPENSSL_LIBRARIES} dl asio::asio Threads::Threads) + target_link_libraries(dvmbridge PRIVATE common vocoder ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} dl asio::asio Threads::Threads) endif (ARCH STREQUAL "arm64" OR ARCH STREQUAL "armhf") endif (COMPILE_WIN32) -target_include_directories(dvmbridge PRIVATE ${OPENSSL_INCLUDE_DIR} src src/bridge) +target_include_directories(dvmbridge PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} src src/bridge) # ## dvmpatch @@ -162,8 +162,8 @@ include(src/patch/CMakeLists.txt) add_executable(dvmpatch ${common_INCLUDE} ${patch_SRC}) if (COMPILE_WIN32) target_sources(dvmpatch PRIVATE ${patch_RC}) - target_link_libraries(dvmpatch PRIVATE common ${OPENSSL_LIBRARIES} asio::asio Threads::Threads) + target_link_libraries(dvmpatch PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio Threads::Threads) else () - target_link_libraries(dvmpatch PRIVATE common ${OPENSSL_LIBRARIES} dl asio::asio Threads::Threads) + target_link_libraries(dvmpatch PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} dl asio::asio Threads::Threads) endif (COMPILE_WIN32) -target_include_directories(dvmpatch PRIVATE ${OPENSSL_INCLUDE_DIR} src src/patch) +target_include_directories(dvmpatch PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} src src/patch) diff --git a/src/bridge/BridgeMain.cpp b/src/bridge/BridgeMain.cpp index 1bfdb2e9..819a504c 100644 --- a/src/bridge/BridgeMain.cpp +++ b/src/bridge/BridgeMain.cpp @@ -281,6 +281,8 @@ int main(int argc, char** argv) } } + backtrace::SignalHandling sh(g_foreground); + ::signal(SIGINT, sigHandler); ::signal(SIGTERM, sigHandler); #if !defined(_WIN32) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index ea4983db..53286d00 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -108,6 +108,7 @@ file(GLOB common_INCLUDE "src/common/network/tcp/*.h" "src/common/network/udp/*.h" "src/common/network/viface/*.h" + "src/common/backtrace/*.h" "src/common/yaml/*.h" "src/common/zlib/*.h" "src/common/*.h" diff --git a/src/common/Log.cpp b/src/common/Log.cpp index 89d4580f..bee01405 100644 --- a/src/common/Log.cpp +++ b/src/common/Log.cpp @@ -56,6 +56,8 @@ static struct tm m_tm; static std::ostream m_outStream { std::cerr.rdbuf() }; +bool backtrace::SignalHandling::m_foreground = false; + // --------------------------------------------------------------------------- // Global Functions // --------------------------------------------------------------------------- @@ -273,3 +275,24 @@ void log_internal::LogInternal(uint32_t level, const std::string& log) exit(1); } } + +/* Internal helper to get the log file path. */ + +std::string log_internal::GetLogFilePath() +{ + return m_filePath; +} + +/* Internal helper to get the log file root name. */ + +std::string log_internal::GetLogFileRoot() +{ + return m_fileRoot; +} + +/* Internal helper to get the log file handle pointer. */ + +FILE* log_internal::GetLogFile() +{ + return m_fpLog; +} diff --git a/src/common/Log.h b/src/common/Log.h index 7450d4e4..120936c9 100644 --- a/src/common/Log.h +++ b/src/common/Log.h @@ -28,6 +28,10 @@ #include #endif // defined(_WIN32) +#if !defined(CATCH2_TEST_COMPILATION) +#include "common/backtrace/backward.h" +#endif + #include #include @@ -128,6 +132,27 @@ */ #define LogFatal(_module, fmt, ...) Log(6U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) +// --------------------------------------------------------------------------- +// Externs +// --------------------------------------------------------------------------- + +/** + * @brief (Global) Display log level. + */ +extern uint32_t g_logDisplayLevel; +/** + * @brief (Global) Flag for displaying timestamps on log entries (does not apply to syslog logging). + */ +extern bool g_disableTimeDisplay; +/** + * @brief (Global) Flag indicating whether or not logging goes to the syslog. + */ +extern bool g_useSyslog; +/** + * @brief (Global) Flag indicating whether or not network logging is disabled. + */ +extern bool g_disableNetworkLog; + namespace log_internal { constexpr static char LOG_LEVELS[] = " DMIWEF"; @@ -180,31 +205,539 @@ namespace log_internal * @param log Fully formatted log message. */ extern HOST_SW_API void LogInternal(uint32_t level, const std::string& log); + + /** + * @brief Internal helper to get the log file path. + * @returns std::string Configured log file path. + */ + extern HOST_SW_API std::string GetLogFilePath(); + /** + * @brief Internal helper to get the log file root name. + * @returns std::string Configured log file root name. + */ + extern HOST_SW_API std::string GetLogFileRoot(); + /** + * @brief Internal helper to get the log file handle pointer. + * @returns FILE* Pointer to the open log file. + */ + extern HOST_SW_API FILE* GetLogFile(); } // namespace log_internal -// --------------------------------------------------------------------------- -// Externs -// --------------------------------------------------------------------------- +namespace backtrace +{ +#if !defined(CATCH2_TEST_COMPILATION) +#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- -/** - * @brief (Global) Display log level. - */ -extern uint32_t g_logDisplayLevel; -/** - * @brief (Global) Flag for displaying timestamps on log entries (does not apply to syslog logging). - */ -extern bool g_disableTimeDisplay; -/** - * @brief (Global) Flag indicating whether or not logging goes to the syslog. - */ -extern bool g_useSyslog; -/** - * @brief (Global) Flag indicating whether or not network logging is disabled. - */ -extern bool g_disableNetworkLog; + /** + * @brief Backward backtrace signal handling class. + * @ingroup logger + */ + class HOST_SW_API SignalHandling { + public: + /** + * @brief Helper to generate a default list of POSIX signals to handle. + * @return std::vector List of POSIX signals. + */ + static std::vector makeDefaultSignals() { + const int posixSignals[] = { + // Signals for which the default action is "Core". + SIGABRT, // Abort signal from abort(3) + SIGBUS, // Bus error (bad memory access) + SIGFPE, // Floating point exception + SIGILL, // Illegal Instruction + SIGIOT, // IOT trap. A synonym for SIGABRT + SIGQUIT, // Quit from keyboard + SIGSEGV, // Invalid memory reference + SIGSYS, // Bad argument to routine (SVr4) + SIGTRAP, // Trace/breakpoint trap + SIGXCPU, // CPU time limit exceeded (4.2BSD) + SIGXFSZ, // File size limit exceeded (4.2BSD) + #if defined(BACKWARD_SYSTEM_DARWIN) + SIGEMT, // emulation instruction executed + #endif + }; + + return std::vector(posixSignals, posixSignals + sizeof posixSignals / sizeof posixSignals[0]); + } + + /** + * @brief Initializes a new instance of the SignalHandling class + * @param foreground Log stacktrace to stderr. + * @param posixSignals List of signals to handle. + */ + SignalHandling(bool foreground, const std::vector& posixSignals = makeDefaultSignals()) : + m_loaded(false) + { + bool success = true; + + m_foreground = foreground; + + const size_t stackSize = 1024 * 1024 * 8; + m_stackContent.reset(static_cast(malloc(stackSize))); + if (m_stackContent) { + stack_t ss; + ss.ss_sp = m_stackContent.get(); + ss.ss_size = stackSize; + ss.ss_flags = 0; + + if (sigaltstack(&ss, nullptr) < 0) { + success = false; + } + } else { + success = false; + } + + for (size_t i = 0; i < posixSignals.size(); ++i) { + struct sigaction action; + memset(&action, 0, sizeof action); + action.sa_flags = static_cast(SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND); + sigfillset(&action.sa_mask); + sigdelset(&action.sa_mask, posixSignals[i]); + #if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdisabled-macro-expansion" + #endif + action.sa_sigaction = &sig_handler; + #if defined(__clang__) + #pragma clang diagnostic pop + #endif + int r = sigaction(posixSignals[i], &action, nullptr); + if (r < 0) + success = false; + } + + m_loaded = success; + } + + /** + * @brief Helper to return whether or not the SignalHandling class is loaded. + * @return bool True if signal handler is loaded, otherwise false. + */ + bool loaded() const { return m_loaded; } + + /** + * @brief Helper to handle a signal. + * @param signo Signal number. + * @param info Signal informational structure. + * @param _ctx Signal user context data. + */ + static void handleSignal(int signo, siginfo_t* info, void* _ctx) + { + ucontext_t *uctx = static_cast(_ctx); + + backward::StackTrace st; + void* errorAddr = nullptr; + #ifdef REG_RIP // x86_64 + errorAddr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); + #elif defined(REG_EIP) // x86_32 + errorAddr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); + #elif defined(__arm__) + errorAddr = reinterpret_cast(uctx->uc_mcontext.arm_pc); + #elif defined(__aarch64__) + #if defined(__APPLE__) + errorAddr = reinterpret_cast(uctx->uc_mcontext->__ss.__pc); + #else + errorAddr = reinterpret_cast(uctx->uc_mcontext.pc); + #endif + #elif defined(__mips__) + errorAddr = reinterpret_cast(reinterpret_cast(&uctx->uc_mcontext)->sc_pc); + #elif defined(__APPLE__) && defined(__POWERPC__) + errorAddr = reinterpret_cast(uctx->uc_mcontext->__ss.__srr0); + #elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ + defined(__POWERPC__) + errorAddr = reinterpret_cast(uctx->uc_mcontext.regs->nip); + #elif defined(__riscv) + errorAddr = reinterpret_cast(uctx->uc_mcontext.__gregs[REG_PC]); + #elif defined(__s390x__) + errorAddr = reinterpret_cast(uctx->uc_mcontext.psw.addr); + #elif defined(__APPLE__) && defined(__x86_64__) + errorAddr = reinterpret_cast(uctx->uc_mcontext->__ss.__rip); + #elif defined(__APPLE__) + errorAddr = reinterpret_cast(uctx->uc_mcontext->__ss.__eip); + #elif defined(__loongarch__) + errorAddr = reinterpret_cast(uctx->uc_mcontext.__pc); + #else + #warning ":/ sorry, ain't know no nothing none not of your architecture!" + #endif + + if (errorAddr) { + st.load_from(errorAddr, 32, reinterpret_cast(uctx), info->si_addr); + } else { + st.load_here(32, reinterpret_cast(uctx), info->si_addr); + } + + backward::Printer p; + p.address = true; + p.snippet = false; + p.color_mode = backward::ColorMode::never; + + log_internal::LogInternal(3U, "UNRECOVERABLE FATAL ERROR!"); + if (m_foreground > 0) { + p.print(st, stderr); + } + + // log stack trace to a file if we're using syslog + if (g_useSyslog) { + std::string filePath = log_internal::GetLogFilePath(); + std::string fileRoot = log_internal::GetLogFileRoot(); + + time_t now; + ::time(&now); + + struct tm* tm = ::localtime(&now); + + char filename[200U]; + ::sprintf(filename, "%s/%s-%04d-%02d-%02d.stacktrace.log", filePath.c_str(), fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + + FILE* stacktraceFp = ::fopen(filename, "a+t"); + ::fprintf(stacktraceFp, "UNRECOVERABLE FATAL ERROR!\r\n"); + p.print(st, stacktraceFp); + ::fflush(stacktraceFp); + ::fclose(stacktraceFp); + } else { + FILE *stacktraceFp = log_internal::GetLogFile(); + p.print(st, stacktraceFp); + ::fflush(stacktraceFp); + ::fclose(stacktraceFp); + } + + (void)info; + } + + private: + backward::details::handle m_stackContent; + bool m_loaded; + static bool m_foreground; + + /** + * @brief Internal helper to handle a signal. + * @param signo Signal number. + * @param info Signal informational structure. + * @param _ctx Signal user context data. + */ + #ifdef __GNUC__ + __attribute__((noreturn)) + #endif + static void sig_handler(int signo, siginfo_t* info, void* _ctx) + { + handleSignal(signo, info, _ctx); + + // try to forward the signal. + raise(info->si_signo); + + // terminate the process immediately. + puts("Abnormal termination."); + _exit(EXIT_FAILURE); + } + }; +#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN +#ifdef BACKWARD_SYSTEM_WINDOWS + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Backward backtrace signal handling class. + * @ingroup logger + */ + class HOST_SW_API SignalHandling { + public: + /** + * @brief Initializes a new instance of the SignalHandling class + * @param foreground Log stacktrace to stderr. (Windows always logs to the foreground, this is ignored.) + * @param posixSignals List of signals to handle. + */ + SignalHandling(bool foreground, const std::vector& = std::vector()) : + m_reporterThread([]() { + /* We handle crashes in a utility thread: + ** backward structures and some Windows functions called here + ** need stack space, which we do not have when we encounter a + ** stack overflow. + ** To support reporting stack traces during a stack overflow, + ** we create a utility thread at startup, which waits until a + ** crash happens or the program exits normally. + */ + { + std::unique_lock lk(mtx()); + cv().wait(lk, [] { return crashed() != CRASH_STATUS::RUNNING; }); + } + + if (crashed() == CRASH_STATUS::CRASHED) { + handleStackTrace(skipRecs()); + } + + { + std::unique_lock lk(mtx()); + crashed() = CRASH_STATUS::ENDING; + } + cv().notify_one(); + }) + { + SetUnhandledExceptionFilter(crashHandler); + + signal(SIGABRT, signalHandler); + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + + std::set_terminate(&terminator); + #ifndef BACKWARD_ATLEAST_CXX17 + std::set_unexpected(&terminator); + #endif + _set_purecall_handler(&terminator); + _set_invalid_parameter_handler(&invalidParameterHandler); + } + /** + * @brief Finalizes a instance of the SignalHandling class + */ + ~SignalHandling() + { + { + std::unique_lock lk(mtx()); + crashed() = CRASH_STATUS::NORMAL_EXIT; + } + + cv().notify_one(); + + m_reporterThread.join(); + } + + /** + * @brief Helper to return whether or not the SignalHandling class is loaded. + * @return bool True if signal handler is loaded, otherwise false. + */ + bool loaded() const { return true; } + + private: + enum class CRASH_STATUS { + RUNNING, + CRASHED, + NORMAL_EXIT, + ENDING + }; + + /** + * @brief + * @return CONTEXT* + */ + static CONTEXT* ctx() + { + static CONTEXT data; + return &data; + } + + /** + * @brief + * @return crash_status& + */ + static CRASH_STATUS& crashed() + { + static CRASH_STATUS data; + return data; + } + + /** + * @brief + * @return std::mutex& + */ + static std::mutex& mtx() + { + static std::mutex data; + return data; + } + + /** + * @brief + * @return std::condition_variable& + */ + static std::condition_variable& cv() + { + static std::condition_variable data; + return data; + } + + /** + * @brief + * @return HANDLE& + */ + static HANDLE& threadHandle() + { + static HANDLE handle; + return handle; + } + + std::thread m_reporterThread; + static bool m_foreground; + + // TODO: how not to hardcode these? + static const constexpr int signalSkipRecs = + #ifdef __clang__ + // With clang, RtlCaptureContext also captures the stack frame of the + // current function Below that, there are 3 internal Windows functions + 4 + #else + // With MSVC cl, RtlCaptureContext misses the stack frame of the current + // function The first entries during StackWalk are the 3 internal Windows + // functions + 3 + #endif + ; + + /** + * @brief + * @return int& + */ + static int& skipRecs() + { + static int data; + return data; + } + + /** + * @brief + */ + static inline void terminator() + { + crashHandler(signalSkipRecs); + abort(); + } + + /** + * @brief + */ + static inline void signalHandler(int) + { + crashHandler(signalSkipRecs); + abort(); + } + + /** + * @brief + * @param int + */ + static inline void __cdecl invalidParameterHandler(const wchar_t *, const wchar_t *, const wchar_t *, + unsigned int, uintptr_t) + { + crashHandler(signalSkipRecs); + abort(); + } + + /** + * @brief + * @param info + * @return NOINLINE + */ + NOINLINE static LONG WINAPI crashHandler(EXCEPTION_POINTERS* info) + { + // the exception info supplies a trace from exactly where the issue was, + // no need to skip records + crashHandler(0, info->ContextRecord); + return EXCEPTION_CONTINUE_SEARCH; + } + + /** + * @brief + * @param skip + * @param ct + * @return NOINLINE + */ + NOINLINE static void crashHandler(int skip, CONTEXT* ct = nullptr) + { + if (ct == nullptr) { + RtlCaptureContext(ctx()); + } else { + memcpy(ctx(), ct, sizeof(CONTEXT)); + } + + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &threadHandle(), 0, FALSE, DUPLICATE_SAME_ACCESS); + + skip_recs() = skip; + + { + std::unique_lock lk(mtx()); + crashed() = CRASH_STATUS::CRASHED; + } + + cv().notify_one(); + + { + std::unique_lock lk(mtx()); + cv().wait(lk, [] { return crashed() != CRASH_STATUS::CRASHED; }); + } + } + + /** + * @brief + * @param skipFrames + */ + static void handleStackTrace(int skipFrames = 0) + { + // printer creates the TraceResolver, which can supply us a machine type + // for stack walking. Without this, StackTrace can only guess using some + // macros. + // StackTrace also requires that the PDBs are already loaded, which is done + // in the constructor of TraceResolver + backward::Printer p; + + backward::StackTrace st; + st.set_machine_type(printer.resolver().machine_type()); + st.set_thread_handle(threadHandle()); + st.load_here(32 + skipFrames, ctx()); + st.skip_n_firsts(skipFrames); + + p.address = true; + p.snippet = false; + p.color_mode = backward::ColorMode::never; + + log_internal::LogInternal(3U, "UNRECOVERABLE FATAL ERROR!"); + p.print(st, std::cerr); + + // log stack trace to a file if we're using syslog + FILE *stacktraceFp = log_internal::GetLogFile(); + ::fprintf(stacktraceFp, "UNRECOVERABLE FATAL ERROR!\r\n"); + p.print(st, stacktraceFp); + ::fflush(stacktraceFp); + ::fclose(stacktraceFp); + } + }; +#endif // BACKWARD_SYSTEM_WINDOWS +#ifdef BACKWARD_SYSTEM_UNKNOWN + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Backward backtrace signal handling class. + * @ingroup logger + */ + class HOST_SW_API SignalHandling { + public: + /** + * @brief Initializes a new instance of the SignalHandling class + * @param foreground Log stacktrace to stderr. + */ + SignalHandling(bool forground, const std::vector& = std::vector()) + { + /* stub */ + } + + /** + * @brief Helper to return whether or not the SignalHandling class is loaded. + * @return bool True if signal handler is loaded, otherwise false. + */ + bool loaded() { return false; } + + private: + static bool m_foreground; + }; +#endif // BACKWARD_SYSTEM_UNKNOWN +#endif // !defined(CATCH2_TEST_COMPILATION) +} // --------------------------------------------------------------------------- -// Global Functions +// Global Function Externs // --------------------------------------------------------------------------- /** diff --git a/src/common/backtrace/backward.h b/src/common/backtrace/backward.h new file mode 100644 index 00000000..914df36f --- /dev/null +++ b/src/common/backtrace/backward.h @@ -0,0 +1,4539 @@ +// SPDX-License-Identifier: MIT +/* + * Digital Voice Modem - Common Library + * MIT Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + */ +/* + * backward.hpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef H_6B9572DA_A64B_49E6_B234_051480991C89 +#define H_6B9572DA_A64B_49E6_B234_051480991C89 + +#ifndef __cplusplus +#error "It's not going to compile without a C++ compiler..." +#endif + +#if defined(BACKWARD_CXX11) +#elif defined(BACKWARD_CXX98) +#else +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) +#define BACKWARD_CXX11 +#define BACKWARD_ATLEAST_CXX11 +#define BACKWARD_ATLEAST_CXX98 +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define BACKWARD_ATLEAST_CXX17 +#endif +#else +#define BACKWARD_CXX98 +#define BACKWARD_ATLEAST_CXX98 +#endif +#endif + +// You can define one of the following (or leave it to the auto-detection): +// +// #define BACKWARD_SYSTEM_LINUX +// - specialization for linux +// +// #define BACKWARD_SYSTEM_DARWIN +// - specialization for Mac OS X 10.5 and later. +// +// #define BACKWARD_SYSTEM_WINDOWS +// - specialization for Windows (Clang 9 and MSVC2017) +// +// #define BACKWARD_SYSTEM_UNKNOWN +// - placebo implementation, does nothing. +// +#if defined(BACKWARD_SYSTEM_LINUX) +#elif defined(BACKWARD_SYSTEM_DARWIN) +#elif defined(BACKWARD_SYSTEM_UNKNOWN) +#elif defined(BACKWARD_SYSTEM_WINDOWS) +#else +#if defined(__linux) || defined(__linux__) +#define BACKWARD_SYSTEM_LINUX +#elif defined(__APPLE__) +#define BACKWARD_SYSTEM_DARWIN +#elif defined(_WIN32) +#define BACKWARD_SYSTEM_WINDOWS +#else +#define BACKWARD_SYSTEM_UNKNOWN +#endif +#endif + +#define NOINLINE __attribute__((noinline)) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BACKWARD_SYSTEM_LINUX) + +// On linux, backtrace can back-trace or "walk" the stack using the following +// libraries: +// +// #define BACKWARD_HAS_UNWIND 1 +// - unwind comes from libgcc, but I saw an equivalent inside clang itself. +// - with unwind, the stacktrace is as accurate as it can possibly be, since +// this is used by the C++ runtime in gcc/clang for stack unwinding on +// exception. +// - normally libgcc is already linked to your program by default. +// +// #define BACKWARD_HAS_LIBUNWIND 1 +// - libunwind provides, in some cases, a more accurate stacktrace as it knows +// to decode signal handler frames and lets us edit the context registers when +// unwinding, allowing stack traces over bad function references. +// +// #define BACKWARD_HAS_BACKTRACE == 1 +// - backtrace seems to be a little bit more portable than libunwind, but on +// linux, it uses unwind anyway, but abstract away a tiny information that is +// sadly really important in order to get perfectly accurate stack traces. +// - backtrace is part of the (e)glib library. +// +// The default is: +// #define BACKWARD_HAS_UNWIND == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_UNWIND == 1 +#elif BACKWARD_HAS_LIBUNWIND == 1 +#elif BACKWARD_HAS_BACKTRACE == 1 +#else +#undef BACKWARD_HAS_UNWIND +#define BACKWARD_HAS_UNWIND 1 +#undef BACKWARD_HAS_LIBUNWIND +#define BACKWARD_HAS_LIBUNWIND 0 +#undef BACKWARD_HAS_BACKTRACE +#define BACKWARD_HAS_BACKTRACE 0 +#endif + +// On linux, backward can extract detailed information about a stack trace +// using one of the following libraries: +// +// #define BACKWARD_HAS_DW 1 +// - libdw gives you the most juicy details out of your stack traces: +// - object filename +// - function name +// - source filename +// - line and column numbers +// - source code snippet (assuming the file is accessible) +// - variable names (if not optimized out) +// - variable values (not supported by backward-cpp) +// - You need to link with the lib "dw": +// - apt-get install libdw-dev +// - g++/clang++ -ldw ... +// +// #define BACKWARD_HAS_BFD 1 +// - With libbfd, you get a fair amount of details: +// - object filename +// - function name +// - source filename +// - line numbers +// - source code snippet (assuming the file is accessible) +// - You need to link with the lib "bfd": +// - apt-get install binutils-dev +// - g++/clang++ -lbfd ... +// +// #define BACKWARD_HAS_DWARF 1 +// - libdwarf gives you the most juicy details out of your stack traces: +// - object filename +// - function name +// - source filename +// - line and column numbers +// - source code snippet (assuming the file is accessible) +// - variable names (if not optimized out) +// - variable values (not supported by backward-cpp) +// - You need to link with the lib "dwarf": +// - apt-get install libdwarf-dev +// - g++/clang++ -ldwarf ... +// +// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +// - backtrace provides minimal details for a stack trace: +// - object filename +// - function name +// - backtrace is part of the (e)glib library. +// +// The default is: +// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_DW == 1 +#elif BACKWARD_HAS_BFD == 1 +#elif BACKWARD_HAS_DWARF == 1 +#elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +#else +#undef BACKWARD_HAS_DW +#define BACKWARD_HAS_DW 0 +#undef BACKWARD_HAS_BFD +#define BACKWARD_HAS_BFD 0 +#undef BACKWARD_HAS_DWARF +#define BACKWARD_HAS_DWARF 0 +#undef BACKWARD_HAS_BACKTRACE_SYMBOL +#define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +#endif + +#include +#include +#ifdef __ANDROID__ +// Old Android API levels define _Unwind_Ptr in both link.h and +// unwind.h Rename the one in link.h as we are not going to be using +// it +#define _Unwind_Ptr _Unwind_Ptr_Custom +#include +#undef _Unwind_Ptr +#else +#include +#endif +#if defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ + defined(__POWERPC__) +// Linux kernel header required for the struct pt_regs definition +// to access the NIP (Next Instruction Pointer) register value +#include +#endif +#include +#include +#include +#include +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#else +#include +#endif + +#if BACKWARD_HAS_BFD == 1 +// NOTE: defining PACKAGE{,_VERSION} is required before including +// bfd.h on some platforms, see also: +// https://sourceware.org/bugzilla/show_bug.cgi?id=14243 +#ifndef PACKAGE +#define PACKAGE +#endif +#ifndef PACKAGE_VERSION +#define PACKAGE_VERSION +#endif +#include +#endif + +#if BACKWARD_HAS_DW == 1 +#include +#include +#include +#endif + +#if BACKWARD_HAS_DWARF == 1 +#include +#include +#include +#include +#include +#endif + +#if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) +// then we shall rely on backtrace +#include +#endif + +#endif // defined(BACKWARD_SYSTEM_LINUX) + +#if defined(BACKWARD_SYSTEM_DARWIN) +// On Darwin, backtrace can back-trace or "walk" the stack using the following +// libraries: +// +// #define BACKWARD_HAS_UNWIND 1 +// - unwind comes from libgcc, but I saw an equivalent inside clang itself. +// - with unwind, the stacktrace is as accurate as it can possibly be, since +// this is used by the C++ runtime in gcc/clang for stack unwinding on +// exception. +// - normally libgcc is already linked to your program by default. +// +// #define BACKWARD_HAS_LIBUNWIND 1 +// - libunwind comes from clang, which implements an API compatible version. +// - libunwind provides, in some cases, a more accurate stacktrace as it knows +// to decode signal handler frames and lets us edit the context registers when +// unwinding, allowing stack traces over bad function references. +// +// #define BACKWARD_HAS_BACKTRACE == 1 +// - backtrace is available by default, though it does not produce as much +// information as another library might. +// +// The default is: +// #define BACKWARD_HAS_UNWIND == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_UNWIND == 1 +#elif BACKWARD_HAS_BACKTRACE == 1 +#elif BACKWARD_HAS_LIBUNWIND == 1 +#else +#undef BACKWARD_HAS_UNWIND +#define BACKWARD_HAS_UNWIND 1 +#undef BACKWARD_HAS_BACKTRACE +#define BACKWARD_HAS_BACKTRACE 0 +#undef BACKWARD_HAS_LIBUNWIND +#define BACKWARD_HAS_LIBUNWIND 0 +#endif + +// On Darwin, backward can extract detailed information about a stack trace +// using one of the following libraries: +// +// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +// - backtrace provides minimal details for a stack trace: +// - object filename +// - function name +// +// The default is: +// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +// +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +#else +#undef BACKWARD_HAS_BACKTRACE_SYMBOL +#define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +#endif + +#include +#include +#include +#include +#include +#include + +#if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) +#include +#endif +#endif // defined(BACKWARD_SYSTEM_DARWIN) + +#if defined(BACKWARD_SYSTEM_WINDOWS) + +#include +#include +#include + +#include + +#ifdef _WIN64 +typedef SSIZE_T ssize_t; +#else +typedef int ssize_t; +#endif + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include + +#include +#include + +#ifndef __clang__ +#undef NOINLINE +#define NOINLINE __declspec(noinline) +#endif + +#ifdef _MSC_VER +#pragma comment(lib, "psapi.lib") +#pragma comment(lib, "dbghelp.lib") +#endif + +// Comment / packing is from stackoverflow: +// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 +// Some versions of imagehlp.dll lack the proper packing directives themselves +// so we need to do it. +#pragma pack(push, before_imagehlp, 8) +#include +#pragma pack(pop, before_imagehlp) + +// TODO maybe these should be undefined somewhere else? +#undef BACKWARD_HAS_UNWIND +#undef BACKWARD_HAS_BACKTRACE +#if BACKWARD_HAS_PDB_SYMBOL == 1 +#else +#undef BACKWARD_HAS_PDB_SYMBOL +#define BACKWARD_HAS_PDB_SYMBOL 1 +#endif + +#endif + +#if BACKWARD_HAS_UNWIND == 1 + +#include +// while gcc's unwind.h defines something like that: +// extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); +// extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *); +// +// clang's unwind.h defines something like this: +// uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context); +// +// Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we +// cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr +// anyway. +// +// Luckily we can play on the fact that the guard macros have a different name: +#ifdef __CLANG_UNWIND_H +// In fact, this function still comes from libgcc (on my different linux boxes, +// clang links against libgcc). +#include +extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context *, int *); +#endif + +#endif // BACKWARD_HAS_UNWIND == 1 + +#if BACKWARD_HAS_LIBUNWIND == 1 +#define UNW_LOCAL_ONLY +#include +#endif // BACKWARD_HAS_LIBUNWIND == 1 + +#ifdef BACKWARD_ATLEAST_CXX11 +#include +#include // for std::swap +namespace backward { +namespace details { +template struct hashtable { + typedef std::unordered_map type; +}; +using std::move; +} // namespace details +} // namespace backward +#else // NOT BACKWARD_ATLEAST_CXX11 +#define nullptr NULL +#define override +#include +namespace backward { +namespace details { +template struct hashtable { + typedef std::map type; +}; +template const T &move(const T &v) { return v; } +template T &move(T &v) { return v; } +} // namespace details +} // namespace backward +#endif // BACKWARD_ATLEAST_CXX11 + +namespace backward { +namespace details { +#if defined(BACKWARD_SYSTEM_WINDOWS) +const char kBackwardPathDelimiter[] = ";"; +#else +const char kBackwardPathDelimiter[] = ":"; +#endif +} // namespace details +} // namespace backward + +namespace backward { + +namespace system_tag { +struct linux_tag; // seems that I cannot call that "linux" because the name +// is already defined... so I am adding _tag everywhere. +struct darwin_tag; +struct windows_tag; +struct unknown_tag; + +#if defined(BACKWARD_SYSTEM_LINUX) +typedef linux_tag current_tag; +#elif defined(BACKWARD_SYSTEM_DARWIN) +typedef darwin_tag current_tag; +#elif defined(BACKWARD_SYSTEM_WINDOWS) +typedef windows_tag current_tag; +#elif defined(BACKWARD_SYSTEM_UNKNOWN) +typedef unknown_tag current_tag; +#else +#error "May I please get my system defines?" +#endif +} // namespace system_tag + +namespace trace_resolver_tag { +#if defined(BACKWARD_SYSTEM_LINUX) +struct libdw; +struct libbfd; +struct libdwarf; +struct backtrace_symbol; + +#if BACKWARD_HAS_DW == 1 +typedef libdw current; +#elif BACKWARD_HAS_BFD == 1 +typedef libbfd current; +#elif BACKWARD_HAS_DWARF == 1 +typedef libdwarf current; +#elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +typedef backtrace_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#elif defined(BACKWARD_SYSTEM_DARWIN) +struct backtrace_symbol; + +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +typedef backtrace_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#elif defined(BACKWARD_SYSTEM_WINDOWS) +struct pdb_symbol; +#if BACKWARD_HAS_PDB_SYMBOL == 1 +typedef pdb_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#endif +} // namespace trace_resolver_tag + +namespace details { + +template struct rm_ptr { typedef T type; }; + +template struct rm_ptr { typedef T type; }; + +template struct rm_ptr { typedef const T type; }; + +template struct deleter { + template void operator()(U &ptr) const { (*F)(ptr); } +}; + +template struct default_delete { + void operator()(T &ptr) const { delete ptr; } +}; + +template > +class handle { + struct dummy; + T _val; + bool _empty; + +#ifdef BACKWARD_ATLEAST_CXX11 + handle(const handle &) = delete; + handle &operator=(const handle &) = delete; +#endif + +public: + ~handle() { + if (!_empty) { + Deleter()(_val); + } + } + + explicit handle() : _val(), _empty(true) {} + explicit handle(T val) : _val(val), _empty(false) { + if (!_val) + _empty = true; + } + +#ifdef BACKWARD_ATLEAST_CXX11 + handle(handle &&from) : _empty(true) { swap(from); } + handle &operator=(handle &&from) { + swap(from); + return *this; + } +#else + explicit handle(const handle &from) : _empty(true) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + } + handle &operator=(const handle &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + return *this; + } +#endif + + void reset(T new_val) { + handle tmp(new_val); + swap(tmp); + } + + void update(T new_val) { + _val = new_val; + _empty = !static_cast(new_val); + } + + operator const dummy *() const { + if (_empty) { + return nullptr; + } + return reinterpret_cast(_val); + } + T get() { return _val; } + T release() { + _empty = true; + return _val; + } + void swap(handle &b) { + using std::swap; + swap(b._val, _val); // can throw, we are safe here. + swap(b._empty, _empty); // should not throw: if you cannot swap two + // bools without throwing... It's a lost cause anyway! + } + + T &operator->() { return _val; } + const T &operator->() const { return _val; } + + typedef typename rm_ptr::type &ref_t; + typedef const typename rm_ptr::type &const_ref_t; + ref_t operator*() { return *_val; } + const_ref_t operator*() const { return *_val; } + ref_t operator[](size_t idx) { return _val[idx]; } + + // Watch out, we've got a badass over here + T *operator&() { + _empty = false; + return &_val; + } +}; + +// Default demangler implementation (do nothing). +template struct demangler_impl { + static std::string demangle(const char *funcname) { return funcname; } +}; + +#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) + +template <> struct demangler_impl { + demangler_impl() : _demangle_buffer_length(0) {} + + std::string demangle(const char *funcname) { + using namespace details; + char *result = abi::__cxa_demangle(funcname, _demangle_buffer.get(), + &_demangle_buffer_length, nullptr); + if (result) { + _demangle_buffer.update(result); + return result; + } + return funcname; + } + +private: + details::handle _demangle_buffer; + size_t _demangle_buffer_length; +}; + +#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN + +struct demangler : public demangler_impl {}; + +// Split a string on the platform's PATH delimiter. Example: if delimiter +// is ":" then: +// "" --> [] +// ":" --> ["",""] +// "::" --> ["","",""] +// "/a/b/c" --> ["/a/b/c"] +// "/a/b/c:/d/e/f" --> ["/a/b/c","/d/e/f"] +// etc. +inline std::vector split_source_prefixes(const std::string &s) { + std::vector out; + size_t last = 0; + size_t next = 0; + size_t delimiter_size = sizeof(kBackwardPathDelimiter) - 1; + while ((next = s.find(kBackwardPathDelimiter, last)) != std::string::npos) { + out.push_back(s.substr(last, next - last)); + last = next + delimiter_size; + } + if (last <= s.length()) { + out.push_back(s.substr(last)); + } + return out; +} + +} // namespace details + +/*************** A TRACE ***************/ + +struct Trace { + void *addr; + size_t idx; + + Trace() : addr(nullptr), idx(0) {} + + explicit Trace(void *_addr, size_t _idx) : addr(_addr), idx(_idx) {} +}; + +struct ResolvedTrace : public Trace { + + struct SourceLoc { + std::string function; + std::string filename; + unsigned line; + unsigned col; + + SourceLoc() : line(0), col(0) {} + + bool operator==(const SourceLoc &b) const { + return function == b.function && filename == b.filename && + line == b.line && col == b.col; + } + + bool operator!=(const SourceLoc &b) const { return !(*this == b); } + }; + + // In which binary object this trace is located. + std::string object_filename; + + // The function in the object that contain the trace. This is not the same + // as source.function which can be an function inlined in object_function. + std::string object_function; + + // The source location of this trace. It is possible for filename to be + // empty and for line/col to be invalid (value 0) if this information + // couldn't be deduced, for example if there is no debug information in the + // binary object. + SourceLoc source; + + // An optionals list of "inliners". All the successive sources location + // from where the source location of the trace (the attribute right above) + // is inlined. It is especially useful when you compiled with optimization. + typedef std::vector source_locs_t; + source_locs_t inliners; + + ResolvedTrace() : Trace() {} + ResolvedTrace(const Trace &mini_trace) : Trace(mini_trace) {} +}; + +/*************** STACK TRACE ***************/ + +// default implemention. +template class StackTraceImpl { +public: + size_t size() const { return 0; } + Trace operator[](size_t) const { return Trace(); } + size_t load_here(size_t = 0) { return 0; } + size_t load_from(void *, size_t = 0, void * = nullptr, void * = nullptr) { + return 0; + } + size_t thread_id() const { return 0; } + void skip_n_firsts(size_t) {} + void *const *begin() const { return nullptr; } +}; + +class StackTraceImplBase { +public: + StackTraceImplBase() + : _thread_id(0), _skip(0), _context(nullptr), _error_addr(nullptr) {} + + size_t thread_id() const { return _thread_id; } + + void skip_n_firsts(size_t n) { _skip = n; } + +protected: + void load_thread_info() { +#ifdef BACKWARD_SYSTEM_LINUX +#ifndef __ANDROID__ + _thread_id = static_cast(syscall(SYS_gettid)); +#else + _thread_id = static_cast(gettid()); +#endif + if (_thread_id == static_cast(getpid())) { + // If the thread is the main one, let's hide that. + // I like to keep little secret sometimes. + _thread_id = 0; + } +#elif defined(BACKWARD_SYSTEM_DARWIN) + _thread_id = reinterpret_cast(pthread_self()); + if (pthread_main_np() == 1) { + // If the thread is the main one, let's hide that. + _thread_id = 0; + } +#endif + } + + void set_context(void *context) { _context = context; } + void *context() const { return _context; } + + void set_error_addr(void *error_addr) { _error_addr = error_addr; } + void *error_addr() const { return _error_addr; } + + size_t skip_n_firsts() const { return _skip; } + +private: + size_t _thread_id; + size_t _skip; + void *_context; + void *_error_addr; +}; + +class StackTraceImplHolder : public StackTraceImplBase { +public: + size_t size() const { + return (_stacktrace.size() >= skip_n_firsts()) + ? _stacktrace.size() - skip_n_firsts() + : 0; + } + Trace operator[](size_t idx) const { + if (idx >= size()) { + return Trace(); + } + return Trace(_stacktrace[idx + skip_n_firsts()], idx); + } + void *const *begin() const { + if (size()) { + return &_stacktrace[skip_n_firsts()]; + } + return nullptr; + } + +protected: + std::vector _stacktrace; +}; + +#if BACKWARD_HAS_UNWIND == 1 + +namespace details { + +template class Unwinder { +public: + size_t operator()(F &f, size_t depth) { + _f = &f; + _index = -1; + _depth = depth; + _Unwind_Backtrace(&this->backtrace_trampoline, this); + if (_index == -1) { + // _Unwind_Backtrace has failed to obtain any backtraces + return 0; + } else { + return static_cast(_index); + } + } + +private: + F *_f; + ssize_t _index; + size_t _depth; + + static _Unwind_Reason_Code backtrace_trampoline(_Unwind_Context *ctx, + void *self) { + return (static_cast(self))->backtrace(ctx); + } + + _Unwind_Reason_Code backtrace(_Unwind_Context *ctx) { + if (_index >= 0 && static_cast(_index) >= _depth) + return _URC_END_OF_STACK; + + int ip_before_instruction = 0; + uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction); + + if (!ip_before_instruction) { + // calculating 0-1 for unsigned, looks like a possible bug to sanitizers, + // so let's do it explicitly: + if (ip == 0) { + ip = std::numeric_limits::max(); // set it to 0xffff... (as + // from casting 0-1) + } else { + ip -= 1; // else just normally decrement it (no overflow/underflow will + // happen) + } + } + + if (_index >= 0) { // ignore first frame. + (*_f)(static_cast(_index), reinterpret_cast(ip)); + } + _index += 1; + return _URC_NO_REASON; + } +}; + +template size_t unwind(F f, size_t depth) { + Unwinder unwinder; + return unwinder(f, depth); +} + +} // namespace details + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_thread_info(); + set_context(context); + set_error_addr(error_addr); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth); + size_t trace_cnt = details::unwind(callback(*this), depth); + _stacktrace.resize(trace_cnt); + skip_n_firsts(0); + return size(); + } + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } + +private: + struct callback { + StackTraceImpl &self; + callback(StackTraceImpl &_self) : self(_self) {} + + void operator()(size_t idx, void *addr) { self._stacktrace[idx] = addr; } + }; +}; + +#elif BACKWARD_HAS_LIBUNWIND == 1 + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + __attribute__((noinline)) size_t load_here(size_t depth = 32, + void *_context = nullptr, + void *_error_addr = nullptr) { + set_context(_context); + set_error_addr(_error_addr); + load_thread_info(); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth + 1); + + int result = 0; + + unw_context_t ctx; + size_t index = 0; + + // Add the tail call. If the Instruction Pointer is the crash address it + // means we got a bad function pointer dereference, so we "unwind" the + // bad pointer manually by using the return address pointed to by the + // Stack Pointer as the Instruction Pointer and letting libunwind do + // the rest + + if (context()) { + ucontext_t *uctx = reinterpret_cast(context()); +#ifdef REG_RIP // x86_64 + if (uctx->uc_mcontext.gregs[REG_RIP] == + reinterpret_cast(error_addr())) { + uctx->uc_mcontext.gregs[REG_RIP] = + *reinterpret_cast(uctx->uc_mcontext.gregs[REG_RSP]); + } + _stacktrace[index] = + reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); + ++index; + ctx = *reinterpret_cast(uctx); +#elif defined(REG_EIP) // x86_32 + if (uctx->uc_mcontext.gregs[REG_EIP] == + reinterpret_cast(error_addr())) { + uctx->uc_mcontext.gregs[REG_EIP] = + *reinterpret_cast(uctx->uc_mcontext.gregs[REG_ESP]); + } + _stacktrace[index] = + reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); + ++index; + ctx = *reinterpret_cast(uctx); +#elif defined(__arm__) // clang libunwind/arm + // libunwind uses its own context type for ARM unwinding. + // Copy the registers from the signal handler's context so we can + // unwind + unw_getcontext(&ctx); + ctx.regs[UNW_ARM_R0] = uctx->uc_mcontext.arm_r0; + ctx.regs[UNW_ARM_R1] = uctx->uc_mcontext.arm_r1; + ctx.regs[UNW_ARM_R2] = uctx->uc_mcontext.arm_r2; + ctx.regs[UNW_ARM_R3] = uctx->uc_mcontext.arm_r3; + ctx.regs[UNW_ARM_R4] = uctx->uc_mcontext.arm_r4; + ctx.regs[UNW_ARM_R5] = uctx->uc_mcontext.arm_r5; + ctx.regs[UNW_ARM_R6] = uctx->uc_mcontext.arm_r6; + ctx.regs[UNW_ARM_R7] = uctx->uc_mcontext.arm_r7; + ctx.regs[UNW_ARM_R8] = uctx->uc_mcontext.arm_r8; + ctx.regs[UNW_ARM_R9] = uctx->uc_mcontext.arm_r9; + ctx.regs[UNW_ARM_R10] = uctx->uc_mcontext.arm_r10; + ctx.regs[UNW_ARM_R11] = uctx->uc_mcontext.arm_fp; + ctx.regs[UNW_ARM_R12] = uctx->uc_mcontext.arm_ip; + ctx.regs[UNW_ARM_R13] = uctx->uc_mcontext.arm_sp; + ctx.regs[UNW_ARM_R14] = uctx->uc_mcontext.arm_lr; + ctx.regs[UNW_ARM_R15] = uctx->uc_mcontext.arm_pc; + + // If we have crashed in the PC use the LR instead, as this was + // a bad function dereference + if (reinterpret_cast(error_addr()) == + uctx->uc_mcontext.arm_pc) { + ctx.regs[UNW_ARM_R15] = + uctx->uc_mcontext.arm_lr - sizeof(unsigned long); + } + _stacktrace[index] = reinterpret_cast(ctx.regs[UNW_ARM_R15]); + ++index; +#elif defined(__aarch64__) // gcc libunwind/arm64 + unw_getcontext(&ctx); + // If the IP is the same as the crash address we have a bad function + // dereference The caller's address is pointed to by the link pointer, so + // we dereference that value and set it to be the next frame's IP. + if (uctx->uc_mcontext.pc == reinterpret_cast<__uint64_t>(error_addr())) { + uctx->uc_mcontext.pc = uctx->uc_mcontext.regs[UNW_TDEP_IP]; + } + + // 29 general purpose registers + for (int i = UNW_AARCH64_X0; i <= UNW_AARCH64_X28; i++) { + ctx.uc_mcontext.regs[i] = uctx->uc_mcontext.regs[i]; + } + ctx.uc_mcontext.sp = uctx->uc_mcontext.sp; + ctx.uc_mcontext.pc = uctx->uc_mcontext.pc; + ctx.uc_mcontext.fault_address = uctx->uc_mcontext.fault_address; + _stacktrace[index] = reinterpret_cast(ctx.uc_mcontext.pc); + ++index; +#elif defined(__APPLE__) && defined(__x86_64__) + unw_getcontext(&ctx); + // OS X's implementation of libunwind uses its own context object + // so we need to convert the passed context to libunwind's format + // (information about the data layout taken from unw_getcontext.s + // in Apple's libunwind source + ctx.data[0] = uctx->uc_mcontext->__ss.__rax; + ctx.data[1] = uctx->uc_mcontext->__ss.__rbx; + ctx.data[2] = uctx->uc_mcontext->__ss.__rcx; + ctx.data[3] = uctx->uc_mcontext->__ss.__rdx; + ctx.data[4] = uctx->uc_mcontext->__ss.__rdi; + ctx.data[5] = uctx->uc_mcontext->__ss.__rsi; + ctx.data[6] = uctx->uc_mcontext->__ss.__rbp; + ctx.data[7] = uctx->uc_mcontext->__ss.__rsp; + ctx.data[8] = uctx->uc_mcontext->__ss.__r8; + ctx.data[9] = uctx->uc_mcontext->__ss.__r9; + ctx.data[10] = uctx->uc_mcontext->__ss.__r10; + ctx.data[11] = uctx->uc_mcontext->__ss.__r11; + ctx.data[12] = uctx->uc_mcontext->__ss.__r12; + ctx.data[13] = uctx->uc_mcontext->__ss.__r13; + ctx.data[14] = uctx->uc_mcontext->__ss.__r14; + ctx.data[15] = uctx->uc_mcontext->__ss.__r15; + ctx.data[16] = uctx->uc_mcontext->__ss.__rip; + + // If the IP is the same as the crash address we have a bad function + // dereference The caller's address is pointed to by %rsp, so we + // dereference that value and set it to be the next frame's IP. + if (uctx->uc_mcontext->__ss.__rip == + reinterpret_cast<__uint64_t>(error_addr())) { + ctx.data[16] = + *reinterpret_cast<__uint64_t *>(uctx->uc_mcontext->__ss.__rsp); + } + _stacktrace[index] = reinterpret_cast(ctx.data[16]); + ++index; +#elif defined(__APPLE__) + unw_getcontext(&ctx) + // TODO: Convert the ucontext_t to libunwind's unw_context_t like + // we do in 64 bits + if (ctx.uc_mcontext->__ss.__eip == + reinterpret_cast(error_addr())) { + ctx.uc_mcontext->__ss.__eip = ctx.uc_mcontext->__ss.__esp; + } + _stacktrace[index] = + reinterpret_cast(ctx.uc_mcontext->__ss.__eip); + ++index; +#endif + } + + unw_cursor_t cursor; + if (context()) { +#if defined(UNW_INIT_SIGNAL_FRAME) + result = unw_init_local2(&cursor, &ctx, UNW_INIT_SIGNAL_FRAME); +#else + result = unw_init_local(&cursor, &ctx); +#endif + } else { + unw_getcontext(&ctx); + ; + result = unw_init_local(&cursor, &ctx); + } + + if (result != 0) + return 1; + + unw_word_t ip = 0; + + while (index <= depth && unw_step(&cursor) > 0) { + result = unw_get_reg(&cursor, UNW_REG_IP, &ip); + if (result == 0) { + _stacktrace[index] = reinterpret_cast(--ip); + ++index; + } + } + --index; + + _stacktrace.resize(index + 1); + skip_n_firsts(0); + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i]); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } +}; + +#elif defined(BACKWARD_HAS_BACKTRACE) + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + set_context(context); + set_error_addr(error_addr); + load_thread_info(); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth + 1); + size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size()); + _stacktrace.resize(trace_cnt); + skip_n_firsts(1); + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i] + 1); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } +}; + +#elif defined(BACKWARD_SYSTEM_WINDOWS) + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + // We have to load the machine type from the image info + // So we first initialize the resolver, and it tells us this info + void set_machine_type(DWORD machine_type) { machine_type_ = machine_type; } + void set_context(CONTEXT *ctx) { ctx_ = ctx; } + void set_thread_handle(HANDLE handle) { thd_ = handle; } + + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + set_context(static_cast(context)); + set_error_addr(error_addr); + CONTEXT localCtx; // used when no context is provided + + if (depth == 0) { + return 0; + } + + if (!ctx_) { + ctx_ = &localCtx; + RtlCaptureContext(ctx_); + } + + if (!thd_) { + thd_ = GetCurrentThread(); + } + + HANDLE process = GetCurrentProcess(); + + STACKFRAME64 s; + memset(&s, 0, sizeof(STACKFRAME64)); + + // TODO: 32 bit context capture + s.AddrStack.Mode = AddrModeFlat; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrPC.Mode = AddrModeFlat; +#if defined(_M_X64) + s.AddrPC.Offset = ctx_->Rip; + s.AddrStack.Offset = ctx_->Rsp; + s.AddrFrame.Offset = ctx_->Rbp; +#elif defined(_M_ARM64) + s.AddrPC.Offset = ctx_->Pc; + s.AddrStack.Offset = ctx_->Sp; + s.AddrFrame.Offset = ctx_->Fp; +#elif defined(_M_ARM) + s.AddrPC.Offset = ctx_->Pc; + s.AddrStack.Offset = ctx_->Sp; + s.AddrFrame.Offset = ctx_->R11; +#else + s.AddrPC.Offset = ctx_->Eip; + s.AddrStack.Offset = ctx_->Esp; + s.AddrFrame.Offset = ctx_->Ebp; +#endif + + if (!machine_type_) { +#if defined(_M_X64) + machine_type_ = IMAGE_FILE_MACHINE_AMD64; +#elif defined(_M_ARM64) + machine_type_ = IMAGE_FILE_MACHINE_ARM64; +#elif defined(_M_ARM) + machine_type_ = IMAGE_FILE_MACHINE_ARMNT; +#else + machine_type_ = IMAGE_FILE_MACHINE_I386; +#endif + } + + for (;;) { + // NOTE: this only works if PDBs are already loaded! + SetLastError(0); + if (!StackWalk64(machine_type_, process, thd_, &s, ctx_, NULL, + SymFunctionTableAccess64, SymGetModuleBase64, NULL)) + break; + + if (s.AddrReturn.Offset == 0) + break; + + _stacktrace.push_back(reinterpret_cast(s.AddrPC.Offset)); + + if (size() >= depth) + break; + } + + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } + +private: + DWORD machine_type_ = 0; + HANDLE thd_ = 0; + CONTEXT *ctx_ = nullptr; +}; + +#endif + +class StackTrace : public StackTraceImpl {}; + +/*************** TRACE RESOLVER ***************/ + +class TraceResolverImplBase { +public: + virtual ~TraceResolverImplBase() {} + + virtual void load_addresses(void *const*addresses, int address_count) { + (void)addresses; + (void)address_count; + } + + template void load_stacktrace(ST &st) { + load_addresses(st.begin(), static_cast(st.size())); + } + + virtual ResolvedTrace resolve(ResolvedTrace t) { return t; } + +protected: + std::string demangle(const char *funcname) { + return _demangler.demangle(funcname); + } + +private: + details::demangler _demangler; +}; + +template class TraceResolverImpl; + +#ifdef BACKWARD_SYSTEM_UNKNOWN + +template <> class TraceResolverImpl + : public TraceResolverImplBase {}; + +#endif + +#ifdef BACKWARD_SYSTEM_LINUX + +class TraceResolverLinuxBase : public TraceResolverImplBase { +public: + TraceResolverLinuxBase() + : argv0_(get_argv0()), exec_path_(read_symlink("/proc/self/exe")) {} + std::string resolve_exec_path(Dl_info &symbol_info) const { + // mutates symbol_info.dli_fname to be filename to open and returns filename + // to display + if (symbol_info.dli_fname == argv0_) { + // dladdr returns argv[0] in dli_fname for symbols contained in + // the main executable, which is not a valid path if the + // executable was found by a search of the PATH environment + // variable; In that case, we actually open /proc/self/exe, which + // is always the actual executable (even if it was deleted/replaced!) + // but display the path that /proc/self/exe links to. + // However, this right away reduces probability of successful symbol + // resolution, because libbfd may try to find *.debug files in the + // same dir, in case symbols are stripped. As a result, it may try + // to find a file /proc/self/.debug, which obviously does + // not exist. /proc/self/exe is a last resort. First load attempt + // should go for the original executable file path. + symbol_info.dli_fname = "/proc/self/exe"; + return exec_path_; + } else { + return symbol_info.dli_fname; + } + } + +private: + std::string argv0_; + std::string exec_path_; + + static std::string get_argv0() { + std::string argv0; + std::ifstream ifs("/proc/self/cmdline"); + std::getline(ifs, argv0, '\0'); + return argv0; + } + + static std::string read_symlink(std::string const &symlink_path) { + std::string path; + path.resize(100); + + while (true) { + ssize_t len = + ::readlink(symlink_path.c_str(), &*path.begin(), path.size()); + if (len < 0) { + return ""; + } + if (static_cast(len) == path.size()) { + path.resize(path.size() * 2); + } else { + path.resize(static_cast(len)); + break; + } + } + + return path; + } +}; + +template class TraceResolverLinuxImpl; + +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + void load_addresses(void *const*addresses, int address_count) override { + if (address_count == 0) { + return; + } + _symbols.reset(backtrace_symbols(addresses, address_count)); + } + + ResolvedTrace resolve(ResolvedTrace trace) override { + char *filename = _symbols[trace.idx]; + char *funcname = filename; + while (*funcname && *funcname != '(') { + funcname += 1; + } + trace.object_filename.assign(filename, + funcname); // ok even if funcname is the ending + // \0 (then we assign entire string) + + if (*funcname) { // if it's not end of string (e.g. from last frame ip==0) + funcname += 1; + char *funcname_end = funcname; + while (*funcname_end && *funcname_end != ')' && *funcname_end != '+') { + funcname_end += 1; + } + *funcname_end = '\0'; + trace.object_function = this->demangle(funcname); + trace.source.function = trace.object_function; // we cannot do better. + } + return trace; + } + +private: + details::handle _symbols; +}; + +#endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1 + +#if BACKWARD_HAS_BFD == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _bfd_loaded(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + Dl_info symbol_info; + + // trace.addr is a virtual address in memory pointing to some code. + // Let's try to find from which loaded object it comes from. + // The loaded object can be yourself btw. + if (!dladdr(trace.addr, &symbol_info)) { + return trace; // dat broken trace... + } + + // Now we get in symbol_info: + // .dli_fname: + // pathname of the shared object that contains the address. + // .dli_fbase: + // where the object is loaded in memory. + // .dli_sname: + // the name of the nearest symbol to trace.addr, we expect a + // function name. + // .dli_saddr: + // the exact address corresponding to .dli_sname. + + if (symbol_info.dli_sname) { + trace.object_function = demangle(symbol_info.dli_sname); + } + + if (!symbol_info.dli_fname) { + return trace; + } + + trace.object_filename = resolve_exec_path(symbol_info); + bfd_fileobject *fobj; + // Before rushing to resolution need to ensure the executable + // file still can be used. For that compare inode numbers of + // what is stored by the executable's file path, and in the + // dli_fname, which not necessarily equals to the executable. + // It can be a shared library, or /proc/self/exe, and in the + // latter case has drawbacks. See the exec path resolution for + // details. In short - the dli object should be used only as + // the last resort. + // If inode numbers are equal, it is known dli_fname and the + // executable file are the same. This is guaranteed by Linux, + // because if the executable file is changed/deleted, it will + // be done in a new inode. The old file will be preserved in + // /proc/self/exe, and may even have inode 0. The latter can + // happen if the inode was actually reused, and the file was + // kept only in the main memory. + // + struct stat obj_stat; + struct stat dli_stat; + if (stat(trace.object_filename.c_str(), &obj_stat) == 0 && + stat(symbol_info.dli_fname, &dli_stat) == 0 && + obj_stat.st_ino == dli_stat.st_ino) { + // The executable file, and the shared object containing the + // address are the same file. Safe to use the original path. + // this is preferable. Libbfd will search for stripped debug + // symbols in the same directory. + fobj = load_object_with_bfd(trace.object_filename); + } else{ + // The original object file was *deleted*! The only hope is + // that the debug symbols are either inside the shared + // object file, or are in the same directory, and this is + // not /proc/self/exe. + fobj = nullptr; + } + if (fobj == nullptr || !fobj->handle) { + fobj = load_object_with_bfd(symbol_info.dli_fname); + if (!fobj->handle) { + return trace; + } + } + + find_sym_result *details_selected; // to be filled. + + // trace.addr is the next instruction to be executed after returning + // from the nested stack frame. In C++ this usually relate to the next + // statement right after the function call that leaded to a new stack + // frame. This is not usually what you want to see when printing out a + // stacktrace... + find_sym_result details_call_site = + find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); + details_selected = &details_call_site; + +#if BACKWARD_HAS_UNWIND == 0 + // ...this is why we also try to resolve the symbol that is right + // before the return address. If we are lucky enough, we will get the + // line of the function that was called. But if the code is optimized, + // we might get something absolutely not related since the compiler + // can reschedule the return address with inline functions and + // tail-call optimization (among other things that I don't even know + // or cannot even dream about with my tiny limited brain). + find_sym_result details_adjusted_call_site = find_symbol_details( + fobj, (void *)(uintptr_t(trace.addr) - 1), symbol_info.dli_fbase); + + // In debug mode, we should always get the right thing(TM). + if (details_call_site.found && details_adjusted_call_site.found) { + // Ok, we assume that details_adjusted_call_site is a better estimation. + details_selected = &details_adjusted_call_site; + trace.addr = (void *)(uintptr_t(trace.addr) - 1); + } + + if (details_selected == &details_call_site && details_call_site.found) { + // we have to re-resolve the symbol in order to reset some + // internal state in BFD... so we can call backtrace_inliners + // thereafter... + details_call_site = + find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); + } +#endif // BACKWARD_HAS_UNWIND + + if (details_selected->found) { + if (details_selected->filename) { + trace.source.filename = details_selected->filename; + } + trace.source.line = details_selected->line; + + if (details_selected->funcname) { + // this time we get the name of the function where the code is + // located, instead of the function were the address is + // located. In short, if the code was inlined, we get the + // function corresponding to the code. Else we already got in + // trace.function. + trace.source.function = demangle(details_selected->funcname); + + if (!symbol_info.dli_sname) { + // for the case dladdr failed to find the symbol name of + // the function, we might as well try to put something + // here. + trace.object_function = trace.source.function; + } + } + + // Maybe the source of the trace got inlined inside the function + // (trace.source.function). Let's see if we can get all the inlined + // calls along the way up to the initial call site. + trace.inliners = backtrace_inliners(fobj, *details_selected); + +#if 0 + if (trace.inliners.size() == 0) { + // Maybe the trace was not inlined... or maybe it was and we + // are lacking the debug information. Let's try to make the + // world better and see if we can get the line number of the + // function (trace.source.function) now. + // + // We will get the location of where the function start (to be + // exact: the first instruction that really start the + // function), not where the name of the function is defined. + // This can be quite far away from the name of the function + // btw. + // + // If the source of the function is the same as the source of + // the trace, we cannot say if the trace was really inlined or + // not. However, if the filename of the source is different + // between the function and the trace... we can declare it as + // an inliner. This is not 100% accurate, but better than + // nothing. + + if (symbol_info.dli_saddr) { + find_sym_result details = find_symbol_details(fobj, + symbol_info.dli_saddr, + symbol_info.dli_fbase); + + if (details.found) { + ResolvedTrace::SourceLoc diy_inliner; + diy_inliner.line = details.line; + if (details.filename) { + diy_inliner.filename = details.filename; + } + if (details.funcname) { + diy_inliner.function = demangle(details.funcname); + } else { + diy_inliner.function = trace.source.function; + } + if (diy_inliner != trace.source) { + trace.inliners.push_back(diy_inliner); + } + } + } + } +#endif + } + + return trace; + } + +private: + bool _bfd_loaded; + + typedef details::handle > + bfd_handle_t; + + typedef details::handle bfd_symtab_t; + + struct bfd_fileobject { + bfd_handle_t handle; + bfd_vma base_addr; + bfd_symtab_t symtab; + bfd_symtab_t dynamic_symtab; + }; + + typedef details::hashtable::type fobj_bfd_map_t; + fobj_bfd_map_t _fobj_bfd_map; + + bfd_fileobject *load_object_with_bfd(const std::string &filename_object) { + using namespace details; + + if (!_bfd_loaded) { + using namespace details; + bfd_init(); + _bfd_loaded = true; + } + + fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object); + if (it != _fobj_bfd_map.end()) { + return &it->second; + } + + // this new object is empty for now. + bfd_fileobject *r = &_fobj_bfd_map[filename_object]; + + // we do the work temporary in this one; + bfd_handle_t bfd_handle; + + int fd = open(filename_object.c_str(), O_RDONLY); + bfd_handle.reset(bfd_fdopenr(filename_object.c_str(), "default", fd)); + if (!bfd_handle) { + close(fd); + return r; + } + + if (!bfd_check_format(bfd_handle.get(), bfd_object)) { + return r; // not an object? You lose. + } + + if ((bfd_get_file_flags(bfd_handle.get()) & HAS_SYMS) == 0) { + return r; // that's what happen when you forget to compile in debug. + } + + ssize_t symtab_storage_size = bfd_get_symtab_upper_bound(bfd_handle.get()); + + ssize_t dyn_symtab_storage_size = + bfd_get_dynamic_symtab_upper_bound(bfd_handle.get()); + + if (symtab_storage_size <= 0 && dyn_symtab_storage_size <= 0) { + return r; // weird, is the file is corrupted? + } + + bfd_symtab_t symtab, dynamic_symtab; + ssize_t symcount = 0, dyn_symcount = 0; + + if (symtab_storage_size > 0) { + symtab.reset(static_cast( + malloc(static_cast(symtab_storage_size)))); + symcount = bfd_canonicalize_symtab(bfd_handle.get(), symtab.get()); + } + + if (dyn_symtab_storage_size > 0) { + dynamic_symtab.reset(static_cast( + malloc(static_cast(dyn_symtab_storage_size)))); + dyn_symcount = bfd_canonicalize_dynamic_symtab(bfd_handle.get(), + dynamic_symtab.get()); + } + + if (symcount <= 0 && dyn_symcount <= 0) { + return r; // damned, that's a stripped file that you got there! + } + + r->handle = move(bfd_handle); + r->symtab = move(symtab); + r->dynamic_symtab = move(dynamic_symtab); + return r; + } + + struct find_sym_result { + bool found; + const char *filename; + const char *funcname; + unsigned int line; + }; + + struct find_sym_context { + TraceResolverLinuxImpl *self; + bfd_fileobject *fobj; + void *addr; + void *base_addr; + find_sym_result result; + }; + + find_sym_result find_symbol_details(bfd_fileobject *fobj, void *addr, + void *base_addr) { + find_sym_context context; + context.self = this; + context.fobj = fobj; + context.addr = addr; + context.base_addr = base_addr; + context.result.found = false; + bfd_map_over_sections(fobj->handle.get(), &find_in_section_trampoline, + static_cast(&context)); + return context.result; + } + + static void find_in_section_trampoline(bfd *, asection *section, void *data) { + find_sym_context *context = static_cast(data); + context->self->find_in_section( + reinterpret_cast(context->addr), + reinterpret_cast(context->base_addr), context->fobj, section, + context->result); + } + + void find_in_section(bfd_vma addr, bfd_vma base_addr, bfd_fileobject *fobj, + asection *section, find_sym_result &result) { + if (result.found) + return; + +#ifdef bfd_get_section_flags + if ((bfd_get_section_flags(fobj->handle.get(), section) & SEC_ALLOC) == 0) +#else + if ((bfd_section_flags(section) & SEC_ALLOC) == 0) +#endif + return; // a debug section is never loaded automatically. + +#ifdef bfd_get_section_vma + bfd_vma sec_addr = bfd_get_section_vma(fobj->handle.get(), section); +#else + bfd_vma sec_addr = bfd_section_vma(section); +#endif +#ifdef bfd_get_section_size + bfd_size_type size = bfd_get_section_size(section); +#else + bfd_size_type size = bfd_section_size(section); +#endif + + // are we in the boundaries of the section? + if (addr < sec_addr || addr >= sec_addr + size) { + addr -= base_addr; // oops, a relocated object, lets try again... + if (addr < sec_addr || addr >= sec_addr + size) { + return; + } + } + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif + if (!result.found && fobj->symtab) { + result.found = bfd_find_nearest_line( + fobj->handle.get(), section, fobj->symtab.get(), addr - sec_addr, + &result.filename, &result.funcname, &result.line); + } + + if (!result.found && fobj->dynamic_symtab) { + result.found = bfd_find_nearest_line( + fobj->handle.get(), section, fobj->dynamic_symtab.get(), + addr - sec_addr, &result.filename, &result.funcname, &result.line); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + } + + ResolvedTrace::source_locs_t + backtrace_inliners(bfd_fileobject *fobj, find_sym_result previous_result) { + // This function can be called ONLY after a SUCCESSFUL call to + // find_symbol_details. The state is global to the bfd_handle. + ResolvedTrace::source_locs_t results; + while (previous_result.found) { + find_sym_result result; + result.found = bfd_find_inliner_info(fobj->handle.get(), &result.filename, + &result.funcname, &result.line); + + if (result + .found) /* and not ( + cstrings_eq(previous_result.filename, + result.filename) and + cstrings_eq(previous_result.funcname, result.funcname) + and result.line == previous_result.line + )) */ + { + ResolvedTrace::SourceLoc src_loc; + src_loc.line = result.line; + if (result.filename) { + src_loc.filename = result.filename; + } + if (result.funcname) { + src_loc.function = demangle(result.funcname); + } + results.push_back(src_loc); + } + previous_result = result; + } + return results; + } + + bool cstrings_eq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + return strcmp(a, b) == 0; + } +}; +#endif // BACKWARD_HAS_BFD == 1 + +#if BACKWARD_HAS_DW == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _dwfl_handle_initialized(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + using namespace details; + + Dwarf_Addr trace_addr = reinterpret_cast(trace.addr); + + if (!_dwfl_handle_initialized) { + // initialize dwfl... + _dwfl_cb.reset(new Dwfl_Callbacks); + _dwfl_cb->find_elf = &dwfl_linux_proc_find_elf; + _dwfl_cb->find_debuginfo = &dwfl_standard_find_debuginfo; + _dwfl_cb->debuginfo_path = 0; + + _dwfl_handle.reset(dwfl_begin(_dwfl_cb.get())); + _dwfl_handle_initialized = true; + + if (!_dwfl_handle) { + return trace; + } + + // ...from the current process. + dwfl_report_begin(_dwfl_handle.get()); + int r = dwfl_linux_proc_report(_dwfl_handle.get(), getpid()); + dwfl_report_end(_dwfl_handle.get(), NULL, NULL); + if (r < 0) { + return trace; + } + } + + if (!_dwfl_handle) { + return trace; + } + + // find the module (binary object) that contains the trace's address. + // This is not using any debug information, but the addresses ranges of + // all the currently loaded binary object. + Dwfl_Module *mod = dwfl_addrmodule(_dwfl_handle.get(), trace_addr); + if (mod) { + // now that we found it, lets get the name of it, this will be the + // full path to the running binary or one of the loaded library. + const char *module_name = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0); + if (module_name) { + trace.object_filename = module_name; + } + // We also look after the name of the symbol, equal or before this + // address. This is found by walking the symtab. We should get the + // symbol corresponding to the function (mangled) containing the + // address. If the code corresponding to the address was inlined, + // this is the name of the out-most inliner function. + const char *sym_name = dwfl_module_addrname(mod, trace_addr); + if (sym_name) { + trace.object_function = demangle(sym_name); + } + } + + // now let's get serious, and find out the source location (file and + // line number) of the address. + + // This function will look in .debug_aranges for the address and map it + // to the location of the compilation unit DIE in .debug_info and + // return it. + Dwarf_Addr mod_bias = 0; + Dwarf_Die *cudie = dwfl_module_addrdie(mod, trace_addr, &mod_bias); + +#if 1 + if (!cudie) { + // Sadly clang does not generate the section .debug_aranges, thus + // dwfl_module_addrdie will fail early. Clang doesn't either set + // the lowpc/highpc/range info for every compilation unit. + // + // So in order to save the world: + // for every compilation unit, we will iterate over every single + // DIEs. Normally functions should have a lowpc/highpc/range, which + // we will use to infer the compilation unit. + + // note that this is probably badly inefficient. + while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias))) { + Dwarf_Die die_mem; + Dwarf_Die *fundie = + find_fundie_by_pc(cudie, trace_addr - mod_bias, &die_mem); + if (fundie) { + break; + } + } + } +#endif + +//#define BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE +#ifdef BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE + if (!cudie) { + // If it's still not enough, lets dive deeper in the shit, and try + // to save the world again: for every compilation unit, we will + // load the corresponding .debug_line section, and see if we can + // find our address in it. + + Dwarf_Addr cfi_bias; + Dwarf_CFI *cfi_cache = dwfl_module_eh_cfi(mod, &cfi_bias); + + Dwarf_Addr bias; + while ((cudie = dwfl_module_nextcu(mod, cudie, &bias))) { + if (dwarf_getsrc_die(cudie, trace_addr - bias)) { + + // ...but if we get a match, it might be a false positive + // because our (address - bias) might as well be valid in a + // different compilation unit. So we throw our last card on + // the table and lookup for the address into the .eh_frame + // section. + + handle frame; + dwarf_cfi_addrframe(cfi_cache, trace_addr - cfi_bias, &frame); + if (frame) { + break; + } + } + } + } +#endif + + if (!cudie) { + return trace; // this time we lost the game :/ + } + + // Now that we have a compilation unit DIE, this function will be able + // to load the corresponding section in .debug_line (if not already + // loaded) and hopefully find the source location mapped to our + // address. + Dwarf_Line *srcloc = dwarf_getsrc_die(cudie, trace_addr - mod_bias); + + if (srcloc) { + const char *srcfile = dwarf_linesrc(srcloc, 0, 0); + if (srcfile) { + trace.source.filename = srcfile; + } + int line = 0, col = 0; + dwarf_lineno(srcloc, &line); + dwarf_linecol(srcloc, &col); + trace.source.line = static_cast(line); + trace.source.col = static_cast(col); + } + + deep_first_search_by_pc(cudie, trace_addr - mod_bias, + inliners_search_cb(trace)); + if (trace.source.function.size() == 0) { + // fallback. + trace.source.function = trace.object_function; + } + + return trace; + } + +private: + typedef details::handle > + dwfl_handle_t; + details::handle > + _dwfl_cb; + dwfl_handle_t _dwfl_handle; + bool _dwfl_handle_initialized; + + // defined here because in C++98, template function cannot take locally + // defined types... grrr. + struct inliners_search_cb { + void operator()(Dwarf_Die *die) { + switch (dwarf_tag(die)) { + const char *name; + case DW_TAG_subprogram: + if ((name = dwarf_diename(die))) { + trace.source.function = name; + } + break; + + case DW_TAG_inlined_subroutine: + ResolvedTrace::SourceLoc sloc; + Dwarf_Attribute attr_mem; + + if ((name = dwarf_diename(die))) { + sloc.function = name; + } + if ((name = die_call_file(die))) { + sloc.filename = name; + } + + Dwarf_Word line = 0, col = 0; + dwarf_formudata(dwarf_attr(die, DW_AT_call_line, &attr_mem), &line); + dwarf_formudata(dwarf_attr(die, DW_AT_call_column, &attr_mem), &col); + sloc.line = static_cast(line); + sloc.col = static_cast(col); + + trace.inliners.push_back(sloc); + break; + }; + } + ResolvedTrace &trace; + inliners_search_cb(ResolvedTrace &t) : trace(t) {} + }; + + static bool die_has_pc(Dwarf_Die *die, Dwarf_Addr pc) { + Dwarf_Addr low, high; + + // continuous range + if (dwarf_hasattr(die, DW_AT_low_pc) && dwarf_hasattr(die, DW_AT_high_pc)) { + if (dwarf_lowpc(die, &low) != 0) { + return false; + } + if (dwarf_highpc(die, &high) != 0) { + Dwarf_Attribute attr_mem; + Dwarf_Attribute *attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem); + Dwarf_Word value; + if (dwarf_formudata(attr, &value) != 0) { + return false; + } + high = low + value; + } + return pc >= low && pc < high; + } + + // non-continuous range. + Dwarf_Addr base; + ptrdiff_t offset = 0; + while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) { + if (pc >= low && pc < high) { + return true; + } + } + return false; + } + + static Dwarf_Die *find_fundie_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, + Dwarf_Die *result) { + if (dwarf_child(parent_die, result) != 0) { + return 0; + } + + Dwarf_Die *die = result; + do { + switch (dwarf_tag(die)) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + if (die_has_pc(die, pc)) { + return result; + } + }; + bool declaration = false; + Dwarf_Attribute attr_mem; + dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), + &declaration); + if (!declaration) { + // let's be curious and look deeper in the tree, + // function are not necessarily at the first level, but + // might be nested inside a namespace, structure etc. + Dwarf_Die die_mem; + Dwarf_Die *indie = find_fundie_by_pc(die, pc, &die_mem); + if (indie) { + *result = die_mem; + return result; + } + } + } while (dwarf_siblingof(die, result) == 0); + return 0; + } + + template + static bool deep_first_search_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, + CB cb) { + Dwarf_Die die_mem; + if (dwarf_child(parent_die, &die_mem) != 0) { + return false; + } + + bool branch_has_pc = false; + Dwarf_Die *die = &die_mem; + do { + bool declaration = false; + Dwarf_Attribute attr_mem; + dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), + &declaration); + if (!declaration) { + // let's be curious and look deeper in the tree, function are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + branch_has_pc = deep_first_search_by_pc(die, pc, cb); + } + if (!branch_has_pc) { + branch_has_pc = die_has_pc(die, pc); + } + if (branch_has_pc) { + cb(die); + } + } while (dwarf_siblingof(die, &die_mem) == 0); + return branch_has_pc; + } + + static const char *die_call_file(Dwarf_Die *die) { + Dwarf_Attribute attr_mem; + Dwarf_Word file_idx = 0; + + dwarf_formudata(dwarf_attr(die, DW_AT_call_file, &attr_mem), &file_idx); + + if (file_idx == 0) { + return 0; + } + + Dwarf_Die die_mem; + Dwarf_Die *cudie = dwarf_diecu(die, &die_mem, 0, 0); + if (!cudie) { + return 0; + } + + Dwarf_Files *files = 0; + size_t nfiles; + dwarf_getsrcfiles(cudie, &files, &nfiles); + if (!files) { + return 0; + } + + return dwarf_filesrc(files, file_idx, 0, 0); + } +}; +#endif // BACKWARD_HAS_DW == 1 + +#if BACKWARD_HAS_DWARF == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _dwarf_loaded(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + // trace.addr is a virtual address in memory pointing to some code. + // Let's try to find from which loaded object it comes from. + // The loaded object can be yourself btw. + + Dl_info symbol_info; + int dladdr_result = 0; +#if defined(__GLIBC__) + link_map *link_map; + // We request the link map so we can get information about offsets + dladdr_result = + dladdr1(trace.addr, &symbol_info, reinterpret_cast(&link_map), + RTLD_DL_LINKMAP); +#else + // Android doesn't have dladdr1. Don't use the linker map. + dladdr_result = dladdr(trace.addr, &symbol_info); +#endif + if (!dladdr_result) { + return trace; // dat broken trace... + } + + // Now we get in symbol_info: + // .dli_fname: + // pathname of the shared object that contains the address. + // .dli_fbase: + // where the object is loaded in memory. + // .dli_sname: + // the name of the nearest symbol to trace.addr, we expect a + // function name. + // .dli_saddr: + // the exact address corresponding to .dli_sname. + // + // And in link_map: + // .l_addr: + // difference between the address in the ELF file and the address + // in memory + // l_name: + // absolute pathname where the object was found + + if (symbol_info.dli_sname) { + trace.object_function = demangle(symbol_info.dli_sname); + } + + if (!symbol_info.dli_fname) { + return trace; + } + + trace.object_filename = resolve_exec_path(symbol_info); + dwarf_fileobject &fobj = load_object_with_dwarf(symbol_info.dli_fname); + if (!fobj.dwarf_handle) { + return trace; // sad, we couldn't load the object :( + } + +#if defined(__GLIBC__) + // Convert the address to a module relative one by looking at + // the module's loading address in the link map + Dwarf_Addr address = reinterpret_cast(trace.addr) - + reinterpret_cast(link_map->l_addr); +#else + Dwarf_Addr address = reinterpret_cast(trace.addr); +#endif + + if (trace.object_function.empty()) { + symbol_cache_t::iterator it = fobj.symbol_cache.lower_bound(address); + + if (it != fobj.symbol_cache.end()) { + if (it->first != address) { + if (it != fobj.symbol_cache.begin()) { + --it; + } + } + trace.object_function = demangle(it->second.c_str()); + } + } + + // Get the Compilation Unit DIE for the address + Dwarf_Die die = find_die(fobj, address); + + if (!die) { + return trace; // this time we lost the game :/ + } + + // libdwarf doesn't give us direct access to its objects, it always + // allocates a copy for the caller. We keep that copy alive in a cache + // and we deallocate it later when it's no longer required. + die_cache_entry &die_object = get_die_cache(fobj, die); + if (die_object.isEmpty()) + return trace; // We have no line section for this DIE + + die_linemap_t::iterator it = die_object.line_section.lower_bound(address); + + if (it != die_object.line_section.end()) { + if (it->first != address) { + if (it == die_object.line_section.begin()) { + // If we are on the first item of the line section + // but the address does not match it means that + // the address is below the range of the DIE. Give up. + return trace; + } else { + --it; + } + } + } else { + return trace; // We didn't find the address. + } + + // Get the Dwarf_Line that the address points to and call libdwarf + // to get source file, line and column info. + Dwarf_Line line = die_object.line_buffer[it->second]; + Dwarf_Error error = DW_DLE_NE; + + char *filename; + if (dwarf_linesrc(line, &filename, &error) == DW_DLV_OK) { + trace.source.filename = std::string(filename); + dwarf_dealloc(fobj.dwarf_handle.get(), filename, DW_DLA_STRING); + } + + Dwarf_Unsigned number = 0; + if (dwarf_lineno(line, &number, &error) == DW_DLV_OK) { + trace.source.line = number; + } else { + trace.source.line = 0; + } + + if (dwarf_lineoff_b(line, &number, &error) == DW_DLV_OK) { + trace.source.col = number; + } else { + trace.source.col = 0; + } + + std::vector namespace_stack; + deep_first_search_by_pc(fobj, die, address, namespace_stack, + inliners_search_cb(trace, fobj, die)); + + dwarf_dealloc(fobj.dwarf_handle.get(), die, DW_DLA_DIE); + + return trace; + } + +public: + static int close_dwarf(Dwarf_Debug dwarf) { + return dwarf_finish(dwarf, NULL); + } + +private: + bool _dwarf_loaded; + + typedef details::handle > + dwarf_file_t; + + typedef details::handle > + dwarf_elf_t; + + typedef details::handle > + dwarf_handle_t; + + typedef std::map die_linemap_t; + + typedef std::map die_specmap_t; + + struct die_cache_entry { + die_specmap_t spec_section; + die_linemap_t line_section; + Dwarf_Line *line_buffer; + Dwarf_Signed line_count; + Dwarf_Line_Context line_context; + + inline bool isEmpty() { + return line_buffer == NULL || line_count == 0 || line_context == NULL || + line_section.empty(); + } + + die_cache_entry() : line_buffer(0), line_count(0), line_context(0) {} + + ~die_cache_entry() { + if (line_context) { + dwarf_srclines_dealloc_b(line_context); + } + } + }; + + typedef std::map die_cache_t; + + typedef std::map symbol_cache_t; + + struct dwarf_fileobject { + dwarf_file_t file_handle; + dwarf_elf_t elf_handle; + dwarf_handle_t dwarf_handle; + symbol_cache_t symbol_cache; + + // Die cache + die_cache_t die_cache; + die_cache_entry *current_cu; + }; + + typedef details::hashtable::type + fobj_dwarf_map_t; + fobj_dwarf_map_t _fobj_dwarf_map; + + static bool cstrings_eq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + return strcmp(a, b) == 0; + } + + dwarf_fileobject &load_object_with_dwarf(const std::string &filename_object) { + + if (!_dwarf_loaded) { + // Set the ELF library operating version + // If that fails there's nothing we can do + _dwarf_loaded = elf_version(EV_CURRENT) != EV_NONE; + } + + fobj_dwarf_map_t::iterator it = _fobj_dwarf_map.find(filename_object); + if (it != _fobj_dwarf_map.end()) { + return it->second; + } + + // this new object is empty for now + dwarf_fileobject &r = _fobj_dwarf_map[filename_object]; + + dwarf_file_t file_handle; + file_handle.reset(open(filename_object.c_str(), O_RDONLY)); + if (file_handle.get() < 0) { + return r; + } + + // Try to get an ELF handle. We need to read the ELF sections + // because we want to see if there is a .gnu_debuglink section + // that points to a split debug file + dwarf_elf_t elf_handle; + elf_handle.reset(elf_begin(file_handle.get(), ELF_C_READ, NULL)); + if (!elf_handle) { + return r; + } + + const char *e_ident = elf_getident(elf_handle.get(), 0); + if (!e_ident) { + return r; + } + + // Get the number of sections + // We use the new APIs as elf_getshnum is deprecated + size_t shdrnum = 0; + if (elf_getshdrnum(elf_handle.get(), &shdrnum) == -1) { + return r; + } + + // Get the index to the string section + size_t shdrstrndx = 0; + if (elf_getshdrstrndx(elf_handle.get(), &shdrstrndx) == -1) { + return r; + } + + std::string debuglink; + // Iterate through the ELF sections to try to get a gnu_debuglink + // note and also to cache the symbol table. + // We go the preprocessor way to avoid having to create templated + // classes or using gelf (which might throw a compiler error if 64 bit + // is not supported +#define ELF_GET_DATA(ARCH) \ + Elf_Scn *elf_section = 0; \ + Elf_Data *elf_data = 0; \ + Elf##ARCH##_Shdr *section_header = 0; \ + Elf_Scn *symbol_section = 0; \ + size_t symbol_count = 0; \ + size_t symbol_strings = 0; \ + Elf##ARCH##_Sym *symbol = 0; \ + const char *section_name = 0; \ + \ + while ((elf_section = elf_nextscn(elf_handle.get(), elf_section)) != NULL) { \ + section_header = elf##ARCH##_getshdr(elf_section); \ + if (section_header == NULL) { \ + return r; \ + } \ + \ + if ((section_name = elf_strptr(elf_handle.get(), shdrstrndx, \ + section_header->sh_name)) == NULL) { \ + return r; \ + } \ + \ + if (cstrings_eq(section_name, ".gnu_debuglink")) { \ + elf_data = elf_getdata(elf_section, NULL); \ + if (elf_data && elf_data->d_size > 0) { \ + debuglink = \ + std::string(reinterpret_cast(elf_data->d_buf)); \ + } \ + } \ + \ + switch (section_header->sh_type) { \ + case SHT_SYMTAB: \ + symbol_section = elf_section; \ + symbol_count = section_header->sh_size / section_header->sh_entsize; \ + symbol_strings = section_header->sh_link; \ + break; \ + \ + /* We use .dynsyms as a last resort, we prefer .symtab */ \ + case SHT_DYNSYM: \ + if (!symbol_section) { \ + symbol_section = elf_section; \ + symbol_count = section_header->sh_size / section_header->sh_entsize; \ + symbol_strings = section_header->sh_link; \ + } \ + break; \ + } \ + } \ + \ + if (symbol_section && symbol_count && symbol_strings) { \ + elf_data = elf_getdata(symbol_section, NULL); \ + symbol = reinterpret_cast(elf_data->d_buf); \ + for (size_t i = 0; i < symbol_count; ++i) { \ + int type = ELF##ARCH##_ST_TYPE(symbol->st_info); \ + if (type == STT_FUNC && symbol->st_value > 0) { \ + r.symbol_cache[symbol->st_value] = std::string( \ + elf_strptr(elf_handle.get(), symbol_strings, symbol->st_name)); \ + } \ + ++symbol; \ + } \ + } + + if (e_ident[EI_CLASS] == ELFCLASS32) { + ELF_GET_DATA(32) + } else if (e_ident[EI_CLASS] == ELFCLASS64) { + // libelf might have been built without 64 bit support +#if __LIBELF64 + ELF_GET_DATA(64) +#endif + } + + if (!debuglink.empty()) { + // We have a debuglink section! Open an elf instance on that + // file instead. If we can't open the file, then return + // the elf handle we had already opened. + dwarf_file_t debuglink_file; + debuglink_file.reset(open(debuglink.c_str(), O_RDONLY)); + if (debuglink_file.get() > 0) { + dwarf_elf_t debuglink_elf; + debuglink_elf.reset(elf_begin(debuglink_file.get(), ELF_C_READ, NULL)); + + // If we have a valid elf handle, return the new elf handle + // and file handle and discard the original ones + if (debuglink_elf) { + elf_handle = move(debuglink_elf); + file_handle = move(debuglink_file); + } + } + } + + // Ok, we have a valid ELF handle, let's try to get debug symbols + Dwarf_Debug dwarf_debug; + Dwarf_Error error = DW_DLE_NE; + dwarf_handle_t dwarf_handle; + + int dwarf_result = dwarf_elf_init(elf_handle.get(), DW_DLC_READ, NULL, NULL, + &dwarf_debug, &error); + + // We don't do any special handling for DW_DLV_NO_ENTRY specially. + // If we get an error, or the file doesn't have debug information + // we just return. + if (dwarf_result != DW_DLV_OK) { + return r; + } + + dwarf_handle.reset(dwarf_debug); + + r.file_handle = move(file_handle); + r.elf_handle = move(elf_handle); + r.dwarf_handle = move(dwarf_handle); + + return r; + } + + die_cache_entry &get_die_cache(dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Error error = DW_DLE_NE; + + // Get the die offset, we use it as the cache key + Dwarf_Off die_offset; + if (dwarf_dieoffset(die, &die_offset, &error) != DW_DLV_OK) { + die_offset = 0; + } + + die_cache_t::iterator it = fobj.die_cache.find(die_offset); + + if (it != fobj.die_cache.end()) { + fobj.current_cu = &it->second; + return it->second; + } + + die_cache_entry &de = fobj.die_cache[die_offset]; + fobj.current_cu = &de; + + Dwarf_Addr line_addr; + Dwarf_Small table_count; + + // The addresses in the line section are not fully sorted (they might + // be sorted by block of code belonging to the same file), which makes + // it necessary to do so before searching is possible. + // + // As libdwarf allocates a copy of everything, let's get the contents + // of the line section and keep it around. We also create a map of + // program counter to line table indices so we can search by address + // and get the line buffer index. + // + // To make things more difficult, the same address can span more than + // one line, so we need to keep the index pointing to the first line + // by using insert instead of the map's [ operator. + + // Get the line context for the DIE + if (dwarf_srclines_b(die, 0, &table_count, &de.line_context, &error) == + DW_DLV_OK) { + // Get the source lines for this line context, to be deallocated + // later + if (dwarf_srclines_from_linecontext(de.line_context, &de.line_buffer, + &de.line_count, + &error) == DW_DLV_OK) { + + // Add all the addresses to our map + for (int i = 0; i < de.line_count; i++) { + if (dwarf_lineaddr(de.line_buffer[i], &line_addr, &error) != + DW_DLV_OK) { + line_addr = 0; + } + de.line_section.insert(std::pair(line_addr, i)); + } + } + } + + // For each CU, cache the function DIEs that contain the + // DW_AT_specification attribute. When building with -g3 the function + // DIEs are separated in declaration and specification, with the + // declaration containing only the name and parameters and the + // specification the low/high pc and other compiler attributes. + // + // We cache those specifications so we don't skip over the declarations, + // because they have no pc, and we can do namespace resolution for + // DWARF function names. + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Die current_die = 0; + if (dwarf_child(die, ¤t_die, &error) == DW_DLV_OK) { + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + if (tag_value == DW_TAG_subprogram || + tag_value == DW_TAG_inlined_subroutine) { + + Dwarf_Bool has_attr = 0; + if (dwarf_hasattr(current_die, DW_AT_specification, &has_attr, + &error) == DW_DLV_OK) { + if (has_attr) { + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_specification, &attr_mem, + &error) == DW_DLV_OK) { + Dwarf_Off spec_offset = 0; + if (dwarf_formref(attr_mem, &spec_offset, &error) == + DW_DLV_OK) { + Dwarf_Off spec_die_offset; + if (dwarf_dieoffset(current_die, &spec_die_offset, &error) == + DW_DLV_OK) { + de.spec_section[spec_offset] = spec_die_offset; + } + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + } + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + break; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + } + return de; + } + + static Dwarf_Die get_referenced_die(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Half attr, bool global) { + Dwarf_Error error = DW_DLE_NE; + Dwarf_Attribute attr_mem; + + Dwarf_Die found_die = NULL; + if (dwarf_attr(die, attr, &attr_mem, &error) == DW_DLV_OK) { + Dwarf_Off offset; + int result = 0; + if (global) { + result = dwarf_global_formref(attr_mem, &offset, &error); + } else { + result = dwarf_formref(attr_mem, &offset, &error); + } + + if (result == DW_DLV_OK) { + if (dwarf_offdie(dwarf, offset, &found_die, &error) != DW_DLV_OK) { + found_die = NULL; + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + return found_die; + } + + static std::string get_referenced_die_name(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Half attr, bool global) { + Dwarf_Error error = DW_DLE_NE; + std::string value; + + Dwarf_Die found_die = get_referenced_die(dwarf, die, attr, global); + + if (found_die) { + char *name; + if (dwarf_diename(found_die, &name, &error) == DW_DLV_OK) { + if (name) { + value = std::string(name); + } + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } + dwarf_dealloc(dwarf, found_die, DW_DLA_DIE); + } + + return value; + } + + // Returns a spec DIE linked to the passed one. The caller should + // deallocate the DIE + static Dwarf_Die get_spec_die(dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Off die_offset; + if (fobj.current_cu && + dwarf_die_CU_offset(die, &die_offset, &error) == DW_DLV_OK) { + die_specmap_t::iterator it = + fobj.current_cu->spec_section.find(die_offset); + + // If we have a DIE that completes the current one, check if + // that one has the pc we are looking for + if (it != fobj.current_cu->spec_section.end()) { + Dwarf_Die spec_die = 0; + if (dwarf_offdie(dwarf, it->second, &spec_die, &error) == DW_DLV_OK) { + return spec_die; + } + } + } + + // Maybe we have an abstract origin DIE with the function information? + return get_referenced_die(fobj.dwarf_handle.get(), die, + DW_AT_abstract_origin, true); + } + + static bool die_has_pc(dwarf_fileobject &fobj, Dwarf_Die die, Dwarf_Addr pc) { + Dwarf_Addr low_pc = 0, high_pc = 0; + Dwarf_Half high_pc_form = 0; + Dwarf_Form_Class return_class; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + bool has_lowpc = false; + bool has_highpc = false; + bool has_ranges = false; + + if (dwarf_lowpc(die, &low_pc, &error) == DW_DLV_OK) { + // If we have a low_pc check if there is a high pc. + // If we don't have a high pc this might mean we have a base + // address for the ranges list or just an address. + has_lowpc = true; + + if (dwarf_highpc_b(die, &high_pc, &high_pc_form, &return_class, &error) == + DW_DLV_OK) { + // We do have a high pc. In DWARF 4+ this is an offset from the + // low pc, but in earlier versions it's an absolute address. + + has_highpc = true; + // In DWARF 2/3 this would be a DW_FORM_CLASS_ADDRESS + if (return_class == DW_FORM_CLASS_CONSTANT) { + high_pc = low_pc + high_pc; + } + + // We have low and high pc, check if our address + // is in that range + return pc >= low_pc && pc < high_pc; + } + } else { + // Reset the low_pc, in case dwarf_lowpc failing set it to some + // undefined value. + low_pc = 0; + } + + // Check if DW_AT_ranges is present and search for the PC in the + // returned ranges list. We always add the low_pc, as it not set it will + // be 0, in case we had a DW_AT_low_pc and DW_AT_ranges pair + bool result = false; + + Dwarf_Attribute attr; + if (dwarf_attr(die, DW_AT_ranges, &attr, &error) == DW_DLV_OK) { + + Dwarf_Off offset; + if (dwarf_global_formref(attr, &offset, &error) == DW_DLV_OK) { + Dwarf_Ranges *ranges; + Dwarf_Signed ranges_count = 0; + Dwarf_Unsigned byte_count = 0; + + if (dwarf_get_ranges_a(dwarf, offset, die, &ranges, &ranges_count, + &byte_count, &error) == DW_DLV_OK) { + has_ranges = ranges_count != 0; + for (int i = 0; i < ranges_count; i++) { + if (ranges[i].dwr_addr1 != 0 && + pc >= ranges[i].dwr_addr1 + low_pc && + pc < ranges[i].dwr_addr2 + low_pc) { + result = true; + break; + } + } + dwarf_ranges_dealloc(dwarf, ranges, ranges_count); + } + } + } + + // Last attempt. We might have a single address set as low_pc. + if (!result && low_pc != 0 && pc == low_pc) { + result = true; + } + + // If we don't have lowpc, highpc and ranges maybe this DIE is a + // declaration that relies on a DW_AT_specification DIE that happens + // later. Use the specification cache we filled when we loaded this CU. + if (!result && (!has_lowpc && !has_highpc && !has_ranges)) { + Dwarf_Die spec_die = get_spec_die(fobj, die); + if (spec_die) { + result = die_has_pc(fobj, spec_die, pc); + dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); + } + } + + return result; + } + + static void get_type(Dwarf_Debug dwarf, Dwarf_Die die, std::string &type) { + Dwarf_Error error = DW_DLE_NE; + + Dwarf_Die child = 0; + if (dwarf_child(die, &child, &error) == DW_DLV_OK) { + get_type(dwarf, child, type); + } + + if (child) { + type.insert(0, "::"); + dwarf_dealloc(dwarf, child, DW_DLA_DIE); + } + + char *name; + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + type.insert(0, std::string(name)); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + type.insert(0, ""); + } + } + + static std::string get_type_by_signature(Dwarf_Debug dwarf, Dwarf_Die die) { + Dwarf_Error error = DW_DLE_NE; + + Dwarf_Sig8 signature; + Dwarf_Bool has_attr = 0; + if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == DW_DLV_OK) { + if (has_attr) { + Dwarf_Attribute attr_mem; + if (dwarf_attr(die, DW_AT_signature, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formsig8(attr_mem, &signature, &error) != DW_DLV_OK) { + return std::string(""); + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + } + + Dwarf_Unsigned next_cu_header; + Dwarf_Sig8 tu_signature; + std::string result; + bool found = false; + + while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, &tu_signature, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + + if (strncmp(signature.signature, tu_signature.signature, 8) == 0) { + Dwarf_Die type_cu_die = 0; + if (dwarf_siblingof_b(dwarf, 0, 0, &type_cu_die, &error) == DW_DLV_OK) { + Dwarf_Die child_die = 0; + if (dwarf_child(type_cu_die, &child_die, &error) == DW_DLV_OK) { + get_type(dwarf, child_die, result); + found = !result.empty(); + dwarf_dealloc(dwarf, child_die, DW_DLA_DIE); + } + dwarf_dealloc(dwarf, type_cu_die, DW_DLA_DIE); + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Unfortunately, libdwarf's + // next_cu_header API keeps its own iterator per Dwarf_Debug + // that can't be reset. We need to keep fetching elements until + // the end. + } + } else { + // If we couldn't resolve the type just print out the signature + std::ostringstream string_stream; + string_stream << "<0x" << std::hex << std::setfill('0'); + for (int i = 0; i < 8; ++i) { + string_stream << std::setw(2) << std::hex + << (int)(unsigned char)(signature.signature[i]); + } + string_stream << ">"; + result = string_stream.str(); + } + return result; + } + + struct type_context_t { + bool is_const; + bool is_typedef; + bool has_type; + bool has_name; + std::string text; + + type_context_t() + : is_const(false), is_typedef(false), has_type(false), has_name(false) { + } + }; + + // Types are resolved from right to left: we get the variable name first + // and then all specifiers (like const or pointer) in a chain of DW_AT_type + // DIEs. Call this function recursively until we get a complete type + // string. + static void set_parameter_string(dwarf_fileobject &fobj, Dwarf_Die die, + type_context_t &context) { + char *name; + Dwarf_Error error = DW_DLE_NE; + + // typedefs contain also the base type, so we skip it and only + // print the typedef name + if (!context.is_typedef) { + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + if (!context.text.empty()) { + context.text.insert(0, " "); + } + context.text.insert(0, std::string(name)); + dwarf_dealloc(fobj.dwarf_handle.get(), name, DW_DLA_STRING); + } + } else { + context.is_typedef = false; + context.has_type = true; + if (context.is_const) { + context.text.insert(0, "const "); + context.is_const = false; + } + } + + bool next_type_is_const = false; + bool is_keyword = true; + + Dwarf_Half tag = 0; + Dwarf_Bool has_attr = 0; + if (dwarf_tag(die, &tag, &error) == DW_DLV_OK) { + switch (tag) { + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + case DW_TAG_enumeration_type: + context.has_type = true; + if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == + DW_DLV_OK) { + // If we have a signature it means the type is defined + // in .debug_types, so we need to load the DIE pointed + // at by the signature and resolve it + if (has_attr) { + std::string type = + get_type_by_signature(fobj.dwarf_handle.get(), die); + if (context.is_const) + type.insert(0, "const "); + + if (!context.text.empty()) + context.text.insert(0, " "); + context.text.insert(0, type); + } + + // Treat enums like typedefs, and skip printing its + // base type + context.is_typedef = (tag == DW_TAG_enumeration_type); + } + break; + case DW_TAG_const_type: + next_type_is_const = true; + break; + case DW_TAG_pointer_type: + context.text.insert(0, "*"); + break; + case DW_TAG_reference_type: + context.text.insert(0, "&"); + break; + case DW_TAG_restrict_type: + context.text.insert(0, "restrict "); + break; + case DW_TAG_rvalue_reference_type: + context.text.insert(0, "&&"); + break; + case DW_TAG_volatile_type: + context.text.insert(0, "volatile "); + break; + case DW_TAG_typedef: + // Propagate the const-ness to the next type + // as typedefs are linked to its base type + next_type_is_const = context.is_const; + context.is_typedef = true; + context.has_type = true; + break; + case DW_TAG_base_type: + context.has_type = true; + break; + case DW_TAG_formal_parameter: + context.has_name = true; + break; + default: + is_keyword = false; + break; + } + } + + if (!is_keyword && context.is_const) { + context.text.insert(0, "const "); + } + + context.is_const = next_type_is_const; + + Dwarf_Die ref = + get_referenced_die(fobj.dwarf_handle.get(), die, DW_AT_type, true); + if (ref) { + set_parameter_string(fobj, ref, context); + dwarf_dealloc(fobj.dwarf_handle.get(), ref, DW_DLA_DIE); + } + + if (!context.has_type && context.has_name) { + context.text.insert(0, "void "); + context.has_type = true; + } + } + + // Resolve the function return type and parameters + static void set_function_parameters(std::string &function_name, + std::vector &ns, + dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Die current_die = 0; + std::string parameters; + bool has_spec = true; + // Check if we have a spec DIE. If we do we use it as it contains + // more information, like parameter names. + Dwarf_Die spec_die = get_spec_die(fobj, die); + if (!spec_die) { + has_spec = false; + spec_die = die; + } + + std::vector::const_iterator it = ns.begin(); + std::string ns_name; + for (it = ns.begin(); it < ns.end(); ++it) { + ns_name.append(*it).append("::"); + } + + if (!ns_name.empty()) { + function_name.insert(0, ns_name); + } + + // See if we have a function return type. It can be either on the + // current die or in its spec one (usually true for inlined functions) + std::string return_type = + get_referenced_die_name(dwarf, die, DW_AT_type, true); + if (return_type.empty()) { + return_type = get_referenced_die_name(dwarf, spec_die, DW_AT_type, true); + } + if (!return_type.empty()) { + return_type.append(" "); + function_name.insert(0, return_type); + } + + if (dwarf_child(spec_die, ¤t_die, &error) == DW_DLV_OK) { + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + if (tag_value == DW_TAG_formal_parameter) { + // Ignore artificial (ie, compiler generated) parameters + bool is_artificial = false; + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_artificial, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + is_artificial = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!is_artificial) { + type_context_t context; + set_parameter_string(fobj, current_die, context); + + if (parameters.empty()) { + parameters.append("("); + } else { + parameters.append(", "); + } + parameters.append(context.text); + } + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + break; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + } + if (parameters.empty()) + parameters = "("; + parameters.append(")"); + + // If we got a spec DIE we need to deallocate it + if (has_spec) + dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); + + function_name.append(parameters); + } + + // defined here because in C++98, template function cannot take locally + // defined types... grrr. + struct inliners_search_cb { + void operator()(Dwarf_Die die, std::vector &ns) { + Dwarf_Error error = DW_DLE_NE; + Dwarf_Half tag_value; + Dwarf_Attribute attr_mem; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + + dwarf_tag(die, &tag_value, &error); + + switch (tag_value) { + char *name; + case DW_TAG_subprogram: + if (!trace.source.function.empty()) + break; + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + trace.source.function = std::string(name); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + // We don't have a function name in this DIE. + // Check if there is a referenced non-defining + // declaration. + trace.source.function = + get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); + if (trace.source.function.empty()) { + trace.source.function = + get_referenced_die_name(dwarf, die, DW_AT_specification, true); + } + } + + // Append the function parameters, if available + set_function_parameters(trace.source.function, ns, fobj, die); + + // If the object function name is empty, it's possible that + // there is no dynamic symbol table (maybe the executable + // was stripped or not built with -rdynamic). See if we have + // a DWARF linkage name to use instead. We try both + // linkage_name and MIPS_linkage_name because the MIPS tag + // was the unofficial one until it was adopted in DWARF4. + // Old gcc versions generate MIPS_linkage_name + if (trace.object_function.empty()) { + details::demangler demangler; + + if (dwarf_attr(die, DW_AT_linkage_name, &attr_mem, &error) != + DW_DLV_OK) { + if (dwarf_attr(die, DW_AT_MIPS_linkage_name, &attr_mem, &error) != + DW_DLV_OK) { + break; + } + } + + char *linkage; + if (dwarf_formstring(attr_mem, &linkage, &error) == DW_DLV_OK) { + trace.object_function = demangler.demangle(linkage); + dwarf_dealloc(dwarf, linkage, DW_DLA_STRING); + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + break; + + case DW_TAG_inlined_subroutine: + ResolvedTrace::SourceLoc sloc; + + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + sloc.function = std::string(name); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + // We don't have a name for this inlined DIE, it could + // be that there is an abstract origin instead. + // Get the DW_AT_abstract_origin value, which is a + // reference to the source DIE and try to get its name + sloc.function = + get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); + } + + set_function_parameters(sloc.function, ns, fobj, die); + + std::string file = die_call_file(dwarf, die, cu_die); + if (!file.empty()) + sloc.filename = file; + + Dwarf_Unsigned number = 0; + if (dwarf_attr(die, DW_AT_call_line, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { + sloc.line = number; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (dwarf_attr(die, DW_AT_call_column, &attr_mem, &error) == + DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { + sloc.col = number; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + trace.inliners.push_back(sloc); + break; + }; + } + ResolvedTrace &trace; + dwarf_fileobject &fobj; + Dwarf_Die cu_die; + inliners_search_cb(ResolvedTrace &t, dwarf_fileobject &f, Dwarf_Die c) + : trace(t), fobj(f), cu_die(c) {} + }; + + static Dwarf_Die find_fundie_by_pc(dwarf_fileobject &fobj, + Dwarf_Die parent_die, Dwarf_Addr pc, + Dwarf_Die result) { + Dwarf_Die current_die = 0; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + + if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { + return NULL; + } + + for (;;) { + Dwarf_Die sibling_die = 0; + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + switch (tag_value) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + if (die_has_pc(fobj, current_die, pc)) { + return current_die; + } + }; + bool declaration = false; + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + declaration = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!declaration) { + // let's be curious and look deeper in the tree, functions are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + Dwarf_Die die_mem = 0; + Dwarf_Die indie = find_fundie_by_pc(fobj, current_die, pc, die_mem); + if (indie) { + result = die_mem; + return result; + } + } + + int res = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (res == DW_DLV_ERROR) { + return NULL; + } else if (res == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != parent_die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + return NULL; + } + + template + static bool deep_first_search_by_pc(dwarf_fileobject &fobj, + Dwarf_Die parent_die, Dwarf_Addr pc, + std::vector &ns, CB cb) { + Dwarf_Die current_die = 0; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + + if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { + return false; + } + + bool branch_has_pc = false; + bool has_namespace = false; + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag; + if (dwarf_tag(current_die, &tag, &error) == DW_DLV_OK) { + if (tag == DW_TAG_namespace || tag == DW_TAG_class_type) { + char *ns_name = NULL; + if (dwarf_diename(current_die, &ns_name, &error) == DW_DLV_OK) { + if (ns_name) { + ns.push_back(std::string(ns_name)); + } else { + ns.push_back(""); + } + dwarf_dealloc(dwarf, ns_name, DW_DLA_STRING); + } else { + ns.push_back(""); + } + has_namespace = true; + } + } + + bool declaration = false; + Dwarf_Attribute attr_mem; + if (tag != DW_TAG_class_type && + dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + declaration = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!declaration) { + // let's be curious and look deeper in the tree, function are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + branch_has_pc = deep_first_search_by_pc(fobj, current_die, pc, ns, cb); + } + + if (!branch_has_pc) { + branch_has_pc = die_has_pc(fobj, current_die, pc); + } + + if (branch_has_pc) { + cb(current_die, ns); + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + return false; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != parent_die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + if (has_namespace) { + has_namespace = false; + ns.pop_back(); + } + current_die = sibling_die; + } + + if (has_namespace) { + ns.pop_back(); + } + return branch_has_pc; + } + + static std::string die_call_file(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Die cu_die) { + Dwarf_Attribute attr_mem; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Unsigned file_index; + + std::string file; + + if (dwarf_attr(die, DW_AT_call_file, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &file_index, &error) != DW_DLV_OK) { + file_index = 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + + if (file_index == 0) { + return file; + } + + char **srcfiles = 0; + Dwarf_Signed file_count = 0; + if (dwarf_srcfiles(cu_die, &srcfiles, &file_count, &error) == DW_DLV_OK) { + if (file_count > 0 && file_index <= static_cast(file_count)) { + file = std::string(srcfiles[file_index - 1]); + } + + // Deallocate all strings! + for (int i = 0; i < file_count; ++i) { + dwarf_dealloc(dwarf, srcfiles[i], DW_DLA_STRING); + } + dwarf_dealloc(dwarf, srcfiles, DW_DLA_LIST); + } + } + return file; + } + + Dwarf_Die find_die(dwarf_fileobject &fobj, Dwarf_Addr addr) { + // Let's get to work! First see if we have a debug_aranges section so + // we can speed up the search + + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Arange *aranges; + Dwarf_Signed arange_count; + + Dwarf_Die returnDie; + bool found = false; + if (dwarf_get_aranges(dwarf, &aranges, &arange_count, &error) != + DW_DLV_OK) { + aranges = NULL; + } + + if (aranges) { + // We have aranges. Get the one where our address is. + Dwarf_Arange arange; + if (dwarf_get_arange(aranges, arange_count, addr, &arange, &error) == + DW_DLV_OK) { + + // We found our address. Get the compilation-unit DIE offset + // represented by the given address range. + Dwarf_Off cu_die_offset; + if (dwarf_get_cu_die_offset(arange, &cu_die_offset, &error) == + DW_DLV_OK) { + // Get the DIE at the offset returned by the aranges search. + // We set is_info to 1 to specify that the offset is from + // the .debug_info section (and not .debug_types) + int dwarf_result = + dwarf_offdie_b(dwarf, cu_die_offset, 1, &returnDie, &error); + + found = dwarf_result == DW_DLV_OK; + } + dwarf_dealloc(dwarf, arange, DW_DLA_ARANGE); + } + } + + if (found) + return returnDie; // The caller is responsible for freeing the die + + // The search for aranges failed. Try to find our address by scanning + // all compilation units. + Dwarf_Unsigned next_cu_header; + Dwarf_Half tag = 0; + returnDie = 0; + + while (!found && + dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + + if (returnDie) + dwarf_dealloc(dwarf, returnDie, DW_DLA_DIE); + + if (dwarf_siblingof(dwarf, 0, &returnDie, &error) == DW_DLV_OK) { + if ((dwarf_tag(returnDie, &tag, &error) == DW_DLV_OK) && + tag == DW_TAG_compile_unit) { + if (die_has_pc(fobj, returnDie, addr)) { + found = true; + } + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Libdwarf's next_cu_header API + // keeps its own iterator per Dwarf_Debug that can't be reset. + // We need to keep fetching elements until the end. + } + } + + if (found) + return returnDie; + + // We couldn't find any compilation units with ranges or a high/low pc. + // Try again by looking at all DIEs in all compilation units. + Dwarf_Die cudie; + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + if (dwarf_siblingof(dwarf, 0, &cudie, &error) == DW_DLV_OK) { + Dwarf_Die die_mem = 0; + Dwarf_Die resultDie = find_fundie_by_pc(fobj, cudie, addr, die_mem); + + if (resultDie) { + found = true; + break; + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Libdwarf's next_cu_header API + // keeps its own iterator per Dwarf_Debug that can't be reset. + // We need to keep fetching elements until the end. + } + } + + if (found) + return cudie; + + // We failed. + return NULL; + } +}; +#endif // BACKWARD_HAS_DWARF == 1 + +template <> +class TraceResolverImpl + : public TraceResolverLinuxImpl {}; + +#endif // BACKWARD_SYSTEM_LINUX + +#ifdef BACKWARD_SYSTEM_DARWIN + +template class TraceResolverDarwinImpl; + +template <> +class TraceResolverDarwinImpl + : public TraceResolverImplBase { +public: + void load_addresses(void *const*addresses, int address_count) override { + if (address_count == 0) { + return; + } + _symbols.reset(backtrace_symbols(addresses, address_count)); + } + + ResolvedTrace resolve(ResolvedTrace trace) override { + // parse: + // + + char *filename = _symbols[trace.idx]; + + // skip " " + while (*filename && *filename != ' ') + filename++; + while (*filename == ' ') + filename++; + + // find start of from end ( may contain a space) + char *p = filename + strlen(filename) - 1; + // skip to start of " + " + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + char *funcname_end = p + 1; + + // skip to start of "" + while (p > filename && *p != ' ') + p--; + char *funcname = p + 1; + + // skip to start of " " + while (p > filename && *p == ' ') + p--; + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + + // skip "", handling the case where it contains a + char *filename_end = p + 1; + if (p == filename) { + // something went wrong, give up + filename_end = filename + strlen(filename); + funcname = filename_end; + } + trace.object_filename.assign( + filename, filename_end); // ok even if filename_end is the ending \0 + // (then we assign entire string) + + if (*funcname) { // if it's not end of string + *funcname_end = '\0'; + + trace.object_function = this->demangle(funcname); + trace.object_function += " "; + trace.object_function += (funcname_end + 1); + trace.source.function = trace.object_function; // we cannot do better. + } + return trace; + } + +private: + details::handle _symbols; +}; + +template <> +class TraceResolverImpl + : public TraceResolverDarwinImpl {}; + +#endif // BACKWARD_SYSTEM_DARWIN + +#ifdef BACKWARD_SYSTEM_WINDOWS + +// Load all symbol info +// Based on: +// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 + +struct module_data { + std::string image_name; + std::string module_name; + void *base_address; + DWORD load_size; +}; + +class get_mod_info { + HANDLE process; + static const int buffer_length = 4096; + +public: + get_mod_info(HANDLE h) : process(h) {} + + module_data operator()(HMODULE module) { + module_data ret; + char temp[buffer_length]; + MODULEINFO mi; + + GetModuleInformation(process, module, &mi, sizeof(mi)); + ret.base_address = mi.lpBaseOfDll; + ret.load_size = mi.SizeOfImage; + + GetModuleFileNameExA(process, module, temp, sizeof(temp)); + ret.image_name = temp; + GetModuleBaseNameA(process, module, temp, sizeof(temp)); + ret.module_name = temp; + std::vector img(ret.image_name.begin(), ret.image_name.end()); + std::vector mod(ret.module_name.begin(), ret.module_name.end()); + SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, + ret.load_size); + return ret; + } +}; + +template <> class TraceResolverImpl + : public TraceResolverImplBase { +public: + TraceResolverImpl() { + + HANDLE process = GetCurrentProcess(); + + std::vector modules; + DWORD cbNeeded; + std::vector module_handles(1); + SymInitialize(process, NULL, false); + DWORD symOptions = SymGetOptions(); + symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME; + SymSetOptions(symOptions); + EnumProcessModules(process, &module_handles[0], + static_cast(module_handles.size() * sizeof(HMODULE)), + &cbNeeded); + module_handles.resize(cbNeeded / sizeof(HMODULE)); + EnumProcessModules(process, &module_handles[0], + static_cast(module_handles.size() * sizeof(HMODULE)), + &cbNeeded); + std::transform(module_handles.begin(), module_handles.end(), + std::back_inserter(modules), get_mod_info(process)); + void *base = modules[0].base_address; + IMAGE_NT_HEADERS *h = ImageNtHeader(base); + image_type = h->FileHeader.Machine; + } + + static const int max_sym_len = 255; + struct symbol_t { + SYMBOL_INFO sym; + char buffer[max_sym_len]; + } sym; + + DWORD64 displacement; + + ResolvedTrace resolve(ResolvedTrace t) override { + HANDLE process = GetCurrentProcess(); + + char name[256]; + + memset(&sym, 0, sizeof(sym)); + sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO); + sym.sym.MaxNameLen = max_sym_len; + + if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) { + // TODO: error handling everywhere + char* lpMsgBuf; + DWORD dw = GetLastError(); + + if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*)&lpMsgBuf, 0, NULL)) { + std::fprintf(stderr, "%s\n", lpMsgBuf); + LocalFree(lpMsgBuf); + } + + // abort(); + } + UnDecorateSymbolName(sym.sym.Name, (PSTR)name, 256, UNDNAME_COMPLETE); + + DWORD offset = 0; + IMAGEHLP_LINE line; + if (SymGetLineFromAddr(process, (ULONG64)t.addr, &offset, &line)) { + t.object_filename = line.FileName; + t.source.filename = line.FileName; + t.source.line = line.LineNumber; + t.source.col = offset; + } + + t.source.function = name; + t.object_filename = ""; + t.object_function = name; + + return t; + } + + DWORD machine_type() const { return image_type; } + +private: + DWORD image_type; +}; + +#endif + +class TraceResolver : public TraceResolverImpl {}; + +/*************** CODE SNIPPET ***************/ + +class SourceFile { +public: + typedef std::vector > lines_t; + + SourceFile() {} + SourceFile(const std::string &path) { + // 1. If BACKWARD_CXX_SOURCE_PREFIXES is set then assume it contains + // a colon-separated list of path prefixes. Try prepending each + // to the given path until a valid file is found. + const std::vector &prefixes = get_paths_from_env_variable(); + for (size_t i = 0; i < prefixes.size(); ++i) { + // Double slashes (//) should not be a problem. + std::string new_path = prefixes[i] + '/' + path; + _file.reset(new std::ifstream(new_path.c_str())); + if (is_open()) + break; + } + // 2. If no valid file found then fallback to opening the path as-is. + if (!_file || !is_open()) { + _file.reset(new std::ifstream(path.c_str())); + } + } + bool is_open() const { return _file->is_open(); } + + lines_t &get_lines(unsigned line_start, unsigned line_count, lines_t &lines) { + using namespace std; + // This function make uses of the dumbest algo ever: + // 1) seek(0) + // 2) read lines one by one and discard until line_start + // 3) read line one by one until line_start + line_count + // + // If you are getting snippets many time from the same file, it is + // somewhat a waste of CPU, feel free to benchmark and propose a + // better solution ;) + + _file->clear(); + _file->seekg(0); + string line; + unsigned line_idx; + + for (line_idx = 1; line_idx < line_start; ++line_idx) { + std::getline(*_file, line); + if (!*_file) { + return lines; + } + } + + // think of it like a lambda in C++98 ;) + // but look, I will reuse it two times! + // What a good boy am I. + struct isspace { + bool operator()(char c) { return std::isspace(c); } + }; + + bool started = false; + for (; line_idx < line_start + line_count; ++line_idx) { + getline(*_file, line); + if (!*_file) { + return lines; + } + if (!started) { + if (std::find_if(line.begin(), line.end(), not_isspace()) == line.end()) + continue; + started = true; + } + lines.push_back(make_pair(line_idx, line)); + } + + lines.erase( + std::find_if(lines.rbegin(), lines.rend(), not_isempty()).base(), + lines.end()); + return lines; + } + + lines_t get_lines(unsigned line_start, unsigned line_count) { + lines_t lines; + return get_lines(line_start, line_count, lines); + } + + // there is no find_if_not in C++98, lets do something crappy to + // workaround. + struct not_isspace { + bool operator()(char c) { return !std::isspace(c); } + }; + // and define this one here because C++98 is not happy with local defined + // struct passed to template functions, fuuuu. + struct not_isempty { + bool operator()(const lines_t::value_type &p) { + return !(std::find_if(p.second.begin(), p.second.end(), not_isspace()) == + p.second.end()); + } + }; + + void swap(SourceFile &b) { _file.swap(b._file); } + +#ifdef BACKWARD_ATLEAST_CXX11 + SourceFile(SourceFile &&from) : _file(nullptr) { swap(from); } + SourceFile &operator=(SourceFile &&from) { + swap(from); + return *this; + } +#else + explicit SourceFile(const SourceFile &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + } + SourceFile &operator=(const SourceFile &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + return *this; + } +#endif + + // Allow adding to paths gotten from BACKWARD_CXX_SOURCE_PREFIXES after loading the + // library; this can be useful when the library is loaded when the locations are unknown + // Warning: Because this edits the static paths variable, it is *not* intrinsiclly thread safe + static void add_paths_to_env_variable_impl(const std::string & to_add) { + get_mutable_paths_from_env_variable().push_back(to_add); + } + +private: + details::handle > + _file; + + static std::vector get_paths_from_env_variable_impl() { + std::vector paths; + const char *prefixes_str = std::getenv("BACKWARD_CXX_SOURCE_PREFIXES"); + if (prefixes_str && prefixes_str[0]) { + paths = details::split_source_prefixes(prefixes_str); + } + return paths; + } + + static std::vector &get_mutable_paths_from_env_variable() { + static volatile std::vector paths = get_paths_from_env_variable_impl(); + return const_cast&>(paths); + } + + static const std::vector &get_paths_from_env_variable() { + return get_mutable_paths_from_env_variable(); + } + +#ifdef BACKWARD_ATLEAST_CXX11 + SourceFile(const SourceFile &) = delete; + SourceFile &operator=(const SourceFile &) = delete; +#endif +}; + +class SnippetFactory { +public: + typedef SourceFile::lines_t lines_t; + + lines_t get_snippet(const std::string &filename, unsigned line_start, + unsigned context_size) { + + SourceFile &src_file = get_src_file(filename); + unsigned start = line_start - context_size / 2; + return src_file.get_lines(start, context_size); + } + + lines_t get_combined_snippet(const std::string &filename_a, unsigned line_a, + const std::string &filename_b, unsigned line_b, + unsigned context_size) { + SourceFile &src_file_a = get_src_file(filename_a); + SourceFile &src_file_b = get_src_file(filename_b); + + lines_t lines = + src_file_a.get_lines(line_a - context_size / 4, context_size / 2); + src_file_b.get_lines(line_b - context_size / 4, context_size / 2, lines); + return lines; + } + + lines_t get_coalesced_snippet(const std::string &filename, unsigned line_a, + unsigned line_b, unsigned context_size) { + SourceFile &src_file = get_src_file(filename); + + using std::max; + using std::min; + unsigned a = min(line_a, line_b); + unsigned b = max(line_a, line_b); + + if ((b - a) < (context_size / 3)) { + return src_file.get_lines((a + b - context_size + 1) / 2, context_size); + } + + lines_t lines = src_file.get_lines(a - context_size / 4, context_size / 2); + src_file.get_lines(b - context_size / 4, context_size / 2, lines); + return lines; + } + +private: + typedef details::hashtable::type src_files_t; + src_files_t _src_files; + + SourceFile &get_src_file(const std::string &filename) { + src_files_t::iterator it = _src_files.find(filename); + if (it != _src_files.end()) { + return it->second; + } + SourceFile &new_src_file = _src_files[filename]; + new_src_file = SourceFile(filename); + return new_src_file; + } +}; + +/*************** PRINTER ***************/ + +namespace ColorMode { +enum type { automatic, never, always }; +} + +class cfile_streambuf : public std::streambuf { +public: + cfile_streambuf(FILE *_sink) : sink(_sink) {} + int_type underflow() override { return traits_type::eof(); } + int_type overflow(int_type ch) override { + if (traits_type::not_eof(ch) && fputc(ch, sink) != EOF) { + return ch; + } + return traits_type::eof(); + } + + std::streamsize xsputn(const char_type *s, std::streamsize count) override { + return static_cast( + fwrite(s, sizeof *s, static_cast(count), sink)); + } + +#ifdef BACKWARD_ATLEAST_CXX11 +public: + cfile_streambuf(const cfile_streambuf &) = delete; + cfile_streambuf &operator=(const cfile_streambuf &) = delete; +#else +private: + cfile_streambuf(const cfile_streambuf &); + cfile_streambuf &operator=(const cfile_streambuf &); +#endif + +private: + FILE *sink; + std::vector buffer; +}; + +#ifdef BACKWARD_SYSTEM_LINUX + +namespace Color { +enum type { yellow = 33, purple = 35, reset = 39 }; +} // namespace Color + +class Colorize { +public: + Colorize(std::ostream &os) : _os(os), _reset(false), _enabled(false) {} + + void activate(ColorMode::type mode) { _enabled = mode == ColorMode::always; } + + void activate(ColorMode::type mode, FILE *fp) { activate(mode, fileno(fp)); } + + void set_color(Color::type ccode) { + if (!_enabled) + return; + + // I assume that the terminal can handle basic colors. Seriously I + // don't want to deal with all the termcap shit. + _os << "\033[" << static_cast(ccode) << "m"; + _reset = (ccode != Color::reset); + } + + ~Colorize() { + if (_reset) { + set_color(Color::reset); + } + } + +private: + void activate(ColorMode::type mode, int fd) { + activate(mode == ColorMode::automatic && isatty(fd) ? ColorMode::always + : mode); + } + + std::ostream &_os; + bool _reset; + bool _enabled; +}; + +#else // ndef BACKWARD_SYSTEM_LINUX + +namespace Color { +enum type { yellow = 0, purple = 0, reset = 0 }; +} // namespace Color + +class Colorize { +public: + Colorize(std::ostream &) {} + void activate(ColorMode::type) {} + void activate(ColorMode::type, FILE *) {} + void set_color(Color::type) {} +}; + +#endif // BACKWARD_SYSTEM_LINUX + +class Printer { +public: + bool snippet; + ColorMode::type color_mode; + bool address; + bool object; + int inliner_context_size; + int trace_context_size; + bool reverse; + + Printer() + : snippet(true), color_mode(ColorMode::automatic), address(false), + object(false), inliner_context_size(5), trace_context_size(7), + reverse(true) {} + + template FILE *print(ST &st, FILE *fp = stderr) { + cfile_streambuf obuf(fp); + std::ostream os(&obuf); + Colorize colorize(os); + colorize.activate(color_mode, fp); + print_stacktrace(st, os, colorize); + return fp; + } + + template std::ostream &print(ST &st, std::ostream &os) { + Colorize colorize(os); + colorize.activate(color_mode); + print_stacktrace(st, os, colorize); + return os; + } + + template + FILE *print(IT begin, IT end, FILE *fp = stderr, size_t thread_id = 0) { + cfile_streambuf obuf(fp); + std::ostream os(&obuf); + Colorize colorize(os); + colorize.activate(color_mode, fp); + print_stacktrace(begin, end, os, thread_id, colorize); + return fp; + } + + template + std::ostream &print(IT begin, IT end, std::ostream &os, + size_t thread_id = 0) { + Colorize colorize(os); + colorize.activate(color_mode); + print_stacktrace(begin, end, os, thread_id, colorize); + return os; + } + + TraceResolver const &resolver() const { return _resolver; } + +private: + TraceResolver _resolver; + SnippetFactory _snippets; + + template + void print_stacktrace(ST &st, std::ostream &os, Colorize &colorize) { + print_header(os, st.thread_id()); + _resolver.load_stacktrace(st); + if ( reverse ) { + for (size_t trace_idx = st.size(); trace_idx > 0; --trace_idx) { + print_trace(os, _resolver.resolve(st[trace_idx - 1]), colorize); + } + } else { + for (size_t trace_idx = 0; trace_idx < st.size(); ++trace_idx) { + print_trace(os, _resolver.resolve(st[trace_idx]), colorize); + } + } + } + + template + void print_stacktrace(IT begin, IT end, std::ostream &os, size_t thread_id, + Colorize &colorize) { + print_header(os, thread_id); + for (; begin != end; ++begin) { + print_trace(os, *begin, colorize); + } + } + + void print_header(std::ostream &os, size_t thread_id) { + os << "Stack trace (most recent call last)"; + if (thread_id) { + os << " in thread " << thread_id; + } + os << ":\n"; + } + + void print_trace(std::ostream &os, const ResolvedTrace &trace, + Colorize &colorize) { + os << "#" << std::left << std::setw(2) << trace.idx << std::right; + bool already_indented = true; + + if (!trace.source.filename.size() || object) { + os << " Object \"" << trace.object_filename << "\", at " << trace.addr + << ", in " << trace.object_function << "\n"; + already_indented = false; + } + + for (size_t inliner_idx = trace.inliners.size(); inliner_idx > 0; + --inliner_idx) { + if (!already_indented) { + os << " "; + } + const ResolvedTrace::SourceLoc &inliner_loc = + trace.inliners[inliner_idx - 1]; + print_source_loc(os, " | ", inliner_loc); + if (snippet) { + print_snippet(os, " | ", inliner_loc, colorize, Color::purple, + inliner_context_size); + } + already_indented = false; + } + + if (trace.source.filename.size()) { + if (!already_indented) { + os << " "; + } + print_source_loc(os, " ", trace.source, trace.addr); + if (snippet) { + print_snippet(os, " ", trace.source, colorize, Color::yellow, + trace_context_size); + } + } + } + + void print_snippet(std::ostream &os, const char *indent, + const ResolvedTrace::SourceLoc &source_loc, + Colorize &colorize, Color::type color_code, + int context_size) { + using namespace std; + typedef SnippetFactory::lines_t lines_t; + + lines_t lines = _snippets.get_snippet(source_loc.filename, source_loc.line, + static_cast(context_size)); + + for (lines_t::const_iterator it = lines.begin(); it != lines.end(); ++it) { + if (it->first == source_loc.line) { + colorize.set_color(color_code); + os << indent << ">"; + } else { + os << indent << " "; + } + os << std::setw(4) << it->first << ": " << it->second << "\n"; + if (it->first == source_loc.line) { + colorize.set_color(Color::reset); + } + } + } + + void print_source_loc(std::ostream &os, const char *indent, + const ResolvedTrace::SourceLoc &source_loc, + void *addr = nullptr) { + os << indent << "Source \"" << source_loc.filename << "\", line " + << source_loc.line << ", in " << source_loc.function; + + if (address && addr != nullptr) { + os << " [" << addr << "]"; + } + os << "\n"; + } +}; + +/*************** SIGNALS HANDLING ***************/ + +#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) + +class SignalHandling { +public: + static std::vector make_default_signals() { + const int posix_signals[] = { + // Signals for which the default action is "Core". + SIGABRT, // Abort signal from abort(3) + SIGBUS, // Bus error (bad memory access) + SIGFPE, // Floating point exception + SIGILL, // Illegal Instruction + SIGIOT, // IOT trap. A synonym for SIGABRT + SIGQUIT, // Quit from keyboard + SIGSEGV, // Invalid memory reference + SIGSYS, // Bad argument to routine (SVr4) + SIGTRAP, // Trace/breakpoint trap + SIGXCPU, // CPU time limit exceeded (4.2BSD) + SIGXFSZ, // File size limit exceeded (4.2BSD) +#if defined(BACKWARD_SYSTEM_DARWIN) + SIGEMT, // emulation instruction executed +#endif + }; + return std::vector(posix_signals, + posix_signals + + sizeof posix_signals / sizeof posix_signals[0]); + } + + SignalHandling(const std::vector &posix_signals = make_default_signals()) + : _loaded(false) { + bool success = true; + + const size_t stack_size = 1024 * 1024 * 8; + _stack_content.reset(static_cast(malloc(stack_size))); + if (_stack_content) { + stack_t ss; + ss.ss_sp = _stack_content.get(); + ss.ss_size = stack_size; + ss.ss_flags = 0; + if (sigaltstack(&ss, nullptr) < 0) { + success = false; + } + } else { + success = false; + } + + for (size_t i = 0; i < posix_signals.size(); ++i) { + struct sigaction action; + memset(&action, 0, sizeof action); + action.sa_flags = + static_cast(SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND); + sigfillset(&action.sa_mask); + sigdelset(&action.sa_mask, posix_signals[i]); +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" +#endif + action.sa_sigaction = &sig_handler; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + int r = sigaction(posix_signals[i], &action, nullptr); + if (r < 0) + success = false; + } + + _loaded = success; + } + + bool loaded() const { return _loaded; } + + static void handleSignal(int, siginfo_t *info, void *_ctx) { + ucontext_t *uctx = static_cast(_ctx); + + StackTrace st; + void *error_addr = nullptr; +#ifdef REG_RIP // x86_64 + error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); +#elif defined(REG_EIP) // x86_32 + error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); +#elif defined(__arm__) + error_addr = reinterpret_cast(uctx->uc_mcontext.arm_pc); +#elif defined(__aarch64__) + #if defined(__APPLE__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__pc); + #else + error_addr = reinterpret_cast(uctx->uc_mcontext.pc); + #endif +#elif defined(__mips__) + error_addr = reinterpret_cast( + reinterpret_cast(&uctx->uc_mcontext)->sc_pc); +#elif defined(__APPLE__) && defined(__POWERPC__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__srr0); +#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ + defined(__POWERPC__) + error_addr = reinterpret_cast(uctx->uc_mcontext.regs->nip); +#elif defined(__riscv) + error_addr = reinterpret_cast(uctx->uc_mcontext.__gregs[REG_PC]); +#elif defined(__s390x__) + error_addr = reinterpret_cast(uctx->uc_mcontext.psw.addr); +#elif defined(__APPLE__) && defined(__x86_64__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__rip); +#elif defined(__APPLE__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__eip); +#elif defined(__loongarch__) + error_addr = reinterpret_cast(uctx->uc_mcontext.__pc); +#else +#warning ":/ sorry, ain't know no nothing none not of your architecture!" +#endif + if (error_addr) { + st.load_from(error_addr, 32, reinterpret_cast(uctx), + info->si_addr); + } else { + st.load_here(32, reinterpret_cast(uctx), info->si_addr); + } + + Printer printer; + printer.address = true; + printer.print(st, stderr); + +#if (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) || \ + (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) + psiginfo(info, nullptr); +#else + (void)info; +#endif + } + +private: + details::handle _stack_content; + bool _loaded; + +#ifdef __GNUC__ + __attribute__((noreturn)) +#endif + static void + sig_handler(int signo, siginfo_t *info, void *_ctx) { + handleSignal(signo, info, _ctx); + + // try to forward the signal. + raise(info->si_signo); + + // terminate the process immediately. + puts("watf? exit"); + _exit(EXIT_FAILURE); + } +}; + +#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN + +#ifdef BACKWARD_SYSTEM_WINDOWS + +class SignalHandling { +public: + SignalHandling(const std::vector & = std::vector()) + : reporter_thread_([]() { + /* We handle crashes in a utility thread: + backward structures and some Windows functions called here + need stack space, which we do not have when we encounter a + stack overflow. + To support reporting stack traces during a stack overflow, + we create a utility thread at startup, which waits until a + crash happens or the program exits normally. */ + + { + std::unique_lock lk(mtx()); + cv().wait(lk, [] { return crashed() != crash_status::running; }); + } + if (crashed() == crash_status::crashed) { + handle_stacktrace(skip_recs()); + } + { + std::unique_lock lk(mtx()); + crashed() = crash_status::ending; + } + cv().notify_one(); + }) { + SetUnhandledExceptionFilter(crash_handler); + + signal(SIGABRT, signal_handler); + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + + std::set_terminate(&terminator); +#ifndef BACKWARD_ATLEAST_CXX17 + std::set_unexpected(&terminator); +#endif + _set_purecall_handler(&terminator); + _set_invalid_parameter_handler(&invalid_parameter_handler); + } + bool loaded() const { return true; } + + ~SignalHandling() { + { + std::unique_lock lk(mtx()); + crashed() = crash_status::normal_exit; + } + + cv().notify_one(); + + reporter_thread_.join(); + } + +private: + static CONTEXT *ctx() { + static CONTEXT data; + return &data; + } + + enum class crash_status { running, crashed, normal_exit, ending }; + + static crash_status &crashed() { + static crash_status data; + return data; + } + + static std::mutex &mtx() { + static std::mutex data; + return data; + } + + static std::condition_variable &cv() { + static std::condition_variable data; + return data; + } + + static HANDLE &thread_handle() { + static HANDLE handle; + return handle; + } + + std::thread reporter_thread_; + + // TODO: how not to hardcode these? + static const constexpr int signal_skip_recs = +#ifdef __clang__ + // With clang, RtlCaptureContext also captures the stack frame of the + // current function Below that, there are 3 internal Windows functions + 4 +#else + // With MSVC cl, RtlCaptureContext misses the stack frame of the current + // function The first entries during StackWalk are the 3 internal Windows + // functions + 3 +#endif + ; + + static int &skip_recs() { + static int data; + return data; + } + + static inline void terminator() { + crash_handler(signal_skip_recs); + abort(); + } + + static inline void signal_handler(int) { + crash_handler(signal_skip_recs); + abort(); + } + + static inline void __cdecl invalid_parameter_handler(const wchar_t *, + const wchar_t *, + const wchar_t *, + unsigned int, + uintptr_t) { + crash_handler(signal_skip_recs); + abort(); + } + + NOINLINE static LONG WINAPI crash_handler(EXCEPTION_POINTERS *info) { + // The exception info supplies a trace from exactly where the issue was, + // no need to skip records + crash_handler(0, info->ContextRecord); + return EXCEPTION_CONTINUE_SEARCH; + } + + NOINLINE static void crash_handler(int skip, CONTEXT *ct = nullptr) { + + if (ct == nullptr) { + RtlCaptureContext(ctx()); + } else { + memcpy(ctx(), ct, sizeof(CONTEXT)); + } + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &thread_handle(), 0, FALSE, + DUPLICATE_SAME_ACCESS); + + skip_recs() = skip; + + { + std::unique_lock lk(mtx()); + crashed() = crash_status::crashed; + } + + cv().notify_one(); + + { + std::unique_lock lk(mtx()); + cv().wait(lk, [] { return crashed() != crash_status::crashed; }); + } + } + + static void handle_stacktrace(int skip_frames = 0) { + // printer creates the TraceResolver, which can supply us a machine type + // for stack walking. Without this, StackTrace can only guess using some + // macros. + // StackTrace also requires that the PDBs are already loaded, which is done + // in the constructor of TraceResolver + Printer printer; + + StackTrace st; + st.set_machine_type(printer.resolver().machine_type()); + st.set_thread_handle(thread_handle()); + st.load_here(32 + skip_frames, ctx()); + st.skip_n_firsts(skip_frames); + + printer.address = true; + printer.print(st, std::cerr); + } +}; + +#endif // BACKWARD_SYSTEM_WINDOWS + +#ifdef BACKWARD_SYSTEM_UNKNOWN + +class SignalHandling { +public: + SignalHandling(const std::vector & = std::vector()) {} + bool init() { return false; } + bool loaded() { return false; } +}; + +#endif // BACKWARD_SYSTEM_UNKNOWN + +} // namespace backward + +#endif /* H_GUARD */ diff --git a/src/fne/FNEMain.cpp b/src/fne/FNEMain.cpp index 32ed5d46..1b80fbfb 100644 --- a/src/fne/FNEMain.cpp +++ b/src/fne/FNEMain.cpp @@ -218,6 +218,8 @@ int main(int argc, char** argv) } } + backtrace::SignalHandling sh(g_foreground); + ::signal(SIGINT, sigHandler); ::signal(SIGTERM, sigHandler); #if !defined(_WIN32) diff --git a/src/host/HostMain.cpp b/src/host/HostMain.cpp index 6047517e..80d85241 100644 --- a/src/host/HostMain.cpp +++ b/src/host/HostMain.cpp @@ -290,6 +290,8 @@ int main(int argc, char** argv) } } + backtrace::SignalHandling sh(g_foreground); + ::signal(SIGINT, sigHandler); ::signal(SIGTERM, sigHandler); #if !defined(_WIN32) diff --git a/src/patch/PatchMain.cpp b/src/patch/PatchMain.cpp index f03ef9b7..d20d5d22 100644 --- a/src/patch/PatchMain.cpp +++ b/src/patch/PatchMain.cpp @@ -194,6 +194,8 @@ int main(int argc, char** argv) } } + backtrace::SignalHandling sh(g_foreground); + ::signal(SIGINT, sigHandler); ::signal(SIGTERM, sigHandler); #if !defined(_WIN32) From a6f5e498acbb0a414dfcbaa967356e459e5ddd6d Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 3 Oct 2025 23:22:31 -0400 Subject: [PATCH 043/200] fix FNE compilation when SSL is not available; make Win32 builds work again; --- src/common/Clock.h | 13 +++++++++++++ src/common/Log.h | 4 ++-- src/common/backtrace/backward.h | 21 ++++++++++++++++----- src/fne/network/FNENetwork.cpp | 1 + 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/common/Clock.h b/src/common/Clock.h index 52ef3608..46a1a1e4 100644 --- a/src/common/Clock.h +++ b/src/common/Clock.h @@ -24,8 +24,21 @@ #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN +#define NOMINMAX #include #include + +/* +** bryanb: because we've included Windows.h in a header -- take the nuclear option +** make sure Windows.h and any of its includes *DO NOT* define min/max macros +*/ +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif + #else #include #endif // defined(_WIN32) diff --git a/src/common/Log.h b/src/common/Log.h index 120936c9..3bf95470 100644 --- a/src/common/Log.h +++ b/src/common/Log.h @@ -653,7 +653,7 @@ namespace backtrace DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &threadHandle(), 0, FALSE, DUPLICATE_SAME_ACCESS); - skip_recs() = skip; + skipRecs() = skip; { std::unique_lock lk(mtx()); @@ -682,7 +682,7 @@ namespace backtrace backward::Printer p; backward::StackTrace st; - st.set_machine_type(printer.resolver().machine_type()); + st.set_machine_type(p.resolver().machine_type()); st.set_thread_handle(threadHandle()); st.load_here(32 + skipFrames, ctx()); st.skip_n_firsts(skipFrames); diff --git a/src/common/backtrace/backward.h b/src/common/backtrace/backward.h index 914df36f..6cfd737d 100644 --- a/src/common/backtrace/backward.h +++ b/src/common/backtrace/backward.h @@ -347,18 +347,29 @@ #include -#ifdef _WIN64 +#if defined(_WIN32) +#ifndef _SSIZE_T_DECLARED typedef SSIZE_T ssize_t; -#else -typedef int ssize_t; +#define _SSIZE_T_DECLARED #endif +#endif // defined(_WIN32) -#ifndef NOMINMAX +#define WIN32_LEAN_AND_MEAN #define NOMINMAX -#endif #include #include +/* +** bryanb: because we've included Windows.h in a header -- take the nuclear option +** make sure Windows.h and any of its includes *DO NOT* define min/max macros +*/ +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif + #include #include diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 1834a562..783fa6f4 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -201,6 +201,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) } } #else + uint16_t kmfOtarPort = 64414U; // hardcoded m_kmfServicesEnabled = false; LogWarning(LOG_P25, "FNE is compiled without OpenSSL support, KMF services are unavailable."); #endif // ENABLE_SSL From a3087c670f45a89e953a6f4af6ea5455f8145ca6 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 3 Oct 2025 23:26:19 -0400 Subject: [PATCH 044/200] remove double error message; --- src/common/Log.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/Log.h b/src/common/Log.h index 3bf95470..a3032c1c 100644 --- a/src/common/Log.h +++ b/src/common/Log.h @@ -696,7 +696,6 @@ namespace backtrace // log stack trace to a file if we're using syslog FILE *stacktraceFp = log_internal::GetLogFile(); - ::fprintf(stacktraceFp, "UNRECOVERABLE FATAL ERROR!\r\n"); p.print(st, stacktraceFp); ::fflush(stacktraceFp); ::fclose(stacktraceFp); From fb4e2bc3c0287c1ea6a7c58f99f27983e35f9d20 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 4 Oct 2025 09:05:45 -0400 Subject: [PATCH 045/200] generate stacktrace file if main logger file is not initialized or unavailable; --- src/common/Log.h | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/common/Log.h b/src/common/Log.h index a3032c1c..70c9968a 100644 --- a/src/common/Log.h +++ b/src/common/Log.h @@ -379,19 +379,19 @@ namespace backtrace p.print(st, stderr); } - // log stack trace to a file if we're using syslog - if (g_useSyslog) { - std::string filePath = log_internal::GetLogFilePath(); - std::string fileRoot = log_internal::GetLogFileRoot(); + std::string filePath = log_internal::GetLogFilePath(); + std::string fileRoot = log_internal::GetLogFileRoot(); - time_t now; - ::time(&now); + time_t now; + ::time(&now); - struct tm* tm = ::localtime(&now); + struct tm* tm = ::localtime(&now); - char filename[200U]; - ::sprintf(filename, "%s/%s-%04d-%02d-%02d.stacktrace.log", filePath.c_str(), fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + char filename[200U]; + ::sprintf(filename, "%s/%s-%04d-%02d-%02d.stacktrace.log", filePath.c_str(), fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + // log stack trace to a file if we're using syslog + if (g_useSyslog) { FILE* stacktraceFp = ::fopen(filename, "a+t"); ::fprintf(stacktraceFp, "UNRECOVERABLE FATAL ERROR!\r\n"); p.print(st, stacktraceFp); @@ -399,6 +399,10 @@ namespace backtrace ::fclose(stacktraceFp); } else { FILE *stacktraceFp = log_internal::GetLogFile(); + if (stacktraceFp == nullptr) { + stacktraceFp = ::fopen(filename, "a+t"); + ::fprintf(stacktraceFp, "UNRECOVERABLE FATAL ERROR!\r\n"); + } p.print(st, stacktraceFp); ::fflush(stacktraceFp); ::fclose(stacktraceFp); @@ -694,8 +698,24 @@ namespace backtrace log_internal::LogInternal(3U, "UNRECOVERABLE FATAL ERROR!"); p.print(st, std::cerr); + std::string filePath = log_internal::GetLogFilePath(); + std::string fileRoot = log_internal::GetLogFileRoot(); + + time_t now; + ::time(&now); + + struct tm* tm = ::localtime(&now); + + char filename[200U]; + ::sprintf(filename, "%s/%s-%04d-%02d-%02d.stacktrace.log", filePath.c_str(), fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + // log stack trace to a file if we're using syslog FILE *stacktraceFp = log_internal::GetLogFile(); + if (stacktraceFp == nullptr) { + stacktraceFp = ::fopen(filename, "a+t"); + ::fprintf(stacktraceFp, "UNRECOVERABLE FATAL ERROR!\r\n"); + } + p.print(st, stacktraceFp); ::fflush(stacktraceFp); ::fclose(stacktraceFp); From dc8a35b3f181e0ee6dcfbe22bc60feb9ebd64849 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 4 Oct 2025 12:46:03 -0400 Subject: [PATCH 046/200] reduce the use of unordered_map::at(); --- src/common/lookups/AffiliationLookup.cpp | 25 ++++++++++++++++-------- src/common/network/udp/Socket.cpp | 12 ++++++------ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/common/lookups/AffiliationLookup.cpp b/src/common/lookups/AffiliationLookup.cpp index 9463f86c..57fa9d93 100644 --- a/src/common/lookups/AffiliationLookup.cpp +++ b/src/common/lookups/AffiliationLookup.cpp @@ -228,11 +228,11 @@ bool AffiliationLookup::groupUnaff(uint32_t srcId) __lock(); // lookup dynamic affiliation table entry - if (m_grpAffTable.find(srcId) != m_grpAffTable.end()) { - uint32_t tblDstId = m_grpAffTable.at(srcId); + auto it = m_grpAffTable.find(srcId); + if (it != m_grpAffTable.end()) { if (m_verbose) { LogMessage(LOG_HOST, "%s, group unaffiliation, srcId = %u, dstId = %u", - m_name.c_str(), srcId, tblDstId); + m_name.c_str(), srcId, it->second); } } else { __unlock(); @@ -280,9 +280,9 @@ bool AffiliationLookup::isGroupAff(uint32_t srcId, uint32_t dstId) const // lookup dynamic affiliation table entry m_grpAffTable.lock(false); - if (m_grpAffTable.find(srcId) != m_grpAffTable.end()) { - uint32_t tblDstId = m_grpAffTable.at(srcId); - if (tblDstId == dstId) { + auto it = m_grpAffTable.find(srcId); + if (it != m_grpAffTable.end()) { + if (it->second == dstId) { m_grpAffTable.unlock(); return true; } @@ -402,7 +402,7 @@ bool AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) if (dstId == 0U && releaseAll) { LogWarning(LOG_HOST, "%s, force releasing all channel grants", m_name.c_str()); - m_grantChTable.lock(); + m_grantChTable.lock(false); std::vector gntsToRel = std::vector(); for (auto entry : m_grantChTable) { uint32_t dstId = entry.first; @@ -419,7 +419,16 @@ bool AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) } if (isGranted(dstId)) { - uint32_t chNo = m_grantChTable.at(dstId); + uint32_t chNo = 0U; + m_grantChTable.lock(false); + for (auto entry : m_grantChTable) { + if (entry.first == dstId) { + chNo = entry.second; + break; + } + } + m_grantChTable.unlock(); + uint32_t srcId = getGrantedSrcId(dstId); if (m_verbose) { diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index 1608a492..f2f71668 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -573,7 +573,7 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept // reallocate buffer and copy cryptoBuffer = new uint8_t[cryptedLen]; ::memset(cryptoBuffer, 0x00U, cryptedLen); - ::memcpy(cryptoBuffer, buffers.at(i)->buffer, length); + ::memcpy(cryptoBuffer, buffers[i]->buffer, length); } // encrypt @@ -607,12 +607,12 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept buffers[i]->length = cryptedLen + 2U; } - chunks[i].iov_len = buffers.at(i)->length; - chunks[i].iov_base = buffers.at(i)->buffer; - sent += buffers.at(i)->length; + chunks[i].iov_len = buffers[i]->length; + chunks[i].iov_base = buffers[i]->buffer; + sent += buffers[i]->length; - headers[i].msg_hdr.msg_name = (void*)&buffers.at(i)->address; - headers[i].msg_hdr.msg_namelen = buffers.at(i)->addrLen; + headers[i].msg_hdr.msg_name = (void*)&buffers[i]->address; + headers[i].msg_hdr.msg_namelen = buffers[i]->addrLen; headers[i].msg_hdr.msg_iov = &chunks[i]; headers[i].msg_hdr.msg_iovlen = 1; headers[i].msg_hdr.msg_control = 0; From 4db617af1b9c586a090f4a7e1278d079b81347c9 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 5 Oct 2025 17:13:50 -0400 Subject: [PATCH 047/200] begin relabeling peer-link to peer replication/peer replica; --- configs/peer_list.example.dat | 8 +- src/common/lookups/PeerListLookup.cpp | 14 ++-- src/common/lookups/PeerListLookup.h | 10 +-- src/common/network/BaseNetwork.h | 2 +- src/common/network/RTPFNEHeader.h | 10 +-- src/fne/HostFNE.cpp | 8 +- src/fne/HostFNE.h | 2 +- src/fne/network/DiagNetwork.cpp | 40 +++++----- src/fne/network/FNENetwork.cpp | 78 +++++++++---------- src/fne/network/FNENetwork.h | 14 ++-- src/fne/network/PeerNetwork.cpp | 40 +++++----- src/fne/network/PeerNetwork.h | 16 ++-- src/fne/network/RESTAPI.cpp | 22 +++--- src/fne/network/callhandler/TagAnalogData.cpp | 10 +-- src/fne/network/callhandler/TagDMRData.cpp | 10 +-- src/fne/network/callhandler/TagNXDNData.cpp | 10 +-- src/fne/network/callhandler/TagP25Data.cpp | 10 +-- src/peered/PeerEditWnd.h | 18 ++--- src/peered/PeerListWnd.h | 4 +- src/sysview/network/PeerNetwork.cpp | 22 +++--- src/sysview/network/PeerNetwork.h | 8 +- 21 files changed, 178 insertions(+), 178 deletions(-) diff --git a/configs/peer_list.example.dat b/configs/peer_list.example.dat index 4b9b2691..2f7c0af3 100644 --- a/configs/peer_list.example.dat +++ b/configs/peer_list.example.dat @@ -6,10 +6,10 @@ # * PEER ID [REQUIRED] - Unique ID for a peer. # Peer IDs are valid numbers between 1 and 999999999. # * PEER PASSWORD [REQUIRED] - Unique password for this peer to use when authenticating. -# * PEER LINK [OPTIONAL] - Flag indicating whether or not the peer connection is another FNE, and will receive -# full configuration from this FNE. When peer link is set, and the connection is +# * PEER REPLICATION [OPTIONAL] - Flag indicating whether or not the peer connection is another FNE and will receive +# full configuration from this FNE. When peer replication is set, and the connection is # another FNE, that FNE will receive all the talkgroups, radio ID lists, and -# peer lists from this FNE. +# peer lists from this FNE, it will also receive all system traffic. # * CAN REQUEST KEYS [OPTIONAL] - Flag indicating the peer connection is allowed to request encryption keys. # If this flag is disabled (0), and the connected peer requests and encryption key # the encryption key request will be dropped and ignored. @@ -17,7 +17,7 @@ # If this flag is disabled (0), and the connected peer issues an inhibit to the network # this FNE will drop the packet and ignore it. # -# Entry Format: "Peer ID,Peer Password,Peer Link (1 = Enabled / 0 = Disabled),Peer Alias (optional),Can Request Keys (1 = Enabled / 0 = Disabled),Can Issue Inhibit (1 = Enabled / 0 = Disabled)" +# Entry Format: "Peer ID,Peer Password,Peer Replication (1 = Enabled / 0 = Disabled),Peer Alias (optional),Can Request Keys (1 = Enabled / 0 = Disabled),Can Issue Inhibit (1 = Enabled / 0 = Disabled)" # Examples: #1234,,0,,1,0, #5678,MYSECUREPASSWORD,0,,0,0, diff --git a/src/common/lookups/PeerListLookup.cpp b/src/common/lookups/PeerListLookup.cpp index 9215e3b9..8af24edd 100644 --- a/src/common/lookups/PeerListLookup.cpp +++ b/src/common/lookups/PeerListLookup.cpp @@ -223,9 +223,9 @@ bool PeerListLookup::load() alias = parsed[3].c_str(); // parse peer link flag - bool peerLink = false; + bool peerReplica = false; if (parsed.size() >= 3) - peerLink = ::atoi(parsed[2].c_str()) == 1; + peerReplica = ::atoi(parsed[2].c_str()) == 1; // parse can request keys flag bool canRequestKeys = false; @@ -244,7 +244,7 @@ bool PeerListLookup::load() // load into table PeerId entry = PeerId(id, alias, password, false); - entry.peerLink(peerLink); + entry.peerReplica(peerReplica); entry.canRequestKeys(canRequestKeys); entry.canIssueInhibit(canIssueInhibit); @@ -254,7 +254,7 @@ bool PeerListLookup::load() LogMessage(LOG_HOST, "Loaded peer ID %u%s into peer ID lookup table, %s%s%s", id, (!alias.empty() ? (" (" + alias + ")").c_str() : ""), (!password.empty() ? "using unique peer password" : "using master password"), - (peerLink) ? ", Peer-Link Enabled" : "", + (peerReplica) ? ", Replication Enabled" : "", (canRequestKeys) ? ", Can Request Keys" : "", (canIssueInhibit) ? ", Can Issue Inhibit" : ""); } @@ -310,9 +310,9 @@ bool PeerListLookup::save() } line += ","; - // add peerLink flag - bool peerLink = entry.second.peerLink(); - if (peerLink) { + // add peer replication flag + bool peerReplica = entry.second.peerReplica(); + if (peerReplica) { line += "1,"; } else { line += "0,"; diff --git a/src/common/lookups/PeerListLookup.h b/src/common/lookups/PeerListLookup.h index 65f54917..65130f22 100644 --- a/src/common/lookups/PeerListLookup.h +++ b/src/common/lookups/PeerListLookup.h @@ -49,7 +49,7 @@ namespace lookups m_peerId(0U), m_peerAlias(), m_peerPassword(), - m_peerLink(false), + m_peerReplica(false), m_canRequestKeys(false), m_canIssueInhibit(false), m_peerDefault(false) @@ -68,7 +68,7 @@ namespace lookups m_peerId(peerId), m_peerAlias(peerAlias), m_peerPassword(peerPassword), - m_peerLink(false), + m_peerReplica(false), m_canRequestKeys(false), m_canIssueInhibit(false), m_peerDefault(peerDefault) @@ -86,7 +86,7 @@ namespace lookups m_peerId = data.m_peerId; m_peerAlias = data.m_peerAlias; m_peerPassword = data.m_peerPassword; - m_peerLink = data.m_peerLink; + m_peerReplica = data.m_peerReplica; m_canRequestKeys = data.m_canRequestKeys; m_canIssueInhibit = data.m_canIssueInhibit; m_peerDefault = data.m_peerDefault; @@ -125,9 +125,9 @@ namespace lookups */ DECLARE_PROPERTY_PLAIN(std::string, peerPassword); /** - * @brief Flag indicating if the peer participates in peer link and should be sent configuration. + * @brief Flag indicating if the peer participates in peer replication and should be sent configuration. */ - DECLARE_PROPERTY_PLAIN(bool, peerLink); + DECLARE_PROPERTY_PLAIN(bool, peerReplica); /** * @brief Flag indicating if the peer can request encryption keys. */ diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index 2f2a8df4..39bd2dd0 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -69,7 +69,7 @@ #define TAG_TRANSFER_STATUS "TRNSSTS" #define TAG_ANNOUNCE "ANNC" -#define TAG_PEER_LINK "PRLNK" +#define TAG_PEER_REPLICA "REPL" #define MAX_PEER_PING_TIME 60U // 60 seconds diff --git a/src/common/network/RTPFNEHeader.h b/src/common/network/RTPFNEHeader.h index 0edb2f65..52b6d8f4 100644 --- a/src/common/network/RTPFNEHeader.h +++ b/src/common/network/RTPFNEHeader.h @@ -72,7 +72,7 @@ namespace network ANNOUNCE = 0x91U, //!< Network Announce Function - PEER_LINK = 0x92U //!< FNE Peer-Link Function + REPL = 0x92U //!< FNE Replication Function }; }; @@ -105,11 +105,11 @@ namespace network ANNC_SUBFUNC_AFFILS = 0x90U, //!< Update All Affiliations ANNC_SUBFUNC_SITE_VC = 0x9AU, //!< Announce Site VCs - PL_TALKGROUP_LIST = 0x00U, //!< FNE Peer-Link Talkgroup Transfer - PL_RID_LIST = 0x01U, //!< FNE Peer-Link Radio ID Transfer - PL_PEER_LIST = 0x02U, //!< FNE Peer-Link Peer List Transfer + REPL_TALKGROUP_LIST = 0x00U, //!< FNE Replication Talkgroup Transfer + REPL_RID_LIST = 0x01U, //!< FNE Replication Radio ID Transfer + REPL_PEER_LIST = 0x02U, //!< FNE Replication Peer List Transfer - PL_ACT_PEER_LIST = 0xA2U, //!< FNE Peer-Link Active Peer List Transfer + REPL_ACT_PEER_LIST = 0xA2U, //!< FNE Replication Active Peer List Transfer }; }; diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index a1a52537..a58ab53a 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -78,7 +78,7 @@ HostFNE::HostFNE(const std::string& confFile) : m_pingTime(5U), m_maxMissedPings(5U), m_updateLookupTime(10U), - m_peerLinkSavesACL(false), + m_peerReplicaSavesACL(false), m_useAlternatePortForDiagnostics(false), m_allowActivityTransfer(false), m_allowDiagnosticTransfer(false), @@ -339,7 +339,7 @@ bool HostFNE::readParams() m_maxMissedPings = systemConf["maxMissedPings"].as(5U); m_updateLookupTime = systemConf["aclRuleUpdateTime"].as(10U); bool sendTalkgroups = systemConf["sendTalkgroups"].as(true); - m_peerLinkSavesACL = systemConf["peerLinkSaveACL"].as(false); + m_peerReplicaSavesACL = systemConf["peerReplicaSaveACL"].as(false); if (m_pingTime == 0U) { m_pingTime = 5U; @@ -385,7 +385,7 @@ bool HostFNE::readParams() LogInfo(" ACL Rule Update Time: %u mins", m_updateLookupTime); LogInfo(" Send Talkgroups: %s", sendTalkgroups ? "yes" : "no"); - LogInfo(" Peer Link ACL is retained: %s", m_peerLinkSavesACL ? "yes" : "no"); + LogInfo(" Peer Replication ACL is retained: %s", m_peerReplicaSavesACL ? "yes" : "no"); if (m_useAlternatePortForDiagnostics) LogInfo(" Use Alternate Port for Diagnostics: yes"); @@ -861,7 +861,7 @@ bool HostFNE::createPeerNetworks() network->setMetadata(identity, rxFrequency, txFrequency, 0.0F, 0.0F, 0, 0, 0, latitude, longitude, 0, location); network->setLookups(m_ridLookup, m_tidLookup); network->setPeerLookups(m_peerListLookup); - network->setPeerLinkSaveACL(m_peerLinkSavesACL); + network->setPeerReplicationSaveACL(m_peerReplicaSavesACL); if (encrypted) { network->setPresharedKey(presharedKey); } diff --git a/src/fne/HostFNE.h b/src/fne/HostFNE.h index 3720036a..88dd4882 100644 --- a/src/fne/HostFNE.h +++ b/src/fne/HostFNE.h @@ -118,7 +118,7 @@ class HOST_SW_API HostFNE { uint32_t m_maxMissedPings; uint32_t m_updateLookupTime; - bool m_peerLinkSavesACL; + bool m_peerReplicaSavesACL; bool m_useAlternatePortForDiagnostics; diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index c281a627..5bd20db0 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -195,7 +195,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_peers.find(req->rtpHeader.getSSRC()) != network->m_peers.end()) { FNEPeerConnection* connection = network->m_peers[req->rtpHeader.getSSRC()]; if (connection != nullptr) { - if (connection->isExternalFNEPeer() && connection->isPeerLink()) { + if (connection->isExternalFNEPeer() && connection->isPeerReplica()) { validPeerId = true; pktPeerId = req->rtpHeader.getSSRC(); } @@ -254,7 +254,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerLink()) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { peer.second->writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_ACTIVITY }, req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false, true, pktPeerId); } @@ -343,7 +343,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerLink()) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { peer.second->writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_STATUS }, req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false, true, pktPeerId); } @@ -368,8 +368,8 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } break; - case NET_FUNC::PEER_LINK: - if (req->fneHeader.getSubFunction() == NET_SUBFUNC::PL_ACT_PEER_LIST) { // Peer-Link Active Peer List + case NET_FUNC::REPL: + if (req->fneHeader.getSubFunction() == NET_SUBFUNC::REPL_ACT_PEER_LIST) { // Peer Replication Active Peer List if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) { FNEPeerConnection* connection = network->m_peers[peerId]; if (connection != nullptr) { @@ -377,24 +377,24 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) // validate peer (simple validation really) if (connection->connected() && connection->address() == ip && connection->isExternalFNEPeer() && - connection->isPeerLink()) { + connection->isPeerReplica()) { DECLARE_UINT8_ARRAY(rawPayload, req->length); ::memcpy(rawPayload, req->buffer, req->length); - // Utils::dump(1U, "DiagNetwork::taskNetworkRx(), PEER_LINK, Raw Payload", rawPayload, req->length); + // Utils::dump(1U, "DiagNetwork::taskNetworkRx(), REPL_ACT_PEER_LIST, Raw Payload", rawPayload, req->length); - if (network->m_peerLinkActPkt.find(peerId) == network->m_peerLinkActPkt.end()) { - network->m_peerLinkActPkt.insert(peerId, FNENetwork::PacketBufferEntry()); + if (network->m_peerReplicaActPkt.find(peerId) == network->m_peerReplicaActPkt.end()) { + network->m_peerReplicaActPkt.insert(peerId, FNENetwork::PacketBufferEntry()); - FNENetwork::PacketBufferEntry& pkt = network->m_peerLinkActPkt[peerId]; - pkt.buffer = new PacketBuffer(true, "Peer-Link, Active Peer List"); + FNENetwork::PacketBufferEntry& pkt = network->m_peerReplicaActPkt[peerId]; + pkt.buffer = new PacketBuffer(true, "Peer Replication, Active Peer List"); pkt.streamId = streamId; pkt.locked = false; } else { - FNENetwork::PacketBufferEntry& pkt = network->m_peerLinkActPkt[peerId]; + FNENetwork::PacketBufferEntry& pkt = network->m_peerReplicaActPkt[peerId]; if (!pkt.locked && pkt.streamId != streamId) { - LogError(LOG_NET, "PEER %u Peer-Link, Active Peer List, stream ID mismatch, expected %u, got %u", peerId, pkt.streamId, streamId); + LogError(LOG_NET, "PEER %u Peer Replication, Active Peer List, stream ID mismatch, expected %u, got %u", peerId, pkt.streamId, streamId); pkt.buffer->clear(); pkt.streamId = streamId; } @@ -405,7 +405,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } } - FNENetwork::PacketBufferEntry& pkt = network->m_peerLinkActPkt[peerId]; + FNENetwork::PacketBufferEntry& pkt = network->m_peerReplicaActPkt[peerId]; if (pkt.locked) { while (pkt.locked) Thread::sleep(1U); @@ -429,7 +429,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } - network->m_peerLinkActPkt.erase(peerId); + network->m_peerReplicaActPkt.erase(peerId); break; } else { @@ -442,13 +442,13 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } - network->m_peerLinkActPkt.erase(peerId); + network->m_peerReplicaActPkt.erase(peerId); break; } else { json::array arr = v.get(); - LogInfoEx(LOG_NET, "PEER %u Peer-Link, Active Peer List, updating %u peer entries", peerId, arr.size()); - network->m_peerLinkPeers[peerId] = arr; + LogInfoEx(LOG_NET, "PEER %u Peer Replication, Active Peer List, updating %u peer entries", peerId, arr.size()); + network->m_peerReplicaPeers[peerId] = arr; } } @@ -458,13 +458,13 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } - network->m_peerLinkActPkt.erase(peerId); + network->m_peerReplicaActPkt.erase(peerId); } else { pkt.locked = false; } } else { - network->writePeerNAK(peerId, 0U, TAG_PEER_LINK, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, 0U, TAG_PEER_REPLICA, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 783fa6f4..0654155b 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -87,11 +87,11 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_cryptoLookup(nullptr), m_status(NET_STAT_INVALID), m_peers(), - m_peerLinkPeers(), + m_peerReplicaPeers(), m_peerAffiliations(), m_ccPeerMap(), - m_peerLinkKeyQueue(), - m_peerLinkActPkt(), + m_peerReplicaKeyQueue(), + m_peerReplicaActPkt(), m_maintainenceTimer(1000U, pingTime), m_updateLookupTimer(1000U, (updateLookupTime * 60U)), m_softConnLimit(0U), @@ -364,7 +364,7 @@ void FNENetwork::clock(uint32_t ms) FNEPeerConnection* connection = peer.second; if (connection != nullptr) { uint64_t dt = 0U; - if (connection->isExternalFNEPeer() || connection->isPeerLink()) + if (connection->isExternalFNEPeer() || connection->isPeerReplica()) dt = connection->lastPing() + ((m_host->m_pingTime * 1000) * (m_host->m_maxMissedPings * 2U)); else dt = connection->lastPing() + ((m_host->m_pingTime * 1000) * m_host->m_maxMissedPings); @@ -377,9 +377,9 @@ void FNENetwork::clock(uint32_t ms) connection->connected(false); connection->connectionState(NET_STAT_INVALID); - // if the connection was an external FNE neighbor peer or a peer link -- be noisy about a possible + // if the connection was an external FNE neighbor peer or a peer replica -- be noisy about a possible // netsplit - if (connection->isExternalFNEPeer() || connection->isPeerLink()) { + if (connection->isExternalFNEPeer() || connection->isPeerReplica()) { for (uint8_t i = 0U; i < 3U; i++) LogWarning(LOG_NET, "PEER %u (%s) downstream netsplit, dt = %u, now = %u", id, connection->identity().c_str(), dt, now); @@ -409,7 +409,7 @@ void FNENetwork::clock(uint32_t ms) if (m_host->m_peerNetworks.size() > 0) { for (auto peer : m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerLink()) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { if (!peer.second->getAttachedKeyRSPHandler()) { peer.second->setAttachedKeyRSPHandler(true); // this is the only place this should happen peer.second->setKeyResponseCallback([=](p25::kmm::KeyItem ki, uint8_t algId, uint8_t keyLength) { @@ -448,8 +448,8 @@ void FNENetwork::clock(uint32_t ms) uint32_t id = peer.first; FNEPeerConnection* connection = peer.second; if (connection != nullptr) { - // if this connection is a peer link *always* send the update -- no stream checking - if (connection->connected() && connection->isPeerLink()) { + // if this connection is a peer replica *always* send the update -- no stream checking + if (connection->connected() && connection->isPeerReplica()) { LogInfoEx(LOG_NET, "PEER %u (%s), Peer-Link, updating ACL list", id, connection->identity().c_str()); peerACLUpdate(id); @@ -1031,14 +1031,14 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // check if the peer is participating in peer link lookups::PeerId peerEntry = network->m_peerListLookup->find(req->peerId); if (!peerEntry.peerDefault()) { - if (peerEntry.peerLink()) { + if (peerEntry.peerReplica()) { if (network->m_host->m_useAlternatePortForDiagnostics) { - connection->isPeerLink(true); + connection->isPeerReplica(true); if (external) - LogInfoEx(LOG_NET, "PEER %u configured for Peer-Link", peerId); + LogInfoEx(LOG_NET, "PEER %u configured for peer replication", peerId); } else { - LogError(LOG_NET, "PEER %u, Peer-Link operations *require* the alternate diagnostics port option to be enabled.", peerId); - LogError(LOG_NET, "PEER %u, will not receive Peer-Link ACL updates.", peerId); + LogError(LOG_NET, "PEER %u, Peer replication operations *require* the alternate diagnostics port option to be enabled.", peerId); + LogError(LOG_NET, "PEER %u, will not receive peer replication ACL updates.", peerId); } } } @@ -1305,12 +1305,12 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerLink()) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { LogMessage(LOG_NET, "PEER %u (%s) no local key or container, requesting key from upstream master, algId = $%02X, kID = $%04X", peerId, connection->identity().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); bool locked = network->m_keyQueueMutex.try_lock_for(std::chrono::milliseconds(60)); - network->m_peerLinkKeyQueue[peerId] = modifyKey->getKId(); + network->m_peerReplicaKeyQueue[peerId] = modifyKey->getKId(); if (locked) network->m_keyQueueMutex.unlock(); @@ -1369,7 +1369,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerLink()) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_GRP_AFFIL }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); } @@ -1406,7 +1406,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerLink()) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_UNIT_REG }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false, 0U, ssrc); } @@ -1442,7 +1442,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerLink()) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_UNIT_DEREG }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); } @@ -1479,7 +1479,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerLink()) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_GRP_UNAFFIL }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); } @@ -1529,7 +1529,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerLink()) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_AFFILS }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); } @@ -1578,7 +1578,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerLink()) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_SITE_VC }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); } @@ -1689,11 +1689,11 @@ void FNENetwork::erasePeer(uint32_t peerId) } } - // erase any Peer-Link entries for this peer + // erase any peer replication entries for this peer { - auto it = std::find_if(m_peerLinkPeers.begin(), m_peerLinkPeers.end(), [&](auto& x) { return x.first == peerId; }); - if (it != m_peerLinkPeers.end()) { - m_peerLinkPeers.erase(peerId); + auto it = std::find_if(m_peerReplicaPeers.begin(), m_peerReplicaPeers.end(), [&](auto& x) { return x.first == peerId; }); + if (it != m_peerReplicaPeers.end()) { + m_peerReplicaPeers.erase(peerId); } } @@ -1857,7 +1857,7 @@ void FNENetwork::taskACLUpdate(ACLUpdateRequest* req) // if the connection is an external peer, and peer is participating in peer link, // send the peer proper configuration data - if (connection->isExternalFNEPeer() && connection->isPeerLink()) { + if (connection->isExternalFNEPeer() && connection->isPeerReplica()) { LogInfoEx(LOG_NET, "PEER %u (%s) sending Peer-Link ACL list updates", req->peerId, peerIdentity.c_str()); network->writeWhitelistRIDs(req->peerId, aclStreamId, true); @@ -1909,13 +1909,13 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool isE DECLARE_UINT8_ARRAY(buffer, len); ::memcpy(buffer, b.str().data(), len); - PacketBuffer pkt(true, "Peer-Link, RID List"); + PacketBuffer pkt(true, "Peer Replication, RID List"); pkt.encode((uint8_t*)buffer, len); - LogInfoEx(LOG_NET, "PEER %u Peer-Link, RID List, blocks %u, streamId = %u", peerId, pkt.fragments.size(), streamId); + LogInfoEx(LOG_NET, "PEER %u Peer Replication, RID List, blocks %u, streamId = %u", peerId, pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { - writePeer(peerId, m_peerId, { NET_FUNC::PEER_LINK, NET_SUBFUNC::PL_RID_LIST }, + writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_RID_LIST }, frag.second->data, FRAG_SIZE, 0U, streamId, false, true, true); Thread::sleep(60U); // pace block transmission } @@ -2098,13 +2098,13 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool isExternalP DECLARE_UINT8_ARRAY(buffer, len); ::memcpy(buffer, b.str().data(), len); - PacketBuffer pkt(true, "Peer-Link, TGID List"); + PacketBuffer pkt(true, "Peer Replication, TGID List"); pkt.encode((uint8_t*)buffer, len); - LogInfoEx(LOG_NET, "PEER %u Peer-Link, TGID List, blocks %u, streamId = %u", peerId, pkt.fragments.size(), streamId); + LogInfoEx(LOG_NET, "PEER %u Peer Replication, TGID List, blocks %u, streamId = %u", peerId, pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { - writePeer(peerId, m_peerId, { NET_FUNC::PEER_LINK, NET_SUBFUNC::PL_TALKGROUP_LIST }, + writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_TALKGROUP_LIST }, frag.second->data, FRAG_SIZE, 0U, streamId, false, true, true); Thread::sleep(60U); // pace block transmission } @@ -2276,13 +2276,13 @@ void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) DECLARE_UINT8_ARRAY(buffer, len); ::memcpy(buffer, b.str().data(), len); - PacketBuffer pkt(true, "Peer-Link, PID List"); + PacketBuffer pkt(true, "Peer Replication, PID List"); pkt.encode((uint8_t*)buffer, len); - LogInfoEx(LOG_NET, "PEER %u Peer-Link, PID List, blocks %u, streamId = %u", peerId, pkt.fragments.size(), streamId); + LogInfoEx(LOG_NET, "PEER %u Peer Replication, PID List, blocks %u, streamId = %u", peerId, pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { - writePeer(peerId, m_peerId, { NET_FUNC::PEER_LINK, NET_SUBFUNC::PL_PEER_LIST }, + writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_PEER_LIST }, frag.second->data, FRAG_SIZE, 0U, streamId, false, true, true); Thread::sleep(60U); // pace block transmission } @@ -2339,7 +2339,7 @@ bool FNENetwork::writePeer(uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePai if (m_maskOutboundPeerID) ssrc = m_peerId; // mask the source SSRC to our own peer ID else { - if ((connection->isExternalFNEPeer() && !connection->isPeerLink()) && m_maskOutboundPeerIDForNonPL) { + if ((connection->isExternalFNEPeer() && !connection->isPeerReplica()) && m_maskOutboundPeerIDForNonPL) { // if the peer is an external FNE neighbor peer, and not a Peer-Link peer, we need to send the packet // to the external FNE neighbor peer with our peer ID as the source instead of the originating peer // because we have routed it @@ -2494,7 +2494,7 @@ void FNENetwork::processTEKResponse(p25::kmm::KeyItem* rspKi, uint8_t algId, uin m_keyQueueMutex.lock(); std::vector peersToRemove; - for (auto entry : m_peerLinkKeyQueue) { + for (auto entry : m_peerReplicaKeyQueue) { uint16_t keyId = entry.second; if (keyId == rspKi->kId() && algId > 0U) { uint32_t peerId = entry.first; @@ -2542,7 +2542,7 @@ void FNENetwork::processTEKResponse(p25::kmm::KeyItem* rspKi, uint8_t algId, uin // remove peers who were sent keys for (auto peerId : peersToRemove) - m_peerLinkKeyQueue.erase(peerId); + m_peerReplicaKeyQueue.erase(peerId); m_keyQueueMutex.unlock(); } diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index bce59fac..a668d148 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -125,7 +125,7 @@ namespace network m_isExternalFNEPeer(false), m_isConventionalPeer(false), m_isSysView(false), - m_isPeerLink(false), + m_isPeerReplica(false), m_config(), m_streamSeqMutex(), m_streamSeqNos() @@ -154,7 +154,7 @@ namespace network m_isExternalFNEPeer(false), m_isConventionalPeer(false), m_isSysView(false), - m_isPeerLink(false), + m_isPeerReplica(false), m_config(), m_streamSeqMutex(), m_streamSeqNos() @@ -355,9 +355,9 @@ namespace network DECLARE_PROPERTY_PLAIN(bool, isSysView); /** - * @brief Flag indicating this connection is from an external peer that is peer link enabled. + * @brief Flag indicating this connection is from a neighbor FNE peer that is replica enabled. */ - DECLARE_PROPERTY_PLAIN(bool, isPeerLink); + DECLARE_PROPERTY_PLAIN(bool, isPeerReplica); /** * @brief JSON objecting containing peer configuration information. @@ -578,12 +578,12 @@ namespace network typedef std::pair PeerMapPair; concurrent::unordered_map m_peers; - concurrent::unordered_map m_peerLinkPeers; + concurrent::unordered_map m_peerReplicaPeers; typedef std::pair PeerAffiliationMapPair; concurrent::unordered_map m_peerAffiliations; concurrent::unordered_map> m_ccPeerMap; static std::timed_mutex m_keyQueueMutex; - std::unordered_map m_peerLinkKeyQueue; + std::unordered_map m_peerReplicaKeyQueue; /** * @brief Represents a packet buffer entry in a map. @@ -602,7 +602,7 @@ namespace network bool locked; }; - concurrent::unordered_map m_peerLinkActPkt; + concurrent::unordered_map m_peerReplicaActPkt; Timer m_maintainenceTimer; Timer m_updateLookupTimer; diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index dc4ec26c..d8f3a763 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -46,8 +46,8 @@ PeerNetwork::PeerNetwork(const std::string& address, uint16_t port, uint16_t loc m_nxdnCallback(nullptr), m_analogCallback(nullptr), m_pidLookup(nullptr), - m_peerLink(false), - m_peerLinkSavesACL(false), + m_peerReplica(false), + m_peerReplicaSavesACL(false), m_tgidPkt(true, "Peer-Link, TGID List"), m_ridPkt(true, "Peer-Link, RID List"), m_pidPkt(true, "Peer-Link, PID List"), @@ -112,24 +112,24 @@ bool PeerNetwork::writePeerLinkPeers(json::array* peerList) if (peerList->size() == 0) return false; - if (peerList->size() > 0 && m_peerLink) { + if (peerList->size() > 0 && m_peerReplica) { json::value v = json::value(*peerList); std::string json = std::string(v.serialize()); size_t len = json.length() + 9U; DECLARE_CHAR_ARRAY(buffer, len); - ::memcpy(buffer + 0U, TAG_PEER_LINK, 4U); + ::memcpy(buffer + 0U, TAG_PEER_REPLICA, 4U); ::snprintf(buffer + 8U, json.length() + 1U, "%s", json.c_str()); - PacketBuffer pkt(true, "Peer-Link, Active Peer List"); + PacketBuffer pkt(true, "Peer Replication, Active Peer List"); pkt.encode((uint8_t*)buffer, len); uint32_t streamId = createStreamId(); - LogInfoEx(LOG_NET, "PEER %u Peer-Link, Active Peer List, blocks %u, streamId = %u", m_peerId, pkt.fragments.size(), streamId); + LogInfoEx(LOG_NET, "PEER %u Peer Replication, Active Peer List, blocks %u, streamId = %u", m_peerId, pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { - writeMaster({ NET_FUNC::PEER_LINK, NET_SUBFUNC::PL_ACT_PEER_LIST }, + writeMaster({ NET_FUNC::REPL, NET_SUBFUNC::REPL_ACT_PEER_LIST }, frag.second->data, FRAG_SIZE, RTP_END_OF_CALL_SEQ, streamId, false, true); Thread::sleep(60U); // pace block transmission } @@ -182,10 +182,10 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco } break; - case NET_FUNC::PEER_LINK: // Peer Link + case NET_FUNC::REPL: // Peer Replication { switch (opcode.second) { - case NET_SUBFUNC::PL_TALKGROUP_LIST: // Talkgroup List + case NET_SUBFUNC::REPL_TALKGROUP_LIST: // Talkgroup List { uint32_t decompressedLen = 0U; uint8_t* decompressed = nullptr; @@ -205,7 +205,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco // randomize filename std::ostringstream s; - if (!m_peerLinkSavesACL) { + if (!m_peerReplicaSavesACL) { std::random_device rd; std::mt19937 mt(rd()); std::uniform_int_distribution dist(0x00U, 0xFFFFFFFFU); @@ -231,8 +231,8 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco m_tidLookup->filename(filename); m_tidLookup->reload(); - // flag this peer as Peer-Link enabled - m_peerLink = true; + // flag this peer as replica enabled + m_peerReplica = true; // cleanup temporary file ::remove(filename.c_str()); @@ -242,7 +242,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco } break; - case NET_SUBFUNC::PL_RID_LIST: // Radio ID List + case NET_SUBFUNC::REPL_RID_LIST: // Radio ID List { uint32_t decompressedLen = 0U; uint8_t* decompressed = nullptr; @@ -262,7 +262,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco // randomize filename std::ostringstream s; - if (!m_peerLinkSavesACL) { + if (!m_peerReplicaSavesACL) { std::random_device rd; std::mt19937 mt(rd()); std::uniform_int_distribution dist(0x00U, 0xFFFFFFFFU); @@ -288,8 +288,8 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco m_ridLookup->filename(filename); m_ridLookup->reload(); - // flag this peer as Peer-Link enabled - m_peerLink = true; + // flag this peer as replica enabled + m_peerReplica= true; // cleanup temporary file ::remove(filename.c_str()); @@ -299,7 +299,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco } break; - case NET_SUBFUNC::PL_PEER_LIST: // Peer List + case NET_SUBFUNC::REPL_PEER_LIST: // Peer List { uint32_t decompressedLen = 0U; uint8_t* decompressed = nullptr; @@ -319,7 +319,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco // randomize filename std::ostringstream s; - if (!m_peerLinkSavesACL) { + if (!m_peerReplicaSavesACL) { std::random_device rd; std::mt19937 mt(rd()); std::uniform_int_distribution dist(0x00U, 0xFFFFFFFFU); @@ -345,8 +345,8 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco m_pidLookup->filename(filename); m_pidLookup->reload(); - // flag this peer as Peer-Link enabled - m_peerLink = true; + // flag this peer as replica enabled + m_peerReplica = true; // cleanup temporary file ::remove(filename.c_str()); diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index 34624655..4c72cd96 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -132,16 +132,16 @@ namespace network bool writePeerLinkPeers(json::array* peerList); /** - * @brief Returns flag indicating whether or not this peer connection is Peer-Link enabled. - * @returns bool True, if Peer-Link enabled, otherwise false. + * @brief Returns flag indicating whether or not this peer connection is peer replication enabled. + * @returns bool True, if peer replication enabled, otherwise false. */ - bool isPeerLink() const { return m_peerLink; } + bool isPeerReplica() const { return m_peerReplica; } /** - * @brief Enables the option that will save the pushed peer link ACL data to the local ACL files. - * @param enabled Flag to enable ACL data saving. + * @brief Enables the option that will save replicated ACL data to the local ACL files. + * @param enabled Flag to enable replicated ACL data saving. */ - void setPeerLinkSaveACL(bool enabled) { m_peerLinkSavesACL = enabled; } + void setPeerReplicationSaveACL(bool enabled) { m_peerReplicaSavesACL = enabled; } public: /** @@ -192,8 +192,8 @@ namespace network private: lookups::PeerListLookup* m_pidLookup; - bool m_peerLink; - bool m_peerLinkSavesACL; + bool m_peerReplica; + bool m_peerReplicaSavesACL; PacketBuffer m_tgidPkt; PacketBuffer m_ridPkt; diff --git a/src/fne/network/RESTAPI.cpp b/src/fne/network/RESTAPI.cpp index f57c36ae..1e527ca4 100644 --- a/src/fne/network/RESTAPI.cpp +++ b/src/fne/network/RESTAPI.cpp @@ -873,9 +873,9 @@ void RESTAPI::restAPI_GetPeerQuery(const HTTPPayload& request, HTTPPayload& repl LogDebug(LOG_REST, "No peers connected to this FNE"); } - // report any Peer-Link reported peers - if (m_network->m_peerLinkPeers.size() > 0) { - for (auto entry : m_network->m_peerLinkPeers) { + // report any peers from replica peers + if (m_network->m_peerReplicaPeers.size() > 0) { + for (auto entry : m_network->m_peerReplicaPeers) { json::array peerObjs = entry.second; if (entry.second.size() > 0) { for (auto linkEntry : entry.second) { @@ -1221,11 +1221,11 @@ void RESTAPI::restAPI_GetPeerList(const HTTPPayload& request, HTTPPayload& reply uint32_t peerId = entry.first; std::string peerAlias = entry.second.peerAlias(); - bool peerLink = entry.second.peerLink(); + bool peerReplica = entry.second.peerReplica(); bool peerPassword = !entry.second.peerPassword().empty(); // True if password is not empty, otherwise false peerObj["peerId"].set(peerId); peerObj["peerAlias"].set(peerAlias); - peerObj["peerLink"].set(peerLink); + peerObj["peerReplica"].set(peerReplica); peerObj["peerPassword"].set(peerPassword); peers.push_back(json::value(peerObj)); } @@ -1272,15 +1272,15 @@ void RESTAPI::restAPI_PutPeerAdd(const HTTPPayload& request, HTTPPayload& reply, } // Get peer link setting (optional) - bool peerLink = false; - if (req.find("peerLink") != req.end()) { + bool peerReplica = false; + if (req.find("peerReplica") != req.end()) { // Validate - if (!req["peerLink"].is()) { - errorPayload(reply, "peerLink was not a valid boolean"); + if (!req["peerReplica"].is()) { + errorPayload(reply, "peerReplica was not a valid boolean"); return; } // Get - peerLink = req["peerLink"].get(); + peerReplica = req["peerReplica"].get(); } // Get peer password (optional) @@ -1296,7 +1296,7 @@ void RESTAPI::restAPI_PutPeerAdd(const HTTPPayload& request, HTTPPayload& reply, } PeerId entry = PeerId(peerId, peerAlias, peerPassword, false); - entry.peerLink(peerLink); + entry.peerReplica(peerReplica); m_peerListLookup->addEntry(peerId, entry); } diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index c0d81c38..4a048fa2 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -301,8 +301,8 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // perform TGID route rewrites if configured routeRewrite(outboundPeerBuffer, dstPeerId, dstId); - // are we a peer link? - if (peer.second->isPeerLink()) + // are we a replica peer? + if (peer.second->isPeerReplica()) peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId); @@ -426,10 +426,10 @@ bool TagAnalogData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32 connection = m_network->m_peers[peerId]; } - // is this peer a Peer-Link peer? + // is this peer a replica peer? if (connection != nullptr) { - if (connection->isPeerLink()) { - return true; // Peer Link peers are *always* allowed to receive traffic and no other rules may filter + if (connection->isPeerReplica()) { + return true; // replica peers are *always* allowed to receive traffic and no other rules may filter // these peers } } diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 1de1c20f..93fcd5bf 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -494,8 +494,8 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // perform TGID route rewrites if configured routeRewrite(outboundPeerBuffer, dstPeerId, dmrData, dataType, dstId, slotNo); - // are we a peer link? - if (peer.second->isPeerLink()) + // are we a replica peer? + if (peer.second->isPeerReplica()) peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId); @@ -808,10 +808,10 @@ bool TagDMRData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t connection = m_network->m_peers[peerId]; } - // is this peer a Peer-Link peer? + // is this peer a replica peer? if (connection != nullptr) { - if (connection->isPeerLink()) { - return true; // Peer Link peers are *always* allowed to receive traffic and no other rules may filter + if (connection->isPeerReplica()) { + return true; // replica peers are *always* allowed to receive traffic and no other rules may filter // these peers } } diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 28fa8958..62666bac 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -524,8 +524,8 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // perform TGID route rewrites if configured routeRewrite(outboundPeerBuffer, dstPeerId, messageType, dstId); - // are we a peer link? - if (peer.second->isPeerLink()) + // are we a replica peer? + if (peer.second->isPeerReplica()) peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId); @@ -700,10 +700,10 @@ bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t message connection = m_network->m_peers[peerId]; } - // is this peer a Peer-Link peer? + // is this peer a replica peer? if (connection != nullptr) { - if (connection->isPeerLink()) { - return true; // Peer Link peers are *always* allowed to receive traffic and no other rules may filter + if (connection->isPeerReplica()) { + return true; // replica peers are *always* allowed to receive traffic and no other rules may filter // these peers } } diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 1717f9bc..d89442fe 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -555,8 +555,8 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // process TSDUs going to external peers if (processTSDUToExternal(outboundPeerBuffer, peerId, dstPeerId, duid)) { - // are we a peer link? - if (peer.second->isPeerLink()) + // are we a replica peer? + if (peer.second->isPeerReplica()) peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId); @@ -1115,10 +1115,10 @@ bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, DUID::E duid, connection = m_network->m_peers[peerId]; } - // is this peer a Peer-Link peer? + // is this peer a replica peer? if (connection != nullptr) { - if (connection->isPeerLink()) { - return true; // Peer Link peers are *always* allowed to receive traffic and no other rules may filter + if (connection->isPeerReplica()) { + return true; // replica peers are *always* allowed to receive traffic and no other rules may filter // these peers } } diff --git a/src/peered/PeerEditWnd.h b/src/peered/PeerEditWnd.h index 68a92080..f42fd99f 100644 --- a/src/peered/PeerEditWnd.h +++ b/src/peered/PeerEditWnd.h @@ -150,7 +150,7 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { FLineEdit m_peerPassword{&m_sourceGroup}; FButtonGroup m_configGroup{"Configuration", this}; - FCheckBox m_peerLinkEnabled{"Peer Link", &m_configGroup}; + FCheckBox m_peerReplicaEnabled{"Peer Replica", &m_configGroup}; FCheckBox m_canReqKeysEnabled{"Request Keys", &m_configGroup}; FCheckBox m_canInhibitEnabled{"Issue Inhibit", &m_configGroup}; @@ -265,10 +265,10 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { { m_configGroup.setGeometry(FPoint(39, 5), FSize(23, 5)); - m_peerLinkEnabled.setGeometry(FPoint(2, 1), FSize(10, 1)); - m_peerLinkEnabled.setChecked(m_rule.peerLink()); - m_peerLinkEnabled.addCallback("toggled", [&]() { - m_rule.peerLink(m_peerLinkEnabled.isChecked()); + m_peerReplicaEnabled.setGeometry(FPoint(2, 1), FSize(10, 1)); + m_peerReplicaEnabled.setChecked(m_rule.peerReplica()); + m_peerReplicaEnabled.addCallback("toggled", [&]() { + m_rule.peerReplica(m_peerReplicaEnabled.isChecked()); }); m_canReqKeysEnabled.setGeometry(FPoint(2, 2), FSize(10, 1)); @@ -294,11 +294,11 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { { std::string peerAlias = m_rule.peerAlias(); uint32_t peerId = m_rule.peerId(); - bool peerLink = m_rule.peerLink(); + bool peerReplica = m_rule.peerReplica(); bool canRequestKeys = m_rule.canRequestKeys(); bool canIssueInhibit = m_rule.canIssueInhibit(); - ::LogInfoEx(LOG_HOST, "Peer ALIAS: %s PEERID: %u PEER LINK: %u CAN REQUEST KEYS: %u CAN ISSUE INHIBIT: %u", peerAlias.c_str(), peerId, peerLink, canRequestKeys, canIssueInhibit); + ::LogInfoEx(LOG_HOST, "Peer ALIAS: %s PEERID: %u REPLICA: %u CAN REQUEST KEYS: %u CAN ISSUE INHIBIT: %u", peerAlias.c_str(), peerId, peerReplica, canRequestKeys, canIssueInhibit); } /* @@ -365,7 +365,7 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { g_pidLookups->eraseEntry(m_origPeerId); lookups::PeerId entry = lookups::PeerId(m_rule.peerId(), m_rule.peerAlias(), m_rule.peerPassword(), false); - entry.peerLink(m_rule.peerLink()); + entry.peerReplica(m_rule.peerReplica()); entry.canRequestKeys(m_rule.canRequestKeys()); entry.canIssueInhibit(m_rule.canIssueInhibit()); @@ -401,7 +401,7 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { } lookups::PeerId entry = lookups::PeerId(m_rule.peerId(), m_rule.peerAlias(), m_rule.peerPassword(), false); - entry.peerLink(m_rule.peerLink()); + entry.peerReplica(m_rule.peerReplica()); entry.canRequestKeys(m_rule.canRequestKeys()); entry.canIssueInhibit(m_rule.canIssueInhibit()); diff --git a/src/peered/PeerListWnd.h b/src/peered/PeerListWnd.h index 7fa6fd14..f4a82d4e 100644 --- a/src/peered/PeerListWnd.h +++ b/src/peered/PeerListWnd.h @@ -122,7 +122,7 @@ class HOST_SW_API PeerListWnd final : public FDblDialog { const std::array columns = { oss.str(), (masterPassword) ? "X" : "", - (entry.peerLink()) ? "X" : "", + (entry.peerReplica()) ? "X" : "", (entry.canRequestKeys()) ? "X" : "", (entry.canIssueInhibit()) ? "X" : "", entry.peerAlias() @@ -210,7 +210,7 @@ class HOST_SW_API PeerListWnd final : public FDblDialog { // configure list view columns m_listView.addColumn("Peer ID", 10); m_listView.addColumn("Master Password", 16); - m_listView.addColumn("Peer Link", 12); + m_listView.addColumn("Peer Replica", 12); m_listView.addColumn("Request Keys", 12); m_listView.addColumn("Can Inhibit", 12); m_listView.addColumn("Alias", 40); diff --git a/src/sysview/network/PeerNetwork.cpp b/src/sysview/network/PeerNetwork.cpp index 7ebe2a58..ce378515 100644 --- a/src/sysview/network/PeerNetwork.cpp +++ b/src/sysview/network/PeerNetwork.cpp @@ -4,7 +4,7 @@ * 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 + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ #include "sysview/Defines.h" @@ -38,9 +38,9 @@ PeerNetwork::PeerNetwork(const std::string& address, uint16_t port, uint16_t loc bool duplex, bool debug, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup) : Network(address, port, localPort, peerId, password, duplex, debug, true, true, true, true, true, true, allowActivityTransfer, allowDiagnosticTransfer, updateLookup, saveLookup), peerStatus(), - m_peerLink(false), - m_tgidPkt(true, "Peer-Link, TGID List"), - m_ridPkt(true, "Peer-Link, RID List") + m_peerReplica(false), + m_tgidPkt(true, "Peer Replication, TGID List"), + m_ridPkt(true, "Peer Replication, RID List") { assert(!address.empty()); assert(port > 0U); @@ -116,10 +116,10 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco } break; - case NET_FUNC::PEER_LINK: + case NET_FUNC::REPL: { switch (opcode.second) { - case NET_SUBFUNC::PL_TALKGROUP_LIST: + case NET_SUBFUNC::REPL_TALKGROUP_LIST: { uint32_t decompressedLen = 0U; uint8_t* decompressed = nullptr; @@ -160,8 +160,8 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco m_tidLookup->filename(filename); m_tidLookup->reload(); - // flag this peer as Peer-Link enabled - m_peerLink = true; + // flag this peer as replica enabled + m_peerReplica = true; // cleanup temporary file ::remove(filename.c_str()); @@ -171,7 +171,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco } break; - case NET_SUBFUNC::PL_RID_LIST: + case NET_SUBFUNC::REPL_RID_LIST: { uint32_t decompressedLen = 0U; uint8_t* decompressed = nullptr; @@ -212,8 +212,8 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco m_ridLookup->filename(filename); m_ridLookup->reload(); - // flag this peer as Peer-Link enabled - m_peerLink = true; + // flag this peer as replica enabled + m_peerReplica = true; // cleanup temporary file ::remove(filename.c_str()); diff --git a/src/sysview/network/PeerNetwork.h b/src/sysview/network/PeerNetwork.h index c4bb5daa..82a63e1f 100644 --- a/src/sysview/network/PeerNetwork.h +++ b/src/sysview/network/PeerNetwork.h @@ -4,7 +4,7 @@ * 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 + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -57,9 +57,9 @@ namespace network bool duplex, bool debug, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup); /** - * @brief Flag indicating whether or not SysView has received Peer-Link data transfers. + * @brief Flag indicating whether or not SysView has received peer replication data transfers. */ - bool hasPeerLink() const { return m_peerLink; } + bool hasPeerReplica() const { return m_peerReplica; } /** * @brief Helper to lock the peer status mutex. @@ -97,7 +97,7 @@ namespace network private: static std::mutex m_peerStatusMutex; - bool m_peerLink; + bool m_peerReplica; PacketBuffer m_tgidPkt; PacketBuffer m_ridPkt; From bf23495b4c053fd012781724c7b061e95f05c3dc Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 6 Oct 2025 11:04:17 -0400 Subject: [PATCH 048/200] implement identity with qualifier, this makes logs (and only logs) easier to trace by uniquely identifying certain peer types, a peer qualifier is simply a symbol appended to the beginning of a resolved peer identity in the FNE logs (@ = SysView, + = External/Linked FNE, % = Replica FNE); silence the Call Source Switched log messages, we will only actually print these if the call source ID changes; fix missed Peer-Link log branding/naming; --- src/fne/network/DiagNetwork.cpp | 10 +- src/fne/network/FNENetwork.cpp | 103 ++++++++++++-------- src/fne/network/FNENetwork.h | 29 +++++- src/fne/network/PeerNetwork.cpp | 6 +- src/fne/network/callhandler/TagDMRData.cpp | 10 +- src/fne/network/callhandler/TagNXDNData.cpp | 10 +- src/fne/network/callhandler/TagP25Data.cpp | 10 +- 7 files changed, 118 insertions(+), 60 deletions(-) diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 5bd20db0..5267c05e 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -191,7 +191,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) pktPeerId = peerId; } else { if (peerId > 0) { - // this could be a peer-link transfer -- in which case, we need to check the SSRC of the packet not the peer ID + // this could be a replica transfer -- in which case, we need to check the SSRC of the packet not the peer ID if (network->m_peers.find(req->rtpHeader.getSSRC()) != network->m_peers.end()) { FNEPeerConnection* connection = network->m_peers[req->rtpHeader.getSSRC()]; if (connection != nullptr) { @@ -220,7 +220,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) ::memcpy(rawPayload, req->buffer + 11U, req->length - 11U); std::string payload(rawPayload, rawPayload + (req->length - 11U)); - ::ActivityLog("%.9u (%8s) %s", pktPeerId, connection->identity().c_str(), payload.c_str()); + ::ActivityLog("%.9u (%8s) %s", pktPeerId, connection->identWithQualifier().c_str(), payload.c_str()); // report activity log to InfluxDB if (network->m_enableInfluxDB) { @@ -250,7 +250,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } } - // attempt to repeat traffic to Peer-Link masters + // attempt to repeat traffic to replica masters if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { @@ -287,7 +287,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) bool currState = g_disableTimeDisplay; g_disableTimeDisplay = true; - ::Log(9999U, {nullptr, nullptr, 0U, nullptr}, "%.9u (%8s) %s", peerId, connection->identity().c_str(), payload.c_str()); + ::Log(9999U, {nullptr, nullptr, 0U, nullptr}, "%.9u (%8s) %s", peerId, connection->identWithQualifier().c_str(), payload.c_str()); g_disableTimeDisplay = currState; // report diagnostic log to InfluxDB @@ -339,7 +339,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } } - // attempt to repeat status traffic to Peer-Link masters + // attempt to repeat status traffic to replica masters if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 0654155b..4e30924c 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -370,7 +370,7 @@ void FNENetwork::clock(uint32_t ms) dt = connection->lastPing() + ((m_host->m_pingTime * 1000) * m_host->m_maxMissedPings); if (dt < now) { - LogInfoEx(LOG_NET, "PEER %u (%s) timed out, dt = %u, now = %u", id, connection->identity().c_str(), + LogInfoEx(LOG_NET, "PEER %u (%s) timed out, dt = %u, now = %u", id, connection->identWithQualifier().c_str(), dt, now); // set connection states for this stale connection @@ -381,7 +381,7 @@ void FNENetwork::clock(uint32_t ms) // netsplit if (connection->isExternalFNEPeer() || connection->isPeerReplica()) { for (uint8_t i = 0U; i < 3U; i++) - LogWarning(LOG_NET, "PEER %u (%s) downstream netsplit, dt = %u, now = %u", id, connection->identity().c_str(), + LogWarning(LOG_NET, "PEER %u (%s) downstream netsplit, dt = %u, now = %u", id, connection->identWithQualifier().c_str(), dt, now); } @@ -405,7 +405,7 @@ void FNENetwork::clock(uint32_t ms) m_frameQueue->clearTimestamps(); } - // send active peer list to Peer-Link masters + // send active peer list to replica masters if (m_host->m_peerNetworks.size() > 0) { for (auto peer : m_host->m_peerNetworks) { if (peer.second != nullptr) { @@ -450,7 +450,7 @@ void FNENetwork::clock(uint32_t ms) if (connection != nullptr) { // if this connection is a peer replica *always* send the update -- no stream checking if (connection->connected() && connection->isPeerReplica()) { - LogInfoEx(LOG_NET, "PEER %u (%s), Peer-Link, updating ACL list", id, connection->identity().c_str()); + LogInfoEx(LOG_NET, "PEER %u (%s), Peer Replication, updating ACL list", id, connection->identWithQualifier().c_str()); peerACLUpdate(id); connection->missedACLUpdates(0U); @@ -459,14 +459,14 @@ void FNENetwork::clock(uint32_t ms) if (connection->connected()) { if ((connection->streamCount() <= 1) || (connection->missedACLUpdates() > MAX_MISSED_ACL_UPDATES)) { - LogInfoEx(LOG_NET, "PEER %u (%s) updating ACL list", id, connection->identity().c_str()); + LogInfoEx(LOG_NET, "PEER %u (%s) updating ACL list", id, connection->identWithQualifier().c_str()); peerACLUpdate(id); connection->missedACLUpdates(0U); } else { uint32_t missed = connection->missedACLUpdates(); missed++; - LogInfoEx(LOG_NET, "PEER %u (%s) skipped for ACL update, traffic in progress", id, connection->identity().c_str()); + LogInfoEx(LOG_NET, "PEER %u (%s) skipped for ACL update, traffic in progress", id, connection->identWithQualifier().c_str()); connection->missedACLUpdates(missed); } } @@ -633,15 +633,40 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) connection->eraseStreamPktSeq(streamId); // attempt to erase packet sequence for the stream } } else { + bool skip = false; if (connection->hasStreamPktSeq(streamId)) { uint16_t currPkt = connection->getStreamPktSeq(streamId); if ((pktSeq != currPkt) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U)) && pktSeq != 0U) { - LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; %u != %u", peerId, connection->identity().c_str(), + LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; got %u, expected %u", peerId, connection->identWithQualifier().c_str(), streamId, pktSeq, currPkt); + + if (req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) { + // possibly lost frames + if (pktSeq > currPkt) { + LogError(LOG_NET, "PEER %u (%s) stream %u possible lost frames; got %u, expected %u, resetting sequence to %u", peerId, connection->identWithQualifier().c_str(), + streamId, pktSeq, currPkt, pktSeq); + connection->setStreamPktSeq(streamId, pktSeq + 1U, true); + skip = true; // don't alter the packet sequence any further + } + + // received out of order frames + if (pktSeq < currPkt) { + LogError(LOG_NET, "PEER %u (%s) stream %u out-of-order; got %u, expected %u, dropped", peerId, connection->identWithQualifier().c_str(), + streamId, pktSeq, currPkt); + + // don't process blatently out-of-order frames + if (req->buffer != nullptr) + delete[] req->buffer; + delete req; + + return; + } + } } } - connection->incStreamPktSeq(streamId, pktSeq + 1U); + if (!skip) + connection->setStreamPktSeq(streamId, pktSeq + 1U); } } @@ -820,7 +845,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) FNEPeerConnection* connection = network->m_peers[peerId]; if (connection != nullptr) { if (connection->connectionState() == NET_STAT_RUNNING) { - LogMessage(LOG_NET, "PEER %u (%s) resetting peer connection, connectionState = %u", peerId, connection->identity().c_str(), + LogMessage(LOG_NET, "PEER %u (%s) resetting peer connection, connectionState = %u", peerId, connection->identWithQualifier().c_str(), connection->connectionState()); delete connection; @@ -848,7 +873,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } else { network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); - LogWarning(LOG_NET, "PEER %u (%s) RPTL NAK, bad connection state, connectionState = %u", peerId, connection->identity().c_str(), + LogWarning(LOG_NET, "PEER %u (%s) RPTL NAK, bad connection state, connectionState = %u", peerId, connection->identWithQualifier().c_str(), connection->connectionState()); network->erasePeer(peerId); @@ -1078,7 +1103,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } } else { - LogWarning(LOG_NET, "PEER %u (%s) RPTC NAK, login exchange while in an incorrect state, connectionState = %u", peerId, connection->identity().c_str(), + LogWarning(LOG_NET, "PEER %u (%s) RPTC NAK, login exchange while in an incorrect state, connectionState = %u", peerId, connection->identWithQualifier().c_str(), connection->connectionState()); network->writePeerNAK(peerId, TAG_REPEATER_CONFIG, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); network->erasePeer(peerId); @@ -1102,7 +1127,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // validate peer (simple validation really) if (connection->connected() && connection->address() == ip) { - LogInfoEx(LOG_NET, "PEER %u (%s) disconnected", peerId, connection->identity().c_str()); + LogInfoEx(LOG_NET, "PEER %u (%s) disconnected", peerId, connection->identWithQualifier().c_str()); network->erasePeer(peerId); delete connection; } @@ -1143,7 +1168,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) network->writePeerCommand(peerId, { NET_FUNC::PONG, NET_SUBFUNC::NOP }, payload, 8U, streamId, false); if (network->m_reportPeerPing) { - LogInfoEx(LOG_NET, "PEER %u (%s) ping, pingsReceived = %u, lastPing = %u, now = %u", peerId, connection->identity().c_str(), + LogInfoEx(LOG_NET, "PEER %u (%s) ping, pingsReceived = %u, lastPing = %u, now = %u", peerId, connection->identWithQualifier().c_str(), connection->pingsReceived(), lastPing, now); } } @@ -1240,7 +1265,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) break; } else { if (!peerEntry.canRequestKeys()) { - LogError(LOG_NET, "PEER %u (%s) requested enc. key but is not allowed, no response", peerId, connection->identity().c_str()); + LogError(LOG_NET, "PEER %u (%s) requested enc. key but is not allowed, no response", peerId, connection->identWithQualifier().c_str()); break; } } @@ -1248,7 +1273,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) std::unique_ptr frame = KMMFactory::create(req->buffer + 11U); if (frame == nullptr) { - LogWarning(LOG_NET, "PEER %u (%s), undecodable KMM frame from peer", peerId, connection->identity().c_str()); + LogWarning(LOG_NET, "PEER %u (%s), undecodable KMM frame from peer", peerId, connection->identWithQualifier().c_str()); break; } @@ -1257,7 +1282,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) { KMMModifyKey* modifyKey = static_cast(frame.get()); if (modifyKey->getAlgId() > 0U && modifyKey->getKId() > 0U) { - LogMessage(LOG_NET, "PEER %u (%s) requested enc. key, algId = $%02X, kID = $%04X", peerId, connection->identity().c_str(), + LogMessage(LOG_NET, "PEER %u (%s) requested enc. key, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); ::KeyItem keyItem = network->m_cryptoLookup->find(modifyKey->getKId()); if (!keyItem.isInvalid()) { @@ -1270,7 +1295,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) Utils::dump(1U, "FNENetwork::taskNetworkRx(), Key", key, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); } - LogMessage(LOG_NET, "PEER %u (%s) local enc. key, algId = $%02X, kID = $%04X", peerId, connection->identity().c_str(), + LogMessage(LOG_NET, "PEER %u (%s) local enc. key, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); // build response buffer @@ -1301,12 +1326,12 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) network->writePeer(peerId, network->m_peerId, { NET_FUNC::KEY_RSP, NET_SUBFUNC::NOP }, buffer, modifyKeyRsp.length() + 11U, RTP_END_OF_CALL_SEQ, network->createStreamId(), false, false, true); } else { - // attempt to forward KMM key request to Peer-Link masters + // attempt to forward KMM key request to replica masters if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isPeerReplica()) { - LogMessage(LOG_NET, "PEER %u (%s) no local key or container, requesting key from upstream master, algId = $%02X, kID = $%04X", peerId, connection->identity().c_str(), + LogMessage(LOG_NET, "PEER %u (%s) no local key or container, requesting key from upstream master, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); bool locked = network->m_keyQueueMutex.try_lock_for(std::chrono::milliseconds(60)); @@ -1354,7 +1379,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str()); + LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } @@ -1365,7 +1390,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) aff->groupUnaff(srcId); aff->groupAff(srcId, dstId); - // attempt to repeat traffic to Peer-Link masters + // attempt to repeat traffic to replica masters if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { @@ -1393,7 +1418,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); fne_lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str()); + LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } @@ -1402,7 +1427,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) uint32_t srcId = GET_UINT24(req->buffer, 0U); // Source Address aff->unitReg(srcId, ssrc); - // attempt to repeat traffic to Peer-Link masters + // attempt to repeat traffic to replica masters if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { @@ -1429,7 +1454,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str()); + LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } @@ -1438,7 +1463,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) uint32_t srcId = GET_UINT24(req->buffer, 0U); // Source Address aff->unitDereg(srcId); - // attempt to repeat traffic to Peer-Link masters + // attempt to repeat traffic to replica masters if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { @@ -1466,7 +1491,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str()); + LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } @@ -1475,7 +1500,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) uint32_t srcId = GET_UINT24(req->buffer, 0U); // Source Address aff->groupUnaff(srcId); - // attempt to repeat traffic to Peer-Link masters + // attempt to repeat traffic to replica masters if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { @@ -1506,7 +1531,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (connection->connected() && connection->address() == ip) { lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str()); + LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } @@ -1523,9 +1548,9 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) aff->groupAff(srcId, dstId); offs += 8U; } - LogMessage(LOG_NET, "PEER %u (%s) announced %u affiliations", peerId, connection->identity().c_str(), len); + LogMessage(LOG_NET, "PEER %u (%s) announced %u affiliations", peerId, connection->identWithQualifier().c_str(), len); - // attempt to repeat traffic to Peer-Link masters + // attempt to repeat traffic to replica masters if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { @@ -1571,10 +1596,10 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } offs += 4U; } - LogMessage(LOG_NET, "PEER %u (%s) announced %u VCs", peerId, connection->identity().c_str(), len); + LogMessage(LOG_NET, "PEER %u (%s) announced %u VCs", peerId, connection->identWithQualifier().c_str(), len); network->m_ccPeerMap[peerId] = vcPeers; - // attempt to repeat traffic to Peer-Link masters + // attempt to repeat traffic to replica masters if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { @@ -1767,7 +1792,7 @@ bool FNENetwork::resetPeer(uint32_t peerId) sockaddr_storage addr = connection->socketStorage(); uint32_t addrLen = connection->sockStorageLen(); - LogInfoEx(LOG_NET, "PEER %u (%s) resetting peer connection", peerId, connection->identity().c_str()); + LogInfoEx(LOG_NET, "PEER %u (%s) resetting peer connection", peerId, connection->identWithQualifier().c_str()); writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_PEER_RESET, addr, addrLen); @@ -1790,7 +1815,7 @@ std::string FNENetwork::resolvePeerIdentity(uint32_t peerId) if (it != m_peers.end()) { if (it->second != nullptr) { FNEPeerConnection* peer = it->second; - return peer->identity(); + return peer->identWithQualifier(); } } @@ -1858,7 +1883,7 @@ void FNENetwork::taskACLUpdate(ACLUpdateRequest* req) // if the connection is an external peer, and peer is participating in peer link, // send the peer proper configuration data if (connection->isExternalFNEPeer() && connection->isPeerReplica()) { - LogInfoEx(LOG_NET, "PEER %u (%s) sending Peer-Link ACL list updates", req->peerId, peerIdentity.c_str()); + LogInfoEx(LOG_NET, "PEER %u (%s) sending replica ACL list updates", req->peerId, peerIdentity.c_str()); network->writeWhitelistRIDs(req->peerId, aclStreamId, true); network->writeTGIDs(req->peerId, aclStreamId, true); @@ -1978,7 +2003,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool isE uint32_t id = ridWhitelist.at(j + (i * MAX_RID_LIST_CHUNK)); if (m_debug) - LogDebug(LOG_NET, "PEER %u (%s) whitelisting RID %u (%d / %d)", peerId, connection->identity().c_str(), + LogDebug(LOG_NET, "PEER %u (%s) whitelisting RID %u (%d / %d)", peerId, connection->identWithQualifier().c_str(), id, i, j); SET_UINT32(id, payload, offs); @@ -2050,7 +2075,7 @@ void FNENetwork::writeBlacklistRIDs(uint32_t peerId, uint32_t streamId) uint32_t id = ridBlacklist.at(j + (i * MAX_RID_LIST_CHUNK)); if (m_debug) - LogDebug(LOG_NET, "PEER %u (%s) blacklisting RID %u (%d / %d)", peerId, connection->identity().c_str(), + LogDebug(LOG_NET, "PEER %u (%s) blacklisting RID %u (%d / %d)", peerId, connection->identWithQualifier().c_str(), id, i, j); SET_UINT32(id, payload, offs); @@ -2333,14 +2358,14 @@ bool FNENetwork::writePeer(uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePai uint32_t addrLen = connection->sockStorageLen(); if (incPktSeq) { - pktSeq = connection->incStreamPktSeq(streamId, pktSeq); + pktSeq = connection->setStreamPktSeq(streamId, pktSeq); } if (m_maskOutboundPeerID) ssrc = m_peerId; // mask the source SSRC to our own peer ID else { if ((connection->isExternalFNEPeer() && !connection->isPeerReplica()) && m_maskOutboundPeerIDForNonPL) { - // if the peer is an external FNE neighbor peer, and not a Peer-Link peer, we need to send the packet + // if the peer is an external FNE neighbor peer, and not a replica peer, we need to send the packet // to the external FNE neighbor peer with our peer ID as the source instead of the originating peer // because we have routed it ssrc = m_peerId; diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index a668d148..587fa83e 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -174,6 +174,22 @@ namespace network return m_streamSeqNos.size(); } + /** + * @brief Returns the identity with qualifier symbols. + * @return std::string Identity with qualifier. + */ + std::string identWithQualifier() const + { + if (isSysView()) + return "@" + identity(); + if (isExternalFNEPeer()) + return "+" + identity(); + if (isPeerReplica()) + return "%" + identity(); + + return " " + m_identity; + } + /** * @brief Helper to determine if the stream ID has a stored RTP sequence. * @param streamId Stream ID. @@ -228,12 +244,13 @@ namespace network } /** - * @brief Helper to increment the stored RTP sequence for the given stream ID. + * @brief Helper to set/increment the stored RTP sequence for the given stream ID. * @param streamId Stream ID. * @param initialSeq Initial sequence number to set. + * @param force Force sequence number to be reset to the value provided by initialSeq. * @returns uint16_t */ - uint16_t incStreamPktSeq(uint64_t streamId, uint16_t initialSeq) + uint16_t setStreamPktSeq(uint64_t streamId, uint16_t initialSeq, bool force = false) { bool locked = m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60)); @@ -244,9 +261,13 @@ namespace network if (it == m_streamSeqNos.end()) { m_streamSeqNos.insert({streamId, initialSeq}); } else { - pktSeq = m_streamSeqNos[streamId]; + if (!force) { + pktSeq = m_streamSeqNos[streamId]; + ++pktSeq; + } else { + pktSeq = initialSeq; + } - ++pktSeq; if (pktSeq > RTP_END_OF_CALL_SEQ) { pktSeq = 0U; } diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index d8f3a763..7f0e3733 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -48,9 +48,9 @@ PeerNetwork::PeerNetwork(const std::string& address, uint16_t port, uint16_t loc m_pidLookup(nullptr), m_peerReplica(false), m_peerReplicaSavesACL(false), - m_tgidPkt(true, "Peer-Link, TGID List"), - m_ridPkt(true, "Peer-Link, RID List"), - m_pidPkt(true, "Peer-Link, PID List"), + m_tgidPkt(true, "Peer Replication, TGID List"), + m_ridPkt(true, "Peer Replication, RID List"), + m_pidPkt(true, "Peer Replication, PID List"), m_threadPool(WORKER_CNT, "peer") { assert(!address.empty()); diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 93fcd5bf..4b88542e 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -244,10 +244,14 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver && status.slotNo == slotNo) { - LogMessage(LOG_NET, "DMR, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, external = %u", - peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, external); status.streamId = streamId; - status.srcId = srcId; + if (status.srcId == 0U) + status.srcId = srcId; + if (status.srcId != srcId) { + LogMessage(LOG_NET, "DMR, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, external = %u", + peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, external); + status.srcId = srcId; + } } if (status.srcId != 0U && status.srcId != srcId) { diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 62666bac..c0146e71 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -282,10 +282,14 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver) { - LogMessage(LOG_NET, "NXDN, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", - peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); status.streamId = streamId; - status.srcId = srcId; + if (status.srcId == 0U) + status.srcId = srcId; + if (status.srcId != srcId) { + LogMessage(LOG_NET, "NXDN, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", + peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + status.srcId = srcId; + } } if (status.srcId != 0U && status.srcId != srcId) { diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index d89442fe..9bfa6ac4 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -287,10 +287,14 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver) { - LogMessage(LOG_NET, "P25, Call Source Switched, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", - peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); status.streamId = streamId; - status.srcId = srcId; + if (status.srcId == 0U) + status.srcId = srcId; + if (status.srcId != srcId) { + LogMessage(LOG_NET, "P25, Call Source Switched, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", + peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + status.srcId = srcId; + } } if (status.srcId != 0U && status.srcId != srcId) { From fb884beb7391dfd7dbda32249bc339f8cf47c7a6 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 6 Oct 2025 12:18:21 -0400 Subject: [PATCH 049/200] lock m_status and m_statusPVCall before trying to update data elements; --- src/fne/network/callhandler/TagAnalogData.cpp | 5 ++++- src/fne/network/callhandler/TagDMRData.cpp | 6 ++++++ src/fne/network/callhandler/TagNXDNData.cpp | 6 ++++++ src/fne/network/callhandler/TagP25Data.cpp | 6 ++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 4a048fa2..79184bb6 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -192,13 +192,14 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } // this is a new call stream - // bryanb: this could be problematic and is naive, if a dstId appears on both slots (which shouldn't happen) + m_status.lock(false); m_status[dstId].callStartTime = pktTime; m_status[dstId].srcId = srcId; m_status[dstId].dstId = dstId; m_status[dstId].streamId = streamId; m_status[dstId].peerId = peerId; m_status[dstId].activeCall = true; + m_status.unlock(); LogMessage(LOG_NET, "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); @@ -230,7 +231,9 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } } + m_status.lock(false); m_status[dstId].lastPacket = hrc::now(); + m_status.unlock(); // repeat traffic to the connected peers if (m_network->m_peers.size() > 0U) { diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 4b88542e..a5da723f 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -285,6 +285,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // this is a new call stream // bryanb: this could be problematic and is naive, if a dstId appears on both slots (which shouldn't happen) + m_status.lock(false); m_status[dstId].callStartTime = pktTime; m_status[dstId].srcId = srcId; m_status[dstId].dstId = dstId; @@ -292,9 +293,11 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_status[dstId].streamId = streamId; m_status[dstId].peerId = peerId; m_status[dstId].activeCall = true; + m_status.unlock(); // is this a private call? if (flco == FLCO::PRIVATE) { + m_statusPVCall.lock(false); m_statusPVCall[dstId].callStartTime = pktTime; m_statusPVCall[dstId].srcId = srcId; m_statusPVCall[dstId].dstId = dstId; @@ -306,6 +309,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // find the SSRC of the peer that registered this unit uint32_t regSSRC = m_network->findPeerUnitReg(srcId); m_statusPVCall[dstId].dstPeerId = regSSRC; + m_statusPVCall.unlock(); LogMessage(LOG_NET, "DMR, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); @@ -348,7 +352,9 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId return false; } + m_status.lock(false); m_status[dstId].lastPacket = hrc::now(); + m_status.unlock(); bool noConnectedPeerRepeat = false; bool privateCallInProgress = false; diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index c0146e71..64e787e3 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -322,15 +322,18 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI } // this is a new call stream + m_status.lock(false); m_status[dstId].callStartTime = pktTime; m_status[dstId].srcId = srcId; m_status[dstId].dstId = dstId; m_status[dstId].streamId = streamId; m_status[dstId].peerId = peerId; m_status[dstId].activeCall = true; + m_status.unlock(); // is this a private call? if (!group) { + m_statusPVCall.lock(false); m_statusPVCall[dstId].callStartTime = pktTime; m_statusPVCall[dstId].srcId = srcId; m_statusPVCall[dstId].dstId = dstId; @@ -341,6 +344,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // find the SSRC of the peer that registered this unit uint32_t regSSRC = m_network->findPeerUnitReg(srcId); m_statusPVCall[dstId].dstPeerId = regSSRC; + m_statusPVCall.unlock(); LogMessage(LOG_NET, "NXDN, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); @@ -378,7 +382,9 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI } } + m_status.lock(false); m_status[dstId].lastPacket = hrc::now(); + m_status.unlock(); bool noConnectedPeerRepeat = false; bool privateCallInProgress = false; diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 9bfa6ac4..53066a69 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -327,15 +327,18 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } // this is a new call stream + m_status.lock(false); m_status[dstId].callStartTime = pktTime; m_status[dstId].srcId = srcId; m_status[dstId].dstId = dstId; m_status[dstId].streamId = streamId; m_status[dstId].peerId = peerId; m_status[dstId].activeCall = true; + m_status.unlock(); // is this a private call? if (lco == LCO::PRIVATE) { + m_statusPVCall.lock(false); m_statusPVCall[dstId].callStartTime = pktTime; m_statusPVCall[dstId].srcId = srcId; m_statusPVCall[dstId].dstId = dstId; @@ -346,6 +349,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // find the SSRC of the peer that registered this unit uint32_t regSSRC = m_network->findPeerUnitReg(dstId); m_statusPVCall[dstId].dstPeerId = regSSRC; + m_statusPVCall.unlock(); LogMessage(LOG_NET, "P25, Private Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, external); @@ -388,7 +392,9 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId return false; } + m_status.lock(false); m_status[dstId].lastPacket = hrc::now(); + m_status.unlock(); bool noConnectedPeerRepeat = false; bool privateCallInProgress = false; From d972ab54ef59bb53adb11cba6f0457de1daa99f0 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 6 Oct 2025 13:11:27 -0400 Subject: [PATCH 050/200] don't drop out-of-order packets (this was a bad idea, instead log an issue, the real fix for this will be some sort of RTP jitter buffer); if the current packet sequence reaches the maximum allowed, roll over to 0; --- src/fne/network/FNENetwork.cpp | 51 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 4e30924c..26f62051 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -621,7 +621,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } // update current peer packet sequence and stream ID - if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end()) && streamId != 0U) { + if (peerId > 0U && (network->m_peers.find(peerId) != network->m_peers.end()) && streamId != 0U) { FNEPeerConnection* connection = network->m_peers[peerId]; uint16_t pktSeq = req->rtpHeader.getSequence(); @@ -636,35 +636,40 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) bool skip = false; if (connection->hasStreamPktSeq(streamId)) { uint16_t currPkt = connection->getStreamPktSeq(streamId); - if ((pktSeq != currPkt) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U)) && pktSeq != 0U) { - LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; got %u, expected %u", peerId, connection->identWithQualifier().c_str(), - streamId, pktSeq, currPkt); - - if (req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) { - // possibly lost frames - if (pktSeq > currPkt) { - LogError(LOG_NET, "PEER %u (%s) stream %u possible lost frames; got %u, expected %u, resetting sequence to %u", peerId, connection->identWithQualifier().c_str(), - streamId, pktSeq, currPkt, pktSeq); - connection->setStreamPktSeq(streamId, pktSeq + 1U, true); - skip = true; // don't alter the packet sequence any further - } - - // received out of order frames - if (pktSeq < currPkt) { - LogError(LOG_NET, "PEER %u (%s) stream %u out-of-order; got %u, expected %u, dropped", peerId, connection->identWithQualifier().c_str(), - streamId, pktSeq, currPkt); - // don't process blatently out-of-order frames - if (req->buffer != nullptr) - delete[] req->buffer; - delete req; + if (currPkt == RTP_END_OF_CALL_SEQ) { + // update the expected current packet sequence for the next sequence number + connection->setStreamPktSeq(streamId, 0U, true); + skip = true; // don't alter the packet sequence any further + } + else { + if ((pktSeq != currPkt) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U)) && pktSeq != 0U) { + LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; got %u, expected %u", peerId, connection->identWithQualifier().c_str(), + streamId, pktSeq, currPkt); + + if (req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) { + // possibly lost frames + if (pktSeq > currPkt) { + LogError(LOG_NET, "PEER %u (%s) stream %u possible lost frames; got %u, expected %u, resetting sequence to %u", peerId, connection->identWithQualifier().c_str(), + streamId, pktSeq, currPkt, pktSeq); + + // update the expected current packet sequence for the next sequence number + connection->setStreamPktSeq(streamId, pktSeq + 1U, true); + skip = true; // don't alter the packet sequence any further + } - return; + // received out of order frames + if (pktSeq < currPkt) { + LogError(LOG_NET, "PEER %u (%s) stream %u out-of-order; got %u, expected %u", peerId, connection->identWithQualifier().c_str(), + streamId, pktSeq, currPkt); + skip = true; // don't alter the packet sequence -- this can result in odd behavior + } } } } } + // update the expected current packet sequence for the next sequence number if (!skip) connection->setStreamPktSeq(streamId, pktSeq + 1U); } From f3aaf07162a7668941ff91cd5959956633710cad Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 6 Oct 2025 13:15:45 -0400 Subject: [PATCH 051/200] update order of operations for peer ident; --- src/fne/network/FNENetwork.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 587fa83e..391ca459 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -182,10 +182,10 @@ namespace network { if (isSysView()) return "@" + identity(); - if (isExternalFNEPeer()) - return "+" + identity(); if (isPeerReplica()) return "%" + identity(); + if (isExternalFNEPeer()) + return "+" + identity(); return " " + m_identity; } From e81f8427205520e01fb1368f1b69adc5fbbbb7ee Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 6 Oct 2025 13:36:39 -0400 Subject: [PATCH 052/200] normalize log messages; --- src/fne/network/FNENetwork.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 26f62051..2d55a683 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1914,7 +1914,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool isE { uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - // sending PEER_LINK style RID list to external peers + // sending REPL style RID list to external peers if (isExternalPeer) { FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { @@ -1942,7 +1942,8 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool isE PacketBuffer pkt(true, "Peer Replication, RID List"); pkt.encode((uint8_t*)buffer, len); - LogInfoEx(LOG_NET, "PEER %u Peer Replication, RID List, blocks %u, streamId = %u", peerId, pkt.fragments.size(), streamId); + LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, RID List, blocks %u, streamId = %u", peerId, connection->identWithQualifier().c_str(), + pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_RID_LIST }, @@ -2103,7 +2104,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool isExternalP return; } - // sending PEER_LINK style TGID list to external peers + // sending REPL style TGID list to external peers if (isExternalPeer) { FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { @@ -2131,7 +2132,8 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool isExternalP PacketBuffer pkt(true, "Peer Replication, TGID List"); pkt.encode((uint8_t*)buffer, len); - LogInfoEx(LOG_NET, "PEER %u Peer Replication, TGID List, blocks %u, streamId = %u", peerId, pkt.fragments.size(), streamId); + LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, TGID List, blocks %u, streamId = %u", peerId, connection->identWithQualifier().c_str(), + pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_TALKGROUP_LIST }, @@ -2282,7 +2284,7 @@ void FNENetwork::writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId) void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) { - // sending PEER_LINK style RID list to external peers + // sending REPL style RID list to external peers FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { std::string filename = m_peerListLookup->filename(); @@ -2309,7 +2311,8 @@ void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) PacketBuffer pkt(true, "Peer Replication, PID List"); pkt.encode((uint8_t*)buffer, len); - LogInfoEx(LOG_NET, "PEER %u Peer Replication, PID List, blocks %u, streamId = %u", peerId, pkt.fragments.size(), streamId); + LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, PID List, blocks %u, streamId = %u", peerId, connection->identWithQualifier().c_str(), + pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_PEER_LIST }, From 7891977f0113da6c4a836732353f3eaa6ec021f7 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 6 Oct 2025 18:54:57 -0400 Subject: [PATCH 053/200] differentiate a call end collision from a call collision; --- src/fne/network/callhandler/TagP25Data.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 53066a69..f2caf4b2 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -210,7 +210,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_status.end()) { if (grantDemand && !switchOver) { - LogWarning(LOG_NET, "P25, Call Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", + LogWarning(LOG_NET, "P25, Call End Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); return false; } From d9486529bb0567a9dd06b7cfb43828fd671956cd Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 6 Oct 2025 18:55:20 -0400 Subject: [PATCH 054/200] differentiate a call grant collision from a call collision; --- src/fne/network/callhandler/TagP25Data.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index f2caf4b2..093f1cd5 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -210,7 +210,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_status.end()) { if (grantDemand && !switchOver) { - LogWarning(LOG_NET, "P25, Call End Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", + LogWarning(LOG_NET, "P25, Call Grant Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); return false; } From 9882a8b3724fbe28c9960073504fb34203096e38 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 6 Oct 2025 20:01:13 -0400 Subject: [PATCH 055/200] fix missing code commenting; --- src/fne/network/callhandler/TagAnalogData.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 79184bb6..8d794b56 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -235,7 +235,11 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status[dstId].lastPacket = hrc::now(); m_status.unlock(); - // repeat traffic to the connected peers + /* + ** MASTER TRAFFIC + */ + + // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U) { uint32_t i = 0U; for (auto peer : m_network->m_peers) { @@ -275,7 +279,11 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_network->m_frameQueue->flushQueue(); } - // repeat traffic to external peers + /* + ** PEER TRAFFIC (e.g. networks this FNE is peered to) + */ + + // repeat traffic to master nodes we have connected to as a peer if (m_network->m_host->m_peerNetworks.size() > 0U && !tg.config().parrot()) { for (auto peer : m_network->m_host->m_peerNetworks) { uint32_t dstPeerId = peer.second->getPeerId(); From 954cbc5d5fec1c499499bc9c08fbf6e9b71154d2 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 7 Oct 2025 15:07:46 -0400 Subject: [PATCH 056/200] initial implementation of naive round-robin HA mechanism. this mechanism allows the FNE master to communicate to a peer connected to it, to announce alternate IPs for the peer to connect to if the primary configured FNE master becomes inaccessible. the HA mechanism requires peer replication to create a loose cluster of FNEs, each FNE in the cluster is configured with the external WAN IP and port for connection to the FNE, and these IPs are then disseminated to all FNE replicas (and downstream connected peers) in the cluster automatically; --- configs/fne-config.example.yml | 13 ++ src/common/network/BaseNetwork.h | 2 + src/common/network/Network.cpp | 67 ++++++- src/common/network/Network.h | 58 ++++++ src/common/network/RTPFNEHeader.h | 2 + src/fne/network/DiagNetwork.cpp | 87 ++++++++- src/fne/network/FNENetwork.cpp | 281 +++++++++++++++++++++++------- src/fne/network/FNENetwork.h | 44 +++-- src/fne/network/HAParameters.h | 79 +++++++++ src/fne/network/PeerNetwork.cpp | 36 ++++ src/fne/network/PeerNetwork.h | 7 + 11 files changed, 599 insertions(+), 77 deletions(-) create mode 100644 src/fne/network/HAParameters.h diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index be70f498..9bd186ef 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -48,6 +48,19 @@ master: # Flag indicating whether or not verbose debug logging is enabled. debug: false + # + # High Availability + # + ha: + # Flag indicating high availability advertisements are enabled. + enable: false + # WAN IP address of this FNE master. + # This IP address is advertised to the network as a globally WAN accessible IP. + advertisedWANAddress: 1.2.3.4 + # WAN port for this FNE master. + # This port is advertised to the network as a globally WAN accessible port. + advertisedWANPort: 62031 + # Flag indicating whether or not denied traffic will be logged. # (This is useful for debugging talkgroup rules and other ACL issues, but can be very noisy on a busy system.) logDenials: false diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index 39bd2dd0..2b9f756d 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -93,6 +93,8 @@ namespace network const uint32_t NXDN_PACKET_LENGTH = 70U; // 20 byte header + NXDN_FRAME_LENGTH_BYTES + 2 byte trailer const uint32_t ANALOG_PACKET_LENGTH = 324U; // 20 byte header + AUDIO_SAMPLES_LENGTH_BYTES + 4 byte trailer + const uint32_t HA_PARAMS_ENTRY_LEN = 20U; + /** * @brief Network Peer Connection Status * @ingroup network_core diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index 5f421df0..bc122525 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -26,6 +26,7 @@ using namespace network; // --------------------------------------------------------------------------- #define MAX_RETRY_BEFORE_RECONNECT 4U +#define MAX_RETRY_HA_RECONNECT 2U #define MAX_SERVER_DIFF 360ULL // maximum difference in time between a server timestamp and local timestamp in milliseconds // --------------------------------------------------------------------------- @@ -41,6 +42,10 @@ Network::Network(const std::string& address, uint16_t port, uint16_t localPort, m_pktLastSeq(0U), m_address(address), m_port(port), + m_configuredAddress(address), + m_configuredPort(port), + m_haIPs(), + m_currentHAIP(0U), m_password(password), m_enabled(false), m_dmrEnabled(dmr), @@ -54,6 +59,7 @@ Network::Network(const std::string& address, uint16_t port, uint16_t localPort, m_salt(nullptr), m_retryTimer(1000U, 10U), m_retryCount(0U), + m_maxRetryCount(MAX_RETRY_BEFORE_RECONNECT), m_timeoutTimer(1000U, MAX_PEER_PING_TIME), m_pktSeq(0U), m_loginStreamId(0U), @@ -187,13 +193,13 @@ void Network::clock(uint32_t ms) m_retryTimer.clock(ms); if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) { if (m_enabled) { - if (m_retryCount > MAX_RETRY_BEFORE_RECONNECT) { - m_retryCount = 0U; + if (m_retryCount > m_maxRetryCount) { LogError(LOG_NET, "PEER %u connection to the master has timed out, retrying connection, remotePeerId = %u", m_peerId, m_remotePeerId); close(); open(); + m_retryCount = 0U; m_retryTimer.start(); return; } @@ -674,6 +680,46 @@ void Network::clock(uint32_t ms) } break; + case NET_SUBFUNC::MASTER_HA_PARAMS: // HA Parameters + { + if (m_enabled) { + if (m_debug) + Utils::dump(1U, "Network::clock(), Network Rx, HA PARAMS", buffer.get(), length); + + m_haIPs.clear(); + m_currentHAIP = 0U; + m_maxRetryCount = MAX_RETRY_HA_RECONNECT; + + // always add the configured address to the HA IP list + m_haIPs.push_back(PeerHAIPEntry(m_configuredAddress, m_configuredPort)); + + uint32_t len = GET_UINT32(buffer, 6U); + if (len > 0U) { + len /= HA_PARAMS_ENTRY_LEN; + } + + uint8_t offs = 10U; + for (uint8_t i = 0U; i < len; i++, offs += HA_PARAMS_ENTRY_LEN) { + uint32_t ipAddr = GET_UINT32(buffer, offs + 4U); + uint16_t port = GET_UINT16(buffer, offs + 8U); + + std::string address = __IP_FROM_UINT(ipAddr); + + LogDebugEx(LOG_NET, "Network::clock()", "HA PARAMS, %s:%u", address.c_str(), port); + + m_haIPs.push_back(PeerHAIPEntry(address, port)); + i++; + } + + if (m_haIPs.size() > 1U) { + m_currentHAIP = 1U; // because the first entry is our configured entry, set + // the current HA IP to the next available + LogMessage(LOG_NET, "Loaded %u HA IPs from master", m_haIPs.size() - 1U); + } + } + } + break; + default: Utils::dump("unknown master control opcode from the master", buffer.get(), length); break; @@ -996,6 +1042,23 @@ bool Network::open() m_retryTimer.start(); m_retryCount = 0U; + // are we rotating IPs for HA reconnect? + if (m_haIPs.size() > 0 && m_retryCount > 0U && + m_maxRetryCount == MAX_RETRY_HA_RECONNECT) { + + PeerHAIPEntry entry = m_haIPs[m_currentHAIP]; + m_currentHAIP++; + + if (m_currentHAIP > m_haIPs.size()) { + m_currentHAIP = 0U; + } + + LogMessage(LOG_NET, "PEER %u connection to the master has timed out, %s:%u is non-responsive, trying next HA %s:%u", m_peerId, + m_address.c_str(), m_port, entry.masterAddress.c_str(), entry.masterPort); + m_address = entry.masterAddress; + m_port = entry.masterPort; + } + if (udp::Socket::lookup(m_address, m_port, m_addr, m_addrLen) != 0) { LogMessage(LOG_NET, "!!! Could not lookup the address of the master!"); return false; diff --git a/src/common/network/Network.h b/src/common/network/Network.h index f2b4e95d..5acb33a0 100644 --- a/src/common/network/Network.h +++ b/src/common/network/Network.h @@ -70,6 +70,57 @@ namespace network /** @} */ }; + // --------------------------------------------------------------------------- + // Structure Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents high availiability IP address data. + * @ingroup network_core + */ + struct PeerHAIPEntry { + public: + /** + * @brief Initializes a new instance of the PeerHAIPEntry class + */ + PeerHAIPEntry() : + masterAddress(), + masterPort(TRAFFIC_DEFAULT_PORT) + { + /* stub **/ + } + + /** + * @brief Initializes a new instance of the PeerHAIPEntry class + * @param address Master IP Address. + * @param port Master Port. + */ + PeerHAIPEntry(std::string address, uint16_t port) : + masterAddress(address), + masterPort(port) + { + /* stub */ + } + + /** + * @brief Equals operator. Copies this PeerHAIPEntry to another PeerHAIPEntry. + * @param data Instance of PeerHAIPEntry to copy. + */ + PeerHAIPEntry& operator=(const PeerHAIPEntry& data) + { + if (this != &data) { + masterAddress = data.masterAddress; + masterPort = data.masterPort; + } + + return *this; + } + + public: + std::string masterAddress; //!< Master IP Address. + uint16_t masterPort; //!< Master Port. + }; + // --------------------------------------------------------------------------- // Class Declaration // --------------------------------------------------------------------------- @@ -241,6 +292,12 @@ namespace network std::string m_address; uint16_t m_port; + std::string m_configuredAddress; + uint16_t m_configuredPort; + + std::vector m_haIPs; + uint8_t m_currentHAIP; + std::string m_password; bool m_enabled; @@ -260,6 +317,7 @@ namespace network Timer m_retryTimer; uint8_t m_retryCount; + uint8_t m_maxRetryCount; Timer m_timeoutTimer; uint32_t* m_rxDMRStreamId; diff --git a/src/common/network/RTPFNEHeader.h b/src/common/network/RTPFNEHeader.h index 52b6d8f4..a2122cac 100644 --- a/src/common/network/RTPFNEHeader.h +++ b/src/common/network/RTPFNEHeader.h @@ -93,6 +93,7 @@ namespace network MASTER_SUBFUNC_BL_RID = 0x01U, //!< Blacklist RIDs MASTER_SUBFUNC_ACTIVE_TGS = 0x02U, //!< Active TGIDs MASTER_SUBFUNC_DEACTIVE_TGS = 0x03U, //!< Deactive TGIDs + MASTER_HA_PARAMS = 0xA3U, //!< HA Parameters TRANSFER_SUBFUNC_ACTIVITY = 0x01U, //!< Activity Log Transfer TRANSFER_SUBFUNC_DIAG = 0x02U, //!< Diagnostic Log Transfer @@ -110,6 +111,7 @@ namespace network REPL_PEER_LIST = 0x02U, //!< FNE Replication Peer List Transfer REPL_ACT_PEER_LIST = 0xA2U, //!< FNE Replication Active Peer List Transfer + REPL_HA_PARAMS = 0xA3U, //!< FNE Replication HA Parameters }; }; diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 5267c05e..5239f209 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -394,7 +394,8 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } else { FNENetwork::PacketBufferEntry& pkt = network->m_peerReplicaActPkt[peerId]; if (!pkt.locked && pkt.streamId != streamId) { - LogError(LOG_NET, "PEER %u Peer Replication, Active Peer List, stream ID mismatch, expected %u, got %u", peerId, pkt.streamId, streamId); + LogError(LOG_NET, "PEER %u (%s) Peer Replication, Active Peer List, stream ID mismatch, expected %u, got %u", peerId, + connection->identWithQualifier().c_str(), pkt.streamId, streamId); pkt.buffer->clear(); pkt.streamId = streamId; } @@ -423,7 +424,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) json::value v; std::string err = json::parse(v, payload); if (!err.empty()) { - LogError(LOG_NET, "PEER %u error parsing active peer list, %s", peerId, err.c_str()); + LogError(LOG_NET, "PEER %u (%s) error parsing active peer list, %s", peerId, connection->identWithQualifier().c_str(), err.c_str()); pkt.buffer->clear(); pkt.streamId = 0U; if (decompressed != nullptr) { @@ -435,7 +436,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) else { // ensure parsed JSON is an array if (!v.is()) { - LogError(LOG_NET, "PEER %u error parsing active peer list, data was not valid", peerId); + LogError(LOG_NET, "PEER %u (%s) error parsing active peer list, data was not valid", peerId, connection->identWithQualifier().c_str()); pkt.buffer->clear(); delete pkt.buffer; pkt.streamId = 0U; @@ -447,7 +448,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } else { json::array arr = v.get(); - LogInfoEx(LOG_NET, "PEER %u Peer Replication, Active Peer List, updating %u peer entries", peerId, arr.size()); + LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, Active Peer List, updating %u peer entries", peerId, connection->identWithQualifier().c_str(), arr.size()); network->m_peerReplicaPeers[peerId] = arr; } } @@ -469,6 +470,84 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } } } + else if (req->fneHeader.getSubFunction() == NET_SUBFUNC::REPL_HA_PARAMS) { // Peer Replication HA Parameters + if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) { + FNEPeerConnection* connection = network->m_peers[peerId]; + if (connection != nullptr) { + std::string ip = udp::Socket::address(req->address); + + // validate peer (simple validation really) + if (connection->connected() && connection->address() == ip && connection->isExternalFNEPeer() && + connection->isPeerReplica()) { + DECLARE_UINT8_ARRAY(rawPayload, req->length); + ::memcpy(rawPayload, req->buffer, req->length); + + std::vector receivedParams; + + uint32_t len = GET_UINT32(rawPayload, 0U); + if (len > 0U) { + len /= HA_PARAMS_ENTRY_LEN; + } + + uint8_t offs = 4U; + for (uint8_t i = 0U; i < len; i++, offs += HA_PARAMS_ENTRY_LEN) { + uint32_t peerId = GET_UINT32(rawPayload, offs); + uint32_t ipAddr = GET_UINT32(rawPayload, offs + 4U); + uint16_t port = GET_UINT16(rawPayload, offs + 8U); + receivedParams.push_back(HAParameters(peerId, ipAddr, port)); + } + + if (receivedParams.size() > 0U) { + for (auto rxEntry : receivedParams) { + auto it = std::find_if(network->m_peerReplicaHAParams.begin(), network->m_peerReplicaHAParams.end(), + [&](HAParameters& x) + { + if (x.peerId == rxEntry.peerId) + return true; + return false; + }); + if (it != network->m_peerReplicaHAParams.end()) { + it->masterIP = rxEntry.masterIP; + it->masterPort = rxEntry.masterPort; + } else { + HAParameters param = rxEntry; + network->m_peerReplicaHAParams.push_back(param); + } + + std::string address = __IP_FROM_UINT(rxEntry.masterIP); + LogDebugEx(LOG_NET, "DiagNetwork::taskNetworkRx", "PEER %u (%s) Peer Replication, HA Parameters, %s:%u", peerId, connection->identWithQualifier().c_str(), + address.c_str(), rxEntry.masterPort); + } + + if (receivedParams.size() > 0) { + LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, HA Parameters, updating %u entries, %u entries", peerId, connection->identWithQualifier().c_str(), receivedParams.size(), + network->m_peerReplicaHAParams.size()); + + // send peer updates to replica peers + if (network->m_host->m_peerNetworks.size() > 0) { + for (auto peer : network->m_host->m_peerNetworks) { + if (peer.second != nullptr) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + std::vector haParams; + network->m_peerReplicaHAParams.lock(false); + for (auto entry : network->m_peerReplicaHAParams) { + haParams.push_back(entry); + } + network->m_peerReplicaHAParams.unlock(); + + peer.second->writeHAParams(haParams); + } + } + } + } + } + } + } else { + network->writePeerNAK(peerId, 0U, TAG_PEER_REPLICA, NET_CONN_NAK_FNE_UNAUTHORIZED); + } + } + } + } break; default: diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 2d55a683..ceb0c4cb 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -13,6 +13,7 @@ #include "common/p25/kmm/KMMFactory.h" #include "common/zlib/Compression.h" #include "common/Log.h" +#include "common/StopWatch.h" #include "common/Utils.h" #include "network/FNENetwork.h" #include "network/callhandler/TagDMRData.h" @@ -22,6 +23,7 @@ #include "network/P25OTARService.h" #include "fne/ActivityLog.h" #include "HostFNE.h" +#include "FNEMain.h" using namespace network; using namespace network::callhandler; @@ -45,6 +47,8 @@ const uint32_t MAX_MISSED_ACL_UPDATES = 10U; const uint64_t PACKET_LATE_TIME = 200U; // 200ms +const uint32_t FIXED_HA_UPDATE_INTERVAL = 30U; // 30s + // --------------------------------------------------------------------------- // Static Class Members // --------------------------------------------------------------------------- @@ -92,8 +96,13 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_ccPeerMap(), m_peerReplicaKeyQueue(), m_peerReplicaActPkt(), + m_peerReplicaHAParams(), + m_advertisedHAAddress(), + m_advertisedHAPort(TRAFFIC_DEFAULT_PORT), + m_haEnabled(false), m_maintainenceTimer(1000U, pingTime), m_updateLookupTimer(1000U, (updateLookupTime * 60U)), + m_haUpdateTimer(1000U, FIXED_HA_UPDATE_INTERVAL), m_softConnLimit(0U), m_callInProgress(false), m_disallowAdjStsBcast(false), @@ -136,6 +145,12 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_tagAnalog = new TagAnalogData(this, debug); m_p25OTARService = new P25OTARService(this, m_tagP25->packetData(), kmfDebug, verbose); + + /* + ** Initialize Threads + */ + + Thread::runAsThread(this, threadParrotHandler); } /* Finalizes a instance of the FNENetwork class. */ @@ -232,6 +247,17 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) } } + yaml::Node& haParams = conf["ha"]; + m_advertisedHAAddress = haParams["advertisedWANAddress"].as(); + m_advertisedHAPort = (uint16_t)haParams["advertisedWANPort"].as(TRAFFIC_DEFAULT_PORT); + m_haEnabled = haParams["enable"].as(false); + + if (m_haEnabled) { + uint32_t ipAddr = __IP_FROM_STR(m_advertisedHAAddress); + HAParameters params = HAParameters(m_peerId, ipAddr, m_advertisedHAPort); + m_peerReplicaHAParams.push_back(params); + } + if (printOptions) { LogInfo(" Maximum Permitted Connections: %u", m_softConnLimit); LogInfo(" Disable adjacent site broadcasts to any peers: %s", m_disallowAdjStsBcast ? "yes" : "no"); @@ -266,6 +292,11 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogInfo(" P25 OTAR KMF Services Enabled: %s", m_kmfServicesEnabled ? "yes" : "no"); LogInfo(" P25 OTAR KMF Listening Address: %s", m_address.c_str()); LogInfo(" P25 OTAR KMF Listening Port: %u", kmfOtarPort); + LogInfo(" High Availability Enabled: %s", m_haEnabled ? "yes" : "no"); + if (m_haEnabled) { + LogInfo(" Advertised HA WAN IP: %s", m_advertisedHAAddress.c_str()); + LogInfo(" Advertised HA WAN Port: %u", m_advertisedHAPort); + } } } @@ -350,7 +381,7 @@ void FNENetwork::clock(uint32_t ms) if (m_forceListUpdate) { for (auto peer : m_peers) { - peerACLUpdate(peer.first); + peerMetadataUpdate(peer.first); } m_forceListUpdate = false; } @@ -405,7 +436,7 @@ void FNENetwork::clock(uint32_t ms) m_frameQueue->clearTimestamps(); } - // send active peer list to replica masters + // send peer updates to replica peers if (m_host->m_peerNetworks.size() > 0) { for (auto peer : m_host->m_peerNetworks) { if (peer.second != nullptr) { @@ -442,7 +473,7 @@ void FNENetwork::clock(uint32_t ms) m_updateLookupTimer.clock(ms); if (m_updateLookupTimer.isRunning() && m_updateLookupTimer.hasExpired()) { - // send ACL updates to peers + // send network metadata updates to peers m_peers.lock(false); for (auto peer : m_peers) { uint32_t id = peer.first; @@ -450,24 +481,24 @@ void FNENetwork::clock(uint32_t ms) if (connection != nullptr) { // if this connection is a peer replica *always* send the update -- no stream checking if (connection->connected() && connection->isPeerReplica()) { - LogInfoEx(LOG_NET, "PEER %u (%s), Peer Replication, updating ACL list", id, connection->identWithQualifier().c_str()); + LogInfoEx(LOG_NET, "PEER %u (%s), Peer Replication, updating network metadata", id, connection->identWithQualifier().c_str()); - peerACLUpdate(id); - connection->missedACLUpdates(0U); + peerMetadataUpdate(id); + connection->missedMetadataUpdates(0U); continue; } if (connection->connected()) { - if ((connection->streamCount() <= 1) || (connection->missedACLUpdates() > MAX_MISSED_ACL_UPDATES)) { + if ((connection->streamCount() <= 1) || (connection->missedMetadataUpdates() > MAX_MISSED_ACL_UPDATES)) { LogInfoEx(LOG_NET, "PEER %u (%s) updating ACL list", id, connection->identWithQualifier().c_str()); - peerACLUpdate(id); - connection->missedACLUpdates(0U); + peerMetadataUpdate(id); + connection->missedMetadataUpdates(0U); } else { - uint32_t missed = connection->missedACLUpdates(); + uint32_t missed = connection->missedMetadataUpdates(); missed++; - LogInfoEx(LOG_NET, "PEER %u (%s) skipped for ACL update, traffic in progress", id, connection->identWithQualifier().c_str()); - connection->missedACLUpdates(missed); + LogInfoEx(LOG_NET, "PEER %u (%s) skipped for metadata update, traffic in progress", id, connection->identWithQualifier().c_str()); + connection->missedMetadataUpdates(missed); } } } @@ -477,34 +508,32 @@ void FNENetwork::clock(uint32_t ms) m_updateLookupTimer.start(); } - m_parrotDelayTimer.clock(ms); - if (m_parrotDelayTimer.isRunning() && m_parrotDelayTimer.hasExpired()) { - // if the DMR handler has parrot frames to playback, playback a frame - if (m_tagDMR->hasParrotFrames()) { - m_tagDMR->playbackParrot(); - } - - // if the P25 handler has parrot frames to playback, playback a frame - if (m_tagP25->hasParrotFrames()) { - m_tagP25->playbackParrot(); - } + // if HA is enabled perform HA parameter updates + if (m_haEnabled) { + m_haUpdateTimer.clock(ms); + if (m_haUpdateTimer.isRunning() && m_haUpdateTimer.hasExpired()) { + // send peer updates to replica peers + if (m_host->m_peerNetworks.size() > 0) { + for (auto peer : m_host->m_peerNetworks) { + if (peer.second != nullptr) { + if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + std::vector haParams; + m_peerReplicaHAParams.lock(false); + for (auto entry : m_peerReplicaHAParams) { + haParams.push_back(entry); + } + m_peerReplicaHAParams.unlock(); - // if the NXDN handler has parrot frames to playback, playback a frame - if (m_tagNXDN->hasParrotFrames()) { - m_tagNXDN->playbackParrot(); - } + peer.second->writeHAParams(haParams); + } + } + } + } - // if the analog handler has parrot frames to playback, playback a frame - if (m_tagAnalog->hasParrotFrames()) { - m_tagAnalog->playbackParrot(); + m_haUpdateTimer.start(); } } - if (!m_tagDMR->hasParrotFrames() && !m_tagP25->hasParrotFrames() && !m_tagNXDN->hasParrotFrames() && !m_tagAnalog->hasParrotFrames() && - m_parrotDelayTimer.isRunning() && m_parrotDelayTimer.hasExpired()) { - m_parrotDelayTimer.stop(); - } - if (m_kmfServicesEnabled) m_p25OTARService->clock(ms); } @@ -527,6 +556,10 @@ bool FNENetwork::open() m_status = NET_STAT_MST_RUNNING; m_maintainenceTimer.start(); m_updateLookupTimer.start(); + + if (m_haEnabled) { + m_haUpdateTimer.start(); + } m_socket = new udp::Socket(m_address, m_port); @@ -586,6 +619,82 @@ void FNENetwork::close() // Private Class Members // --------------------------------------------------------------------------- +/* Entry point to parrot handler thread. */ + +void* FNENetwork::threadParrotHandler(void* arg) +{ + thread_t* th = (thread_t*)arg; + if (th != nullptr) { +#if defined(_WIN32) + ::CloseHandle(th->thread); +#else + ::pthread_detach(th->thread); +#endif // defined(_WIN32) + + std::string threadName("fne:parrot"); + FNENetwork* fne = static_cast(th->obj); + if (fne == nullptr) { + g_killed = true; + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } + + if (g_killed) { + delete th; + return nullptr; + } + + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + StopWatch stopWatch; + stopWatch.start(); + + if (fne != nullptr) { + while (!g_killed) { + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + + fne->m_parrotDelayTimer.clock(ms); + if (fne->m_parrotDelayTimer.isRunning() && fne->m_parrotDelayTimer.hasExpired()) { + // if the DMR handler has parrot frames to playback, playback a frame + if (fne->m_tagDMR->hasParrotFrames()) { + fne->m_tagDMR->playbackParrot(); + } + + // if the P25 handler has parrot frames to playback, playback a frame + if (fne->m_tagP25->hasParrotFrames()) { + fne->m_tagP25->playbackParrot(); + } + + // if the NXDN handler has parrot frames to playback, playback a frame + if (fne->m_tagNXDN->hasParrotFrames()) { + fne->m_tagNXDN->playbackParrot(); + } + + // if the analog handler has parrot frames to playback, playback a frame + if (fne->m_tagAnalog->hasParrotFrames()) { + fne->m_tagAnalog->playbackParrot(); + } + } + + if (!fne->m_tagDMR->hasParrotFrames() && !fne->m_tagP25->hasParrotFrames() && !fne->m_tagNXDN->hasParrotFrames() && !fne->m_tagAnalog->hasParrotFrames() && + fne->m_parrotDelayTimer.isRunning() && fne->m_parrotDelayTimer.hasExpired()) { + fne->m_parrotDelayTimer.stop(); + } + + Thread::sleep(1U); + } + } + + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } + + return nullptr; +} + /* Process a data frames from the network. */ void FNENetwork::taskNetworkRx(NetPacketRequest* req) @@ -1030,7 +1139,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) connection->connected(true); connection->pingsReceived(0U); connection->lastPing(now); - connection->missedACLUpdates(0U); + connection->missedMetadataUpdates(0U); network->m_peers[peerId] = connection; // attach extra notification data to the RPTC ACK to notify the peer of @@ -1102,8 +1211,8 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) peerName << "PEER " << peerId; network->createPeerAffiliations(peerId, peerName.str()); - // spin up a thread and send ACL list over to peer - network->peerACLUpdate(peerId); + // spin up a thread and send metadata over to peer + network->peerMetadataUpdate(peerId); } } } @@ -1727,6 +1836,14 @@ void FNENetwork::erasePeer(uint32_t peerId) } } + // erase any HA parameters for this peer + { + auto it = std::find_if(m_peerReplicaHAParams.begin(), m_peerReplicaHAParams.end(), [&](auto& x) { return x.peerId == peerId; }); + if (it != m_peerReplicaHAParams.end()) { + m_peerReplicaHAParams.erase(it); + } + } + // cleanup peer affiliations erasePeerAffiliations(peerId); } @@ -1848,25 +1965,25 @@ void FNENetwork::setupRepeaterLogin(uint32_t peerId, uint32_t streamId, FNEPeerC LogInfoEx(LOG_NET, "PEER %u RPTL ACK, challenge response sent for login", peerId); } -/* Helper to send the ACL lists to the specified peer in a separate thread. */ +/* Helper to send the network metadata to the specified peer in a separate thread. */ -void FNENetwork::peerACLUpdate(uint32_t peerId) +void FNENetwork::peerMetadataUpdate(uint32_t peerId) { - ACLUpdateRequest* req = new ACLUpdateRequest(); + MetadataUpdateRequest* req = new MetadataUpdateRequest(); req->obj = this; req->peerId = peerId; // enqueue the task - if (!m_threadPool.enqueue(new_pooltask(taskACLUpdate, req))) { - LogError(LOG_NET, "Failed to task enqueue ACL update, peerId = %u", peerId); + if (!m_threadPool.enqueue(new_pooltask(taskMetadataUpdate, req))) { + LogError(LOG_NET, "Failed to task enqueue metadata update, peerId = %u", peerId); if (req != nullptr) delete req; } } -/* Helper to send the ACL lists to the specified peer in a separate thread. */ +/* Helper to send the network metadata to the specified peer in a separate thread. */ -void FNENetwork::taskACLUpdate(ACLUpdateRequest* req) +void FNENetwork::taskMetadataUpdate(MetadataUpdateRequest* req) { if (req != nullptr) { FNENetwork* network = static_cast(req->obj); @@ -1883,24 +2000,28 @@ void FNENetwork::taskACLUpdate(ACLUpdateRequest* req) FNEPeerConnection* connection = network->m_peers[req->peerId]; if (connection != nullptr) { - uint32_t aclStreamId = network->createStreamId(); + uint32_t streamId = network->createStreamId(); // if the connection is an external peer, and peer is participating in peer link, // send the peer proper configuration data if (connection->isExternalFNEPeer() && connection->isPeerReplica()) { - LogInfoEx(LOG_NET, "PEER %u (%s) sending replica ACL list updates", req->peerId, peerIdentity.c_str()); + LogInfoEx(LOG_NET, "PEER %u (%s) sending replica network metadata updates", req->peerId, peerIdentity.c_str()); + + network->writeWhitelistRIDs(req->peerId, streamId, true); + network->writeTGIDs(req->peerId, streamId, true); + network->writePeerList(req->peerId, streamId); - network->writeWhitelistRIDs(req->peerId, aclStreamId, true); - network->writeTGIDs(req->peerId, aclStreamId, true); - network->writePeerList(req->peerId, aclStreamId); + network->writeHAParameters(req->peerId, streamId, true); } else { - LogInfoEx(LOG_NET, "PEER %u (%s) sending ACL list updates", req->peerId, peerIdentity.c_str()); + LogInfoEx(LOG_NET, "PEER %u (%s) sending network metadata updates", req->peerId, peerIdentity.c_str()); - network->writeWhitelistRIDs(req->peerId, aclStreamId, false); - network->writeBlacklistRIDs(req->peerId, aclStreamId); - network->writeTGIDs(req->peerId, aclStreamId, false); - network->writeDeactiveTGIDs(req->peerId, aclStreamId); + network->writeWhitelistRIDs(req->peerId, streamId, false); + network->writeBlacklistRIDs(req->peerId, streamId); + network->writeTGIDs(req->peerId, streamId, false); + network->writeDeactiveTGIDs(req->peerId, streamId); + + network->writeHAParameters(req->peerId, streamId, false); } } @@ -1910,12 +2031,12 @@ void FNENetwork::taskACLUpdate(ACLUpdateRequest* req) /* Helper to send the list of whitelisted RIDs to the specified peer. */ -void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool isExternalPeer) +void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sendISSI) { uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); // sending REPL style RID list to external peers - if (isExternalPeer) { + if (sendISSI) { FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { std::string filename = m_ridLookup->filename(); @@ -2098,14 +2219,14 @@ void FNENetwork::writeBlacklistRIDs(uint32_t peerId, uint32_t streamId) /* Helper to send the list of active TGIDs to the specified peer. */ -void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool isExternalPeer) +void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendISSI) { if (!m_tidLookup->sendTalkgroups()) { return; } // sending REPL style TGID list to external peers - if (isExternalPeer) { + if (sendISSI) { FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { std::string filename = m_tidLookup->filename(); @@ -2327,6 +2448,48 @@ void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) return; } +/* Helper to send the HA parameters to the specified peer. */ + +void FNENetwork::writeHAParameters(uint32_t peerId, uint32_t streamId, bool sendISSI) +{ + if (!m_haEnabled) { + return; + } + + uint32_t len = 4U + (m_peerReplicaHAParams.size() * HA_PARAMS_ENTRY_LEN); + DECLARE_UINT8_ARRAY(buffer, len); + + SET_UINT32((len - 4U), buffer, 0U); + + uint32_t offs = 4U; + m_peerReplicaHAParams.lock(false); + for (uint8_t i = 0U; i < m_peerReplicaHAParams.size(); i++) { + uint32_t peerId = m_peerReplicaHAParams[i].peerId; + uint32_t ipAddr = m_peerReplicaHAParams[i].masterIP; + uint16_t port = m_peerReplicaHAParams[i].masterPort; + + SET_UINT32(peerId, buffer, offs); + SET_UINT32(ipAddr, buffer, offs + 4U); + SET_UINT16(port, buffer, offs + 8U); + + offs += HA_PARAMS_ENTRY_LEN; + } + m_peerReplicaHAParams.unlock(); + + // sending REPL style HA parameters list to external peers + if (sendISSI) { + FNEPeerConnection* connection = m_peers[peerId]; + if (connection != nullptr) { + LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, HA parameters, streamId = %u", peerId, connection->identWithQualifier().c_str(), streamId); + writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_HA_PARAMS}, + buffer, len, 0U, streamId, false, true, true); + } + } + + writePeerCommand(peerId, { NET_FUNC::MASTER, NET_SUBFUNC::MASTER_HA_PARAMS }, + buffer, len, streamId, true); +} + /* Helper to send a In-Call Control command to the specified peer. */ bool FNENetwork::writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::ENUM subFunc, NET_ICC::ENUM command, uint32_t dstId, uint8_t slotNo) diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 391ca459..65ab74ad 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -37,6 +37,7 @@ #include "common/ThreadPool.h" #include "fne/lookups/AffiliationLookup.h" #include "fne/network/influxdb/InfluxDB.h" +#include "fne/network/HAParameters.h" #include "fne/CryptoContainer.h" #include @@ -121,7 +122,7 @@ namespace network m_connectionState(NET_STAT_INVALID), m_pingsReceived(0U), m_lastPing(0U), - m_missedACLUpdates(0U), + m_missedMetadataUpdates(0U), m_isExternalFNEPeer(false), m_isConventionalPeer(false), m_isSysView(false), @@ -150,7 +151,7 @@ namespace network m_connectionState(NET_STAT_INVALID), m_pingsReceived(0U), m_lastPing(0U), - m_missedACLUpdates(0U), + m_missedMetadataUpdates(0U), m_isExternalFNEPeer(false), m_isConventionalPeer(false), m_isSysView(false), @@ -358,9 +359,9 @@ namespace network DECLARE_PROPERTY_PLAIN(uint64_t, lastPing); /** - * @brief Number of missed ACL updates. + * @brief Number of missed network metadata updates. */ - DECLARE_PROPERTY_PLAIN(uint32_t, missedACLUpdates); + DECLARE_PROPERTY_PLAIN(uint32_t, missedMetadataUpdates); /** * @brief Flag indicating this connection is from an external neighbor FNE peer. @@ -395,10 +396,10 @@ namespace network // --------------------------------------------------------------------------- /** - * @brief Represents the data required for a peer ACL update request thread. + * @brief Represents the data required for a network metadata update request thread. * @ingroup fne_network */ - struct ACLUpdateRequest : thread_t { + struct MetadataUpdateRequest : thread_t { uint32_t peerId; //!< Peer ID for this request. }; @@ -625,8 +626,14 @@ namespace network }; concurrent::unordered_map m_peerReplicaActPkt; + concurrent::vector m_peerReplicaHAParams; + std::string m_advertisedHAAddress; + uint16_t m_advertisedHAPort; + bool m_haEnabled; + Timer m_maintainenceTimer; Timer m_updateLookupTimer; + Timer m_haUpdateTimer; uint32_t m_softConnLimit; @@ -670,6 +677,12 @@ namespace network bool m_reportPeerPing; bool m_verbose; + /** + * @brief Entry point to parrot handler thread. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) + */ + static void* threadParrotHandler(void* arg); /** * @brief Entry point to process a given network packet. * @param req Instance of the NetPacketRequest structure. @@ -733,15 +746,15 @@ namespace network void setupRepeaterLogin(uint32_t peerId, uint32_t streamId, FNEPeerConnection* connection); /** - * @brief Helper to send the ACL lists to the specified peer in a separate thread. + * @brief Helper to send the network metadata to the specified peer in a separate thread. * @param peerId Peer ID. */ - void peerACLUpdate(uint32_t peerId); + void peerMetadataUpdate(uint32_t peerId); /** - * @brief Entry point to send the ACL lists to the specified peer in a separate thread. - * @param req Instance of the ACLUpdateRequest structure. + * @brief Entry point to send the network metadata to the specified peer in a separate thread. + * @param req Instance of the MetadataUpdateRequest structure. */ - static void taskACLUpdate(ACLUpdateRequest* req); + static void taskMetadataUpdate(MetadataUpdateRequest* req); /** * @brief Helper to send the list of whitelisted RIDs to the specified peer. @@ -775,7 +788,14 @@ namespace network * @param streamId Stream ID for this message. */ void writePeerList(uint32_t peerId, uint32_t streamId); - + /** + * @brief Helper to send the HA parameters to the specified peer. + * @param peerId Peer ID. + * @param streamId Stream ID for this message. + * @param sendISSI Flag indicating the HA transfer is to an external peer via ISSI. + */ + void writeHAParameters(uint32_t peerId, uint32_t streamId, bool sendISSI); + /** * @brief Helper to send a In-Call Control command to the specified peer. * @param peerId Peer ID. diff --git a/src/fne/network/HAParameters.h b/src/fne/network/HAParameters.h new file mode 100644 index 00000000..c3b68cc4 --- /dev/null +++ b/src/fne/network/HAParameters.h @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Converged FNE Software + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file HAParameters.h + * @ingroup fne_network + */ +#if !defined(__HA_PARAMETERS__) +#define __HA_PARAMETERS__ + +#include "fne/Defines.h" + +namespace network +{ + // --------------------------------------------------------------------------- + // Structure Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents high availiability parameter data. + * @ingroup fne_network + */ + struct HAParameters { + public: + /** + * @brief Initializes a new instance of the HAParameters class + */ + HAParameters() : + peerId(0U), + masterIP(0U), + masterPort(TRAFFIC_DEFAULT_PORT) + { + /* stub **/ + } + + /** + * @brief Initializes a new instance of the HAParameters class + * @param peerId Peer ID. + * @param ipAddr Master IP Address. + * @param port Master Port. + */ + HAParameters(uint32_t peerId, uint32_t ipAddr, uint16_t port) : + peerId(peerId), + masterIP(ipAddr), + masterPort(port) + { + /* stub */ + } + + /** + * @brief Equals operator. Copies this HAParameters to another HAParameters. + * @param data Instance of HAParameters to copy. + */ + HAParameters& operator=(const HAParameters& data) + { + if (this != &data) { + peerId = data.peerId; + masterIP = data.masterIP; + masterPort = data.masterPort; + } + + return *this; + } + + public: + uint32_t peerId; //!< Peer ID. + + uint32_t masterIP; //!< Master IP Address. + uint16_t masterPort; //!< Master Port. + }; +} // namespace network + +#endif // __HA_PARAMETERS__ diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 7f0e3733..de77e4cd 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -142,6 +142,42 @@ bool PeerNetwork::writePeerLinkPeers(json::array* peerList) return false; } +/* Writes a complete update of this CFNE's HA parameters to the network. */ + +bool PeerNetwork::writeHAParams(std::vector& haParams) +{ + if (haParams.size() == 0) + return false; + + if (haParams.size() > 0 && m_peerReplica) { + uint32_t len = 4U + (haParams.size() * HA_PARAMS_ENTRY_LEN); + DECLARE_UINT8_ARRAY(buffer, len); + + SET_UINT32((len - 4U), buffer, 0U); + + uint32_t offs = 4U; + for (uint8_t i = 0U; i < haParams.size(); i++) { + uint32_t peerId = haParams[i].peerId; + uint32_t ipAddr = haParams[i].masterIP; + uint16_t port = haParams[i].masterPort; + + SET_UINT32(peerId, buffer, offs); + SET_UINT32(ipAddr, buffer, offs + 4U); + SET_UINT16(port, buffer, offs + 8U); + + offs += HA_PARAMS_ENTRY_LEN; + } + + // bryanb: this should probably be packet buffered + writeMaster({ NET_FUNC::REPL, NET_SUBFUNC::REPL_HA_PARAMS }, + buffer, len, RTP_END_OF_CALL_SEQ, createStreamId(), false, true); + + return true; + } + + return false; +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index 4c72cd96..90f92de7 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -21,6 +21,7 @@ #include "common/network/Network.h" #include "common/network/PacketBuffer.h" #include "common/ThreadPool.h" +#include "fne/network/HAParameters.h" #include #include @@ -130,6 +131,12 @@ namespace network * @returns bool True, if list was sent, otherwise false. */ bool writePeerLinkPeers(json::array* peerList); + /** + * @brief Writes a complete update of this CFNE's HA parameters to the network. + * @param haParams List of HA parameters. + * @returns bool True, if list was sent, otherwise false. + */ + bool writeHAParams(std::vector& haParams); /** * @brief Returns flag indicating whether or not this peer connection is peer replication enabled. From 2e4b28adea51f55e10daa639f6031266fe8d5833 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 7 Oct 2025 15:15:03 -0400 Subject: [PATCH 057/200] hide debug messages unless debugging is enabled; --- src/common/network/Network.cpp | 3 ++- src/fne/network/DiagNetwork.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index bc122525..24b99b7e 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -705,7 +705,8 @@ void Network::clock(uint32_t ms) std::string address = __IP_FROM_UINT(ipAddr); - LogDebugEx(LOG_NET, "Network::clock()", "HA PARAMS, %s:%u", address.c_str(), port); + if (m_debug) + LogDebugEx(LOG_NET, "Network::clock()", "HA PARAMS, %s:%u", address.c_str(), port); m_haIPs.push_back(PeerHAIPEntry(address, port)); i++; diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 5239f209..ec262fca 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -514,9 +514,11 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) network->m_peerReplicaHAParams.push_back(param); } - std::string address = __IP_FROM_UINT(rxEntry.masterIP); - LogDebugEx(LOG_NET, "DiagNetwork::taskNetworkRx", "PEER %u (%s) Peer Replication, HA Parameters, %s:%u", peerId, connection->identWithQualifier().c_str(), - address.c_str(), rxEntry.masterPort); + if (network->m_debug) { + std::string address = __IP_FROM_UINT(rxEntry.masterIP); + LogDebugEx(LOG_NET, "DiagNetwork::taskNetworkRx", "PEER %u (%s) Peer Replication, HA Parameters, %s:%u", peerId, connection->identWithQualifier().c_str(), + address.c_str(), rxEntry.masterPort); + } } if (receivedParams.size() > 0) { From b93b7f2f95a074e0fea7ea9af40a135e54c7c724 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 7 Oct 2025 16:39:19 -0400 Subject: [PATCH 058/200] remove old call in progress global; refactor call collisions, allow the call collision timeout be user definable (with 0 disabling call collisions, user beware); --- configs/fne-config.example.yml | 3 + src/fne/network/FNENetwork.cpp | 14 ++--- src/fne/network/FNENetwork.h | 2 +- src/fne/network/callhandler/TagAnalogData.cpp | 43 +++++++-------- src/fne/network/callhandler/TagDMRData.cpp | 55 +++++++++---------- src/fne/network/callhandler/TagNXDNData.cpp | 55 +++++++++---------- src/fne/network/callhandler/TagP25Data.cpp | 52 ++++++++++-------- .../callhandler/packetdata/DMRPacketData.cpp | 5 -- 8 files changed, 113 insertions(+), 116 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 9bd186ef..2de43a11 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -111,6 +111,9 @@ master: # Flag indicating whether or not verbose debug logging for P25 OTAR KMF services is enabled. kmfDebug: false + # Amount of time in seconds for a call collision to last before switching over the source of a call. + callCollisionTimeout: 5 + # Flag indicating whether or not a grant responses will only be sent to TGs with affiliations, if the TG is configured for affiliation gating. restrictGrantToAffiliatedOnly: false # Flag indicating whether or not a private call will only be routed to the network peers the RID registers with. diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index ceb0c4cb..e8648093 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -104,7 +104,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_updateLookupTimer(1000U, (updateLookupTime * 60U)), m_haUpdateTimer(1000U, FIXED_HA_UPDATE_INTERVAL), m_softConnLimit(0U), - m_callInProgress(false), + m_callCollisionTimeout(5U), m_disallowAdjStsBcast(false), m_disallowExtAdjStsBcast(true), m_allowConvSiteAffOverride(false), @@ -221,6 +221,8 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogWarning(LOG_P25, "FNE is compiled without OpenSSL support, KMF services are unavailable."); #endif // ENABLE_SSL + m_callCollisionTimeout = conf["callCollisionTimeout"].as(5U); + m_restrictGrantToAffOnly = conf["restrictGrantToAffiliatedOnly"].as(false); m_restrictPVCallToRegOnly = conf["restrictPrivateCallToRegOnly"].as(false); m_filterTerminators = conf["filterTerminators"].as(true); @@ -276,6 +278,10 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) if (m_maskOutboundPeerIDForNonPL) { LogInfo(" Mask Outbound Traffic Peer ID for Non-Peer Link: yes"); } + LogInfo(" Call Collision Timeout: %us", m_callCollisionTimeout); + if (m_callCollisionTimeout == 0U) { + LogWarning(LOG_NET, "Call Collisions are disabled because the call collision timeout is set to 0 seconds. This is not recommended, and can cause undesired behavior."); + } LogInfo(" Restrict grant response by affiliation: %s", m_restrictGrantToAffOnly ? "yes" : "no"); LogInfo(" Restrict private call to registered units: %s", m_restrictPVCallToRegOnly ? "yes" : "no"); LogInfo(" Traffic Terminators Filtered by Destination ID: %s", m_filterTerminators ? "yes" : "no"); @@ -430,12 +436,6 @@ void FNENetwork::clock(uint32_t ms) } } - // roll the RTP timestamp if no call is in progress - if (!m_callInProgress) { - frame::RTPHeader::resetStartTime(); - m_frameQueue->clearTimestamps(); - } - // send peer updates to replica peers if (m_host->m_peerNetworks.size() > 0) { for (auto peer : m_host->m_peerNetworks) { diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 65ab74ad..9eb86473 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -637,7 +637,7 @@ namespace network uint32_t m_softConnLimit; - bool m_callInProgress; + uint32_t m_callCollisionTimeout; bool m_disallowAdjStsBcast; bool m_disallowExtAdjStsBcast; diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 8d794b56..814b1f39 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -27,12 +27,6 @@ using namespace analog::defines; #include #include -// --------------------------------------------------------------------------- -// Constants -// --------------------------------------------------------------------------- - -const uint32_t CALL_COLL_TIMEOUT = 10U; - // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -141,7 +135,6 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } m_network->eraseStreamPktSeq(peerId, streamId); - m_network->m_callInProgress = false; } } @@ -163,16 +156,27 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee RxStatus status = it->second; if (streamId != status.streamId) { if (status.srcId != 0U && status.srcId != srcId) { - uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); - if ((lastPktDuration / 1000) > CALL_COLL_TIMEOUT) { - LogWarning(LOG_NET, "Analog, Call Collision, lasted more then %us with no further updates, forcibly ending call"); - m_status[dstId].reset(); - m_network->m_callInProgress = false; + if (m_network->m_callCollisionTimeout > 0U) { + uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); + if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { + LogWarning(LOG_NET, "Analog, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); + + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status.unlock(); + } + else { + LogWarning(LOG_NET, "Analog, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", + peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + return false; + } + } else { + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status.unlock(); } - - LogWarning(LOG_NET, "Analog, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", - peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); - return false; } } } @@ -202,8 +206,6 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status.unlock(); LogMessage(LOG_NET, "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); - - m_network->m_callInProgress = true; } } @@ -271,8 +273,6 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee ssrc, peerId, peer.first, seqNo, srcId, dstId, len, pktSeq, streamId, external); } - if (!m_network->m_callInProgress) - m_network->m_callInProgress = true; i++; } } @@ -321,9 +321,6 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee LogDebug(LOG_NET, "Analog, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, len = %u, pktSeq = %u, stream = %u, external = %u", ssrc, peerId, dstPeerId, seqNo, srcId, dstId, len, pktSeq, streamId, external); } - - if (!m_network->m_callInProgress) - m_network->m_callInProgress = true; } } } diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index a5da723f..9eb711e8 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -31,12 +31,6 @@ using namespace dmr::defines; #include #include -// --------------------------------------------------------------------------- -// Constants -// --------------------------------------------------------------------------- - -const uint32_t CALL_COLL_TIMEOUT = 10U; - // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -218,7 +212,6 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } m_network->eraseStreamPktSeq(peerId, streamId); - m_network->m_callInProgress = false; } } @@ -244,27 +237,40 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver && status.slotNo == slotNo) { - status.streamId = streamId; + m_status.lock(false); + m_status[dstId].streamId = streamId; if (status.srcId == 0U) - status.srcId = srcId; + m_status[dstId].srcId = srcId; if (status.srcId != srcId) { LogMessage(LOG_NET, "DMR, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, external = %u", peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, external); - status.srcId = srcId; + m_status[dstId].srcId = srcId; } + m_status.unlock(); } - - if (status.srcId != 0U && status.srcId != srcId) { - uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); - if ((lastPktDuration / 1000) > CALL_COLL_TIMEOUT) { - LogWarning(LOG_NET, "DMR, Call Collision, lasted more then %us with no further updates, forcibly ending call"); - m_status[dstId].reset(); - m_network->m_callInProgress = false; + else { + if (status.srcId != 0U && status.srcId != srcId) { + if (m_network->m_callCollisionTimeout > 0U) { + uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); + if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { + LogWarning(LOG_NET, "DMR, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); + + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status.unlock(); + } else { + LogWarning(LOG_NET, "DMR, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, external = %u", + peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, external); + return false; + } + } else { + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status.unlock(); + } } - - LogWarning(LOG_NET, "DMR, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, external = %u", - peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, external); - return false; } } } @@ -316,8 +322,6 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } else LogMessage(LOG_NET, "DMR, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); - - m_network->m_callInProgress = true; } } @@ -457,8 +461,6 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId ssrc, peerId, peer.first, seqNo, srcId, dstId, flco, slotNo, len, pktSeq, streamId, external); } - if (!m_network->m_callInProgress) - m_network->m_callInProgress = true; i++; } } @@ -513,9 +515,6 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId LogDebug(LOG_NET, "DMR, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, flco = $%02X, slotNo = %u, len = %u, pktSeq = %u, stream = %u, external = %u", ssrc, peerId, dstPeerId, seqNo, srcId, dstId, flco, slotNo, len, pktSeq, streamId, external); } - - if (!m_network->m_callInProgress) - m_network->m_callInProgress = true; } } } diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 64e787e3..7118b363 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -34,12 +34,6 @@ using namespace nxdn::defines; #include #include -// --------------------------------------------------------------------------- -// Constants -// --------------------------------------------------------------------------- - -const uint32_t CALL_COLL_TIMEOUT = 10U; - // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -256,7 +250,6 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI } m_network->eraseStreamPktSeq(peerId, streamId); - m_network->m_callInProgress = false; } } @@ -282,27 +275,40 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver) { - status.streamId = streamId; + m_status.lock(false); + m_status[dstId].streamId = streamId; if (status.srcId == 0U) - status.srcId = srcId; + m_status[dstId].srcId = srcId; if (status.srcId != srcId) { LogMessage(LOG_NET, "NXDN, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); - status.srcId = srcId; + m_status[dstId].srcId = srcId; } + m_status.unlock(); } - - if (status.srcId != 0U && status.srcId != srcId) { - uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); - if ((lastPktDuration / 1000) > CALL_COLL_TIMEOUT) { - LogWarning(LOG_NET, "NXDN, Call Collision, lasted more then %us with no further updates, forcibly ending call"); - m_status[dstId].reset(); - m_network->m_callInProgress = false; + else { + if (status.srcId != 0U && status.srcId != srcId) { + if (m_network->m_callCollisionTimeout > 0U) { + uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); + if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { + LogWarning(LOG_NET, "NXDN, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); + + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status.unlock(); + } else { + LogWarning(LOG_NET, "NXDN, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", + peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + return false; + } + } else { + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status.unlock(); + } } - - LogWarning(LOG_NET, "NXDN, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", - peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); - return false; } } } @@ -352,8 +358,6 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI else LogMessage(LOG_NET, "NXDN, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); - - m_network->m_callInProgress = true; } } } @@ -487,8 +491,6 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI ssrc, peerId, peer.first, messageType, srcId, dstId, len, pktSeq, streamId, external); } - if (!m_network->m_callInProgress) - m_network->m_callInProgress = true; i++; } } @@ -543,9 +545,6 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI LogDebug(LOG_NET, "NXDN, ssrc = %u, srcPeer = %u, dstPeer = %u, messageType = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, external = %u", ssrc, peerId, dstPeerId, messageType, srcId, dstId, len, pktSeq, streamId, external); } - - if (!m_network->m_callInProgress) - m_network->m_callInProgress = true; } } } diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 093f1cd5..7eeae720 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -35,7 +35,6 @@ using namespace p25::defines; // --------------------------------------------------------------------------- const uint32_t GRANT_TIMER_TIMEOUT = 15U; -const uint32_t CALL_COLL_TIMEOUT = 10U; // --------------------------------------------------------------------------- // Public Class Members @@ -260,7 +259,6 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } m_network->eraseStreamPktSeq(peerId, streamId); - m_network->m_callInProgress = false; } } } @@ -287,27 +285,40 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver) { - status.streamId = streamId; + m_status.lock(false); + m_status[dstId].streamId = streamId; if (status.srcId == 0U) - status.srcId = srcId; + m_status[dstId].srcId = srcId; if (status.srcId != srcId) { LogMessage(LOG_NET, "P25, Call Source Switched, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); - status.srcId = srcId; + m_status[dstId].srcId = srcId; } - } - - if (status.srcId != 0U && status.srcId != srcId) { - uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); - if ((lastPktDuration / 1000) > CALL_COLL_TIMEOUT) { - LogWarning(LOG_NET, "P25, Call Collision, lasted more then %us with no further updates, forcibly ending call"); - m_status[dstId].reset(); - m_network->m_callInProgress = false; + m_status.unlock(); + } else { + if (status.srcId != 0U && status.srcId != srcId) { + if (m_network->m_callCollisionTimeout > 0U) { + uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); + if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { + LogWarning(LOG_NET, "P25, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); + + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status.unlock(); + } + else { + LogWarning(LOG_NET, "P25, Call Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", + peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + return false; + } + } else { + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status.unlock(); + } } - - LogWarning(LOG_NET, "P25, Call Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", - peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); - return false; } } } @@ -357,8 +368,6 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId else LogMessage(LOG_NET, "P25, Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, external); - - m_network->m_callInProgress = true; } } } @@ -516,8 +525,6 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId ssrc, peerId, peer.first, duid, lco, MFId, srcId, dstId, len, pktSeq, streamId, external); } - if (!m_network->m_callInProgress) - m_network->m_callInProgress = true; i++; } } @@ -575,9 +582,6 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId ssrc, peerId, dstPeerId, duid, lco, MFId, srcId, dstId, len, pktSeq, streamId, external); } } - - if (!m_network->m_callInProgress) - m_network->m_callInProgress = true; } } } diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp index 3a226f54..86acf484 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp @@ -289,8 +289,6 @@ void DMRPacketData::dispatchToFNE(uint32_t peerId, dmr::data::NetData& dmrData, peerId, peer.first, seqNo, srcId, dstId, status->slotNo, len, pktSeq, streamId); } - if (!m_network->m_callInProgress) - m_network->m_callInProgress = true; i++; } } @@ -320,9 +318,6 @@ void DMRPacketData::dispatchToFNE(uint32_t peerId, dmr::data::NetData& dmrData, LogDebug(LOG_NET, "DMR, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, slotNo = %u, len = %u, pktSeq = %u, stream = %u", peerId, dstPeerId, seqNo, srcId, dstId, status->slotNo, len, pktSeq, streamId); } - - if (!m_network->m_callInProgress) - m_network->m_callInProgress = true; } } } From ee24b40328cd7da2d58684f6a7ad499b676f3b7c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 7 Oct 2025 17:38:30 -0400 Subject: [PATCH 059/200] simplify sendmmsg implementation; --- src/common/network/udp/Socket.cpp | 33 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index f2f71668..9a120fce 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -546,14 +546,15 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept // create mmsghdrs from input buffers and send them at once int size = buffers.size(); - for (size_t i = 0; i < buffers.size(); ++i) { - if (buffers[i] == nullptr) { + for (size_t i = 0U; i < buffers.size(); ++i) { + UDPDatagram* packet = buffers[i]; + if (packet == nullptr) { --size; continue; } - uint32_t length = buffers[i]->length; - if (buffers[i]->buffer == nullptr) { + uint32_t length = packet->length; + if (packet->buffer == nullptr) { LogError(LOG_NET, "discarding buffered message with len = %u, but deleted buffer?", length); --size; continue; @@ -563,7 +564,7 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept // are we crypto wrapped? if (m_isCryptoWrapped && m_presharedKey != nullptr) { uint32_t cryptedLen = length * sizeof(uint8_t); - uint8_t* cryptoBuffer = buffers[i]->buffer; + uint8_t* cryptoBuffer = packet->buffer; // do we need to pad the original buffer to be block aligned? if (cryptedLen % crypto::AES::BLOCK_BYTES_LEN != 0) { @@ -573,7 +574,7 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept // reallocate buffer and copy cryptoBuffer = new uint8_t[cryptedLen]; ::memset(cryptoBuffer, 0x00U, cryptedLen); - ::memcpy(cryptoBuffer, buffers[i]->buffer, length); + ::memcpy(cryptoBuffer, packet->buffer, length); } // encrypt @@ -594,25 +595,25 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept // cleanup buffers and replace with new delete[] crypted; - //delete buffers[i]->buffer; + //delete packet->buffer; // this should never happen... - if (buffers[i] == nullptr) { + if (packet == nullptr) { --size; continue; } - buffers[i]->buffer = new uint8_t[cryptedLen + 2U]; - ::memcpy(buffers[i]->buffer, out, cryptedLen + 2U); - buffers[i]->length = cryptedLen + 2U; + packet->buffer = new uint8_t[cryptedLen + 2U]; + ::memcpy(packet->buffer, out, cryptedLen + 2U); + packet->length = cryptedLen + 2U; } - chunks[i].iov_len = buffers[i]->length; - chunks[i].iov_base = buffers[i]->buffer; - sent += buffers[i]->length; + chunks[i].iov_len = packet->length; + chunks[i].iov_base = packet->buffer; + sent += packet->length; - headers[i].msg_hdr.msg_name = (void*)&buffers[i]->address; - headers[i].msg_hdr.msg_namelen = buffers[i]->addrLen; + headers[i].msg_hdr.msg_name = (void*)&packet->address; + headers[i].msg_hdr.msg_namelen = packet->addrLen; headers[i].msg_hdr.msg_iov = &chunks[i]; headers[i].msg_hdr.msg_iovlen = 1; headers[i].msg_hdr.msg_control = 0; From 7eb1d3edfebab30d575d54e4801daa133659dec2 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 8 Oct 2025 12:56:33 -0400 Subject: [PATCH 060/200] BUGFIX: correct scenario where traffic from an upstream master to a downstream peer FNE would lose the RTP sequence numbering; --- 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 a58ab53a..b186c7d4 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -1018,7 +1018,7 @@ void HostFNE::processPeerDMR(network::PeerNetwork* peerNetwork, const uint8_t* d // process DMR data if (length > 0U) { uint32_t peerId = peerNetwork->getPeerId(); - m_network->dmrTrafficHandler()->processFrame(data, length, peerId, rtpHeader.getSSRC(), peerNetwork->pktLastSeq(), streamId, true); + m_network->dmrTrafficHandler()->processFrame(data, length, peerId, rtpHeader.getSSRC(), rtpHeader.getSequence(), streamId, true); } } @@ -1040,7 +1040,7 @@ void HostFNE::processPeerP25(network::PeerNetwork* peerNetwork, const uint8_t* d // process P25 data if (length > 0U) { uint32_t peerId = peerNetwork->getPeerId(); - m_network->p25TrafficHandler()->processFrame(data, length, peerId, rtpHeader.getSSRC(), peerNetwork->pktLastSeq(), streamId, true); + m_network->p25TrafficHandler()->processFrame(data, length, peerId, rtpHeader.getSSRC(), rtpHeader.getSequence(), streamId, true); } } @@ -1062,7 +1062,7 @@ void HostFNE::processPeerNXDN(network::PeerNetwork* peerNetwork, const uint8_t* // process NXDN data if (length > 0U) { uint32_t peerId = peerNetwork->getPeerId(); - m_network->nxdnTrafficHandler()->processFrame(data, length, peerId, rtpHeader.getSSRC(), peerNetwork->pktLastSeq(), streamId, true); + m_network->nxdnTrafficHandler()->processFrame(data, length, peerId, rtpHeader.getSSRC(), rtpHeader.getSequence(), streamId, true); } } @@ -1084,6 +1084,6 @@ void HostFNE::processPeerAnalog(network::PeerNetwork* peerNetwork, const uint8_t // process analog data if (length > 0U) { uint32_t peerId = peerNetwork->getPeerId(); - m_network->analogTrafficHandler()->processFrame(data, length, peerId, rtpHeader.getSSRC(), peerNetwork->pktLastSeq(), streamId, true); + m_network->analogTrafficHandler()->processFrame(data, length, peerId, rtpHeader.getSSRC(), rtpHeader.getSequence(), streamId, true); } } From eade334fbe93aeea07398e55970559de85f04dcd Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 8 Oct 2025 13:02:56 -0400 Subject: [PATCH 061/200] refactor how RTP multiplexing by stream is handled; --- src/CompilerOptions.cmake | 5 + src/common/network/BaseNetwork.h | 201 +++++++++++++++++++++++++++++++ src/common/network/Network.cpp | 70 +++++++++++ src/common/network/Network.h | 1 + src/fne/network/FNENetwork.cpp | 65 +++------- src/fne/network/FNENetwork.h | 139 +-------------------- src/fne/network/PeerNetwork.cpp | 16 +++ 7 files changed, 315 insertions(+), 182 deletions(-) diff --git a/src/CompilerOptions.cmake b/src/CompilerOptions.cmake index 0c8eb7c4..c088e063 100644 --- a/src/CompilerOptions.cmake +++ b/src/CompilerOptions.cmake @@ -48,6 +48,7 @@ option(DEBUG_RINGBUFFER "" off) option(DEBUG_HTTP_PAYLOAD "" off) option(DEBUG_TRELLIS "" off) option(DEBUG_COMPRESS "" off) +option(DEBUG_RTP_MUX "" off) if (DEBUG_DMR_PDU_DATA) message(CHECK_START "DMR PDU Data Debug") @@ -149,6 +150,10 @@ if (DEBUG_COMPRESS) message(CHECK_START "zlib Compression Debug") add_definitions(-DDEBUG_COMPRESS) endif (DEBUG_COMPRESS) +if (DEBUG_RTP_MUX) + message(CHECK_START "RTP Mux Debug") + add_definitions(-DDEBUG_RTP_MUX) +endif (DEBUG_RTP_MUX) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") find_package(Threads REQUIRED) diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index 2b9f756d..fd70569b 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -150,6 +150,207 @@ namespace network NET_CTRL_U2U = 0x01U, //!< Unit-to-Unit }; + /** + * @brief RTP Stream Multiplex Validation Return Codes + * @ingroup network_core + */ + enum MULTIPLEX_RET_CODE { + MUX_VALID_SUCCESS = 0U, //!< Successful Validation + + MUX_LOST_FRAMES = 1U, //!< Lost Frames + MUX_OUT_OF_ORDER = 2U //!< Out-of-Order + }; + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Handles dealing with maintaining RTP sequencing for multiple multiplexed RTP streams. + * @ingroup fne_network + */ + class HOST_SW_API RTPStreamMultiplex { + public: + auto operator=(RTPStreamMultiplex&) -> RTPStreamMultiplex& = delete; + auto operator=(RTPStreamMultiplex&&) -> RTPStreamMultiplex& = delete; + RTPStreamMultiplex(RTPStreamMultiplex&) = delete; + + /** + * @brief Initializes a new instance of the RTPStreamMultiplex class. + */ + RTPStreamMultiplex() : + m_mutex(), + m_streamSeqNos() + { + /* stub */ + } + + /** + * @brief Helper to verify the given RTP sequence for the given multiplexed RTP stream. + * @param streamId Stream ID. + * @param pktSeq Packet Sequence. + * @param func Network function. + * @param[out] lastRxSeq Last Received Sequence. + * @return MULTIPLEX_RET_CODE Return code. + */ + MULTIPLEX_RET_CODE verifyStream(uint64_t streamId, uint16_t pktSeq, uint8_t func, uint16_t* lastRxSeq) + { + MULTIPLEX_RET_CODE ret = MUX_VALID_SUCCESS; + if (pktSeq == RTP_END_OF_CALL_SEQ) { + // only reset packet sequences if we're a PROTOCOL or RPTC function + if ((func == NET_FUNC::PROTOCOL) || (func == NET_FUNC::RPTC)) { + erasePktSeq(streamId); // attempt to erase packet sequence for the stream + } + } else { + if (hasPktSeq(streamId)) { + *lastRxSeq = getPktSeq(streamId); + + if (*lastRxSeq == RTP_END_OF_CALL_SEQ) { + // reset the received sequence back to 0 + setPktSeq(streamId, 0U); + } + else { + if ((pktSeq >= *lastRxSeq) || (pktSeq == 0U)) { + // if the sequence isn't 0, and is greater then the last received sequence + 1 frame + // assume a packet was lost + if ((pktSeq != 0U) && pktSeq >= *lastRxSeq + 1U) { + ret = MUX_LOST_FRAMES; + } + + setPktSeq(streamId, pktSeq); + } + else { + if (pktSeq < *lastRxSeq) { + ret = MUX_OUT_OF_ORDER; + } + } + } + } + } + + return ret; + } + + /** + * @brief Helper to return the current count of multiplexed RTP streams. + * @returns size_t Count of stored streams. + */ + size_t streamCount() + { + return m_streamSeqNos.size(); + } + + /** + * @brief Helper to determine if the given multiplexed stream has a stored RTP sequence. + * @param streamId Stream ID. + * @returns bool True, if stream ID has a stored RTP sequence, otherwise false. + */ + bool hasPktSeq(uint64_t streamId) + { + bool ret = false; + std::lock_guard lock(m_mutex); + + // determine if the stream has a current sequence no and return + { + auto it = m_streamSeqNos.find(streamId); + if (it == m_streamSeqNos.end()) { + ret = false; + } + else { + ret = true; + } + } + + return ret; + } + + /** + * @brief Helper to get the stored RTP sequence for the given multiplexed stream. + * @param streamId Stream ID. + * @returns uint16_t Sequence number. + */ + uint16_t getPktSeq(uint64_t streamId) + { + std::lock_guard lock(m_mutex); + + // find the current sequence no and return + uint32_t pktSeq = 0U; + { + auto it = m_streamSeqNos.find(streamId); + if (it == m_streamSeqNos.end()) { + pktSeq = RTP_END_OF_CALL_SEQ; + } else { + pktSeq = m_streamSeqNos[streamId]; + } + } + + return pktSeq; + } + + /** + * @brief Helper to set the stored RTP sequence for the given multiplexed stream. + * @param streamId Stream ID. + * @param seq Sequence number. + */ + void setPktSeq(uint64_t streamId, uint16_t seq) + { + std::lock_guard lock(m_mutex); + auto it = m_streamSeqNos.find(streamId); + if (it == m_streamSeqNos.end()) { + m_streamSeqNos.insert({streamId, seq}); + } else { + m_streamSeqNos[streamId] = seq; + } + } + + /** + * @brief Helper to increment the stored RTP sequence for the given multiplexed stream. + * @param streamId Stream ID. + * @returns uint16_t Incremented packet sequence. + */ + uint16_t incPktSeq(uint64_t streamId) + { + std::lock_guard lock(m_mutex); + + uint32_t pktSeq = 0U; + auto it = m_streamSeqNos.find(streamId); + if (it == m_streamSeqNos.end()) { + m_streamSeqNos.insert({streamId, pktSeq}); + } else { + pktSeq = m_streamSeqNos[streamId]; + ++pktSeq; + + if (pktSeq > (RTP_END_OF_CALL_SEQ - 1U)) + pktSeq = 0U; + + m_streamSeqNos[streamId] = pktSeq; + } + + return pktSeq; + } + + /** + * @brief Helper to erase the stored RTP sequence for the given multiplexed stream. + * @param streamId Stream ID. + */ + void erasePktSeq(uint64_t streamId) + { + std::lock_guard lock(m_mutex); + + // find the sequence no and erase + { + auto entry = m_streamSeqNos.find(streamId); + if (entry != m_streamSeqNos.end()) { + m_streamSeqNos.erase(streamId); + } + } + } + + private: + std::recursive_mutex m_mutex; + std::unordered_map m_streamSeqNos; + }; + // --------------------------------------------------------------------------- // Class Declaration // --------------------------------------------------------------------------- diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index 24b99b7e..7e1f36a9 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -64,6 +64,7 @@ Network::Network(const std::string& address, uint16_t port, uint16_t localPort, m_pktSeq(0U), m_loginStreamId(0U), m_metadata(nullptr), + m_mux(nullptr), m_remotePeerId(0U), m_promiscuousPeer(false), m_userHandleProtocol(false), @@ -90,6 +91,7 @@ Network::Network(const std::string& address, uint16_t port, uint16_t localPort, m_rxAnalogStreamId = 0U; m_metadata = new PeerMetadata(); + m_mux = new RTPStreamMultiplex(); } /* Finalizes a instance of the Network class. */ @@ -318,6 +320,23 @@ void Network::clock(uint32_t ms) if (m_promiscuousPeer) { m_rxDMRStreamId[slotNo] = streamId; m_pktLastSeq = m_pktSeq; + + uint16_t lastRxSeq = 0U; + + MULTIPLEX_RET_CODE ret = m_mux->verifyStream(streamId, rtpHeader.getSequence(), fneHeader.getFunction(), &lastRxSeq); + if (ret == MUX_LOST_FRAMES) { + LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", peerId, + streamId, rtpHeader.getSequence(), lastRxSeq, rtpHeader.getSequence()); + } + else if (ret == MUX_OUT_OF_ORDER) { + LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", peerId, + streamId, rtpHeader.getSequence(), lastRxSeq); + } +#if DEBUG_RTP_MUX + else { + LogDebugEx(LOG_NET, "Network::clock()", "PEER %u valid mux, seq = %u, streamId = %u", peerId, rtpHeader.getSequence(), streamId); + } +#endif } else { if (m_rxDMRStreamId[slotNo] == 0U) { @@ -370,6 +389,23 @@ void Network::clock(uint32_t ms) if (m_promiscuousPeer) { m_rxP25StreamId = streamId; m_pktLastSeq = m_pktSeq; + + uint16_t lastRxSeq = 0U; + + MULTIPLEX_RET_CODE ret = m_mux->verifyStream(streamId, rtpHeader.getSequence(), fneHeader.getFunction(), &lastRxSeq); + if (ret == MUX_LOST_FRAMES) { + LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", peerId, + streamId, rtpHeader.getSequence(), lastRxSeq, rtpHeader.getSequence()); + } + else if (ret == MUX_OUT_OF_ORDER) { + LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", peerId, + streamId, rtpHeader.getSequence(), lastRxSeq); + } +#if DEBUG_RTP_MUX + else { + LogDebugEx(LOG_NET, "Network::clock()", "PEER %u valid mux, seq = %u, streamId = %u", peerId, rtpHeader.getSequence(), streamId); + } +#endif } else { if (m_rxP25StreamId == 0U) { @@ -431,6 +467,23 @@ void Network::clock(uint32_t ms) if (m_promiscuousPeer) { m_rxNXDNStreamId = streamId; m_pktLastSeq = m_pktSeq; + + uint16_t lastRxSeq = 0U; + + MULTIPLEX_RET_CODE ret = m_mux->verifyStream(streamId, rtpHeader.getSequence(), fneHeader.getFunction(), &lastRxSeq); + if (ret == MUX_LOST_FRAMES) { + LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", peerId, + streamId, rtpHeader.getSequence(), lastRxSeq, rtpHeader.getSequence()); + } + else if (ret == MUX_OUT_OF_ORDER) { + LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", peerId, + streamId, rtpHeader.getSequence(), lastRxSeq); + } +#if DEBUG_RTP_MUX + else { + LogDebugEx(LOG_NET, "Network::clock()", "PEER %u valid mux, seq = %u, streamId = %u", peerId, rtpHeader.getSequence(), streamId); + } +#endif } else { if (m_rxNXDNStreamId == 0U) { @@ -483,6 +536,23 @@ void Network::clock(uint32_t ms) if (m_promiscuousPeer) { m_rxAnalogStreamId = streamId; m_pktLastSeq = m_pktSeq; + + uint16_t lastRxSeq = 0U; + + MULTIPLEX_RET_CODE ret = m_mux->verifyStream(streamId, rtpHeader.getSequence(), fneHeader.getFunction(), &lastRxSeq); + if (ret == MUX_LOST_FRAMES) { + LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", peerId, + streamId, rtpHeader.getSequence(), lastRxSeq, rtpHeader.getSequence()); + } + else if (ret == MUX_OUT_OF_ORDER) { + LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", peerId, + streamId, rtpHeader.getSequence(), lastRxSeq); + } +#if DEBUG_RTP_MUX + else { + LogDebugEx(LOG_NET, "Network::clock()", "PEER %u valid mux, seq = %u, streamId = %u", peerId, rtpHeader.getSequence(), streamId); + } +#endif } else { if (m_rxAnalogStreamId == 0U) { diff --git a/src/common/network/Network.h b/src/common/network/Network.h index 5acb33a0..b83d83b1 100644 --- a/src/common/network/Network.h +++ b/src/common/network/Network.h @@ -329,6 +329,7 @@ namespace network uint32_t m_loginStreamId; PeerMetadata* m_metadata; + RTPStreamMultiplex* m_mux; uint32_t m_remotePeerId; diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index e8648093..ed0e1ec6 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -735,52 +735,16 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) uint16_t pktSeq = req->rtpHeader.getSequence(); if (connection != nullptr) { - if (pktSeq == RTP_END_OF_CALL_SEQ) { - // only reset packet sequences if we're a PROTOCOL or RPTC function - if ((req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) || - (req->fneHeader.getFunction() == NET_FUNC::RPTC)) { - connection->eraseStreamPktSeq(streamId); // attempt to erase packet sequence for the stream - } - } else { - bool skip = false; - if (connection->hasStreamPktSeq(streamId)) { - uint16_t currPkt = connection->getStreamPktSeq(streamId); - - if (currPkt == RTP_END_OF_CALL_SEQ) { - // update the expected current packet sequence for the next sequence number - connection->setStreamPktSeq(streamId, 0U, true); - skip = true; // don't alter the packet sequence any further - } - else { - if ((pktSeq != currPkt) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U)) && pktSeq != 0U) { - LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; got %u, expected %u", peerId, connection->identWithQualifier().c_str(), - streamId, pktSeq, currPkt); - - if (req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) { - // possibly lost frames - if (pktSeq > currPkt) { - LogError(LOG_NET, "PEER %u (%s) stream %u possible lost frames; got %u, expected %u, resetting sequence to %u", peerId, connection->identWithQualifier().c_str(), - streamId, pktSeq, currPkt, pktSeq); - - // update the expected current packet sequence for the next sequence number - connection->setStreamPktSeq(streamId, pktSeq + 1U, true); - skip = true; // don't alter the packet sequence any further - } + uint16_t lastRxSeq = 0U; - // received out of order frames - if (pktSeq < currPkt) { - LogError(LOG_NET, "PEER %u (%s) stream %u out-of-order; got %u, expected %u", peerId, connection->identWithQualifier().c_str(), - streamId, pktSeq, currPkt); - skip = true; // don't alter the packet sequence -- this can result in odd behavior - } - } - } - } - } - - // update the expected current packet sequence for the next sequence number - if (!skip) - connection->setStreamPktSeq(streamId, pktSeq + 1U); + MULTIPLEX_RET_CODE ret = connection->verifyStream(streamId, pktSeq, req->fneHeader.getFunction(), &lastRxSeq); + if (ret == MUX_LOST_FRAMES) { + LogError(LOG_NET, "PEER %u (%s) stream %u possible lost frames; got %u, expected %u", peerId, connection->identWithQualifier().c_str(), + streamId, pktSeq, lastRxSeq, pktSeq); + } + else if (ret == MUX_OUT_OF_ORDER) { + LogError(LOG_NET, "PEER %u (%s) stream %u out-of-order; got %u, expected >%u", peerId, connection->identWithQualifier().c_str(), + streamId, pktSeq, lastRxSeq); } } @@ -1772,7 +1736,7 @@ void FNENetwork::eraseStreamPktSeq(uint32_t peerId, uint32_t streamId) if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) { FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { - connection->eraseStreamPktSeq(streamId); + connection->erasePktSeq(streamId); } } } @@ -2528,10 +2492,13 @@ bool FNENetwork::writePeer(uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePai sockaddr_storage addr = connection->socketStorage(); uint32_t addrLen = connection->sockStorageLen(); - if (incPktSeq) { - pktSeq = connection->setStreamPktSeq(streamId, pktSeq); + if (incPktSeq && pktSeq != RTP_END_OF_CALL_SEQ) { + pktSeq = connection->incPktSeq(streamId); } - +#if DEBUG_RTP_MUX + if (m_debug) + LogDebugEx(LOG_NET, "FNENetwork::writePeer()", "PEER %u, streamId = %u, pktSeq = %u", peerId, streamId, pktSeq); +#endif if (m_maskOutboundPeerID) ssrc = m_peerId; // mask the source SSRC to our own peer ID else { diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 9eb86473..2b4763b6 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -101,7 +101,7 @@ namespace network * @brief Represents an peer connection to the FNE. * @ingroup fne_network */ - class HOST_SW_API FNEPeerConnection { + class HOST_SW_API FNEPeerConnection : public RTPStreamMultiplex { public: auto operator=(FNEPeerConnection&) -> FNEPeerConnection& = delete; auto operator=(FNEPeerConnection&&) -> FNEPeerConnection& = delete; @@ -110,7 +110,8 @@ namespace network /** * @brief Initializes a new instance of the FNEPeerConnection class. */ - FNEPeerConnection() : + FNEPeerConnection() : + RTPStreamMultiplex(), m_id(0U), m_ccPeerId(0U), m_socketStorage(), @@ -127,9 +128,7 @@ namespace network m_isConventionalPeer(false), m_isSysView(false), m_isPeerReplica(false), - m_config(), - m_streamSeqMutex(), - m_streamSeqNos() + m_config() { /* stub */ } @@ -140,6 +139,7 @@ namespace network * @param sockStorageLen */ FNEPeerConnection(uint32_t id, sockaddr_storage& socketStorage, uint32_t sockStorageLen) : + RTPStreamMultiplex(), m_id(id), m_ccPeerId(0U), m_socketStorage(socketStorage), @@ -156,9 +156,7 @@ namespace network m_isConventionalPeer(false), m_isSysView(false), m_isPeerReplica(false), - m_config(), - m_streamSeqMutex(), - m_streamSeqNos() + m_config() { assert(id > 0U); assert(sockStorageLen > 0U); @@ -166,15 +164,6 @@ namespace network assert(m_port > 0U); } - /** - * @brief Helper to return the current count of mapped RTP streams. - * @returns size_t - */ - size_t streamCount() - { - return m_streamSeqNos.size(); - } - /** * @brief Returns the identity with qualifier symbols. * @return std::string Identity with qualifier. @@ -191,118 +180,6 @@ namespace network return " " + m_identity; } - /** - * @brief Helper to determine if the stream ID has a stored RTP sequence. - * @param streamId Stream ID. - * @returns bool - */ - bool hasStreamPktSeq(uint64_t streamId) - { - bool ret = false; - bool locked = m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60)); - - // determine if the stream has a current sequence no and return - { - auto it = m_streamSeqNos.find(streamId); - if (it == m_streamSeqNos.end()) { - ret = false; - } - else { - ret = true; - } - } - - if (locked) - m_streamSeqMutex.unlock(); - - return ret; - } - - /** - * @brief Helper to get the stored RTP sequence for the given stream ID. - * @param streamId Stream ID. - * @returns uint16_t - */ - uint16_t getStreamPktSeq(uint64_t streamId) - { - bool locked = m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60)); - - // find the current sequence no and return - uint32_t pktSeq = 0U; - { - auto it = m_streamSeqNos.find(streamId); - if (it == m_streamSeqNos.end()) { - pktSeq = RTP_END_OF_CALL_SEQ; - } else { - pktSeq = m_streamSeqNos[streamId]; - } - } - - if (locked) - m_streamSeqMutex.unlock(); - - return pktSeq; - } - - /** - * @brief Helper to set/increment the stored RTP sequence for the given stream ID. - * @param streamId Stream ID. - * @param initialSeq Initial sequence number to set. - * @param force Force sequence number to be reset to the value provided by initialSeq. - * @returns uint16_t - */ - uint16_t setStreamPktSeq(uint64_t streamId, uint16_t initialSeq, bool force = false) - { - bool locked = m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60)); - - // find the current sequence no, increment and return - uint32_t pktSeq = 0U; - { - auto it = m_streamSeqNos.find(streamId); - if (it == m_streamSeqNos.end()) { - m_streamSeqNos.insert({streamId, initialSeq}); - } else { - if (!force) { - pktSeq = m_streamSeqNos[streamId]; - ++pktSeq; - } else { - pktSeq = initialSeq; - } - - if (pktSeq > RTP_END_OF_CALL_SEQ) { - pktSeq = 0U; - } - - m_streamSeqNos[streamId] = pktSeq; - } - } - - if (locked) - m_streamSeqMutex.unlock(); - - return pktSeq; - } - /** - * @brief Helper to erase the stored RTP sequence for the given stream ID. - * @param streamId Stream ID. - * @returns uint16_t - */ - void eraseStreamPktSeq(uint64_t streamId) - { - bool locked = m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60)); - - // find the sequence no and erase - { - auto entry = m_streamSeqNos.find(streamId); - if (entry != m_streamSeqNos.end()) { - m_streamSeqNos.erase(streamId); - } - } - - if (locked) - m_streamSeqMutex.unlock(); - } - public: /** * @brief Peer ID. @@ -385,10 +262,6 @@ namespace network * @brief JSON objecting containing peer configuration information. */ DECLARE_PROPERTY_PLAIN(json::object, config); - - private: - std::timed_mutex m_streamSeqMutex; - std::unordered_map m_streamSeqNos; }; // --------------------------------------------------------------------------- diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index de77e4cd..46b09388 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -499,6 +499,22 @@ void PeerNetwork::taskNetworkRx(PeerPacketRequest* req) LogWarning(LOG_NET, "PEER %u packet processing latency >200ms, dt = %u, now = %u", req->peerId, dt, now); } + uint16_t lastRxSeq = 0U; + + MULTIPLEX_RET_CODE ret = network->m_mux->verifyStream(req->streamId, req->rtpHeader.getSequence(), req->fneHeader.getFunction(), &lastRxSeq); + if (ret == MUX_LOST_FRAMES) { + LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", req->fneHeader.getPeerId(), + req->streamId, req->rtpHeader.getSequence(), lastRxSeq, req->rtpHeader.getSequence()); + } + else if (ret == MUX_OUT_OF_ORDER) { + LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", req->fneHeader.getPeerId(), + req->streamId, req->rtpHeader.getSequence(), lastRxSeq); + } +#if DEBUG_RTP_MUX + else { + LogDebugEx(LOG_NET, "PeerNetwork::taskNetworkRx()", "PEER %u valid mux, seq = %u, streamId = %u", req->fneHeader.getPeerId(), req->rtpHeader.getSequence(), req->streamId); + } +#endif // process incomfing message subfunction opcodes switch (req->subFunc) { case NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR: // Encapsulated DMR data frame From 2bd6377db272e98f0a8b787b892c5207a9fba208 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 8 Oct 2025 16:12:10 -0400 Subject: [PATCH 062/200] fix some odd behavior with very fast calls locking up grants on on DVRS channels; --- src/host/p25/Control.cpp | 8 ++++++++ src/host/p25/packet/Voice.cpp | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index 64c84a81..0f391719 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -974,6 +974,14 @@ void Control::clock() if (m_verbose) { LogMessage(LOG_NET, "talkgroup hang has expired, lastDstId = %u", m_netLastDstId); } + + // if the group is still granted at this point -- forcibly release it + if (m_affiliations->isGranted(m_netLastDstId)) { + if (!m_dedicatedControl) { + m_affiliations->releaseGrant(m_netLastDstId, false); + } + } + m_netLastDstId = 0U; m_netLastSrcId = 0U; } diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index f227d843..6803faa0 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -1359,7 +1359,7 @@ bool Voice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L m_netLastDUID = duid; - if (!m_p25->m_enableControl) { + if (!m_p25->m_dedicatedControl) { m_p25->m_affiliations->releaseGrant(m_netLC.getDstId(), false); } From 96ff1e861d45e8b79a91463a9cf4359b92fae32b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 8 Oct 2025 16:18:13 -0400 Subject: [PATCH 063/200] refactor RTP sequence count handling; --- src/common/network/BaseNetwork.h | 7 ++ src/common/network/Network.cpp | 179 +++++++++++++++++++++++-------- src/common/network/Network.h | 7 ++ src/fne/network/FNENetwork.cpp | 2 +- src/fne/network/PeerNetwork.cpp | 2 +- 5 files changed, 150 insertions(+), 47 deletions(-) diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index fd70569b..5cd709a1 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -184,6 +184,13 @@ namespace network { /* stub */ } + /** + * @brief Finalizes a instance of the RTPStreamMultiplex class. + */ + ~RTPStreamMultiplex() + { + m_streamSeqNos.clear(); + } /** * @brief Helper to verify the given RTP sequence for the given multiplexed RTP stream. diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index 7e1f36a9..c5d128a9 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -101,6 +101,7 @@ Network::~Network() delete[] m_salt; delete[] m_rxDMRStreamId; delete m_metadata; + delete m_mux; } /* Resets the DMR ring buffer for the given slot. */ @@ -288,11 +289,6 @@ void Network::clock(uint32_t ms) } m_pktSeq = rtpHeader.getSequence(); - - if (m_pktSeq == RTP_END_OF_CALL_SEQ) { - m_pktSeq = 0U; - m_pktLastSeq = 0U; - } // process incoming message function opcodes switch (fneHeader.getFunction()) { @@ -351,21 +347,36 @@ void Network::clock(uint32_t ms) } else { if (m_rxDMRStreamId[slotNo] == streamId) { - if (m_pktSeq != 0U && m_pktLastSeq != 0U) { - if (m_pktSeq >= 1U && ((m_pktSeq != m_pktLastSeq + 1) && (m_pktSeq - 1 != m_pktLastSeq + 1))) { - LogWarning(LOG_NET, "DMR Stream %u out-of-sequence; %u != %u", streamId, m_pktSeq, m_pktLastSeq + 1); - } - } + uint16_t lastRxSeq = 0U; - m_pktLastSeq = m_pktSeq; - } - - if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { - m_rxDMRStreamId[slotNo] = 0U; + MULTIPLEX_RET_CODE ret = verifyStream(&lastRxSeq); + if (ret == MUX_LOST_FRAMES) { + LogWarning(LOG_NET, "DMR Slot %u stream %u possible lost frames; got %u, expected %u", + slotNo, streamId, m_pktSeq, lastRxSeq); + } + else if (ret == MUX_OUT_OF_ORDER) { + LogWarning(LOG_NET, "DMR Slot %u stream %u out-of-order; got %u, expected %u", + slotNo, streamId, m_pktSeq, lastRxSeq); + } +#if DEBUG_RTP_MUX + else { + LogDebugEx(LOG_NET, "Network::clock()", "DMR Slot %u valid seq, seq = %u, streamId = %u", slotNo, rtpHeader.getSequence(), streamId); + } +#endif + if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { + m_rxDMRStreamId[slotNo] = 0U; + } } } } + // check if we need to skip this stream -- a non-zero stream ID means the network client is locked + // to receiving a specific stream; a zero stream ID means the network is promiscuously + // receiving streams sent to this peer + if (m_rxDMRStreamId[slotNo] != 0U && m_rxDMRStreamId[slotNo] != streamId) { + break; + } + if (m_debug) Utils::dump(1U, "Network::clock(), Network Rx, DMR", buffer.get(), length); if (length > (int)(DMR_PACKET_LENGTH + PACKET_PAD)) @@ -420,21 +431,37 @@ void Network::clock(uint32_t ms) } else { if (m_rxP25StreamId == streamId) { - if (m_pktSeq != 0U && m_pktLastSeq != 0U) { - if (m_pktSeq >= 1U && ((m_pktSeq != m_pktLastSeq + 1) && (m_pktSeq - 1 != m_pktLastSeq + 1))) { - LogWarning(LOG_NET, "P25 Stream %u out-of-sequence; %u != %u", streamId, m_pktSeq, m_pktLastSeq + 1); - } - } - - m_pktLastSeq = m_pktSeq; - } + uint16_t lastRxSeq = 0U; - if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { - m_rxP25StreamId = 0U; + MULTIPLEX_RET_CODE ret = verifyStream(&lastRxSeq); + if (ret == MUX_LOST_FRAMES) { + LogWarning(LOG_NET, "P25 stream %u possible lost frames; got %u, expected %u", + streamId, m_pktSeq, lastRxSeq); + } + else if (ret == MUX_OUT_OF_ORDER) { + LogWarning(LOG_NET, "P25 stream %u out-of-order; got %u, expected %u", + streamId, m_pktSeq, lastRxSeq); + } +#if DEBUG_RTP_MUX + else { + LogDebugEx(LOG_NET, "Network::clock()", "P25 valid seq, seq = %u, streamId = %u", rtpHeader.getSequence(), streamId); + } +#endif + if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { + m_rxP25StreamId = 0U; + } } } } + // check if we need to skip this stream -- a non-zero stream ID means the network client is locked + // to receiving a specific stream; a zero stream ID means the network is promiscuously + // receiving streams sent to this peer + if (m_rxP25StreamId != 0U && m_rxP25StreamId != streamId) + { + break; + } + if (m_debug) Utils::dump(1U, "Network::clock(), Network Rx, P25", buffer.get(), length); if (length > 512) @@ -498,21 +525,36 @@ void Network::clock(uint32_t ms) } else { if (m_rxNXDNStreamId == streamId) { - if (m_pktSeq != 0U && m_pktLastSeq != 0U) { - if (m_pktSeq >= 1U && ((m_pktSeq != m_pktLastSeq + 1) && (m_pktSeq - 1 != m_pktLastSeq + 1))) { - LogWarning(LOG_NET, "NXDN Stream %u out-of-sequence; %u != %u", streamId, m_pktSeq, m_pktLastSeq + 1); - } - } - - m_pktLastSeq = m_pktSeq; - } + uint16_t lastRxSeq = 0U; - if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { - m_rxNXDNStreamId = 0U; + MULTIPLEX_RET_CODE ret = verifyStream(&lastRxSeq); + if (ret == MUX_LOST_FRAMES) { + LogWarning(LOG_NET, "NXDN stream %u possible lost frames; got %u, expected %u", + streamId, m_pktSeq, lastRxSeq); + } + else if (ret == MUX_OUT_OF_ORDER) { + LogWarning(LOG_NET, "NXDN stream %u out-of-order; got %u, expected %u", + streamId, m_pktSeq, lastRxSeq); + } +#if DEBUG_RTP_MUX + else { + LogDebugEx(LOG_NET, "Network::clock()", "NXDN valid seq, seq = %u, streamId = %u", rtpHeader.getSequence(), streamId); + } +#endif + if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { + m_rxNXDNStreamId = 0U; + } } } } + // check if we need to skip this stream -- a non-zero stream ID means the network client is locked + // to receiving a specific stream; a zero stream ID means the network is promiscuously + // receiving streams sent to this peer + if (m_rxNXDNStreamId != 0U && m_rxNXDNStreamId != streamId) { + break; + } + if (m_debug) Utils::dump(1U, "Network::clock(), Network Rx, NXDN", buffer.get(), length); if (length > (int)(NXDN_PACKET_LENGTH + PACKET_PAD)) @@ -567,21 +609,36 @@ void Network::clock(uint32_t ms) } else { if (m_rxAnalogStreamId == streamId) { - if (m_pktSeq != 0U && m_pktLastSeq != 0U) { - if (m_pktSeq >= 1U && ((m_pktSeq != m_pktLastSeq + 1) && (m_pktSeq - 1 != m_pktLastSeq + 1))) { - LogWarning(LOG_NET, "Analog Stream %u out-of-sequence; %u != %u", streamId, m_pktSeq, m_pktLastSeq + 1); - } - } - - m_pktLastSeq = m_pktSeq; - } + uint16_t lastRxSeq = 0U; - if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { - m_rxAnalogStreamId = 0U; + MULTIPLEX_RET_CODE ret = verifyStream(&lastRxSeq); + if (ret == MUX_LOST_FRAMES) { + LogWarning(LOG_NET, "Analog stream %u possible lost frames; got %u, expected %u", + streamId, m_pktSeq, lastRxSeq); + } + else if (ret == MUX_OUT_OF_ORDER) { + LogWarning(LOG_NET, "Analog stream %u out-of-order; got %u, expected %u", + streamId, m_pktSeq, lastRxSeq); + } +#if DEBUG_RTP_MUX + else { + LogDebugEx(LOG_NET, "Network::clock()", "Analog valid seq, seq = %u, streamId = %u", rtpHeader.getSequence(), streamId); + } +#endif + if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { + m_rxAnalogStreamId = 0U; + } } } } + // check if we need to skip this stream -- a non-zero stream ID means the network client is locked + // to receiving a specific stream; a zero stream ID means the network is promiscuously + // receiving streams sent to this peer + if (m_rxAnalogStreamId != 0U && m_rxAnalogStreamId != streamId) { + break; + } + if (m_debug) Utils::dump(1U, "Network::clock(), Network Rx, Analog", buffer.get(), length); if (length < (int)ANALOG_PACKET_LENGTH) { @@ -1171,6 +1228,38 @@ void Network::enable(bool enabled) // Protected Class Members // --------------------------------------------------------------------------- +/* Helper to verify the given RTP sequence for the given RTP stream. */ + +MULTIPLEX_RET_CODE Network::verifyStream(uint16_t* lastRxSeq) +{ + MULTIPLEX_RET_CODE ret = MUX_VALID_SUCCESS; + if (m_pktSeq == RTP_END_OF_CALL_SEQ) { + // reset the received sequence back to 0 + m_pktLastSeq = 0U; + } + else { + *lastRxSeq = m_pktLastSeq; + + if ((m_pktSeq >= m_pktLastSeq) || (m_pktSeq == 0U)) { + // if the sequence isn't 0, and is greater then the last received sequence + 1 frame + // assume a packet was lost + if ((m_pktSeq != 0U) && m_pktSeq > m_pktLastSeq + 1U) { + ret = MUX_LOST_FRAMES; + } + + m_pktLastSeq = m_pktSeq; + } + else { + if (m_pktSeq < m_pktLastSeq) { + ret = MUX_OUT_OF_ORDER; + } + } + } + + m_pktLastSeq = m_pktSeq; + return ret; +} + /* User overrideable handler that allows user code to process network packets not handled by this class. */ void Network::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, uint32_t streamId, diff --git a/src/common/network/Network.h b/src/common/network/Network.h index b83d83b1..c02b9818 100644 --- a/src/common/network/Network.h +++ b/src/common/network/Network.h @@ -385,6 +385,13 @@ namespace network */ std::function m_keyRespCallback; + /** + * @brief Helper to verify the given RTP sequence for the given RTP stream. + * @param[out] lastRxSeq Last Received Sequence. + * @return MULTIPLEX_RET_CODE Return code. + */ + MULTIPLEX_RET_CODE verifyStream(uint16_t* lastRxSeq); + /** * @brief User overrideable handler that allows user code to process network packets not handled by this class. * @param peerId Peer ID. diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index ed0e1ec6..2a71159b 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -740,7 +740,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) MULTIPLEX_RET_CODE ret = connection->verifyStream(streamId, pktSeq, req->fneHeader.getFunction(), &lastRxSeq); if (ret == MUX_LOST_FRAMES) { LogError(LOG_NET, "PEER %u (%s) stream %u possible lost frames; got %u, expected %u", peerId, connection->identWithQualifier().c_str(), - streamId, pktSeq, lastRxSeq, pktSeq); + streamId, pktSeq, lastRxSeq); } else if (ret == MUX_OUT_OF_ORDER) { LogError(LOG_NET, "PEER %u (%s) stream %u out-of-order; got %u, expected >%u", peerId, connection->identWithQualifier().c_str(), diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 46b09388..6be2280e 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -504,7 +504,7 @@ void PeerNetwork::taskNetworkRx(PeerPacketRequest* req) MULTIPLEX_RET_CODE ret = network->m_mux->verifyStream(req->streamId, req->rtpHeader.getSequence(), req->fneHeader.getFunction(), &lastRxSeq); if (ret == MUX_LOST_FRAMES) { LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", req->fneHeader.getPeerId(), - req->streamId, req->rtpHeader.getSequence(), lastRxSeq, req->rtpHeader.getSequence()); + req->streamId, req->rtpHeader.getSequence(), lastRxSeq); } else if (ret == MUX_OUT_OF_ORDER) { LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", req->fneHeader.getPeerId(), From d2168e5c2bab59fcc402f71ecf48855794bc78ae Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 8 Oct 2025 16:21:23 -0400 Subject: [PATCH 064/200] whoops this stream ID check was intended for non-promiscuous operation only; --- src/common/network/Network.cpp | 50 +++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index c5d128a9..6724d41c 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -368,13 +368,13 @@ void Network::clock(uint32_t ms) } } } - } - // check if we need to skip this stream -- a non-zero stream ID means the network client is locked - // to receiving a specific stream; a zero stream ID means the network is promiscuously - // receiving streams sent to this peer - if (m_rxDMRStreamId[slotNo] != 0U && m_rxDMRStreamId[slotNo] != streamId) { - break; + // check if we need to skip this stream -- a non-zero stream ID means the network client is locked + // to receiving a specific stream; a zero stream ID means the network is promiscuously + // receiving streams sent to this peer + if (m_rxDMRStreamId[slotNo] != 0U && m_rxDMRStreamId[slotNo] != streamId) { + break; + } } if (m_debug) @@ -452,16 +452,16 @@ void Network::clock(uint32_t ms) } } } - } - // check if we need to skip this stream -- a non-zero stream ID means the network client is locked - // to receiving a specific stream; a zero stream ID means the network is promiscuously - // receiving streams sent to this peer - if (m_rxP25StreamId != 0U && m_rxP25StreamId != streamId) - { - break; + // check if we need to skip this stream -- a non-zero stream ID means the network client is locked + // to receiving a specific stream; a zero stream ID means the network is promiscuously + // receiving streams sent to this peer + if (m_rxP25StreamId != 0U && m_rxP25StreamId != streamId) { + break; + } } + if (m_debug) Utils::dump(1U, "Network::clock(), Network Rx, P25", buffer.get(), length); if (length > 512) @@ -546,13 +546,13 @@ void Network::clock(uint32_t ms) } } } - } - // check if we need to skip this stream -- a non-zero stream ID means the network client is locked - // to receiving a specific stream; a zero stream ID means the network is promiscuously - // receiving streams sent to this peer - if (m_rxNXDNStreamId != 0U && m_rxNXDNStreamId != streamId) { - break; + // check if we need to skip this stream -- a non-zero stream ID means the network client is locked + // to receiving a specific stream; a zero stream ID means the network is promiscuously + // receiving streams sent to this peer + if (m_rxNXDNStreamId != 0U && m_rxNXDNStreamId != streamId) { + break; + } } if (m_debug) @@ -630,13 +630,13 @@ void Network::clock(uint32_t ms) } } } - } - // check if we need to skip this stream -- a non-zero stream ID means the network client is locked - // to receiving a specific stream; a zero stream ID means the network is promiscuously - // receiving streams sent to this peer - if (m_rxAnalogStreamId != 0U && m_rxAnalogStreamId != streamId) { - break; + // check if we need to skip this stream -- a non-zero stream ID means the network client is locked + // to receiving a specific stream; a zero stream ID means the network is promiscuously + // receiving streams sent to this peer + if (m_rxAnalogStreamId != 0U && m_rxAnalogStreamId != streamId) { + break; + } } if (m_debug) From 83d7755542a47a157ee22e15340daa275e1609e9 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 20 Oct 2025 13:42:14 -0400 Subject: [PATCH 065/200] fix memory leak with PacketBuffer::decode(), dont use a direct heap allocated buffer, instead use a unique_ptr buffer; --- src/common/network/PacketBuffer.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/common/network/PacketBuffer.cpp b/src/common/network/PacketBuffer.cpp index 686a34b6..48a4bb78 100644 --- a/src/common/network/PacketBuffer.cpp +++ b/src/common/network/PacketBuffer.cpp @@ -80,7 +80,6 @@ bool PacketBuffer::decode(const uint8_t* data, uint8_t** message, uint32_t* outL // do we have all the blocks? if (fragments.size() == blockCnt + 1U) { - uint8_t* buffer = nullptr; fragments.lock(false); if (fragments[0] == nullptr) { LogError(LOG_NET, "%s, Packet Fragment, error missing block 0? Packet dropped.", m_name); @@ -106,8 +105,7 @@ bool PacketBuffer::decode(const uint8_t* data, uint8_t** message, uint32_t* outL uint32_t compressedLen = fragments[0]->compressedSize; uint32_t len = fragments[0]->size; - buffer = new uint8_t[len + 1U]; - ::memset(buffer, 0x00U, len + 1U); + DECLARE_UINT8_ARRAY(buffer, len + 1U); if (fragments.size() == 1U) { ::memcpy(buffer, fragments[0U]->data, len); } else { @@ -136,7 +134,11 @@ bool PacketBuffer::decode(const uint8_t* data, uint8_t** message, uint32_t* outL } } else { - *message = buffer; + uint8_t* outBuf = new uint8_t[len + 1U]; + ::memset(outBuf, 0x00U, len + 1U); + ::memcpy(outBuf, buffer, len); + + *message = outBuf; if (outLength != nullptr) { *outLength = len; } From 7d4618d2cfbc9cc3d4c72ab5544b7dd25a6ab7b3 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 20 Oct 2025 15:28:50 -0400 Subject: [PATCH 066/200] simplify implementation; --- src/fne/network/FNENetwork.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 2a71159b..823bc746 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -2045,9 +2045,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sen // send radio ID white/black lists std::vector ridWhitelist; - - auto ridLookups = m_ridLookup->table(); - for (auto entry : ridLookups) { + for (auto entry : m_ridLookup->table()) { uint32_t id = entry.first; if (entry.second.radioEnabled()) { ridWhitelist.push_back(id); @@ -2117,9 +2115,7 @@ void FNENetwork::writeBlacklistRIDs(uint32_t peerId, uint32_t streamId) // send radio ID blacklist std::vector ridBlacklist; - - auto ridLookups = m_ridLookup->table(); - for (auto entry : ridLookups) { + for (auto entry : m_ridLookup->table()) { uint32_t id = entry.first; if (!entry.second.radioEnabled()) { ridBlacklist.push_back(id); @@ -2234,8 +2230,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendISSI) } std::vector> tgidList; - auto groupVoice = m_tidLookup->groupVoice(); - for (auto entry : groupVoice) { + for (auto entry : m_tidLookup->groupVoice()) { std::vector inclusion = entry.config().inclusion(); std::vector exclusion = entry.config().exclusion(); std::vector preferred = entry.config().preferred(); @@ -2315,8 +2310,7 @@ void FNENetwork::writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId) } std::vector> tgidList; - auto groupVoice = m_tidLookup->groupVoice(); - for (auto entry : groupVoice) { + for (auto entry : m_tidLookup->groupVoice()) { std::vector inclusion = entry.config().inclusion(); std::vector exclusion = entry.config().exclusion(); From 07c8eb2228230212f11f3350ec7823eae037c5f2 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 20 Oct 2025 20:05:48 -0400 Subject: [PATCH 067/200] rename backtrace namespace to log_stacktrace, when using simple mode the namespace conflicts with a global function backtrace(); add package element for libdw-dev:arm64 for cross compile instructions; --- CMakeLists.txt | 6 ++++++ README.md | 1 + src/common/Log.cpp | 2 +- src/common/Log.h | 2 +- src/fne/FNEMain.cpp | 2 +- src/host/HostMain.cpp | 2 +- src/patch/PatchMain.cpp | 2 +- 7 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 93490d18..f0f95c56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -243,11 +243,17 @@ if (ENABLE_LIBDW_SUPPORT) set(LIBDW_LIBRARY "dw") else() message("-- libdw (libdw-dev) not found, simple backtrace only") + add_definitions(-DBACKWARD_HAS_DW=0 -DBACKWARD_HAS_BACKTRACE_SYMBOL=1) set(LIBDW_INCLUDE_DIR "") set(LIBDW_LIBRARY "") endif (LIBDW_FOUND) mark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARY) +else() + message("-- libdw (libdw-dev) disabled, simple backtrace only") + add_definitions(-DBACKWARD_HAS_DW=0 -DBACKWARD_HAS_BACKTRACE_SYMBOL=1) + set(LIBDW_INCLUDE_DIR "") + set(LIBDW_LIBRARY "") endif (ENABLE_LIBDW_SUPPORT) # FinalCut diff --git a/README.md b/README.md index 9e193349..9c56a8c0 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ If cross-compiling ensure you install the appropriate libraries, for example for sudo dpkg --add-architecture arm64 sudo apt-get update sudo apt-get install libasio-dev:arm64 libncurses-dev:arm64 libssl-dev:arm64 +sudo apt-get install libdw-dev:arm64 ``` ### Build Instructions diff --git a/src/common/Log.cpp b/src/common/Log.cpp index bee01405..a80af6a3 100644 --- a/src/common/Log.cpp +++ b/src/common/Log.cpp @@ -56,7 +56,7 @@ static struct tm m_tm; static std::ostream m_outStream { std::cerr.rdbuf() }; -bool backtrace::SignalHandling::m_foreground = false; +bool log_stacktrace::SignalHandling::m_foreground = false; // --------------------------------------------------------------------------- // Global Functions diff --git a/src/common/Log.h b/src/common/Log.h index 70c9968a..f1d63bf2 100644 --- a/src/common/Log.h +++ b/src/common/Log.h @@ -223,7 +223,7 @@ namespace log_internal extern HOST_SW_API FILE* GetLogFile(); } // namespace log_internal -namespace backtrace +namespace log_stacktrace { #if !defined(CATCH2_TEST_COMPILATION) #if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) diff --git a/src/fne/FNEMain.cpp b/src/fne/FNEMain.cpp index 1b80fbfb..09444c0f 100644 --- a/src/fne/FNEMain.cpp +++ b/src/fne/FNEMain.cpp @@ -218,7 +218,7 @@ int main(int argc, char** argv) } } - backtrace::SignalHandling sh(g_foreground); + log_stacktrace::SignalHandling sh(g_foreground); ::signal(SIGINT, sigHandler); ::signal(SIGTERM, sigHandler); diff --git a/src/host/HostMain.cpp b/src/host/HostMain.cpp index 80d85241..5cc5714c 100644 --- a/src/host/HostMain.cpp +++ b/src/host/HostMain.cpp @@ -290,7 +290,7 @@ int main(int argc, char** argv) } } - backtrace::SignalHandling sh(g_foreground); + log_stacktrace::SignalHandling sh(g_foreground); ::signal(SIGINT, sigHandler); ::signal(SIGTERM, sigHandler); diff --git a/src/patch/PatchMain.cpp b/src/patch/PatchMain.cpp index d20d5d22..87c40afa 100644 --- a/src/patch/PatchMain.cpp +++ b/src/patch/PatchMain.cpp @@ -194,7 +194,7 @@ int main(int argc, char** argv) } } - backtrace::SignalHandling sh(g_foreground); + log_stacktrace::SignalHandling sh(g_foreground); ::signal(SIGINT, sigHandler); ::signal(SIGTERM, sigHandler); From 5754e19b58696fad21e8277324b6d14232f3ad50 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 20 Oct 2025 20:27:01 -0400 Subject: [PATCH 068/200] whoops forgot to update BridgeMain.cpp for log_stacktrace; --- src/bridge/BridgeMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bridge/BridgeMain.cpp b/src/bridge/BridgeMain.cpp index 819a504c..e9f97461 100644 --- a/src/bridge/BridgeMain.cpp +++ b/src/bridge/BridgeMain.cpp @@ -281,7 +281,7 @@ int main(int argc, char** argv) } } - backtrace::SignalHandling sh(g_foreground); + log_stacktrace::SignalHandling sh(g_foreground); ::signal(SIGINT, sigHandler); ::signal(SIGTERM, sigHandler); From 3ba45061f7d02beac9b6a92d10c138bd37a4a72b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 21 Oct 2025 12:40:07 -0400 Subject: [PATCH 069/200] lock the peer list when writing traffic data to prevent peer removal during traffic operations; --- src/fne/network/callhandler/TagAnalogData.cpp | 4 ++++ src/fne/network/callhandler/TagDMRData.cpp | 4 ++++ src/fne/network/callhandler/TagNXDNData.cpp | 4 ++++ src/fne/network/callhandler/TagP25Data.cpp | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 814b1f39..a97e81a9 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -244,7 +244,10 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U) { uint32_t i = 0U; + m_network->m_peers.lock(false); for (auto peer : m_network->m_peers) { + if (peer.second == nullptr) + continue; if (peerId != peer.first) { if (ssrc == peer.first) { // skip the peer if it is the source peer @@ -277,6 +280,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } } m_network->m_frameQueue->flushQueue(); + m_network->m_peers.unlock(); } /* diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 9eb711e8..c0b311d0 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -406,7 +406,10 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; + m_network->m_peers.lock(false); for (auto peer : m_network->m_peers) { + if (peer.second == nullptr) + continue; if (peerId != peer.first) { FNEPeerConnection* conn = peer.second; if (ssrc == peer.first) { @@ -465,6 +468,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } } m_network->m_frameQueue->flushQueue(); + m_network->m_peers.unlock(); } // if this is a private call, and we have already repeated to the connected peer that registered diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 7118b363..3964d0de 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -436,7 +436,10 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; + m_network->m_peers.lock(false); for (auto peer : m_network->m_peers) { + if (peer.second == nullptr) + continue; if (peerId != peer.first) { FNEPeerConnection* conn = peer.second; if (ssrc == peer.first) { @@ -495,6 +498,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI } } m_network->m_frameQueue->flushQueue(); + m_network->m_peers.unlock(); } // if this is a private call, and we have already repeated to the connected peer that registered diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 7eeae720..6baa7625 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -465,7 +465,10 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // repeat traffic to nodes connected to us as peers if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; + m_network->m_peers.lock(false); for (auto peer : m_network->m_peers) { + if (peer.second == nullptr) + continue; if (peerId != peer.first) { FNEPeerConnection* conn = peer.second; if (ssrc == peer.first) { @@ -529,6 +532,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } } m_network->m_frameQueue->flushQueue(); + m_network->m_peers.unlock(); } // if this is a private call, and we have already repeated to the connected peer that registered From a89cac4bbe191391a2bdd478dcfd7def5b63a837 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 21 Oct 2025 13:22:37 -0400 Subject: [PATCH 070/200] utilize a shared_timed_mutex for peer list locking on the FNE, this better keeps the peer list locked during traffic operations using lock counting vs the original spinlock mechanism; --- .../concurrent/concurrent_shared_lock.h | 85 ++++ src/common/concurrent/shared_unordered_map.h | 359 +++++++++++++++ src/common/concurrent/shared_vector.h | 416 ++++++++++++++++++ src/fne/network/FNENetwork.cpp | 4 +- src/fne/network/FNENetwork.h | 3 +- src/fne/network/callhandler/TagAnalogData.cpp | 4 +- src/fne/network/callhandler/TagDMRData.cpp | 4 +- src/fne/network/callhandler/TagNXDNData.cpp | 4 +- src/fne/network/callhandler/TagP25Data.cpp | 4 +- 9 files changed, 872 insertions(+), 11 deletions(-) create mode 100644 src/common/concurrent/concurrent_shared_lock.h create mode 100644 src/common/concurrent/shared_unordered_map.h create mode 100644 src/common/concurrent/shared_vector.h diff --git a/src/common/concurrent/concurrent_shared_lock.h b/src/common/concurrent/concurrent_shared_lock.h new file mode 100644 index 00000000..72b67be7 --- /dev/null +++ b/src/common/concurrent/concurrent_shared_lock.h @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file concurrent_shared_lock.h + * @ingroup concurrency + */ +#if !defined(__CONCURRENCY_CONCURRENT_SHARED_LOCK_H__) +#define __CONCURRENCY_CONCURRENT_SHARED_LOCK_H__ + +#include "common/Thread.h" + +#include +#include + +namespace concurrent +{ + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Base class for a concurrently shared locked container. + * @ingroup concurrency + */ + class concurrent_shared_lock + { + public: + /** + * @brief Initializes a new instance of the concurrent_shared_lock class. + */ + concurrent_shared_lock() : + m_mutex() + { + /* stub */ + } + + /** + * @brief Locks the object. + */ + void lock() const { __lock(); } + /** + * @brief Unlocks the object. + */ + void unlock() const { __unlock(); } + + /** + * @brief Share locks the object. + */ + void shared_lock() const { __shared_lock(); } + /** + * @brief Share unlocks the object. + */ + void shared_unlock() const { __shared_unlock(); } + + protected: + mutable std::shared_timed_mutex m_mutex; //!< Mutex used for locking. + + /** + * @brief Lock the object. + */ + inline void __lock() const { m_mutex.lock(); } + /** + * @brief Lock the object. + */ + inline void __shared_lock() const { m_mutex.lock_shared(); } + + /** + * @brief Unlock the object. + */ + inline void __unlock() const { m_mutex.unlock(); } + /** + * @brief Unlock the object. + */ + inline void __shared_unlock() const { m_mutex.unlock_shared(); } + }; +} // namespace concurrent + +#endif // __CONCURRENCY_CONCURRENT_SHARED_LOCK_H__ diff --git a/src/common/concurrent/shared_unordered_map.h b/src/common/concurrent/shared_unordered_map.h new file mode 100644 index 00000000..35737d6b --- /dev/null +++ b/src/common/concurrent/shared_unordered_map.h @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file shared_unordered_map.h + * @ingroup concurrency + */ +#if !defined(__CONCURRENCY_SHARED_UNORDERED_MAP_H__) +#define __CONCURRENCY_SHARED_UNORDERED_MAP_H__ + +#include "common/concurrent/concurrent_shared_lock.h" +#include "common/Thread.h" + +#include +#include + +namespace concurrent +{ + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Thread-safe share-locked std::unordered_map. Read operations + * must use shared_lock()/shared_unlock() to ensure thread-safety. (This includes iterators.) + * @ingroup concurrency + */ + template + class shared_unordered_map : public concurrent_shared_lock + { + using __std = std::unordered_map; + public: + using iterator = typename __std::iterator; + using const_iterator = typename __std::const_iterator; + + /** + * @brief Initializes a new instance of the shared_unordered_map class. + */ + shared_unordered_map() : concurrent_shared_lock(), + m_map() + { + /* stub */ + } + /** + * @brief Initializes a new instance of the shared_unordered_map class. + * @param size Initial size of the shared_unordered_map. + */ + shared_unordered_map(size_t size) : concurrent_shared_lock(), + m_map(size) + { + /* stub */ + } + /** + * @brief Finalizes a instance of the shared_unordered_map class. + */ + virtual ~shared_unordered_map() + { + m_map.clear(); + } + + /** + * @brief Unordered map assignment operator. + * @param other A map of identical element and allocator types. + */ + shared_unordered_map& operator=(const shared_unordered_map& other) + { + __lock(); + m_map = other.m_map; + __unlock(); + return *this; + } + /** + * @brief Unordered map assignment operator. + * @param other A map of identical element and allocator types. + */ + shared_unordered_map& operator=(const std::unordered_map& other) + { + __lock(); + m_map = other; + __unlock(); + return *this; + } + /** + * @brief Unordered map assignment operator. + * @param other A map of identical element and allocator types. + */ + shared_unordered_map& operator=(shared_unordered_map& other) + { + __lock(); + m_map = other.m_map; + __unlock(); + return *this; + } + /** + * @brief Unordered map assignment operator. + * @param other A map of identical element and allocator types. + */ + shared_unordered_map& operator=(std::unordered_map& other) + { + __lock(); + m_map = other; + __unlock(); + return *this; + } + + /** + * @brief Assigns a given value to a unordered_map. + * @param size Number of elements to be assigned. + * @param value Value to be assigned. + */ + void assign(size_t size, const T& value) + { + __lock(); + m_map.assign(size, value); + __unlock(); + } + + /** + * @brief Returns a read/write iterator that points to the first + * element in the unordered_map. Iteration is done in ordinary + * element order. + * @returns iterator + */ + iterator begin() + { + return m_map.begin(); + } + /** + * @brief Returns a read-only (constant) iterator that points to the + * first element in the unordered_map. Iteration is done in ordinary + * element order. + * @returns const_iterator + */ + const_iterator begin() const + { + return m_map.begin(); + } + /** + * @brief Returns a read/write iterator that points one past the last + * element in the unordered_map. Iteration is done in ordinary + * element order. + * @returns iterator + */ + iterator end() + { + return m_map.end(); + } + /** + * @brief Returns a read-only (constant) iterator that points one past + * the last element in the unordered_map. Iteration is done in ordinary + * element order. + * @returns const_iterator + */ + const_iterator end() const + { + return m_map.end(); + } + + /** + * @brief Returns a read-only (constant) iterator that points to the + * first element in the unordered_map. Iteration is done in ordinary + * element order. + * @returns const_iterator + */ + const_iterator cbegin() const + { + return m_map.cbegin(); + } + /** + * @brief Returns a read-only (constant) iterator that points one past + * the last element in the unordered_map. Iteration is done in ordinary + * element order. + * @returns const_iterator + */ + const_iterator cend() const + { + return m_map.cend(); + } + + /** + * @brief Gets the element at the specified key. + * @param key Key of the element to get. + * @returns T& Element at the specified key. + */ + T& operator[](const Key& key) + { + return m_map[key]; + } + /** + * @brief Gets the element at the specified key. + * @param key Key of the element to get. + * @returns const T& Element at the specified key. + */ + const T& operator[](const Key& key) const + { + return m_map[key]; + } + + /** + * @brief Gets the element at the specified key. + * @param key Key of the element to get. + * @returns T& Element at the specified key. + */ + T& at(const Key& key) + { + return m_map.at(key); + } + /** + * @brief Gets the element at the specified key. + * @param key Key of the element to get. + * @returns const T& Element at the specified key. + */ + const T& at(const Key& key) const + { + return m_map.at(key); + } + + /** + * @brief Gets the total number of elements in the unordered_map. + * @returns size_t Total number of elements in the unordered_map. + */ + size_t size() const + { + return m_map.size(); + } + + /** + * @brief Checks if the unordered_map is empty. + * @returns bool True if the unordered_map is empty, false otherwise. + */ + bool empty() const + { + return m_map.empty(); + } + + /** + * @brief Checks if the unordered_map contains the specified key. + * @param key Key to check. + * @returns bool True if the unordered_map contains the specified key, false otherwise. + */ + bool contains(const Key& key) const + { + return m_map.contains(key); + } + + /** + * @brief Inserts a new element into the unordered_map. + * @param key Key of the element to insert. + * @param value Value of the element to insert. + */ + void insert(const Key& key, const T& value) + { + __lock(); + m_map.insert({key, value}); + __unlock(); + } + + /** + * @brief Removes the element at the specified key. + * @param key Key of the element to remove. + */ + void erase(const Key& key) + { + __lock(); + m_map.erase(key); + __unlock(); + } + /** + * @brief Removes the element at the specified iterator. + * @param position Iterator of the element to remove. + */ + void erase(const_iterator position) + { + __lock(); + m_map.erase(position); + __unlock(); + } + /** + * @brief Removes the elements in the specified range. + * @param first Iterator of the first element to remove. + * @param last Iterator of the last element to remove. + */ + void erase(const_iterator first, const_iterator last) + { + __lock(); + m_map.erase(first, last); + __unlock(); + } + + /** + * @brief Clears the unordered_map. + */ + void clear() + { + __lock(); + m_map.clear(); + __unlock(); + } + + /** + * @brief Tries to locate an element in an unordered_map. + * @param key Key to be located. + * @return iterator Iterator pointing to sought-after element, or end() if not + * found. + */ + iterator find(const Key& key) + { + return m_map.find(key); + } + /** + * @brief Tries to locate an element in an unordered_map. + * @param key Key to be located. + * @return const_iterator Iterator pointing to sought-after element, or end() if not + * found. + */ + const_iterator find(const Key& key) const + { + return m_map.find(key); + } + + /** + * @brief Finds the number of elements. + * @param key Key to count. + * @return size_t Number of elements with specified key. + */ + size_t count(const Key& key) const + { + return m_map.count(key); + } + + /** + * @brief Gets the underlying unordered_map. + * @returns std::unordered_map& Underlying unordered_map. + */ + std::unordered_map& get() + { + return m_map; + } + /** + * @brief Gets the underlying unordered_map. + * @returns const std::unordered_map& Underlying unordered_map. + */ + const std::unordered_map& get() const + { + return m_map; + } + + private: + std::unordered_map m_map; + }; +} // namespace concurrent + +#endif // __CONCURRENCY_SHARED_UNORDERED_MAP_H__ diff --git a/src/common/concurrent/shared_vector.h b/src/common/concurrent/shared_vector.h new file mode 100644 index 00000000..5a4c6eea --- /dev/null +++ b/src/common/concurrent/shared_vector.h @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file shared_vector.h + * @ingroup concurrency + */ +#if !defined(__CONCURRENCY_SHARED_VECTOR_H__) +#define __CONCURRENCY_SHARED_VECTOR_H__ + +#include "common/concurrent/concurrent_shared_lock.h" +#include "common/Thread.h" + +#include +#include + +namespace concurrent +{ + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Thread-safe share-locked std::vector. Read operations + * must use shared_lock()/shared_unlock() to ensure thread-safety. (This includes iterators.) + * @ingroup concurrency + */ + template + class shared_vector : public concurrent_shared_lock + { + using __std = std::vector; + public: + using iterator = typename __std::iterator; + using const_iterator = typename __std::const_iterator; + + /** + * @brief Initializes a new instance of the shared_vector class. + */ + shared_vector() : concurrent_shared_lock(), + m_vector() + { + /* stub */ + } + /** + * @brief Initializes a new instance of the shared_vector class. + * @param size Initial size of the vector. + */ + shared_vector(size_t size) : concurrent_shared_lock(), + m_vector(size) + { + /* stub */ + } + /** + * @brief Finalizes a instance of the shared_vector class. + */ + virtual ~shared_vector() + { + m_vector.clear(); + } + + /** + * @brief Vector assignment operator. + * @param other A vector of identical element and allocator types. + */ + shared_vector& operator=(const shared_vector& other) + { + __lock(); + m_vector = other.m_vector; + __unlock(); + return *this; + } + /** + * @brief Vector assignment operator. + * @param other A vector of identical element and allocator types. + */ + shared_vector& operator=(const std::vector& other) + { + __lock(); + m_vector = other; + __unlock(); + return *this; + } + /** + * @brief Vector assignment operator. + * @param other A vector of identical element and allocator types. + */ + shared_vector& operator=(shared_vector& other) + { + __lock(); + m_vector = other.m_vector; + __unlock(); + return *this; + } + /** + * @brief Vector assignment operator. + * @param other A vector of identical element and allocator types. + */ + shared_vector& operator=(std::vector& other) + { + __lock(); + m_vector = other; + __unlock(); + return *this; + } + + /** + * @brief Assigns a given value to a %vector. + * @param size Number of elements to be assigned. + * @param value Value to be assigned. + */ + void assign(size_t size, const T& value) + { + __lock(); + m_vector.assign(size, value); + __unlock(); + } + + /** + * @brief Returns a read/write iterator that points to the first + * element in the vector. Iteration is done in ordinary + * element order. + * @returns iterator + */ + iterator begin() + { + return m_vector.begin(); + } + /** + * @brief Returns a read-only (constant) iterator that points to the + * first element in the vector. Iteration is done in ordinary + * element order. + * @returns const_iterator + */ + const_iterator begin() const + { + return m_vector.begin(); + } + /** + * @brief Returns a read/write iterator that points one past the last + * element in the vector. Iteration is done in ordinary + * element order. + * @returns iterator + */ + iterator end() + { + return m_vector.end(); + } + /** + * @brief Returns a read-only (constant) iterator that points one past + * the last element in the vector. Iteration is done in ordinary + * element order. + * @returns const_iterator + */ + const_iterator end() const + { + return m_vector.end(); + } + + /** + * @brief Returns a read-only (constant) iterator that points to the + * first element in the vector. Iteration is done in ordinary + * element order. + * @returns const_iterator + */ + const_iterator cbegin() const + { + return m_vector.cbegin(); + } + /** + * @brief Returns a read-only (constant) iterator that points one past + * the last element in the vector. Iteration is done in ordinary + * element order. + * @returns const_iterator + */ + const_iterator cend() const + { + return m_vector.cend(); + } + + /** + * @brief Gets the number of elements in the vector. + * @returns size_t Number of elements in the vector. + */ + size_t size() const + { + return m_vector.size(); + } + /** + * @brief Resizes the %vector to the specified number of elements. + * @param size Number of elements the %vector should contain. + */ + void resize(size_t size) + { + __lock(); + m_vector.resize(size); + __unlock(); + } + + /** + * @brief Returns the total number of elements that the %vector can + * hold before needing to allocate more memory. + * @returns size_t + */ + size_t capacity() const + { + return m_vector.capacity(); + } + + /** + * @brief Checks if the vector is empty. + * @returns bool True if the vector is empty, false otherwise. + */ + bool empty() const + { + return m_vector.empty(); + } + + /** + * @brief Gets the element at the specified index. + * @param index Index of the element to get. + * @returns T& Element at the specified index. + */ + T& operator[](size_t index) + { + return m_vector[index]; + } + /** + * @brief Gets the element at the specified index. + * @param index Index of the element to get. + * @returns const T& Element at the specified index. + */ + const T& operator[](size_t index) const + { + return m_vector[index]; + } + + /** + * @brief Gets the element at the specified index. + * @param index Index of the element to get. + * @returns T& Element at the specified index. + */ + T& at(size_t index) + { + return m_vector.at(index); + } + /** + * @brief Gets the element at the specified index. + * @param index Index of the element to get. + * @returns const T& Element at the specified index. + */ + const T& at(size_t index) const + { + return m_vector.at(index); + } + + /** + * @brief Gets the first element of the vector. + * @returns T& First element of the vector. + */ + T& front() + { + return m_vector.front(); + } + /** + * @brief Gets the first element of the vector. + * @returns const T& First element of the vector. + */ + const T& front() const + { + return m_vector.front(); + } + + /** + * @brief Gets the last element of the vector. + * @returns T& Last element of the vector. + */ + T& back() + { + return m_vector.back(); + } + /** + * @brief Gets the last element of the vector. + * @returns const T& Last element of the vector. + */ + const T& back() const + { + return m_vector.back(); + } + + /** + * @brief Adds an element to the end of the vector. + * @param value Value to add. + */ + void push_back(const T& value) + { + __lock(); + m_vector.push_back(value); + __unlock(); + } + /** + * @brief Adds an element to the end of the vector. + * @param value Value to add. + */ + void push_back(T&& value) + { + __lock(); + m_vector.push_back(std::move(value)); + __unlock(); + } + + /** + * @brief Removes last element. + */ + void pop_back() + { + __lock(); + m_vector.pop_back(); + __unlock(); + } + + /** + * @brief Inserts given value into vector before specified iterator. + * @param position A const_iterator into the vector. + * @param value Data to be inserted. + * @return iterator An iterator that points to the inserted data. + */ + iterator insert(iterator position, const T& value) + { + __lock(); + auto it = m_vector.insert(position, value); + __unlock(); + return it; + } + + /** + * @brief Removes the element at the specified index. + * @param index Index of the element to remove. + */ + void erase(size_t index) + { + __lock(); + m_vector.erase(m_vector.begin() + index); + __unlock(); + } + /** + * @brief Removes the element at the specified iterator. + * @param position Iterator of the element to remove. + */ + void erase(const_iterator position) + { + __lock(); + m_vector.erase(position); + __unlock(); + } + /** + * @brief Removes the elements in the specified range. + * @param first Iterator of the first element to remove. + * @param last Iterator of the last element to remove. + */ + void erase(const_iterator first, const_iterator last) + { + __lock(); + m_vector.erase(first, last); + __unlock(); + } + + /** + * @brief Swaps data with another vector. + * @param other A vector of the same element and allocator types. + */ + void swap(shared_vector& other) + { + __lock(); + m_vector.swap(other.m_vector); + __unlock(); + } + + /** + * @brief Clears the vector. + */ + void clear() + { + __lock(); + m_vector.clear(); + __unlock(); + } + + /** + * @brief Gets the underlying vector. + * @returns std::vector& Underlying vector. + */ + std::vector& get() + { + return m_vector; + } + /** + * @brief Gets the underlying vector. + * @returns const std::vector& Underlying vector. + */ + const std::vector& get() const + { + return m_vector; + } + + private: + std::vector m_vector; + }; +} // namespace concurrent + +#endif // __CONCURRENCY_SHARED_VECTOR_H__ diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 823bc746..e2cb218b 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -474,7 +474,7 @@ void FNENetwork::clock(uint32_t ms) m_updateLookupTimer.clock(ms); if (m_updateLookupTimer.isRunning() && m_updateLookupTimer.hasExpired()) { // send network metadata updates to peers - m_peers.lock(false); + m_peers.shared_lock(); for (auto peer : m_peers) { uint32_t id = peer.first; FNEPeerConnection* connection = peer.second; @@ -503,7 +503,7 @@ void FNENetwork::clock(uint32_t ms) } } } - m_peers.unlock(); + m_peers.shared_unlock(); m_updateLookupTimer.start(); } diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 2b4763b6..954e69ce 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -26,6 +26,7 @@ #include "fne/Defines.h" #include "common/concurrent/unordered_map.h" +#include "common/concurrent/shared_unordered_map.h" #include "common/network/BaseNetwork.h" #include "common/network/json/json.h" #include "common/lookups/RadioIdLookup.h" @@ -472,7 +473,7 @@ namespace network NET_CONN_STATUS m_status; typedef std::pair PeerMapPair; - concurrent::unordered_map m_peers; + concurrent::shared_unordered_map m_peers; concurrent::unordered_map m_peerReplicaPeers; typedef std::pair PeerAffiliationMapPair; concurrent::unordered_map m_peerAffiliations; diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index a97e81a9..32569ce0 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -244,7 +244,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U) { uint32_t i = 0U; - m_network->m_peers.lock(false); + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { if (peer.second == nullptr) continue; @@ -280,7 +280,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } } m_network->m_frameQueue->flushQueue(); - m_network->m_peers.unlock(); + m_network->m_peers.shared_unlock(); } /* diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index c0b311d0..79bc699b 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -406,7 +406,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; - m_network->m_peers.lock(false); + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { if (peer.second == nullptr) continue; @@ -468,7 +468,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } } m_network->m_frameQueue->flushQueue(); - m_network->m_peers.unlock(); + m_network->m_peers.shared_unlock(); } // if this is a private call, and we have already repeated to the connected peer that registered diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 3964d0de..f069ab10 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -436,7 +436,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; - m_network->m_peers.lock(false); + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { if (peer.second == nullptr) continue; @@ -498,7 +498,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI } } m_network->m_frameQueue->flushQueue(); - m_network->m_peers.unlock(); + m_network->m_peers.shared_unlock(); } // if this is a private call, and we have already repeated to the connected peer that registered diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 6baa7625..ed67addf 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -465,7 +465,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // repeat traffic to nodes connected to us as peers if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; - m_network->m_peers.lock(false); + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { if (peer.second == nullptr) continue; @@ -532,7 +532,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } } m_network->m_frameQueue->flushQueue(); - m_network->m_peers.unlock(); + m_network->m_peers.shared_unlock(); } // if this is a private call, and we have already repeated to the connected peer that registered From dcdfe23f2cf4d63da61640026ba0f9e50136cc6f Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 21 Oct 2025 13:40:48 -0400 Subject: [PATCH 071/200] better handling locking peer connections during critical state changes; --- src/fne/network/FNENetwork.cpp | 83 +++++++++++++++++++++++++++------- src/fne/network/FNENetwork.h | 18 +++++++- 2 files changed, 82 insertions(+), 19 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index e2cb218b..79f6f9bc 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -430,7 +430,9 @@ void FNENetwork::clock(uint32_t ms) // remove any peers for (uint32_t peerId : peersToRemove) { FNEPeerConnection* connection = m_peers[peerId]; + connection->lock(); erasePeer(peerId); + connection->unlock(); if (connection != nullptr) { delete connection; } @@ -911,7 +913,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_PEER_ACL, req->address, req->addrLen); + connection->lock(); + connection->connected(false); network->erasePeer(peerId); + connection->unlock(); + delete connection; } } @@ -944,7 +950,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_PEER_ACL, req->address, req->addrLen); + connection->lock(); + connection->connected(false); network->erasePeer(peerId); + connection->unlock(); + delete connection; } } @@ -954,7 +964,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogWarning(LOG_NET, "PEER %u (%s) RPTL NAK, bad connection state, connectionState = %u", peerId, connection->identWithQualifier().c_str(), connection->connectionState()); + connection->lock(); + connection->connected(false); network->erasePeer(peerId); + connection->unlock(); + delete connection; } } else { @@ -1044,18 +1058,32 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) else { LogWarning(LOG_NET, "PEER %u RPTK NAK, failed the login exchange", peerId); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_FNE_UNAUTHORIZED, req->address, req->addrLen); + connection->lock(); + connection->connected(false); network->erasePeer(peerId); + connection->unlock(); + + delete connection; } } else { network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_PEER_ACL, req->address, req->addrLen); + connection->lock(); + connection->connected(false); network->erasePeer(peerId); + connection->unlock(); + + delete connection; } } else { LogWarning(LOG_NET, "PEER %u RPTK NAK, login exchange while in an incorrect state, connectionState = %u", peerId, connection->connectionState()); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); + connection->lock(); + connection->connected(false); network->erasePeer(peerId); + connection->unlock(); + delete connection; } } @@ -1086,7 +1114,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (!err.empty()) { LogWarning(LOG_NET, "PEER %u RPTC NAK, supplied invalid configuration data", peerId); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_INVALID_CONFIG_DATA, req->address, req->addrLen); + connection->lock(); + connection->connected(false); network->erasePeer(peerId); + connection->unlock(); + delete connection; } else { @@ -1094,7 +1126,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (!v.is()) { LogWarning(LOG_NET, "PEER %u RPTC NAK, supplied invalid configuration data", peerId); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_INVALID_CONFIG_DATA, req->address, req->addrLen); + connection->lock(); + connection->connected(false); network->erasePeer(peerId); + connection->unlock(); + delete connection; } else { @@ -1184,7 +1220,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogWarning(LOG_NET, "PEER %u (%s) RPTC NAK, login exchange while in an incorrect state, connectionState = %u", peerId, connection->identWithQualifier().c_str(), connection->connectionState()); network->writePeerNAK(peerId, TAG_REPEATER_CONFIG, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); + connection->lock(); + connection->connected(false); network->erasePeer(peerId); + connection->unlock(); + delete connection; } } @@ -1206,7 +1246,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // validate peer (simple validation really) if (connection->connected() && connection->address() == ip) { LogInfoEx(LOG_NET, "PEER %u (%s) disconnected", peerId, connection->identWithQualifier().c_str()); + connection->lock(); + connection->connected(false); network->erasePeer(peerId); + connection->unlock(); + delete connection; } } @@ -1963,30 +2007,35 @@ void FNENetwork::taskMetadataUpdate(MetadataUpdateRequest* req) std::string peerIdentity = network->resolvePeerIdentity(req->peerId); FNEPeerConnection* connection = network->m_peers[req->peerId]; + connection->lock(); if (connection != nullptr) { - uint32_t streamId = network->createStreamId(); + if (connection->connected()) { + uint32_t streamId = network->createStreamId(); - // if the connection is an external peer, and peer is participating in peer link, - // send the peer proper configuration data - if (connection->isExternalFNEPeer() && connection->isPeerReplica()) { - LogInfoEx(LOG_NET, "PEER %u (%s) sending replica network metadata updates", req->peerId, peerIdentity.c_str()); + // if the connection is an external peer, and peer is participating in peer link, + // send the peer proper configuration data + if (connection->isExternalFNEPeer() && connection->isPeerReplica()) { + LogInfoEx(LOG_NET, "PEER %u (%s) sending replica network metadata updates", req->peerId, peerIdentity.c_str()); - network->writeWhitelistRIDs(req->peerId, streamId, true); - network->writeTGIDs(req->peerId, streamId, true); - network->writePeerList(req->peerId, streamId); + network->writeWhitelistRIDs(req->peerId, streamId, true); + network->writeTGIDs(req->peerId, streamId, true); + network->writePeerList(req->peerId, streamId); - network->writeHAParameters(req->peerId, streamId, true); - } - else { - LogInfoEx(LOG_NET, "PEER %u (%s) sending network metadata updates", req->peerId, peerIdentity.c_str()); + network->writeHAParameters(req->peerId, streamId, true); + } + else { + LogInfoEx(LOG_NET, "PEER %u (%s) sending network metadata updates", req->peerId, peerIdentity.c_str()); - network->writeWhitelistRIDs(req->peerId, streamId, false); - network->writeBlacklistRIDs(req->peerId, streamId); - network->writeTGIDs(req->peerId, streamId, false); - network->writeDeactiveTGIDs(req->peerId, streamId); + network->writeWhitelistRIDs(req->peerId, streamId, false); + network->writeBlacklistRIDs(req->peerId, streamId); + network->writeTGIDs(req->peerId, streamId, false); + network->writeDeactiveTGIDs(req->peerId, streamId); - network->writeHAParameters(req->peerId, streamId, false); + network->writeHAParameters(req->peerId, streamId, false); + } } + + connection->unlock(); } delete req; diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 954e69ce..61555f6c 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -129,7 +129,8 @@ namespace network m_isConventionalPeer(false), m_isSysView(false), m_isPeerReplica(false), - m_config() + m_config(), + m_peerLockMtx() { /* stub */ } @@ -157,7 +158,8 @@ namespace network m_isConventionalPeer(false), m_isSysView(false), m_isPeerReplica(false), - m_config() + m_config(), + m_peerLockMtx() { assert(id > 0U); assert(sockStorageLen > 0U); @@ -181,6 +183,15 @@ namespace network return " " + m_identity; } + /** + * @brief Lock the peer. + */ + inline void lock() const { m_peerLockMtx.lock(); } + /** + * @brief Unlock the peer. + */ + inline void unlock() const { m_peerLockMtx.unlock(); } + public: /** * @brief Peer ID. @@ -263,6 +274,9 @@ namespace network * @brief JSON objecting containing peer configuration information. */ DECLARE_PROPERTY_PLAIN(json::object, config); + + private: + mutable std::mutex m_peerLockMtx; }; // --------------------------------------------------------------------------- From 64c651d220ed8341af50c94582bb82b8c68f1ad8 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 21 Oct 2025 13:54:29 -0400 Subject: [PATCH 072/200] whoops this lock should only take place for a connected peer; --- src/fne/network/FNENetwork.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 79f6f9bc..18a22f8e 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -2007,9 +2007,9 @@ void FNENetwork::taskMetadataUpdate(MetadataUpdateRequest* req) std::string peerIdentity = network->resolvePeerIdentity(req->peerId); FNEPeerConnection* connection = network->m_peers[req->peerId]; - connection->lock(); if (connection != nullptr) { if (connection->connected()) { + connection->lock(); uint32_t streamId = network->createStreamId(); // if the connection is an external peer, and peer is participating in peer link, @@ -2033,9 +2033,9 @@ void FNENetwork::taskMetadataUpdate(MetadataUpdateRequest* req) network->writeHAParameters(req->peerId, streamId, false); } - } - connection->unlock(); + connection->unlock(); + } } delete req; From f053b56de9b0b6aea0fcc49ea8f2f8f75fb73a1e Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 21 Oct 2025 19:55:10 -0400 Subject: [PATCH 073/200] reflect higher requirements for FNE; fix issue on host where sometimes a stuck network call would cause network traffic to stop incorrectly hanging on the previous TG; --- README.md | 4 ++-- src/host/p25/Control.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9c56a8c0..0b244d52 100644 --- a/README.md +++ b/README.md @@ -348,8 +348,8 @@ to run on hardware below the minimal requirements, its is unlikely to provide a `dvmfne`'s requirements can change radically depending on network size. Larger, busier networks will require far more resources then smaller, less busy networks. (`dvmfne` has been tested with daily unique call counts of up to 100,000+ calls on a x86_64 Server with 8GB RAM and 8-core processor, and in this environment it runs comfortably.) -- Minimal Requirements (known "working"): x86_64 Server, 2MB RAM, Dual Core Processor. -- Requirements: x86_64 Server, 2GB RAM or better, Dual/Quad or better Core Processor. +- Minimal Requirements (known "working"): x86_64 Server, 2GB RAM, Quad Core Processor. +- Requirements: x86_64 Server, 4GB RAM or better, Quad or better Core Processor. ## Project Notes diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index 0f391719..86960eb5 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -979,6 +979,7 @@ void Control::clock() if (m_affiliations->isGranted(m_netLastDstId)) { if (!m_dedicatedControl) { m_affiliations->releaseGrant(m_netLastDstId, false); + m_network->resetP25(); } } From f43885c257c7ab2e92cd415040d53a37a1f7eb98 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 21 Oct 2025 21:46:29 -0400 Subject: [PATCH 074/200] change around some naming; --- configs/fne-config.example.yml | 2 +- src/fne/HostFNE.cpp | 2 +- src/fne/network/FNENetwork.cpp | 15 ++++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 2de43a11..348f9d76 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -210,7 +210,7 @@ peers: password: RPT1234 # Textual identity of this peer. identity: EXPEER - # Network Peer ID + # Network Peer ID of this peer on the upstream FNE master. peerId: 9000990 # Flag indicating whether or not peer endpoint networking is encrypted. diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index b186c7d4..2339e722 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -587,7 +587,7 @@ bool HostFNE::createMasterNetwork() } } else { - LogWarning(LOG_HOST, "Invalid master network preshared encryption key length, key should be 32 hex pairs, or 64 characters. Encryption disabled."); + LogWarning(LOG_HOST, "Invalid master network preshared encryption key length, key should be 32 hex pairs, or 64 characters. Encryption disabled."); encrypted = false; } } diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 18a22f8e..b5063c58 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -438,10 +438,11 @@ void FNENetwork::clock(uint32_t ms) } } - // send peer updates to replica peers + // send peer updates to external FNE peers if (m_host->m_peerNetworks.size() > 0) { for (auto peer : m_host->m_peerNetworks) { if (peer.second != nullptr) { + // perform peer replica maintainence tasks if (peer.second->isEnabled() && peer.second->isPeerReplica()) { if (!peer.second->getAttachedKeyRSPHandler()) { peer.second->setAttachedKeyRSPHandler(true); // this is the only place this should happen @@ -1157,7 +1158,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (peerConfig["identity"].is()) { std::string identity = peerConfig["identity"].get(); connection->identity(identity); - LogInfoEx(LOG_NET, "PEER %u reports identity [%8s]", peerId, identity.c_str()); + LogInfoEx(LOG_NET, "PEER %u >> Identity [%8s]", peerId, identity.c_str()); } // is the peer reporting it is an external FNE neighbor peer? @@ -1165,7 +1166,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) bool external = peerConfig["externalPeer"].get(); connection->isExternalFNEPeer(external); if (external) - LogInfoEx(LOG_NET, "PEER %u reports external FNE neighbor peer", peerId); + LogInfoEx(LOG_NET, "PEER %u >> External FNE Neighbor Peer", peerId); // check if the peer is participating in peer link lookups::PeerId peerEntry = network->m_peerListLookup->find(req->peerId); @@ -1174,7 +1175,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_useAlternatePortForDiagnostics) { connection->isPeerReplica(true); if (external) - LogInfoEx(LOG_NET, "PEER %u configured for peer replication", peerId); + LogInfoEx(LOG_NET, "PEER %u >> Participates in Peer Replication", peerId); } else { LogError(LOG_NET, "PEER %u, Peer replication operations *require* the alternate diagnostics port option to be enabled.", peerId); LogError(LOG_NET, "PEER %u, will not receive peer replication ACL updates.", peerId); @@ -1189,7 +1190,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) bool convPeer = peerConfig["conventionalPeer"].get(); connection->isConventionalPeer(convPeer); if (convPeer) - LogInfoEx(LOG_NET, "PEER %u reports conventional peer", peerId); + LogInfoEx(LOG_NET, "PEER %u >> Conventional Peer", peerId); } } @@ -1198,12 +1199,12 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) bool sysView = peerConfig["sysView"].get(); connection->isSysView(sysView); if (sysView) - LogInfoEx(LOG_NET, "PEER %u reports SysView peer", peerId); + LogInfoEx(LOG_NET, "PEER %u >> SysView Peer", peerId); } if (peerConfig["software"].is()) { std::string software = peerConfig["software"].get(); - LogInfoEx(LOG_NET, "PEER %u reports software %s", peerId, software.c_str()); + LogInfoEx(LOG_NET, "PEER %u >> Software Verison [%s]", peerId, software.c_str()); } // setup the affiliations list for this peer From 401acb6d0c91738a5d62e24aaf0d6af1eef73772 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 21 Oct 2025 23:27:02 -0400 Subject: [PATCH 075/200] typo; --- 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 b5063c58..09e3f778 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1204,7 +1204,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (peerConfig["software"].is()) { std::string software = peerConfig["software"].get(); - LogInfoEx(LOG_NET, "PEER %u >> Software Verison [%s]", peerId, software.c_str()); + LogInfoEx(LOG_NET, "PEER %u >> Software Version [%s]", peerId, software.c_str()); } // setup the affiliations list for this peer From 562f6b1ac0a7e145d44593e34dd370d749cd5ddb Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 22 Oct 2025 14:24:43 -0400 Subject: [PATCH 076/200] [EXPERIMENTAL] implement rudimentary spanning tree mechanism to prevent peer looping; refactor how FNENetwork handled peer disconnect cleanups and consolidate into a singular routine; --- configs/fne-config.example.yml | 11 +- src/common/network/BaseNetwork.h | 1 + src/common/network/Network.cpp | 9 + src/common/network/RTPFNEHeader.h | 6 +- src/fne/HostFNE.cpp | 15 ++ src/fne/HostFNE.h | 7 + src/fne/network/DiagNetwork.cpp | 150 ++++++++++++++++- src/fne/network/DiagNetwork.h | 20 +++ src/fne/network/FNENetwork.cpp | 250 ++++++++++++++++++---------- src/fne/network/FNENetwork.h | 230 +++---------------------- src/fne/network/FNEPeerConnection.h | 217 ++++++++++++++++++++++++ src/fne/network/HAParameters.h | 6 +- src/fne/network/MasterTree.cpp | 230 +++++++++++++++++++++++++ src/fne/network/MasterTree.h | 166 ++++++++++++++++++ src/fne/network/PeerNetwork.cpp | 64 +++++++ src/fne/network/PeerNetwork.h | 31 ++++ 16 files changed, 1111 insertions(+), 302 deletions(-) create mode 100644 src/fne/network/FNEPeerConnection.h create mode 100644 src/fne/network/MasterTree.cpp create mode 100644 src/fne/network/MasterTree.h diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 348f9d76..de636e32 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -38,8 +38,8 @@ master: # Hostname/IP address to listen on (blank for all). address: 0.0.0.0 # Port number to listen on. - # NOTE: This port number includes itself for traffic, and master port + 1 for diagnostics and activity logging. (For - # example, a master port of 62031 will use 62032 for diagnostic and activity messages.) + # NOTE: This port number includes itself for traffic, and master port + 1 for diagnostics and activity logging. (For + # example, a master port of 62031 will use 62032 for diagnostic and activity messages.) port: 62031 # FNE access password. password: RPT1234 @@ -71,6 +71,13 @@ master: # Maximum permitted connections (hard maximum is 250 peers). connectionLimit: 100 + # Flag indicating whether or not the peer spanning tree is enabled. + # NOTE: This should not be disabled. Disabling this can cause network loops + # and other issues in a multi-peer FNE network. + enableSpanningTree: true + # Flag indicating whether or not spanning tree changes will be logged. + logSpanningTreeChanges: false + # Flag indicating whether or not peer pinging will be reported. reportPeerPing: true diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index 5cd709a1..4b4efe17 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -133,6 +133,7 @@ namespace network NET_CONN_NAK_PEER_ACL, //!< Peer ACL NET_CONN_NAK_FNE_MAX_CONN, //!< FNE Maximum Connections + NET_CONN_NAK_FNE_DUPLICATE_CONN, //!< FNE Duplicate Connection NET_CONN_NAK_INVALID = 0xFFFF //!< Invalid }; diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index 6724d41c..3f2b485a 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -996,6 +996,14 @@ void Network::clock(uint32_t ms) } break; + case NET_CONN_NAK_FNE_DUPLICATE_CONN: + LogWarning(LOG_NET, "PEER %u master NAK; duplicate connection from FNE, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); + m_status = NET_STAT_WAITING_CONNECT; + m_enabled = false; // duplicate connection give up stop trying to connect + m_retryTimer.stop(); + close(); + break; + case NET_CONN_NAK_GENERAL_FAILURE: default: LogWarning(LOG_NET, "PEER %u master NAK; general failure, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); @@ -1215,6 +1223,7 @@ void Network::close() m_timeoutTimer.stop(); m_status = NET_STAT_WAITING_CONNECT; + m_remotePeerId = 0U; } /* Sets flag enabling network communication. */ diff --git a/src/common/network/RTPFNEHeader.h b/src/common/network/RTPFNEHeader.h index a2122cac..c42abe97 100644 --- a/src/common/network/RTPFNEHeader.h +++ b/src/common/network/RTPFNEHeader.h @@ -72,7 +72,8 @@ namespace network ANNOUNCE = 0x91U, //!< Network Announce Function - REPL = 0x92U //!< FNE Replication Function + REPL = 0x92U, //!< FNE Replication Function + NET_TREE = 0x93U //!< FNE Network Tree Function }; }; @@ -112,6 +113,9 @@ namespace network REPL_ACT_PEER_LIST = 0xA2U, //!< FNE Replication Active Peer List Transfer REPL_HA_PARAMS = 0xA3U, //!< FNE Replication HA Parameters + + NET_TREE_LIST = 0x00U, //!< FNE Network Tree List + NET_TREE_DISC = 0x01U //!< FNE Network Tree Disconnect }; }; diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 2339e722..dd4c7b3a 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -793,6 +793,9 @@ void* HostFNE::threadDiagNetwork(void* arg) bool HostFNE::createPeerNetworks() { + yaml::Node masterConf = m_conf["master"]; + uint32_t masterPeerId = masterConf["peerId"].as(1001U); + yaml::Node& peerList = m_conf["peers"]; if (peerList.size() > 0U) { if (peerList.size() > MAX_RECOMMENDED_PEER_NETWORKS) { @@ -860,6 +863,7 @@ bool HostFNE::createPeerNetworks() m_allowActivityTransfer, m_allowDiagnosticTransfer, false, false); network->setMetadata(identity, rxFrequency, txFrequency, 0.0F, 0.0F, 0, 0, 0, latitude, longitude, 0, location); network->setLookups(m_ridLookup, m_tidLookup); + network->setMasterPeerId(masterPeerId); network->setPeerLookups(m_peerListLookup); network->setPeerReplicationSaveACL(m_peerReplicaSavesACL); if (encrypted) { @@ -871,6 +875,8 @@ bool HostFNE::createPeerNetworks() network->setNXDNCallback(std::bind(&HostFNE::processPeerNXDN, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); network->setAnalogCallback(std::bind(&HostFNE::processPeerAnalog, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); + network->setNetTreeDiscCallback(std::bind(&HostFNE::processNetworkTreeDisconnect, this, std::placeholders::_1, std::placeholders::_2)); + network->enable(enabled); if (enabled) { bool ret = network->open(); @@ -1087,3 +1093,12 @@ void HostFNE::processPeerAnalog(network::PeerNetwork* peerNetwork, const uint8_t m_network->analogTrafficHandler()->processFrame(data, length, peerId, rtpHeader.getSSRC(), rtpHeader.getSequence(), streamId, true); } } + +/* Processes network tree disconnect notification. */ + +void HostFNE::processNetworkTreeDisconnect(network::PeerNetwork* peerNetwork, const uint32_t offendingPeerId) +{ + if (m_network != nullptr) { + m_network->processNetworkTreeDisconnect(offendingPeerId); + } +} diff --git a/src/fne/HostFNE.h b/src/fne/HostFNE.h index 88dd4882..5e93a31e 100644 --- a/src/fne/HostFNE.h +++ b/src/fne/HostFNE.h @@ -214,6 +214,13 @@ class HOST_SW_API HostFNE { */ void processPeerAnalog(network::PeerNetwork* peerNetwork, const uint8_t* data, uint32_t length, uint32_t streamId, const network::frame::RTPFNEHeader& fneHeader, const network::frame::RTPHeader& rtpHeader); + + /** + * @brief Processes network tree disconnect notification. + * @param peerNetwork Peer network instance. + * @param offendingPeerId Offending peer ID. + */ + void processNetworkTreeDisconnect(network::PeerNetwork* peerNetwork, const uint32_t offendingPeerId); }; #endif // __HOST_FNE_H__ diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index ec262fca..835e112e 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -33,6 +33,9 @@ DiagNetwork::DiagNetwork(HostFNE* host, FNENetwork* fneNetwork, const std::strin m_host(host), m_address(address), m_port(port), + m_status(NET_STAT_INVALID), + m_peerReplicaActPkt(), + m_peerTreeListPkt(), m_threadPool(workerCnt, "diag") { assert(fneNetwork != nullptr); @@ -76,6 +79,7 @@ void DiagNetwork::processNetwork() NetPacketRequest* req = new NetPacketRequest(); req->obj = m_fneNetwork; + req->diagObj = this; req->peerId = peerId; req->address = address; @@ -172,6 +176,17 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) return; } + DiagNetwork* diagNetwork = static_cast(req->diagObj); + if (diagNetwork == nullptr) { + if (req != nullptr) { + if (req->buffer != nullptr) + delete[] req->buffer; + delete req; + } + + return; + } + if (req == nullptr) return; @@ -383,16 +398,16 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) // Utils::dump(1U, "DiagNetwork::taskNetworkRx(), REPL_ACT_PEER_LIST, Raw Payload", rawPayload, req->length); - if (network->m_peerReplicaActPkt.find(peerId) == network->m_peerReplicaActPkt.end()) { - network->m_peerReplicaActPkt.insert(peerId, FNENetwork::PacketBufferEntry()); + if (diagNetwork->m_peerReplicaActPkt.find(peerId) == diagNetwork->m_peerReplicaActPkt.end()) { + diagNetwork->m_peerReplicaActPkt.insert(peerId, DiagNetwork::PacketBufferEntry()); - FNENetwork::PacketBufferEntry& pkt = network->m_peerReplicaActPkt[peerId]; + DiagNetwork::PacketBufferEntry& pkt = diagNetwork->m_peerReplicaActPkt[peerId]; pkt.buffer = new PacketBuffer(true, "Peer Replication, Active Peer List"); pkt.streamId = streamId; pkt.locked = false; } else { - FNENetwork::PacketBufferEntry& pkt = network->m_peerReplicaActPkt[peerId]; + DiagNetwork::PacketBufferEntry& pkt = diagNetwork->m_peerReplicaActPkt[peerId]; if (!pkt.locked && pkt.streamId != streamId) { LogError(LOG_NET, "PEER %u (%s) Peer Replication, Active Peer List, stream ID mismatch, expected %u, got %u", peerId, connection->identWithQualifier().c_str(), pkt.streamId, streamId); @@ -406,7 +421,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } } - FNENetwork::PacketBufferEntry& pkt = network->m_peerReplicaActPkt[peerId]; + DiagNetwork::PacketBufferEntry& pkt = diagNetwork->m_peerReplicaActPkt[peerId]; if (pkt.locked) { while (pkt.locked) Thread::sleep(1U); @@ -430,7 +445,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } - network->m_peerReplicaActPkt.erase(peerId); + diagNetwork->m_peerReplicaActPkt.erase(peerId); break; } else { @@ -443,7 +458,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } - network->m_peerReplicaActPkt.erase(peerId); + diagNetwork->m_peerReplicaActPkt.erase(peerId); break; } else { @@ -459,7 +474,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } - network->m_peerReplicaActPkt.erase(peerId); + diagNetwork->m_peerReplicaActPkt.erase(peerId); } else { pkt.locked = false; } @@ -552,6 +567,125 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } break; + case NET_FUNC::NET_TREE: + if (!network->m_enableSpanningTree) + break; + if (req->fneHeader.getSubFunction() == NET_SUBFUNC::NET_TREE_LIST) { // FNE Network Tree List + if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) { + FNEPeerConnection* connection = network->m_peers[peerId]; + if (connection != nullptr) { + std::string ip = udp::Socket::address(req->address); + + // validate peer (simple validation really) + if (connection->connected() && connection->address() == ip && connection->isExternalFNEPeer()) { + DECLARE_UINT8_ARRAY(rawPayload, req->length); + ::memcpy(rawPayload, req->buffer, req->length); + + // Utils::dump(1U, "DiagNetwork::taskNetworkRx(), NET_TREE_LIST, Raw Payload", rawPayload, req->length); + + if (diagNetwork->m_peerTreeListPkt.find(peerId) == diagNetwork->m_peerTreeListPkt.end()) { + diagNetwork->m_peerTreeListPkt.insert(peerId, DiagNetwork::PacketBufferEntry()); + + DiagNetwork::PacketBufferEntry& pkt = diagNetwork->m_peerTreeListPkt[peerId]; + pkt.buffer = new PacketBuffer(true, "Network Tree, Tree List"); + pkt.streamId = streamId; + + pkt.locked = false; + } else { + DiagNetwork::PacketBufferEntry& pkt = diagNetwork->m_peerTreeListPkt[peerId]; + if (!pkt.locked && pkt.streamId != streamId) { + LogError(LOG_NET, "PEER %u (%s) Network Tree, Tree List, stream ID mismatch, expected %u, got %u", peerId, + connection->identWithQualifier().c_str(), pkt.streamId, streamId); + pkt.buffer->clear(); + pkt.streamId = streamId; + } + + if (pkt.streamId != streamId) { + // otherwise drop the packet + break; + } + } + + DiagNetwork::PacketBufferEntry& pkt = diagNetwork->m_peerTreeListPkt[peerId]; + if (pkt.locked) { + while (pkt.locked) + Thread::sleep(1U); + } + + pkt.locked = true; + + uint32_t decompressedLen = 0U; + uint8_t* decompressed = nullptr; + + if (pkt.buffer->decode(rawPayload, &decompressed, &decompressedLen)) { + std::string payload(decompressed + 8U, decompressed + decompressedLen); + + // parse JSON body + json::value v; + std::string err = json::parse(v, payload); + if (!err.empty()) { + LogError(LOG_NET, "PEER %u (%s) error parsing network tree list, %s", peerId, connection->identWithQualifier().c_str(), err.c_str()); + pkt.buffer->clear(); + pkt.streamId = 0U; + if (decompressed != nullptr) { + delete[] decompressed; + } + diagNetwork->m_peerTreeListPkt.erase(peerId); + break; + } + else { + // ensure parsed JSON is an array + if (!v.is()) { + LogError(LOG_NET, "PEER %u (%s) error parsing network tree list, data was not valid", peerId, connection->identWithQualifier().c_str()); + pkt.buffer->clear(); + delete pkt.buffer; + pkt.streamId = 0U; + if (decompressed != nullptr) { + delete[] decompressed; + } + diagNetwork->m_peerTreeListPkt.erase(peerId); + break; + } + else { + json::array arr = v.get(); + LogInfoEx(LOG_NET, "PEER %u (%s) Network Tree, Tree List, updating %u peer entries", peerId, connection->identWithQualifier().c_str(), arr.size()); + std::vector duplicatePeers; + MasterTree::deserializeTree(arr, network->m_fneTree, &duplicatePeers); + + if (network->m_logSpanningTreeChanges) { + LogInfoEx(LOG_NET, "PEER %u (%s) Network Tree, Tree List, current tree:", peerId, connection->identWithQualifier().c_str()); + MasterTree::visualizeTreeToLog(network->m_fneTree); + } + + if (duplicatePeers.size() > 0U) { + for (auto dupPeerId : duplicatePeers) { + LogWarning(LOG_NET, "PEER %u (%s) Network Tree, Tree List, disconnecting duplicate peer connection for PEER %u to prevent network loop", + peerId, connection->identWithQualifier().c_str(), dupPeerId); + network->writeTreeDisconnect(peerId, dupPeerId); + } + } + } + } + + pkt.buffer->clear(); + delete pkt.buffer; + pkt.streamId = 0U; + if (decompressed != nullptr) { + delete[] decompressed; + } + diagNetwork->m_peerTreeListPkt.erase(peerId); + } else { + pkt.locked = false; + } + } + else { + network->writePeerNAK(peerId, 0U, TAG_PEER_REPLICA, NET_CONN_NAK_FNE_UNAUTHORIZED); + } + } + } + } + break; + default: // diagostic network ignores unknowns for everything else... break; diff --git a/src/fne/network/DiagNetwork.h b/src/fne/network/DiagNetwork.h index 2961ede8..0faff118 100644 --- a/src/fne/network/DiagNetwork.h +++ b/src/fne/network/DiagNetwork.h @@ -99,6 +99,26 @@ namespace network NET_CONN_STATUS m_status; + /** + * @brief Represents a packet buffer entry in a map. + */ + class PacketBufferEntry { + public: + /** + * @brief Stream ID of the packet. + */ + uint32_t streamId; + + /** + * @brief Packet fragment buffer. + */ + PacketBuffer* buffer; + + bool locked; + }; + concurrent::unordered_map m_peerReplicaActPkt; + concurrent::unordered_map m_peerTreeListPkt; + ThreadPool m_threadPool; /** diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 09e3f778..d0de5b01 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -95,7 +95,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_peerAffiliations(), m_ccPeerMap(), m_peerReplicaKeyQueue(), - m_peerReplicaActPkt(), + m_fneTree(nullptr), m_peerReplicaHAParams(), m_advertisedHAAddress(), m_advertisedHAPort(TRAFFIC_DEFAULT_PORT), @@ -146,6 +146,8 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_p25OTARService = new P25OTARService(this, m_tagP25->packetData(), kmfDebug, verbose); + m_fneTree = new MasterTree(peerId, peerId, nullptr); + /* ** Initialize Threads */ @@ -187,6 +189,14 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) m_softConnLimit = MAX_HARD_CONN_CAP; } + m_enableSpanningTree = conf["enableSpanningTree"].as(true); + + if (!m_enableSpanningTree) { + LogWarning(LOG_NET, "WARNING: Disabling the peer spanning tree is not recommended! This can cause network loops and other issues in a multi-peer FNE network."); + } + + m_logSpanningTreeChanges = conf["logSpanningTreeChanges"].as(false); + // always force disable ADJ_STS_BCAST to external peers if the all option // is enabled if (m_disallowAdjStsBcast) { @@ -262,6 +272,8 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) if (printOptions) { LogInfo(" Maximum Permitted Connections: %u", m_softConnLimit); + LogInfo(" Enable Peer Spanning Tree: %s", m_enableSpanningTree ? "yes" : "no"); + LogInfo(" Log Spanning Tree Changes: %s", m_logSpanningTreeChanges ? "yes" : "no"); LogInfo(" Disable adjacent site broadcasts to any peers: %s", m_disallowAdjStsBcast ? "yes" : "no"); if (m_disallowAdjStsBcast) { LogWarning(LOG_NET, "NOTICE: All P25 ADJ_STS_BCAST messages will be blocked and dropped!"); @@ -349,6 +361,7 @@ void FNENetwork::processNetwork() NetPacketRequest* req = new NetPacketRequest(); req->obj = this; + req->diagObj = m_host->m_diagNetwork; req->peerId = peerId; req->address = address; @@ -375,6 +388,34 @@ void FNENetwork::processNetwork() } } +/* Process network tree disconnect notification. */ + +void FNENetwork::processNetworkTreeDisconnect(uint32_t offendingPeerId) +{ + if (m_status != NET_STAT_MST_RUNNING) { + return; + } + + if (!m_enableSpanningTree) { + LogWarning(LOG_NET, "FNENetwork::processNetworkTreeDisconnect(), ignoring disconnect request for PEER %u, spanning tree is disabled", offendingPeerId); + return; + } + + if (offendingPeerId > 0 && (m_peers.find(offendingPeerId) != m_peers.end())) { + FNEPeerConnection* connection = m_peers[offendingPeerId]; + if (connection != nullptr) { + LogWarning(LOG_NET, "PEER %u (%s) NAK, server already connected via upstream master, duplicate connection dropped, connectionState = %u", offendingPeerId, connection->identWithQualifier().c_str(), + connection->connectionState()); + writePeerNAK(offendingPeerId, createStreamId(), TAG_REPEATER_CONFIG, NET_CONN_NAK_FNE_DUPLICATE_CONN); + disconnectPeer(offendingPeerId, connection); + } else { + LogError(LOG_NET, "Network Tree Disconnect, upstream master requested disconnect for PEER %u, but connection is null", offendingPeerId); + } + } else { + LogError(LOG_NET, "Network Tree Disconnect, upstream master requested disconnect for unknown PEER %u", offendingPeerId); + } +} + /* Updates the timer by the passed number of milliseconds. */ void FNENetwork::clock(uint32_t ms) @@ -430,20 +471,22 @@ void FNENetwork::clock(uint32_t ms) // remove any peers for (uint32_t peerId : peersToRemove) { FNEPeerConnection* connection = m_peers[peerId]; - connection->lock(); - erasePeer(peerId); - connection->unlock(); - if (connection != nullptr) { - delete connection; - } + disconnectPeer(peerId, connection); } // send peer updates to external FNE peers if (m_host->m_peerNetworks.size() > 0) { for (auto peer : m_host->m_peerNetworks) { if (peer.second != nullptr) { + // perform master tree maintainence tasks + if (peer.second->isEnabled() && peer.second->getRemotePeerId() > 0U && + m_enableSpanningTree) { + peer.second->writeMasterTree(m_fneTree); + } + // perform peer replica maintainence tasks - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->getRemotePeerId() > 0U && + peer.second->isPeerReplica()) { if (!peer.second->getAttachedKeyRSPHandler()) { peer.second->setAttachedKeyRSPHandler(true); // this is the only place this should happen peer.second->setKeyResponseCallback([=](p25::kmm::KeyItem ki, uint8_t algId, uint8_t keyLength) { @@ -913,13 +956,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogWarning(LOG_NET, "PEER %u RPTL, failed peer ACL check", peerId); network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_PEER_ACL, req->address, req->addrLen); - - connection->lock(); - connection->connected(false); - network->erasePeer(peerId); - connection->unlock(); - - delete connection; + network->disconnectPeer(peerId, connection); } } } @@ -950,13 +987,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogWarning(LOG_NET, "PEER %u RPTL, failed peer ACL check", peerId); network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_PEER_ACL, req->address, req->addrLen); - - connection->lock(); - connection->connected(false); - network->erasePeer(peerId); - connection->unlock(); - - delete connection; + network->disconnectPeer(peerId, connection); } } } else { @@ -964,17 +995,10 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogWarning(LOG_NET, "PEER %u (%s) RPTL NAK, bad connection state, connectionState = %u", peerId, connection->identWithQualifier().c_str(), connection->connectionState()); - - connection->lock(); - connection->connected(false); - network->erasePeer(peerId); - connection->unlock(); - - delete connection; + network->disconnectPeer(peerId, connection); } } else { network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); - network->erasePeer(peerId); LogWarning(LOG_NET, "PEER %u RPTL NAK, having no connection", peerId); } @@ -1059,39 +1083,22 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) else { LogWarning(LOG_NET, "PEER %u RPTK NAK, failed the login exchange", peerId); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_FNE_UNAUTHORIZED, req->address, req->addrLen); - connection->lock(); - connection->connected(false); - network->erasePeer(peerId); - connection->unlock(); - - delete connection; + network->disconnectPeer(peerId, connection); } } else { network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_PEER_ACL, req->address, req->addrLen); - connection->lock(); - connection->connected(false); - network->erasePeer(peerId); - connection->unlock(); - - delete connection; + network->disconnectPeer(peerId, connection); } } else { LogWarning(LOG_NET, "PEER %u RPTK NAK, login exchange while in an incorrect state, connectionState = %u", peerId, connection->connectionState()); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); - - connection->lock(); - connection->connected(false); - network->erasePeer(peerId); - connection->unlock(); - - delete connection; + network->disconnectPeer(peerId, connection); } } } else { network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); - network->erasePeer(peerId); LogWarning(LOG_NET, "PEER %u RPTK NAK, having no connection", peerId); } @@ -1115,24 +1122,14 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (!err.empty()) { LogWarning(LOG_NET, "PEER %u RPTC NAK, supplied invalid configuration data", peerId); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_INVALID_CONFIG_DATA, req->address, req->addrLen); - connection->lock(); - connection->connected(false); - network->erasePeer(peerId); - connection->unlock(); - - delete connection; + network->disconnectPeer(peerId, connection); } else { // ensure parsed JSON is an object if (!v.is()) { LogWarning(LOG_NET, "PEER %u RPTC NAK, supplied invalid configuration data", peerId); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_INVALID_CONFIG_DATA, req->address, req->addrLen); - connection->lock(); - connection->connected(false); - network->erasePeer(peerId); - connection->unlock(); - - delete connection; + network->disconnectPeer(peerId, connection); } else { connection->config(v.get()); @@ -1151,16 +1148,19 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) buffer[0U] = 0x80U; } - network->writePeerACK(peerId, streamId, buffer, 1U); - LogInfoEx(LOG_NET, "PEER %u RPTC ACK, completed the configuration exchange", peerId); - json::object peerConfig = connection->config(); + if (peerConfig["identity"].is()) { std::string identity = peerConfig["identity"].get(); connection->identity(identity); LogInfoEx(LOG_NET, "PEER %u >> Identity [%8s]", peerId, identity.c_str()); } + if (peerConfig["software"].is()) { + std::string software = peerConfig["software"].get(); + LogInfoEx(LOG_NET, "PEER %u >> Software Version [%s]", peerId, software.c_str()); + } + // is the peer reporting it is an external FNE neighbor peer? if (peerConfig["externalPeer"].is()) { bool external = peerConfig["externalPeer"].get(); @@ -1168,7 +1168,14 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (external) LogInfoEx(LOG_NET, "PEER %u >> External FNE Neighbor Peer", peerId); - // check if the peer is participating in peer link + uint32_t masterPeerId = 0U; + if (peerConfig["masterPeerId"].is()) { + masterPeerId = peerConfig["masterPeerId"].get(); + connection->masterId(masterPeerId); + LogInfoEx(LOG_NET, "PEER %u >> Master Peer ID [%u]", peerId, masterPeerId); + } + + // check if the peer a peer replication participant lookups::PeerId peerEntry = network->m_peerListLookup->find(req->peerId); if (!peerEntry.peerDefault()) { if (peerEntry.peerReplica()) { @@ -1182,8 +1189,29 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } } } + + if (network->m_enableSpanningTree) { + // check if this peer is already connected via another peer + MasterTree* tree = MasterTree::findByMasterID(masterPeerId); + if (tree != nullptr) { + LogWarning(LOG_NET, "PEER %u (%s) RPTC NAK, server already connected via PEER %u, duplicate connection denied, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + tree->id(), connection->connectionState()); + network->writePeerNAK(peerId, TAG_REPEATER_CONFIG, NET_CONN_NAK_FNE_DUPLICATE_CONN, req->address, req->addrLen); + network->disconnectPeer(peerId, connection); + break; + } else { + new MasterTree(peerId, masterPeerId, network->m_fneTree); + if (network->m_logSpanningTreeChanges) { + LogInfoEx(LOG_NET, "PEER %u (%s) Network Tree, Tree List, current tree:", peerId, connection->identWithQualifier().c_str()); + MasterTree::visualizeTreeToLog(network->m_fneTree); + } + } + } } + network->writePeerACK(peerId, streamId, buffer, 1U); + LogInfoEx(LOG_NET, "PEER %u RPTC ACK, completed the configuration exchange", peerId); + // is the peer reporting it is a conventional peer? if (peerConfig["conventionalPeer"].is()) { if (network->m_allowConvSiteAffOverride) { @@ -1202,11 +1230,6 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogInfoEx(LOG_NET, "PEER %u >> SysView Peer", peerId); } - if (peerConfig["software"].is()) { - std::string software = peerConfig["software"].get(); - LogInfoEx(LOG_NET, "PEER %u >> Software Version [%s]", peerId, software.c_str()); - } - // setup the affiliations list for this peer std::stringstream peerName; peerName << "PEER " << peerId; @@ -1221,12 +1244,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogWarning(LOG_NET, "PEER %u (%s) RPTC NAK, login exchange while in an incorrect state, connectionState = %u", peerId, connection->identWithQualifier().c_str(), connection->connectionState()); network->writePeerNAK(peerId, TAG_REPEATER_CONFIG, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); - connection->lock(); - connection->connected(false); - network->erasePeer(peerId); - connection->unlock(); - - delete connection; + network->disconnectPeer(peerId, connection); } } } @@ -1247,12 +1265,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // validate peer (simple validation really) if (connection->connected() && connection->address() == ip) { LogInfoEx(LOG_NET, "PEER %u (%s) disconnected", peerId, connection->identWithQualifier().c_str()); - connection->lock(); - connection->connected(false); - network->erasePeer(peerId); - connection->unlock(); - - delete connection; + network->disconnectPeer(peerId, connection); } } } @@ -1818,13 +1831,35 @@ bool FNENetwork::erasePeerAffiliations(uint32_t peerId) return false; } +/* Helper to disconnect a downstream peer. */ + +void FNENetwork::disconnectPeer(uint32_t peerId, FNEPeerConnection* connection) +{ + if (peerId == 0U) + return; + if (connection == nullptr) + return; + + connection->connected(false); + connection->connectionState(NET_STAT_INVALID); + + connection->lock(); + erasePeer(peerId); + connection->unlock(); + if (connection != nullptr) { + delete connection; + } +} + /* Helper to erase the peer from the peers list. */ void FNENetwork::erasePeer(uint32_t peerId) { + bool isExternalFNEPeer = false; { auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; }); if (it != m_peers.end()) { + isExternalFNEPeer = it->second->isExternalFNEPeer(); m_peers.erase(peerId); } } @@ -1853,6 +1888,28 @@ void FNENetwork::erasePeer(uint32_t peerId) } } + if (isExternalFNEPeer && m_enableSpanningTree) { + // erase this peer from the master tree + MasterTree* tree = MasterTree::findByPeerID(peerId); + if (tree != nullptr) { + if (tree->hasChildren()) { + uint32_t totalChildren = tree->countChildren(tree); + + // netsplit be as noisy as possible about it... + for (uint8_t i = 0U; i < 3U; i++) + LogWarning(LOG_NET, "PEER %u downstream netsplit, lost %u downstream connections", peerId, totalChildren); + } + + LogWarning(LOG_NET, "PEER %u downstream netsplit, disconnected", peerId); + MasterTree::erasePeer(peerId); + } + + if (m_logSpanningTreeChanges) { + LogInfoEx(LOG_NET, "PEER %u Network Tree, Tree List, current tree:", peerId); + MasterTree::visualizeTreeToLog(m_fneTree); + } + } + // cleanup peer affiliations erasePeerAffiliations(peerId); } @@ -1926,7 +1983,6 @@ bool FNENetwork::resetPeer(uint32_t peerId) LogInfoEx(LOG_NET, "PEER %u (%s) resetting peer connection", peerId, connection->identWithQualifier().c_str()); writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_PEER_RESET, addr, addrLen); - erasePeer(peerId); delete connection; @@ -2498,6 +2554,26 @@ void FNENetwork::writeHAParameters(uint32_t peerId, uint32_t streamId, bool send buffer, len, streamId, true); } +/* Helper to send a network tree disconnect to the specified peer. */ + +void FNENetwork::writeTreeDisconnect(uint32_t peerId, uint32_t offendingPeerId) +{ + if (!m_enableSpanningTree) + return; + + if (peerId == 0) + return; + if (offendingPeerId == 0U) + return; + + uint8_t buffer[DATA_PACKET_LENGTH]; + ::memset(buffer, 0x00U, DATA_PACKET_LENGTH); + + SET_UINT32(offendingPeerId, buffer, 0U); // Offending Peer ID + + writePeerCommand(peerId, { NET_FUNC::NET_TREE, NET_SUBFUNC::NET_TREE_DISC }, buffer, 4U, RTP_END_OF_CALL_SEQ, createStreamId()); +} + /* Helper to send a In-Call Control command to the specified peer. */ bool FNENetwork::writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::ENUM subFunc, NET_ICC::ENUM command, uint32_t dstId, uint8_t slotNo) @@ -2639,6 +2715,10 @@ void FNENetwork::logPeerNAKReason(uint32_t peerId, const char* tag, NET_CONN_NAK LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; ACL rejection", peerId, tag, (uint16_t)reason); break; + case NET_CONN_NAK_FNE_DUPLICATE_CONN: + LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; duplicate connection drop", peerId, tag, (uint16_t)reason); + break; + case NET_CONN_NAK_GENERAL_FAILURE: default: LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; general failure", peerId, tag, (uint16_t)reason); @@ -2662,7 +2742,7 @@ bool FNENetwork::writePeerNAK(uint32_t peerId, uint32_t streamId, const char* ta SET_UINT16((uint16_t)reason, buffer, 10U); // Reason logPeerNAKReason(peerId, tag, reason); - return writePeer(peerId, m_peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, buffer, 10U, RTP_END_OF_CALL_SEQ, streamId, false); + return writePeer(peerId, m_peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, buffer, 12U, RTP_END_OF_CALL_SEQ, streamId, false); } /* Helper to send a NAK response to the specified peer. */ diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 61555f6c..78abf4ab 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -38,6 +38,8 @@ #include "common/ThreadPool.h" #include "fne/lookups/AffiliationLookup.h" #include "fne/network/influxdb/InfluxDB.h" +#include "fne/network/FNEPeerConnection.h" +#include "fne/network/MasterTree.h" #include "fne/network/HAParameters.h" #include "fne/CryptoContainer.h" @@ -94,191 +96,6 @@ namespace network class HOST_SW_API DiagNetwork; class HOST_SW_API FNENetwork; - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief Represents an peer connection to the FNE. - * @ingroup fne_network - */ - class HOST_SW_API FNEPeerConnection : public RTPStreamMultiplex { - public: - auto operator=(FNEPeerConnection&) -> FNEPeerConnection& = delete; - auto operator=(FNEPeerConnection&&) -> FNEPeerConnection& = delete; - FNEPeerConnection(FNEPeerConnection&) = delete; - - /** - * @brief Initializes a new instance of the FNEPeerConnection class. - */ - FNEPeerConnection() : - RTPStreamMultiplex(), - m_id(0U), - m_ccPeerId(0U), - m_socketStorage(), - m_sockStorageLen(0U), - m_address(), - m_port(), - m_salt(0U), - m_connected(false), - m_connectionState(NET_STAT_INVALID), - m_pingsReceived(0U), - m_lastPing(0U), - m_missedMetadataUpdates(0U), - m_isExternalFNEPeer(false), - m_isConventionalPeer(false), - m_isSysView(false), - m_isPeerReplica(false), - m_config(), - m_peerLockMtx() - { - /* stub */ - } - /** - * @brief Initializes a new instance of the FNEPeerConnection class. - * @param id Unique ID of this modem on the network. - * @param socketStorage - * @param sockStorageLen - */ - FNEPeerConnection(uint32_t id, sockaddr_storage& socketStorage, uint32_t sockStorageLen) : - RTPStreamMultiplex(), - m_id(id), - m_ccPeerId(0U), - m_socketStorage(socketStorage), - m_sockStorageLen(sockStorageLen), - m_address(udp::Socket::address(socketStorage)), - m_port(udp::Socket::port(socketStorage)), - m_salt(0U), - m_connected(false), - m_connectionState(NET_STAT_INVALID), - m_pingsReceived(0U), - m_lastPing(0U), - m_missedMetadataUpdates(0U), - m_isExternalFNEPeer(false), - m_isConventionalPeer(false), - m_isSysView(false), - m_isPeerReplica(false), - m_config(), - m_peerLockMtx() - { - assert(id > 0U); - assert(sockStorageLen > 0U); - assert(!m_address.empty()); - assert(m_port > 0U); - } - - /** - * @brief Returns the identity with qualifier symbols. - * @return std::string Identity with qualifier. - */ - std::string identWithQualifier() const - { - if (isSysView()) - return "@" + identity(); - if (isPeerReplica()) - return "%" + identity(); - if (isExternalFNEPeer()) - return "+" + identity(); - - return " " + m_identity; - } - - /** - * @brief Lock the peer. - */ - inline void lock() const { m_peerLockMtx.lock(); } - /** - * @brief Unlock the peer. - */ - inline void unlock() const { m_peerLockMtx.unlock(); } - - public: - /** - * @brief Peer ID. - */ - DECLARE_PROPERTY_PLAIN(uint32_t, id); - /** - * @brief Peer Identity. - */ - DECLARE_PROPERTY_PLAIN(std::string, identity); - - /** - * @brief Control Channel Peer ID. - */ - DECLARE_PROPERTY_PLAIN(uint32_t, ccPeerId); - - /** - * @brief Unix socket storage containing the connected address. - */ - DECLARE_PROPERTY_PLAIN(sockaddr_storage, socketStorage); - /** - * @brief Length of the sockaddr_storage structure. - */ - DECLARE_PROPERTY_PLAIN(uint32_t, sockStorageLen); - - /** - * @brief */ - DECLARE_PROPERTY_PLAIN(std::string, address); - /** - * @brief Port number peer connected with. - */ - DECLARE_PROPERTY_PLAIN(uint16_t, port); - - /** - * @brief Salt value used for peer authentication. - */ - DECLARE_PROPERTY_PLAIN(uint32_t, salt); - - /** - * @brief Flag indicating whether or not the peer is connected. - */ - DECLARE_PROPERTY_PLAIN(bool, connected); - /** - * @brief Connection state. - */ - DECLARE_PROPERTY_PLAIN(NET_CONN_STATUS, connectionState); - - /** - * @brief Number of pings received. - */ - DECLARE_PROPERTY_PLAIN(uint32_t, pingsReceived); - /** - * @brief Last ping received. - */ - DECLARE_PROPERTY_PLAIN(uint64_t, lastPing); - - /** - * @brief Number of missed network metadata updates. - */ - DECLARE_PROPERTY_PLAIN(uint32_t, missedMetadataUpdates); - - /** - * @brief Flag indicating this connection is from an external neighbor FNE peer. - */ - DECLARE_PROPERTY_PLAIN(bool, isExternalFNEPeer); - /** - * @brief Flag indicating this connection is from an conventional peer. - */ - DECLARE_PROPERTY_PLAIN(bool, isConventionalPeer); - /** - * @brief Flag indicating this connection is from an SysView peer. - */ - DECLARE_PROPERTY_PLAIN(bool, isSysView); - - /** - * @brief Flag indicating this connection is from a neighbor FNE peer that is replica enabled. - */ - DECLARE_PROPERTY_PLAIN(bool, isPeerReplica); - - /** - * @brief JSON objecting containing peer configuration information. - */ - DECLARE_PROPERTY_PLAIN(json::object, config); - - private: - mutable std::mutex m_peerLockMtx; - }; - // --------------------------------------------------------------------------- // Structure Declaration // --------------------------------------------------------------------------- @@ -301,6 +118,7 @@ namespace network */ struct NetPacketRequest : thread_t { uint32_t peerId; //!< Peer ID for this request. + void* diagObj; //!< Network diagnostics network object. sockaddr_storage address; //!< IP Address and Port. uint32_t addrLen; //!< @@ -409,6 +227,12 @@ namespace network */ void processNetwork(); + /** + * @brief Process network tree disconnect notification. + * @param offendingPeerId Offending Peer ID. + */ + void processNetworkTreeDisconnect(uint32_t offendingPeerId); + /** * @brief Updates the timer by the passed number of milliseconds. * @param ms Number of milliseconds. @@ -495,24 +319,7 @@ namespace network static std::timed_mutex m_keyQueueMutex; std::unordered_map m_peerReplicaKeyQueue; - /** - * @brief Represents a packet buffer entry in a map. - */ - class PacketBufferEntry { - public: - /** - * @brief Stream ID of the packet. - */ - uint32_t streamId; - - /** - * @brief Packet fragment buffer. - */ - PacketBuffer* buffer; - - bool locked; - }; - concurrent::unordered_map m_peerReplicaActPkt; + MasterTree* m_fneTree; concurrent::vector m_peerReplicaHAParams; std::string m_advertisedHAAddress; @@ -525,6 +332,9 @@ namespace network uint32_t m_softConnLimit; + bool m_enableSpanningTree; + bool m_logSpanningTreeChanges; + uint32_t m_callCollisionTimeout; bool m_disallowAdjStsBcast; @@ -603,6 +413,12 @@ namespace network * @returns bool True, if the peer affiliations were deleted, otherwise false. */ bool erasePeerAffiliations(uint32_t peerId); + /** + * @brief Helper to disconnect a downstream peer. + * @param peerId Peer ID. + * @param connection Instance of the FNEPeerConnection class. + */ + void disconnectPeer(uint32_t peerId, FNEPeerConnection* connection); /** * @brief Helper to erase the peer from the peers list. * @note This does not delete or otherwise free the FNEConnection instance! @@ -684,6 +500,14 @@ namespace network */ void writeHAParameters(uint32_t peerId, uint32_t streamId, bool sendISSI); + /** + * @brief Helper to send a network tree disconnect to the specified peer. + * This will cause the peer to issue a link disconnect to the offending peer to prevent network loops. + * @param peerId Peer ID. + * @param offendingPeerId Offending Peer ID. + */ + void writeTreeDisconnect(uint32_t peerId, uint32_t offendingPeerId); + /** * @brief Helper to send a In-Call Control command to the specified peer. * @param peerId Peer ID. diff --git a/src/fne/network/FNEPeerConnection.h b/src/fne/network/FNEPeerConnection.h new file mode 100644 index 00000000..4f1a278c --- /dev/null +++ b/src/fne/network/FNEPeerConnection.h @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Converged FNE Software + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file FNEPeerConnection.h + * @ingroup fne_network + */ +#if !defined(__FNE_PEER_CONNECTION_H__) +#define __FNE_PEER_CONNECTION_H__ + +#include "fne/Defines.h" +#include "common/network/BaseNetwork.h" + +#include +#include + +namespace network +{ + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents an peer connection to the FNE. + * @ingroup fne_network + */ + class HOST_SW_API FNEPeerConnection : public RTPStreamMultiplex { + public: + auto operator=(FNEPeerConnection&) -> FNEPeerConnection& = delete; + auto operator=(FNEPeerConnection&&) -> FNEPeerConnection& = delete; + FNEPeerConnection(FNEPeerConnection&) = delete; + + /** + * @brief Initializes a new instance of the FNEPeerConnection class. + */ + FNEPeerConnection() : + RTPStreamMultiplex(), + m_id(0U), + m_masterId(0U), + m_ccPeerId(0U), + m_socketStorage(), + m_sockStorageLen(0U), + m_address(), + m_port(), + m_salt(0U), + m_connected(false), + m_connectionState(NET_STAT_INVALID), + m_pingsReceived(0U), + m_lastPing(0U), + m_missedMetadataUpdates(0U), + m_isExternalFNEPeer(false), + m_isConventionalPeer(false), + m_isSysView(false), + m_isPeerReplica(false), + m_config(), + m_peerLockMtx() + { + /* stub */ + } + /** + * @brief Initializes a new instance of the FNEPeerConnection class. + * @param id Unique ID of this modem on the network. + * @param socketStorage + * @param sockStorageLen + */ + FNEPeerConnection(uint32_t id, sockaddr_storage& socketStorage, uint32_t sockStorageLen) : + RTPStreamMultiplex(), + m_id(id), + m_masterId(0U), + m_ccPeerId(0U), + m_socketStorage(socketStorage), + m_sockStorageLen(sockStorageLen), + m_address(udp::Socket::address(socketStorage)), + m_port(udp::Socket::port(socketStorage)), + m_salt(0U), + m_connected(false), + m_connectionState(NET_STAT_INVALID), + m_pingsReceived(0U), + m_lastPing(0U), + m_missedMetadataUpdates(0U), + m_isExternalFNEPeer(false), + m_isConventionalPeer(false), + m_isSysView(false), + m_isPeerReplica(false), + m_config(), + m_peerLockMtx() + { + assert(id > 0U); + assert(sockStorageLen > 0U); + assert(!m_address.empty()); + assert(m_port > 0U); + } + + /** + * @brief Returns the identity with qualifier symbols. + * @return std::string Identity with qualifier. + */ + std::string identWithQualifier() const + { + if (isSysView()) + return "@" + identity(); + if (isPeerReplica()) + return "%" + identity(); + if (isExternalFNEPeer()) + return "+" + identity(); + + return " " + m_identity; + } + + /** + * @brief Lock the peer. + */ + inline void lock() const { m_peerLockMtx.lock(); } + /** + * @brief Unlock the peer. + */ + inline void unlock() const { m_peerLockMtx.unlock(); } + + public: + /** + * @brief Peer ID. + */ + DECLARE_PROPERTY_PLAIN(uint32_t, id); + /** + * @brief Master Peer ID. + */ + DECLARE_PROPERTY_PLAIN(uint32_t, masterId); + /** + * @brief Peer Identity. + */ + DECLARE_PROPERTY_PLAIN(std::string, identity); + + /** + * @brief Control Channel Peer ID. + */ + DECLARE_PROPERTY_PLAIN(uint32_t, ccPeerId); + + /** + * @brief Unix socket storage containing the connected address. + */ + DECLARE_PROPERTY_PLAIN(sockaddr_storage, socketStorage); + /** + * @brief Length of the sockaddr_storage structure. + */ + DECLARE_PROPERTY_PLAIN(uint32_t, sockStorageLen); + + /** + * @brief */ + DECLARE_PROPERTY_PLAIN(std::string, address); + /** + * @brief Port number peer connected with. + */ + DECLARE_PROPERTY_PLAIN(uint16_t, port); + + /** + * @brief Salt value used for peer authentication. + */ + DECLARE_PROPERTY_PLAIN(uint32_t, salt); + + /** + * @brief Flag indicating whether or not the peer is connected. + */ + DECLARE_PROPERTY_PLAIN(bool, connected); + /** + * @brief Connection state. + */ + DECLARE_PROPERTY_PLAIN(NET_CONN_STATUS, connectionState); + + /** + * @brief Number of pings received. + */ + DECLARE_PROPERTY_PLAIN(uint32_t, pingsReceived); + /** + * @brief Last ping received. + */ + DECLARE_PROPERTY_PLAIN(uint64_t, lastPing); + + /** + * @brief Number of missed network metadata updates. + */ + DECLARE_PROPERTY_PLAIN(uint32_t, missedMetadataUpdates); + + /** + * @brief Flag indicating this connection is from an external neighbor FNE peer. + */ + DECLARE_PROPERTY_PLAIN(bool, isExternalFNEPeer); + /** + * @brief Flag indicating this connection is from an conventional peer. + */ + DECLARE_PROPERTY_PLAIN(bool, isConventionalPeer); + /** + * @brief Flag indicating this connection is from an SysView peer. + */ + DECLARE_PROPERTY_PLAIN(bool, isSysView); + + /** + * @brief Flag indicating this connection is from a neighbor FNE peer that is replica enabled. + */ + DECLARE_PROPERTY_PLAIN(bool, isPeerReplica); + + /** + * @brief JSON objecting containing peer configuration information. + */ + DECLARE_PROPERTY_PLAIN(json::object, config); + + private: + mutable std::mutex m_peerLockMtx; + }; +} // namespace network + +#endif // __FNE_PEER_CONNECTION_H__ diff --git a/src/fne/network/HAParameters.h b/src/fne/network/HAParameters.h index c3b68cc4..42137eff 100644 --- a/src/fne/network/HAParameters.h +++ b/src/fne/network/HAParameters.h @@ -11,8 +11,8 @@ * @file HAParameters.h * @ingroup fne_network */ -#if !defined(__HA_PARAMETERS__) -#define __HA_PARAMETERS__ +#if !defined(__HA_PARAMETERS_H__) +#define __HA_PARAMETERS_H__ #include "fne/Defines.h" @@ -76,4 +76,4 @@ namespace network }; } // namespace network -#endif // __HA_PARAMETERS__ +#endif // __HA_PARAMETERS_H__ diff --git a/src/fne/network/MasterTree.cpp b/src/fne/network/MasterTree.cpp new file mode 100644 index 00000000..49fb49ac --- /dev/null +++ b/src/fne/network/MasterTree.cpp @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Converged FNE Software + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "fne/Defines.h" +#include "common/Log.h" +#include "network/MasterTree.h" + +using namespace network; + +// --------------------------------------------------------------------------- +// Static Class Members +// --------------------------------------------------------------------------- + +std::unordered_map MasterTree::m_masterTrees = std::unordered_map(); + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the MasterTree class. */ + +MasterTree::MasterTree(uint32_t id, uint32_t masterId, MasterTree* parent) : + m_parent(parent), + m_children(), + m_id(id), + m_masterId(masterId) +{ + m_masterTrees[id] = this; + if (m_parent != nullptr) + m_parent->m_children.push_back(this); +} + +/* Finalizes a instance of the MasterTree class. */ + +MasterTree::~MasterTree() +{ + for (auto child : m_children) { + if (child != nullptr) { + delete child; + child = nullptr; + } + } + m_children.clear(); +} + +/* Find a peer master tree by peer ID. */ + +MasterTree* MasterTree::findByPeerID(const uint32_t peerId) +{ + auto it = m_masterTrees.find(peerId); + if (it != m_masterTrees.end()) { + return it->second; + } + + return nullptr; +} + +/* Find a peer master tree by master peer ID. */ + +MasterTree* MasterTree::findByMasterID(const uint32_t masterId) +{ + for (auto it : m_masterTrees) { + MasterTree* tree = it.second; + if (tree != nullptr) { + if (tree->masterId() == masterId) { + return tree; + } + } + } + + return nullptr; +} + +/* Count all children of a master tree node. */ + +uint32_t MasterTree::countChildren(MasterTree* node) +{ + if (node == nullptr) + return 0U; + if (node->m_children.size() == 0) + return 0U; + + uint32_t count = 0U; + for (auto child : node->m_children) { + ++count; + count += countChildren(child); + } + + return count; +} + +/* Erase a peer from the tree. */ + +void MasterTree::erasePeer(const uint32_t peerId) +{ + auto it = m_masterTrees.find(peerId); + if (it != m_masterTrees.end()) { + MasterTree* tree = it->second; + if (tree != nullptr) { + if (tree->m_parent != nullptr) { + auto& siblings = tree->m_parent->m_children; + siblings.erase(std::remove(siblings.begin(), siblings.end(), tree), siblings.end()); + } + + if (tree->hasChildren()) { + eraseChildren(tree); + } + + delete tree; + } + + m_masterTrees.erase(it); + } +} + +/* Helper to recursively serialize master tree node to JSON array. */ + +void MasterTree::serializeTree(MasterTree* node, json::array& jsonArray) +{ + if (node == nullptr) + return; + + json::object obj; + uint32_t id = node->id(); + obj["id"].set(id); + uint32_t masterId = node->masterId(); + obj["masterId"].set(masterId); + + json::array childArray; + for (auto child : node->m_children) { + serializeTree(child, childArray); + } + obj["children"].set(childArray); + + jsonArray.push_back(json::value(obj)); +} + +/* Helper to recursively deserialize master tree node from JSON array. */ + +void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std::vector* duplicatePeers) +{ + for (auto& v : jsonArray) { + if (!v.is()) + continue; + + json::object obj = v.get(); + if (!obj["id"].is()) + continue; + if (!obj["masterId"].is()) + continue; + if (!obj["children"].is()) + continue; + + uint32_t id = obj["id"].get(); + uint32_t masterId = obj["masterId"].get(); + + // check if this peer is already connected via another peer + MasterTree* tree = MasterTree::findByMasterID(masterId); + if (tree != nullptr) { + if (tree->id() != id) { + if (duplicatePeers != nullptr) + duplicatePeers->push_back(id); + continue; + } + } + + MasterTree* existingNode = findByPeerID(id); + if (existingNode == nullptr) + existingNode = new MasterTree(id, masterId, parent); + + json::array childArray = obj["children"].get(); + //LogDebugEx(LOG_NET, "MasterTree::deserializeTree()", "peerId = %u, masterId = %u, childrenCnt = %u", + // existingNode->id(), existingNode->masterId(), childArray.size()); + if (childArray.size() > 0U) + deserializeTree(childArray, existingNode, duplicatePeers); + } +} + +/* Debug helper to visualize the tree structure in the log. */ + +void MasterTree::visualizeTreeToLog(MasterTree* node, uint32_t level) +{ + if (node == nullptr) + return; + if (node->m_children.size() == 0) { + return; + } + + if (level == 0U) + LogInfoEx(LOG_NET, "Peer ID: %u, Master Peer ID: %u, Children: %u, IsRoot: %u", + node->id(), node->masterId(), node->m_children.size(), node->isRoot()); + + std::string indent; + for (uint32_t i = 0U; i < level; i++) { + indent += " "; + } + + for (auto child : node->m_children) { + LogInfoEx(LOG_NET, "%s- Peer ID: %u, Master Peer ID: %u, Children: %u, IsRoot: %u", + indent.c_str(), child->id(), child->masterId(), child->m_children.size(), child->isRoot()); + visualizeTreeToLog(child, level + 1U); + } +} + +// --------------------------------------------------------------------------- +// Private Static Class Members +// --------------------------------------------------------------------------- + +/* Erase all children of a master tree node. */ + +void MasterTree::eraseChildren(MasterTree* node) +{ + if (node == nullptr) + return; + + for (auto child : node->m_children) { + if (child != nullptr) { + eraseChildren(child); + delete child; + } + } + + node->m_children.clear(); +} diff --git a/src/fne/network/MasterTree.h b/src/fne/network/MasterTree.h new file mode 100644 index 00000000..7a9f087b --- /dev/null +++ b/src/fne/network/MasterTree.h @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Converged FNE Software + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file MasterTree.h + * @ingroup fne_network + * @file MasterTree.cpp + * @ingroup fne_network + */ +#if !defined(__MASTER_TREE_H__) +#define __MASTER_TREE_H__ + +#include "fne/Defines.h" +#include "common/network/json/json.h" + +#include +#include +#include + +namespace network +{ + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents an FNE spanning tree. + * @ingroup fne_network + * @remarks + * This class implements a extremely rudimentary spanning tree structure to represent + * the master FNE tree topology. + * + * Each MasterTree node represents a single FNE in the tree. The root node is the master FNE + * at the top of the tree. Each node contains a list of child nodes that are directly connected + * to it downstream. + * + * For example, consider the following tree structure: + * + * A + * / \ + * B C + * / \ \ + * D E F + * / \ + * G H + * + * In this example, A is the root node (master FNE), B and C are its children, + * D and E are children of B, F is a child of C, G is a child of D, and H is a child of F. + * + * Child nodes always send their data upstream to their parent node. The tree is always a top-down + * structure, with data flowing from the leaves up to the root. The root node does not have a parent. + * + * Root nodes can have multiple child nodes, and child nodes can have their own children, forming a hierarchical tree. + * Root nodes determine duplicate connections and enforce tree integrity. + * + * Each node in the tree assumes it is a root of its own subtree. For instance, B considers itself + * the root of the subtree containing B, D, E, and G. This allows for easy traversal and management of + * the tree structure. + */ + class HOST_SW_API MasterTree { + public: + auto operator=(MasterTree&) -> MasterTree& = delete; + auto operator=(MasterTree&&) -> MasterTree& = delete; + MasterTree(MasterTree&) = delete; + + /** + * @brief Initializes a new instance of the MasterTree class + * @param id Peer ID. + * @param parent Parent server tree node. + */ + MasterTree(uint32_t id, uint32_t masterId, MasterTree* parent); + /** + * @brief Finalizes a instance of the MasterTree class + */ + ~MasterTree(); + + /** + * @brief Flag indicating whether or not this server is a tree root. + * @return bool True, if server is the tree root, otherwise false. + */ + bool isRoot() const { return (m_parent == nullptr); } + /** + * @brief Flag indicating whether or not this server has child nodes. + * @return bool True, if server has child nodes, otherwise false. + */ + bool hasChildren() const { return !m_children.empty(); } + + /** + * @brief Find a peer master tree by peer ID. + * @param peerId Peer ID. + * @return MasterTree* Pointer to the MasterTree instance, or nullptr if not found. + */ + static MasterTree* findByPeerID(const uint32_t peerId); + /** + * @brief Find a peer master tree by master peer ID. + * @param masterId Master Peer ID. + * @return MasterTree* Pointer to the MasterTree instance, or nullptr if not found. + */ + static MasterTree* findByMasterID(const uint32_t masterId); + + /** + * @brief Count all children of a master tree node. + * @param node Pointer to MasterTree node. + * @return uint32_t Number of child nodes. + */ + static uint32_t countChildren(MasterTree* node); + + /** + * @brief Erase a peer from the tree. + * @param peerId Peer ID. + */ + static void erasePeer(const uint32_t peerId); + + /** + * @brief Helper to recursively serialize master tree node to JSON array. + * @param node Pointer to MasterTree node. + * @param jsonArray JSON array to write node to. + */ + static void serializeTree(MasterTree* node, json::array& jsonArray); + + /** + * @brief Helper to recursively deserialize master tree node from JSON array. + * @param jsonArray JSON array to read node from. + * @param parent Pointer to parent MasterTree node. + * @param duplicatePeers Pointer to vector to receive duplicate peer IDs found during deserialization. + */ + static void deserializeTree(json::array& jsonArray, MasterTree* parent, std::vector* duplicatePeers); + + /** + * @brief Helper to visualize the tree structure in the log. + * @param node Pointer to MasterTree node. + * @param level Current tree level. + */ + static void visualizeTreeToLog(MasterTree* node, uint32_t level = 0U); + + public: + MasterTree* m_parent; //!< Parent master tree node. (i.e. master FNE above this) + std::vector m_children; //!< Child master tree nodes. (i.e. peer FNEs below this) + + static std::unordered_map m_masterTrees; //!< Static map of all master trees by peer ID. + + /** + * @brief Peer ID. + */ + DECLARE_PROPERTY_PLAIN(uint32_t, id); + /** + * @brief Master Peer ID. + */ + DECLARE_PROPERTY_PLAIN(uint32_t, masterId); + + private: + /** + * @brief Helper to erase all children of a master tree node. + * @param node Pointer to MasterTree node. + */ + static void eraseChildren(MasterTree* node); + }; +} // namespace network + +#endif // __MASTER_TREE_H__ diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 6be2280e..ce935321 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -45,6 +45,7 @@ PeerNetwork::PeerNetwork(const std::string& address, uint16_t port, uint16_t loc m_p25Callback(nullptr), m_nxdnCallback(nullptr), m_analogCallback(nullptr), + m_masterPeerId(0U), m_pidLookup(nullptr), m_peerReplica(false), m_peerReplicaSavesACL(false), @@ -142,6 +143,48 @@ bool PeerNetwork::writePeerLinkPeers(json::array* peerList) return false; } +/* Writes a complete update of this CFNE's known master FNE tree upstream to the network. */ + +bool PeerNetwork::writeMasterTree(MasterTree* treeRoot) +{ + if (treeRoot == nullptr) + return false; + if (treeRoot->m_children.size() == 0) + return false; + + if (treeRoot->m_children.size() > 0) { + json::array jsonArray; + MasterTree::serializeTree(treeRoot, jsonArray); + + json::value v = json::value(jsonArray); + std::string json = std::string(v.serialize()); + + size_t len = json.length() + 9U; + DECLARE_CHAR_ARRAY(buffer, len); + + ::memcpy(buffer + 0U, TAG_PEER_REPLICA, 4U); + ::snprintf(buffer + 8U, json.length() + 1U, "%s", json.c_str()); + + PacketBuffer pkt(true, "Network Tree, Tree List"); + pkt.encode((uint8_t*)buffer, len); + + uint32_t streamId = createStreamId(); + LogInfoEx(LOG_NET, "PEER %u Network Tree, Tree List, blocks %u, streamId = %u", m_peerId, pkt.fragments.size(), streamId); + if (pkt.fragments.size() > 0U) { + for (auto frag : pkt.fragments) { + writeMaster({ NET_FUNC::NET_TREE, NET_SUBFUNC::NET_TREE_LIST }, + frag.second->data, FRAG_SIZE, RTP_END_OF_CALL_SEQ, streamId, false, true); + Thread::sleep(60U); // pace block transmission + } + } + + pkt.clear(); + return true; + } + + return false; +} + /* Writes a complete update of this CFNE's HA parameters to the network. */ bool PeerNetwork::writeHAParams(std::vector& haParams) @@ -398,6 +441,26 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco } break; + case NET_FUNC::NET_TREE: // Network Tree + { + switch (opcode.second) { + case NET_SUBFUNC::NET_TREE_DISC: // Network Tree Disconnect + { + uint32_t offendingPeerId = GET_UINT32(data, 6U); + LogWarning(LOG_NET, "PEER %u Network Tree Disconnect, requested from upstream master, posssible duplicate connection for PEER %u", m_peerId, offendingPeerId); + + if (m_netTreeDiscCallback != nullptr) { + m_netTreeDiscCallback(this, offendingPeerId); + } + } + break; + + default: + break; + } + } + break; + default: Utils::dump("Unknown opcode from the master", data, length); break; @@ -449,6 +512,7 @@ bool PeerNetwork::writeConfig() // Flags bool external = true; config["externalPeer"].set(external); // External FNE Neighbor Peer Marker + config["masterPeerId"].set(m_masterPeerId); // Master Peer ID config["software"].set(std::string(software)); // Software ID diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index 90f92de7..b710d8ac 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -21,6 +21,7 @@ #include "common/network/Network.h" #include "common/network/PacketBuffer.h" #include "common/ThreadPool.h" +#include "fne/network/MasterTree.h" #include "fne/network/HAParameters.h" #include @@ -87,6 +88,11 @@ namespace network */ ~PeerNetwork() override; + /** + * @brief Set the peer ID of this FNE's master. + * @param masterPeerId Master Peer ID. + */ + void setMasterPeerId(uint32_t masterPeerId) { m_masterPeerId = masterPeerId; } /** * @brief Sets the instances of the Peer List lookup tables. * @param pidLookup Peer List Lookup Table Instance @@ -125,12 +131,24 @@ namespace network */ void setAnalogCallback(std::function&& callback) { m_analogCallback = callback; } + /** + * @brief Helper to set the network tree disconnect callback. + * @param callback + */ + void setNetTreeDiscCallback(std::function&& callback) { m_netTreeDiscCallback = callback; } + /** * @brief Writes a complete update of this CFNE's active peer list to the network. * @param peerList List of active peers. * @returns bool True, if list was sent, otherwise false. */ bool writePeerLinkPeers(json::array* peerList); + /** + * @brief Writes a complete update of this CFNE's known master FNE tree upstream to the network. + * @param treeRoot Root of the master tree. + * @returns bool True, if list was sent, otherwise false. + */ + bool writeMasterTree(MasterTree* treeRoot); /** * @brief Writes a complete update of this CFNE's HA parameters to the network. * @param haParams List of HA parameters. @@ -150,6 +168,12 @@ namespace network */ void setPeerReplicationSaveACL(bool enabled) { m_peerReplicaSavesACL = enabled; } + /** + * @brief Gets the remote peer ID. + * @returns uint32_t Remote Peer ID. + */ + uint32_t getRemotePeerId() const { return m_remotePeerId; } + public: /** * @brief Flag indicating whether or not this peer network has a key response handler attached. @@ -178,6 +202,11 @@ namespace network */ std::function m_analogCallback; + /** + * @brief Network Tree Disconnect Callback. + */ + std::function m_netTreeDiscCallback; + /** * @brief User overrideable handler that allows user code to process network packets not handled by this class. * @param peerId Peer ID. @@ -198,6 +227,8 @@ namespace network bool writeConfig() override; private: + uint32_t m_masterPeerId; + lookups::PeerListLookup* m_pidLookup; bool m_peerReplica; bool m_peerReplicaSavesACL; From f0ea9ee2091df669c704a60a583ed58318c82349 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 22 Oct 2025 14:46:31 -0400 Subject: [PATCH 077/200] [EXPERIMENTAL] enhance detection for case where FNE A and FNE B are cross-peered to each other (dont do this); --- src/fne/HostFNE.cpp | 2 +- src/fne/network/DiagNetwork.cpp | 6 +++--- src/fne/network/FNENetwork.cpp | 28 +++++++++++++++++++++++----- src/fne/network/FNENetwork.h | 2 +- src/fne/network/PeerNetwork.cpp | 2 +- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index dd4c7b3a..a5a768c1 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -1099,6 +1099,6 @@ void HostFNE::processPeerAnalog(network::PeerNetwork* peerNetwork, const uint8_t void HostFNE::processNetworkTreeDisconnect(network::PeerNetwork* peerNetwork, const uint32_t offendingPeerId) { if (m_network != nullptr) { - m_network->processNetworkTreeDisconnect(offendingPeerId); + m_network->processNetworkTreeDisconnect(peerNetwork->getPeerId(), offendingPeerId); } } diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 835e112e..ea95909f 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -652,14 +652,14 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) std::vector duplicatePeers; MasterTree::deserializeTree(arr, network->m_fneTree, &duplicatePeers); - if (network->m_logSpanningTreeChanges) { - LogInfoEx(LOG_NET, "PEER %u (%s) Network Tree, Tree List, current tree:", peerId, connection->identWithQualifier().c_str()); + if (network->m_logSpanningTreeChanges && network->m_fneTree->hasChildren()) { + LogInfoEx(LOG_NET, "PEER %u (%s) Network Tree, Tree Change, current tree:", peerId, connection->identWithQualifier().c_str()); MasterTree::visualizeTreeToLog(network->m_fneTree); } if (duplicatePeers.size() > 0U) { for (auto dupPeerId : duplicatePeers) { - LogWarning(LOG_NET, "PEER %u (%s) Network Tree, Tree List, disconnecting duplicate peer connection for PEER %u to prevent network loop", + LogWarning(LOG_NET, "PEER %u (%s) Network Tree, Tree Change, disconnecting duplicate peer connection for PEER %u to prevent network loop", peerId, connection->identWithQualifier().c_str(), dupPeerId); network->writeTreeDisconnect(peerId, dupPeerId); } diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index d0de5b01..a956204e 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -390,7 +390,7 @@ void FNENetwork::processNetwork() /* Process network tree disconnect notification. */ -void FNENetwork::processNetworkTreeDisconnect(uint32_t offendingPeerId) +void FNENetwork::processNetworkTreeDisconnect(uint32_t peerId, uint32_t offendingPeerId) { if (m_status != NET_STAT_MST_RUNNING) { return; @@ -408,10 +408,28 @@ void FNENetwork::processNetworkTreeDisconnect(uint32_t offendingPeerId) connection->connectionState()); writePeerNAK(offendingPeerId, createStreamId(), TAG_REPEATER_CONFIG, NET_CONN_NAK_FNE_DUPLICATE_CONN); disconnectPeer(offendingPeerId, connection); + + if (m_logSpanningTreeChanges && m_fneTree->hasChildren()) { + LogInfoEx(LOG_NET, "Network Tree, Tree Change, current tree:"); + MasterTree::visualizeTreeToLog(m_fneTree); + } } else { LogError(LOG_NET, "Network Tree Disconnect, upstream master requested disconnect for PEER %u, but connection is null", offendingPeerId); } } else { + // is this perhaps a peer connection of ours? + if (m_host->m_peerNetworks.size() > 0) { + for (auto peer : m_host->m_peerNetworks) { + if (peer.second != nullptr) { + if (peer.second->getPeerId() == peerId) { + LogWarning(LOG_NET, "PEER %u, upstream master requested disconnect for our peer connection, duplicate connection dropped", peerId); + peer.second->close(); + return; + } + } + } + } + LogError(LOG_NET, "Network Tree Disconnect, upstream master requested disconnect for unknown PEER %u", offendingPeerId); } } @@ -1201,8 +1219,8 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) break; } else { new MasterTree(peerId, masterPeerId, network->m_fneTree); - if (network->m_logSpanningTreeChanges) { - LogInfoEx(LOG_NET, "PEER %u (%s) Network Tree, Tree List, current tree:", peerId, connection->identWithQualifier().c_str()); + if (network->m_logSpanningTreeChanges && network->m_fneTree->hasChildren()) { + LogInfoEx(LOG_NET, "PEER %u (%s) Network Tree, Tree Change, current tree:", peerId, connection->identWithQualifier().c_str()); MasterTree::visualizeTreeToLog(network->m_fneTree); } } @@ -1904,8 +1922,8 @@ void FNENetwork::erasePeer(uint32_t peerId) MasterTree::erasePeer(peerId); } - if (m_logSpanningTreeChanges) { - LogInfoEx(LOG_NET, "PEER %u Network Tree, Tree List, current tree:", peerId); + if (m_logSpanningTreeChanges && m_fneTree->hasChildren()) { + LogInfoEx(LOG_NET, "PEER %u Network Tree, Tree Change, current tree:", peerId); MasterTree::visualizeTreeToLog(m_fneTree); } } diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 78abf4ab..47d96f69 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -231,7 +231,7 @@ namespace network * @brief Process network tree disconnect notification. * @param offendingPeerId Offending Peer ID. */ - void processNetworkTreeDisconnect(uint32_t offendingPeerId); + void processNetworkTreeDisconnect(uint32_t peerId, uint32_t offendingPeerId); /** * @brief Updates the timer by the passed number of milliseconds. diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index ce935321..1e16ecdc 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -447,7 +447,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco case NET_SUBFUNC::NET_TREE_DISC: // Network Tree Disconnect { uint32_t offendingPeerId = GET_UINT32(data, 6U); - LogWarning(LOG_NET, "PEER %u Network Tree Disconnect, requested from upstream master, posssible duplicate connection for PEER %u", m_peerId, offendingPeerId); + LogWarning(LOG_NET, "PEER %u Network Tree Disconnect, requested from upstream master, possible duplicate connection for PEER %u", m_peerId, offendingPeerId); if (m_netTreeDiscCallback != nullptr) { m_netTreeDiscCallback(this, offendingPeerId); From cc8a683d02e3d213926308c17c5050f79fd8e912 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 22 Oct 2025 15:15:38 -0400 Subject: [PATCH 078/200] add REST API endpoint to fetch the master tree; --- src/fne/network/RESTAPI.cpp | 23 +++++++++++++++++++++++ src/fne/network/RESTAPI.h | 8 ++++++++ src/fne/network/RESTDefines.h | 2 ++ src/remote/RESTClientMain.cpp | 9 ++++++++- 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/fne/network/RESTAPI.cpp b/src/fne/network/RESTAPI.cpp index 1e527ca4..fc1e6437 100644 --- a/src/fne/network/RESTAPI.cpp +++ b/src/fne/network/RESTAPI.cpp @@ -17,6 +17,7 @@ #include "fne/network/callhandler/TagDMRData.h" #include "fne/network/callhandler/TagP25Data.h" #include "fne/network/RESTAPI.h" +#include "fne/network/MasterTree.h" #include "HostFNE.h" using namespace network; @@ -666,6 +667,8 @@ void RESTAPI::initializeEndpoints() m_dispatcher.match(FNE_GET_AFF_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetAffList, this)); + m_dispatcher.match(FNE_GET_MASTER_TREE).get(REST_API_BIND(RESTAPI::restAPI_GetMasterTree, this)); + /* ** Digital Mobile Radio */ @@ -1576,6 +1579,26 @@ void RESTAPI::restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, reply.payload(response); } +/* REST API endpoint; implements get master tree list request. */ + +void RESTAPI::restAPI_GetMasterTree(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +{ + if (!validateAuth(request, reply)) { + return; + } + + json::object response = json::object(); + setResponseDefaultStatus(response); + + json::array tree = json::array(); + if (m_network != nullptr) { + MasterTree::serializeTree(m_network->m_fneTree, tree); + } + + response["masterTree"].set(tree); + reply.payload(response); +} + /* ** Digital Mobile Radio */ diff --git a/src/fne/network/RESTAPI.h b/src/fne/network/RESTAPI.h index 03a923de..c106cb36 100644 --- a/src/fne/network/RESTAPI.h +++ b/src/fne/network/RESTAPI.h @@ -334,6 +334,14 @@ class HOST_SW_API RESTAPI : private Thread { */ void restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /** + * @brief REST API endpoint; implements get master tree list request. + * @param request HTTP request. + * @param reply HTTP reply. + * @param match HTTP request matcher. + */ + void restAPI_GetMasterTree(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /* ** Digital Mobile Radio */ diff --git a/src/fne/network/RESTDefines.h b/src/fne/network/RESTDefines.h index 66fe955b..3e5de8c2 100644 --- a/src/fne/network/RESTDefines.h +++ b/src/fne/network/RESTDefines.h @@ -56,4 +56,6 @@ #define FNE_GET_AFF_LIST "/report-affiliations" +#define FNE_GET_MASTER_TREE "/rerpot-master-tree" + #endif // __FNE_REST_DEFINES_H__ diff --git a/src/remote/RESTClientMain.cpp b/src/remote/RESTClientMain.cpp index be168f2b..99784e90 100644 --- a/src/remote/RESTClientMain.cpp +++ b/src/remote/RESTClientMain.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2023,2024,2025 Bryan Biedenkapp, N2PLL * */ #include "remote/RESTClient.h" @@ -53,6 +53,8 @@ #define RCD_FNE_SAVE_TGID_ACL "fne-tgid-commit" #define RCD_FNE_SAVE_PEER_ACL "fne-peer-commit" +#define RCD_FNE_GET_MASTERTREE "fne-master-tree" + #define RCD_MODE "mdm-mode" #define RCD_MODE_OPT_IDLE "idle" #define RCD_MODE_OPT_LCKOUT "lockout" @@ -220,6 +222,8 @@ void usage(const char* message, const char* arg) reply += " fne-tgid-commit Saves the current TGID ACL to permenant storage (Converged FNE only)\r\n"; reply += " fne-peer-commit Saves the current peer ACL to permenant storage (Converged FNE only)\r\n"; reply += "\r\n"; + reply += " fne-master-tree Retrieves the current master FNE tree (Converged FNE only)\r\n"; + reply += "\r\n"; reply += " mdm-mode Set current mode of host (idle, lockout, dmr, p25, nxdn)\r\n"; reply += " mdm-kill Causes the host to quit\r\n"; reply += " mdm-force-kill Causes the host to quit immediately\r\n"; @@ -916,6 +920,9 @@ int main(int argc, char** argv) else if (rcom == RCD_FNE_SAVE_PEER_ACL) { retCode = client->send(HTTP_GET, FNE_GET_PEER_COMMIT, json::object(), response); } + else if (rcom == RCD_FNE_GET_MASTERTREE) { + retCode = client->send(HTTP_GET, FNE_GET_MASTER_TREE, json::object(), response); + } else { args.clear(); LogError(LOG_REST, BAD_CMD_STR " (\"%s\")", rcom.c_str()); From d878d55d116a4a3a7fdf5835301c779d1748b0cc Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 22 Oct 2025 16:59:08 -0400 Subject: [PATCH 079/200] make sure master peer ID 0 doesnt ever happen; --- src/fne/network/FNENetwork.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index a956204e..61db008e 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1193,6 +1193,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogInfoEx(LOG_NET, "PEER %u >> Master Peer ID [%u]", peerId, masterPeerId); } + // master peer ID should never be zero for an external peer -- use the peer ID instead + if (masterPeerId == 0U) { + masterPeerId = peerId; + } + // check if the peer a peer replication participant lookups::PeerId peerEntry = network->m_peerListLookup->find(req->peerId); if (!peerEntry.peerDefault()) { From 11d0d46a80fd7c85baebe51d4ca18147ea89c7e2 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 22 Oct 2025 17:00:19 -0400 Subject: [PATCH 080/200] log condition where masterPeerId is 0 --- 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 61db008e..e83261cc 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1195,6 +1195,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // master peer ID should never be zero for an external peer -- use the peer ID instead if (masterPeerId == 0U) { + LogWarning(LOG_NET, "PEER %u is an external FNE neighbor peer but has not supplied a valid masterPeerId, using own peerId as masterPeerId (old FNE perhaps?)", peerId); masterPeerId = peerId; } From 30c211265711098e7e6cf876571d814fd20f4d3b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 22 Oct 2025 17:10:14 -0400 Subject: [PATCH 081/200] SysView peers announce themselves as an external FNE, but we do not consider them FNE peer links; --- src/fne/network/FNENetwork.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index e83261cc..2df334c6 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1179,6 +1179,14 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogInfoEx(LOG_NET, "PEER %u >> Software Version [%s]", peerId, software.c_str()); } + // is the peer reporting it is a SysView peer? + if (peerConfig["sysView"].is()) { + bool sysView = peerConfig["sysView"].get(); + connection->isSysView(sysView); + if (sysView) + LogInfoEx(LOG_NET, "PEER %u >> SysView Peer", peerId); + } + // is the peer reporting it is an external FNE neighbor peer? if (peerConfig["externalPeer"].is()) { bool external = peerConfig["externalPeer"].get(); @@ -1214,7 +1222,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } } - if (network->m_enableSpanningTree) { + if (network->m_enableSpanningTree && !connection->isSysView()) { // check if this peer is already connected via another peer MasterTree* tree = MasterTree::findByMasterID(masterPeerId); if (tree != nullptr) { @@ -1246,14 +1254,6 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } } - // is the peer reporting it is a SysView peer? - if (peerConfig["sysView"].is()) { - bool sysView = peerConfig["sysView"].get(); - connection->isSysView(sysView); - if (sysView) - LogInfoEx(LOG_NET, "PEER %u >> SysView Peer", peerId); - } - // setup the affiliations list for this peer std::stringstream peerName; peerName << "PEER " << peerId; From 24ec2d994438d4c4ca9842c6a804009fa5c2c8ee Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 22 Oct 2025 17:11:20 -0400 Subject: [PATCH 082/200] SysView masquarades its masterPeerId as itself; --- src/sysview/network/PeerNetwork.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sysview/network/PeerNetwork.cpp b/src/sysview/network/PeerNetwork.cpp index ce378515..2f281a4d 100644 --- a/src/sysview/network/PeerNetwork.cpp +++ b/src/sysview/network/PeerNetwork.cpp @@ -280,6 +280,7 @@ bool PeerNetwork::writeConfig() // Flags bool external = true; config["externalPeer"].set(external); // External Peer Marker + config["masterPeerId"].set(m_peerId); // Master Peer ID bool convPeer = true; config["conventionalPeer"].set(convPeer); // Conventional Peer Marker bool sysView = true; From c6ada3832607ae2f5287999400c72fea81f87a40 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 22 Oct 2025 17:34:36 -0400 Subject: [PATCH 083/200] instead of killing a peer connection instantly on a duplicate conn drop, increase retry time to 30 minutes and allow up to 3 duplicate conn failures before killing; --- src/common/network/Network.cpp | 34 +++++++++++++++++++++++++++++----- src/common/network/Network.h | 1 + 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index 3f2b485a..3dd65389 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -25,8 +25,12 @@ using namespace network; // Constants // --------------------------------------------------------------------------- +#define DEFAULT_RETRY_TIME 10U // 10 seconds +#define DUPLICATE_CONN_RETRY_TIME 1800U // 30 minutes + #define MAX_RETRY_BEFORE_RECONNECT 4U #define MAX_RETRY_HA_RECONNECT 2U +#define MAX_RETRY_DUPLICATE_CONN 3U #define MAX_SERVER_DIFF 360ULL // maximum difference in time between a server timestamp and local timestamp in milliseconds // --------------------------------------------------------------------------- @@ -57,9 +61,10 @@ Network::Network(const std::string& address, uint16_t port, uint16_t localPort, m_ridLookup(nullptr), m_tidLookup(nullptr), m_salt(nullptr), - m_retryTimer(1000U, 10U), + m_retryTimer(1000U, DEFAULT_RETRY_TIME), m_retryCount(0U), m_maxRetryCount(MAX_RETRY_BEFORE_RECONNECT), + m_duplicateConnCount(0U), m_timeoutTimer(1000U, MAX_PEER_PING_TIME), m_pktSeq(0U), m_loginStreamId(0U), @@ -220,6 +225,15 @@ void Network::clock(uint32_t ms) m_status = NET_STAT_WAITING_LOGIN; m_timeoutTimer.start(); } + + if (m_duplicateConnCount >= MAX_RETRY_DUPLICATE_CONN) { + LogError(LOG_NET, "PEER %u exceeded maximum duplicate connection retries, disabling network connection", m_peerId); + m_enabled = false; + m_duplicateConnCount = 0U; + m_retryTimer.stop(); + close(); + return; + } } m_retryTimer.start(); @@ -997,11 +1011,13 @@ void Network::clock(uint32_t ms) break; case NET_CONN_NAK_FNE_DUPLICATE_CONN: - LogWarning(LOG_NET, "PEER %u master NAK; duplicate connection from FNE, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); - m_status = NET_STAT_WAITING_CONNECT; - m_enabled = false; // duplicate connection give up stop trying to connect + LogWarning(LOG_NET, "PEER %u master NAK; duplicate connection to FNE, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); + m_status = NET_STAT_WAITING_LOGIN; + m_remotePeerId = 0U; + m_duplicateConnCount++; m_retryTimer.stop(); - close(); + m_retryTimer.setTimeout(DUPLICATE_CONN_RETRY_TIME); + m_retryTimer.start(); break; case NET_CONN_NAK_GENERAL_FAILURE: @@ -1063,8 +1079,11 @@ void Network::clock(uint32_t ms) m_status = NET_STAT_RUNNING; m_timeoutTimer.start(); + m_retryTimer.setTimeout(DEFAULT_RETRY_TIME); m_retryTimer.start(); + m_duplicateConnCount = 0U; + if (length > 6) { m_useAlternatePortForDiagnostics = (buffer[6U] & 0x80U) == 0x80U; if (m_useAlternatePortForDiagnostics) { @@ -1285,6 +1304,11 @@ bool Network::writeLogin() return false; } + // reset retry timer default timeout + if (m_retryTimer.isRunning()) + m_retryTimer.stop(); + m_retryTimer.setTimeout(DEFAULT_RETRY_TIME); + uint8_t buffer[8U]; ::memcpy(buffer + 0U, TAG_REPEATER_LOGIN, 4U); SET_UINT32(m_peerId, buffer, 4U); // Peer ID diff --git a/src/common/network/Network.h b/src/common/network/Network.h index c02b9818..df5f6c9d 100644 --- a/src/common/network/Network.h +++ b/src/common/network/Network.h @@ -318,6 +318,7 @@ namespace network Timer m_retryTimer; uint8_t m_retryCount; uint8_t m_maxRetryCount; + uint8_t m_duplicateConnCount; Timer m_timeoutTimer; uint32_t* m_rxDMRStreamId; From f0db1fc48bc0bf97dd426d7151da166e2366bcf8 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 22 Oct 2025 20:51:30 -0400 Subject: [PATCH 084/200] better handle duplicate connection disconnect NAKs; add REST API on FNE to force reset a upstream peer connection; --- src/common/network/Network.cpp | 59 ++++++++++++++++++++-------------- src/common/network/Network.h | 9 +++++- src/fne/network/RESTAPI.cpp | 40 +++++++++++++++++++++++ src/fne/network/RESTAPI.h | 7 ++++ src/fne/network/RESTDefines.h | 3 +- src/remote/RESTClientMain.cpp | 9 ++++++ 6 files changed, 101 insertions(+), 26 deletions(-) diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index 3dd65389..a094fbe2 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -26,11 +26,12 @@ using namespace network; // --------------------------------------------------------------------------- #define DEFAULT_RETRY_TIME 10U // 10 seconds -#define DUPLICATE_CONN_RETRY_TIME 1800U // 30 minutes +#define DUPLICATE_CONN_RETRY_TIME 3600U // 60 minutes #define MAX_RETRY_BEFORE_RECONNECT 4U #define MAX_RETRY_HA_RECONNECT 2U -#define MAX_RETRY_DUPLICATE_CONN 3U +#define MAX_RETRY_DUP_RECONNECT 2U + #define MAX_SERVER_DIFF 360ULL // maximum difference in time between a server timestamp and local timestamp in milliseconds // --------------------------------------------------------------------------- @@ -64,7 +65,7 @@ Network::Network(const std::string& address, uint16_t port, uint16_t localPort, m_retryTimer(1000U, DEFAULT_RETRY_TIME), m_retryCount(0U), m_maxRetryCount(MAX_RETRY_BEFORE_RECONNECT), - m_duplicateConnCount(0U), + m_flaggedDuplicateConn(false), m_timeoutTimer(1000U, MAX_PEER_PING_TIME), m_pktSeq(0U), m_loginStreamId(0U), @@ -202,6 +203,10 @@ void Network::clock(uint32_t ms) if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) { if (m_enabled) { if (m_retryCount > m_maxRetryCount) { + if (m_flaggedDuplicateConn) { + LogError(LOG_NET, "PEER %u exceeded maximum duplicate connection retries, increasing delay between connection attempts", m_peerId); + } + LogError(LOG_NET, "PEER %u connection to the master has timed out, retrying connection, remotePeerId = %u", m_peerId, m_remotePeerId); close(); @@ -225,15 +230,6 @@ void Network::clock(uint32_t ms) m_status = NET_STAT_WAITING_LOGIN; m_timeoutTimer.start(); } - - if (m_duplicateConnCount >= MAX_RETRY_DUPLICATE_CONN) { - LogError(LOG_NET, "PEER %u exceeded maximum duplicate connection retries, disabling network connection", m_peerId); - m_enabled = false; - m_duplicateConnCount = 0U; - m_retryTimer.stop(); - close(); - return; - } } m_retryTimer.start(); @@ -1012,13 +1008,12 @@ void Network::clock(uint32_t ms) case NET_CONN_NAK_FNE_DUPLICATE_CONN: LogWarning(LOG_NET, "PEER %u master NAK; duplicate connection to FNE, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); - m_status = NET_STAT_WAITING_LOGIN; + m_status = NET_STAT_WAITING_CONNECT; m_remotePeerId = 0U; - m_duplicateConnCount++; - m_retryTimer.stop(); - m_retryTimer.setTimeout(DUPLICATE_CONN_RETRY_TIME); + m_flaggedDuplicateConn = true; + m_maxRetryCount = MAX_RETRY_DUP_RECONNECT; m_retryTimer.start(); - break; + return; case NET_CONN_NAK_GENERAL_FAILURE: default: @@ -1027,7 +1022,8 @@ 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(); @@ -1082,8 +1078,6 @@ void Network::clock(uint32_t ms) m_retryTimer.setTimeout(DEFAULT_RETRY_TIME); m_retryTimer.start(); - m_duplicateConnCount = 0U; - if (length > 6) { m_useAlternatePortForDiagnostics = (buffer[6U] & 0x80U) == 0x80U; if (m_useAlternatePortForDiagnostics) { @@ -1137,6 +1131,13 @@ void Network::clock(uint32_t ms) uint64_t dt = (uint64_t)fabs((double)now - (double)serverNow); if (dt > MAX_SERVER_DIFF) LogWarning(LOG_NET, "PEER %u pong, time delay greater than %llums, now = %llu, server = %llu, dt = %llu", m_peerId, MAX_SERVER_DIFF, now, serverNow, dt); + + ++m_pingsReceived; + + // if we've been connected for at least 10 PING/PONG cycles and we're flagged duplicate connection, clear the flag + if (m_pingsReceived > 10U && m_flaggedDuplicateConn) { + m_flaggedDuplicateConn = false; + } } break; default: @@ -1193,12 +1194,9 @@ bool Network::open() LogMessage(LOG_NET, "PEER %u opening network", m_peerId); m_status = NET_STAT_WAITING_CONNECT; - m_timeoutTimer.start(); - m_retryTimer.start(); - m_retryCount = 0U; // are we rotating IPs for HA reconnect? - if (m_haIPs.size() > 0 && m_retryCount > 0U && + if (m_haIPs.size() > 0 && m_retryCount > 0U && !m_flaggedDuplicateConn && m_maxRetryCount == MAX_RETRY_HA_RECONNECT) { PeerHAIPEntry entry = m_haIPs[m_currentHAIP]; @@ -1214,6 +1212,12 @@ bool Network::open() m_port = entry.masterPort; } + m_timeoutTimer.start(); + m_retryTimer.start(); + m_retryCount = 0U; + + m_pingsReceived = 0U; + if (udp::Socket::lookup(m_address, m_port, m_addr, m_addrLen) != 0) { LogMessage(LOG_NET, "!!! Could not lookup the address of the master!"); return false; @@ -1239,6 +1243,13 @@ void Network::close() m_socket->close(); m_retryTimer.stop(); + if (m_flaggedDuplicateConn) { + // if we were flagged a duplicate connection, increase the retry time to avoid rapid reconnect attempts + m_retryTimer.setTimeout(DUPLICATE_CONN_RETRY_TIME); + } + else { + m_retryTimer.setTimeout(DEFAULT_RETRY_TIME); + } m_timeoutTimer.stop(); m_status = NET_STAT_WAITING_CONNECT; diff --git a/src/common/network/Network.h b/src/common/network/Network.h index df5f6c9d..042423f7 100644 --- a/src/common/network/Network.h +++ b/src/common/network/Network.h @@ -244,6 +244,11 @@ namespace network */ void enable(bool enabled); + /** + * @brief Helper to clear the duplicate connection flag. + */ + void clearDuplicateConnFlag() { m_flaggedDuplicateConn = false; } + /** * @brief Helper to set the peer connected callback. * @param callback @@ -318,9 +323,11 @@ namespace network Timer m_retryTimer; uint8_t m_retryCount; uint8_t m_maxRetryCount; - uint8_t m_duplicateConnCount; + bool m_flaggedDuplicateConn; Timer m_timeoutTimer; + uint32_t m_pingsReceived; + uint32_t* m_rxDMRStreamId; uint32_t m_rxP25StreamId; uint32_t m_rxNXDNStreamId; diff --git a/src/fne/network/RESTAPI.cpp b/src/fne/network/RESTAPI.cpp index fc1e6437..ab349c5e 100644 --- a/src/fne/network/RESTAPI.cpp +++ b/src/fne/network/RESTAPI.cpp @@ -639,6 +639,7 @@ void RESTAPI::initializeEndpoints() m_dispatcher.match(FNE_GET_PEER_QUERY).get(REST_API_BIND(RESTAPI::restAPI_GetPeerQuery, this)); m_dispatcher.match(FNE_GET_PEER_COUNT).get(REST_API_BIND(RESTAPI::restAPI_GetPeerCount, this)); m_dispatcher.match(FNE_PUT_PEER_RESET).put(REST_API_BIND(RESTAPI::restAPI_PutPeerReset, this)); + m_dispatcher.match(FNE_PUT_PEER_RESET_CONN).put(REST_API_BIND(RESTAPI::restAPI_PutPeerResetConn, this)); m_dispatcher.match(FNE_GET_RID_QUERY).get(REST_API_BIND(RESTAPI::restAPI_GetRIDQuery, this)); m_dispatcher.match(FNE_PUT_RID_ADD).put(REST_API_BIND(RESTAPI::restAPI_PutRIDAdd, this)); @@ -943,6 +944,45 @@ void RESTAPI::restAPI_PutPeerReset(const HTTPPayload& request, HTTPPayload& repl m_network->resetPeer(peerId); } +/* REST API endpoint; implements put reset upstream peer connection request.*/ + +void RESTAPI::restAPI_PutPeerResetConn(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +{ + if (!validateAuth(request, reply)) { + return; + } + + json::object req = json::object(); + if (!parseRequestBody(request, reply, req)) { + return; + } + + errorPayload(reply, "OK", HTTPPayload::OK); + + if (!req["peerId"].is()) { + errorPayload(reply, "peerId was not a valid integer"); + return; + } + + uint32_t peerId = req["peerId"].get(); + + if (m_host->m_peerNetworks.size() > 0) { + for (auto peer : m_host->m_peerNetworks) { + if (peer.second != nullptr) { + if (peer.second->getPeerId() == peerId) { + LogInfoEx(LOG_NET, "PEER %u, request to reset upstream peer connection", peerId); + + peer.second->clearDuplicateConnFlag(); + + peer.second->close(); + peer.second->open(); + break; + } + } + } + } +} + /* REST API endpoint; implements get radio ID query request. */ void RESTAPI::restAPI_GetRIDQuery(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) diff --git a/src/fne/network/RESTAPI.h b/src/fne/network/RESTAPI.h index c106cb36..8084855c 100644 --- a/src/fne/network/RESTAPI.h +++ b/src/fne/network/RESTAPI.h @@ -186,6 +186,13 @@ class HOST_SW_API RESTAPI : private Thread { * @param match HTTP request matcher. */ void restAPI_PutPeerReset(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /** + * @brief REST API endpoint; implements put reset upstream peer connection request. + * @param request HTTP request. + * @param reply HTTP reply. + * @param match HTTP request matcher. + */ + void restAPI_PutPeerResetConn(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); /** * @brief REST API endpoint; implements get radio ID query request. diff --git a/src/fne/network/RESTDefines.h b/src/fne/network/RESTDefines.h index 3e5de8c2..a95590f3 100644 --- a/src/fne/network/RESTDefines.h +++ b/src/fne/network/RESTDefines.h @@ -28,6 +28,7 @@ #define FNE_GET_PEER_QUERY "/peer/query" #define FNE_GET_PEER_COUNT "/peer/count" #define FNE_PUT_PEER_RESET "/peer/reset" +#define FNE_PUT_PEER_RESET_CONN "/peer/connreset" #define FNE_GET_RID_QUERY "/rid/query" #define FNE_PUT_RID_ADD "/rid/add" @@ -56,6 +57,6 @@ #define FNE_GET_AFF_LIST "/report-affiliations" -#define FNE_GET_MASTER_TREE "/rerpot-master-tree" +#define FNE_GET_MASTER_TREE "/report-master-tree" #endif // __FNE_REST_DEFINES_H__ diff --git a/src/remote/RESTClientMain.cpp b/src/remote/RESTClientMain.cpp index 99784e90..312720cc 100644 --- a/src/remote/RESTClientMain.cpp +++ b/src/remote/RESTClientMain.cpp @@ -48,6 +48,7 @@ #define RCD_FNE_PUT_RESETPEER "fne-reset-peer" #define RCD_FNE_PUT_PEER_ACL_ADD "fne-peer-acl-add" #define RCD_FNE_PUT_PEER_ACL_DELETE "fne-peer-acl-del" +#define RCD_FNE_PUT_PEER_RESET_CONN "fne-peer-reset-conn" #define RCD_FNE_SAVE_RID_ACL "fne-rid-commit" #define RCD_FNE_SAVE_TGID_ACL "fne-tgid-commit" @@ -217,6 +218,7 @@ void usage(const char* message, const char* arg) reply += " fne-reset-peer Forces the FNE to reset the connection of the given peer ID (Converged FNE only)\r\n"; reply += " fne-peer-acl-add Adds the specified peer ID to the FNE ACL tables (Converged FNE only)\r\n"; reply += " fne-peer-acl-del Removes the specified peer ID to the FNE ACL tables (Converged FNE only)\r\n"; + reply += " fne-peer-reset-conn Forces the FNE to reset a upstream peer connection of the given peer ID (Converged FNE only)\r\n"; reply += "\r\n"; reply += " fne-rid-commit Saves the current RID ACL to permenant storage (Converged FNE only)\r\n"; reply += " fne-tgid-commit Saves the current TGID ACL to permenant storage (Converged FNE only)\r\n"; @@ -897,6 +899,13 @@ int main(int argc, char** argv) retCode = client->send(HTTP_PUT, FNE_PUT_PEER_RESET, req, response); } + else if (rcom == RCD_FNE_PUT_PEER_RESET_CONN && argCnt >= 1U) { + uint32_t peerId = getArgUInt32(args, 0U); + json::object req = json::object(); + req["peerId"].set(peerId); + + retCode = client->send(HTTP_PUT, FNE_PUT_PEER_RESET_CONN, req, response); + } else if (rcom == RCD_FNE_PUT_PEER_ACL_ADD && argCnt >= 1U) { uint32_t peerId = getArgUInt32(args, 0U); json::object req = json::object(); From 3f04070420503822e96ea7474fa0c45eda3f42a1 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 22 Oct 2025 21:38:57 -0400 Subject: [PATCH 085/200] fix issue with purely ACL virtual FNEs not being able to replicate configuration further down the master tree; --- src/common/lookups/IdenTableLookup.cpp | 2 +- src/common/lookups/IdenTableLookup.h | 3 +- src/common/lookups/LookupTable.h | 3 +- src/common/lookups/PeerListLookup.cpp | 12 +-- src/common/lookups/PeerListLookup.h | 6 +- src/common/lookups/RadioIdLookup.cpp | 12 +-- src/common/lookups/RadioIdLookup.h | 6 +- src/common/lookups/TalkgroupRulesLookup.cpp | 10 +-- src/common/lookups/TalkgroupRulesLookup.h | 6 +- src/fne/HostFNE.cpp | 10 +++ src/fne/HostFNE.h | 5 ++ src/fne/network/FNENetwork.cpp | 83 ++++++++++++++++++--- src/fne/network/FNENetwork.h | 8 ++ src/fne/network/PeerNetwork.cpp | 10 ++- src/fne/network/PeerNetwork.h | 9 +++ 15 files changed, 148 insertions(+), 37 deletions(-) diff --git a/src/common/lookups/IdenTableLookup.cpp b/src/common/lookups/IdenTableLookup.cpp index 50bb082e..40427a58 100644 --- a/src/common/lookups/IdenTableLookup.cpp +++ b/src/common/lookups/IdenTableLookup.cpp @@ -163,7 +163,7 @@ bool IdenTableLookup::load() /* Saves the table to the passed lookup table file. */ -bool IdenTableLookup::save() +bool IdenTableLookup::save(bool quiet) { return false; } \ No newline at end of file diff --git a/src/common/lookups/IdenTableLookup.h b/src/common/lookups/IdenTableLookup.h index 68fd1080..f9752dc7 100644 --- a/src/common/lookups/IdenTableLookup.h +++ b/src/common/lookups/IdenTableLookup.h @@ -154,9 +154,10 @@ namespace lookups /** * @brief Saves the table to the passed lookup table file. + * @param quiet Disable logging during save operation. * @returns bool True, if lookup table was saved, otherwise false. */ - bool save() override; + bool save(bool quiet = false) override; private: static std::mutex m_mutex; diff --git a/src/common/lookups/LookupTable.h b/src/common/lookups/LookupTable.h index 2a80d90b..ecd05612 100644 --- a/src/common/lookups/LookupTable.h +++ b/src/common/lookups/LookupTable.h @@ -199,9 +199,10 @@ namespace lookups /** * @brief Saves the table from the lookup table in memory. + * @param quiet Disable logging during save operation. * @returns bool True, if lookup table was saved, otherwise false. */ - virtual bool save() = 0; + virtual bool save(bool quiet = false) = 0; }; } // namespace lookups diff --git a/src/common/lookups/PeerListLookup.cpp b/src/common/lookups/PeerListLookup.cpp index 8af24edd..4e7affce 100644 --- a/src/common/lookups/PeerListLookup.cpp +++ b/src/common/lookups/PeerListLookup.cpp @@ -123,9 +123,9 @@ PeerId PeerListLookup::find(uint32_t id) /* Commit the table. */ -void PeerListLookup::commit() +void PeerListLookup::commit(bool quiet) { - save(); + save(quiet); } /* Gets whether the lookup is enabled. */ @@ -273,7 +273,7 @@ bool PeerListLookup::load() /* Saves the table to the passed lookup table file. */ -bool PeerListLookup::save() +bool PeerListLookup::save(bool quiet) { if (m_filename.empty()) { return false; @@ -285,7 +285,8 @@ bool PeerListLookup::save() return false; } - LogMessage(LOG_HOST, "Saving peer lookup file to %s", m_filename.c_str()); + if (!quiet) + LogMessage(LOG_HOST, "Saving peer lookup file to %s", m_filename.c_str()); // Counter for lines written unsigned int lines = 0; @@ -352,7 +353,8 @@ bool PeerListLookup::save() if (lines != m_table.size()) return false; - LogInfoEx(LOG_HOST, "Saved %u entries to lookup table file %s", lines, m_filename.c_str()); + if (!quiet) + LogInfoEx(LOG_HOST, "Saved %u entries to lookup table file %s", lines, m_filename.c_str()); return true; } diff --git a/src/common/lookups/PeerListLookup.h b/src/common/lookups/PeerListLookup.h index 65130f22..48f09383 100644 --- a/src/common/lookups/PeerListLookup.h +++ b/src/common/lookups/PeerListLookup.h @@ -186,8 +186,9 @@ namespace lookups /** * @brief Commit the table. + * @param quiet Disable logging during save operation. */ - void commit(); + void commit(bool quiet = false); /** * @brief Gets whether the lookup is enabled. @@ -236,9 +237,10 @@ namespace lookups /** * @brief Saves the table to the passed lookup table file. + * @param quiet Disable logging during save operation. * @return True, if lookup table was saved, otherwise false. */ - bool save() override; + bool save(bool quiet = false) override; private: static std::mutex m_mutex; //!< Mutex used for change locking. diff --git a/src/common/lookups/RadioIdLookup.cpp b/src/common/lookups/RadioIdLookup.cpp index 8cb5fc12..06317b62 100644 --- a/src/common/lookups/RadioIdLookup.cpp +++ b/src/common/lookups/RadioIdLookup.cpp @@ -148,9 +148,9 @@ RadioId RadioIdLookup::find(uint32_t id) /* Saves loaded talkgroup rules. */ -void RadioIdLookup::commit() +void RadioIdLookup::commit(bool quiet) { - save(); + save(quiet); } /* Flag indicating whether radio ID access control is enabled or not. */ @@ -241,7 +241,7 @@ bool RadioIdLookup::load() /* Saves the table to the passed lookup table file. */ -bool RadioIdLookup::save() +bool RadioIdLookup::save(bool quiet) { if (m_filename.empty()) { return false; @@ -253,7 +253,8 @@ bool RadioIdLookup::save() return false; } - LogMessage(LOG_HOST, "Saving RID lookup file to %s", m_filename.c_str()); + if (!quiet) + LogMessage(LOG_HOST, "Saving RID lookup file to %s", m_filename.c_str()); // Counter for lines written unsigned int lines = 0; @@ -296,7 +297,8 @@ bool RadioIdLookup::save() if (lines != m_table.size()) return false; - LogInfoEx(LOG_HOST, "Saved %u entries to lookup table file %s", lines, m_filename.c_str()); + if (!quiet) + LogInfoEx(LOG_HOST, "Saved %u entries to lookup table file %s", lines, m_filename.c_str()); return true; } diff --git a/src/common/lookups/RadioIdLookup.h b/src/common/lookups/RadioIdLookup.h index 167f41a2..cc4ea191 100644 --- a/src/common/lookups/RadioIdLookup.h +++ b/src/common/lookups/RadioIdLookup.h @@ -184,8 +184,9 @@ namespace lookups /** * @brief Saves loaded radio ID lookups. + * @param quiet Disable logging during save operation. */ - void commit(); + void commit(bool quiet = false); /** * @brief Flag indicating whether radio ID access control is enabled or not. @@ -203,9 +204,10 @@ namespace lookups /** * @brief Saves the table to the passed lookup table file. + * @param quiet Disable logging during save operation. * @return True, if lookup table was saved, otherwise false. */ - bool save() override; + bool save(bool quiet = false) override; private: static std::mutex m_mutex; //!< Mutex used for change locking. diff --git a/src/common/lookups/TalkgroupRulesLookup.cpp b/src/common/lookups/TalkgroupRulesLookup.cpp index 5641f8b1..512bed31 100644 --- a/src/common/lookups/TalkgroupRulesLookup.cpp +++ b/src/common/lookups/TalkgroupRulesLookup.cpp @@ -291,9 +291,9 @@ TalkgroupRuleGroupVoice TalkgroupRulesLookup::findByRewrite(uint32_t peerId, uin /* Saves loaded talkgroup rules. */ -bool TalkgroupRulesLookup::commit() +bool TalkgroupRulesLookup::commit(bool quiet) { - return save(); + return save(quiet); } /* Flag indicating whether talkgroup ID access control is enabled or not. */ @@ -383,7 +383,7 @@ bool TalkgroupRulesLookup::load() /* Saves the table to the passed lookup table file. */ -bool TalkgroupRulesLookup::save() +bool TalkgroupRulesLookup::save(bool quiet) { // Make sure file is valid if (m_rulesFile.length() <= 0) { @@ -414,9 +414,9 @@ bool TalkgroupRulesLookup::save() } try { - LogMessage(LOG_HOST, "Saving talkgroup rules file to %s", m_rulesFile.c_str()); + if (!quiet) + LogMessage(LOG_HOST, "Saving talkgroup rules file to %s", m_rulesFile.c_str()); yaml::Serialize(newRules, m_rulesFile.c_str()); - LogDebug(LOG_HOST, "Saved TGID config file to %s", m_rulesFile.c_str()); } catch (yaml::OperationException const& e) { LogError(LOG_HOST, "Cannot save the talkgroup rules lookup file - %s (%s)", m_rulesFile.c_str(), e.message()); diff --git a/src/common/lookups/TalkgroupRulesLookup.h b/src/common/lookups/TalkgroupRulesLookup.h index 9cae89d0..d981a710 100644 --- a/src/common/lookups/TalkgroupRulesLookup.h +++ b/src/common/lookups/TalkgroupRulesLookup.h @@ -609,8 +609,9 @@ namespace lookups /** * @brief Saves loaded talkgroup rules. + * @param quiet Disable logging during save operation. */ - bool commit(); + bool commit(bool quiet = false); /** * @brief Flag indicating whether talkgroup ID access control is enabled or not. @@ -653,9 +654,10 @@ namespace lookups bool load(); /** * @brief Saves the table to the passed lookup table file. + * @param quiet Disable logging during save operation. * @return True, if lookup table was saved, otherwise false. */ - bool save(); + bool save(bool quiet = false); public: /** diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index a5a768c1..ef6ce929 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -876,6 +876,7 @@ bool HostFNE::createPeerNetworks() network->setAnalogCallback(std::bind(&HostFNE::processPeerAnalog, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); network->setNetTreeDiscCallback(std::bind(&HostFNE::processNetworkTreeDisconnect, this, std::placeholders::_1, std::placeholders::_2)); + network->setNotifyPeerReplicaCallback(std::bind(&HostFNE::processPeerReplicaNotify, this, std::placeholders::_1)); network->enable(enabled); if (enabled) { @@ -1102,3 +1103,12 @@ void HostFNE::processNetworkTreeDisconnect(network::PeerNetwork* peerNetwork, co m_network->processNetworkTreeDisconnect(peerNetwork->getPeerId(), offendingPeerId); } } + +/* Processes network tree disconnect notification. */ + +void HostFNE::processPeerReplicaNotify(network::PeerNetwork* peerNetwork) +{ + if (m_network != nullptr) { + m_network->setPeerReplica(true); + } +} diff --git a/src/fne/HostFNE.h b/src/fne/HostFNE.h index 5e93a31e..ce9d21dc 100644 --- a/src/fne/HostFNE.h +++ b/src/fne/HostFNE.h @@ -221,6 +221,11 @@ class HOST_SW_API HostFNE { * @param offendingPeerId Offending peer ID. */ void processNetworkTreeDisconnect(network::PeerNetwork* peerNetwork, const uint32_t offendingPeerId); + /** + * @brief Processes peer replica notification. + * @param peerNetwork Peer network instance. + */ + void processPeerReplicaNotify(network::PeerNetwork* peerNetwork); }; #endif // __HOST_FNE_H__ diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 2df334c6..f684bf3c 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -31,6 +31,7 @@ using namespace compress; #include #include +#include #include #include #include @@ -75,6 +76,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_address(address), m_port(port), m_password(password), + m_isPeerReplica(false), m_dmrEnabled(dmr), m_p25Enabled(p25), m_nxdnEnabled(nxdn), @@ -2018,6 +2020,17 @@ bool FNENetwork::resetPeer(uint32_t peerId) return false; } +/* Helper to set the master is peer replica flag. */ + +void FNENetwork::setPeerReplica(bool peerReplica) +{ + if (!m_isPeerReplica && peerReplica) { + LogInfoEx(LOG_NET, "MASTER set to peer replica, receiving ACL updates from upstream master"); + } + + m_isPeerReplica = peerReplica; +} + /* Helper to resolve the peer ID to its identity string. */ std::string FNENetwork::resolvePeerIdentity(uint32_t peerId) @@ -2133,14 +2146,27 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sen if (sendISSI) { FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { - std::string filename = m_ridLookup->filename(); - if (filename.empty()) { - return; + // save out radio ID table to disk + std::string tempFile; + if (m_isPeerReplica) { + 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); + + tempFile = s.str(); + std::string origFile = m_ridLookup->filename(); + m_ridLookup->filename(tempFile); + m_ridLookup->commit(true); + m_ridLookup->filename(origFile); + } else { + tempFile = m_ridLookup->filename(); } // read entire file into string buffer std::stringstream b; - std::ifstream stream(filename); + std::ifstream stream(tempFile); if (stream.is_open()) { while (stream.peek() != EOF) { b << (char)stream.get(); @@ -2149,6 +2175,9 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sen stream.close(); } + if (m_isPeerReplica) + ::remove(tempFile.c_str()); + // convert to a byte array uint32_t len = b.str().size(); DECLARE_UINT8_ARRAY(buffer, len); @@ -2319,14 +2348,26 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendISSI) if (sendISSI) { FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { - std::string filename = m_tidLookup->filename(); - if (filename.empty()) { - return; + std::string tempFile; + if (m_isPeerReplica) { + 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); + + tempFile = s.str(); + std::string origFile = m_tidLookup->filename(); + m_tidLookup->filename(tempFile); + m_tidLookup->commit(true); + m_tidLookup->filename(origFile); + } else { + tempFile = m_tidLookup->filename(); } // read entire file into string buffer std::stringstream b; - std::ifstream stream(filename); + std::ifstream stream(tempFile); if (stream.is_open()) { while (stream.peek() != EOF) { b << (char)stream.get(); @@ -2335,6 +2376,9 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendISSI) stream.close(); } + if (m_isPeerReplica) + ::remove(tempFile.c_str()); + // convert to a byte array uint32_t len = b.str().size(); DECLARE_UINT8_ARRAY(buffer, len); @@ -2496,14 +2540,26 @@ void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) // sending REPL style RID list to external peers FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { - std::string filename = m_peerListLookup->filename(); - if (filename.empty()) { - return; + std::string tempFile; + if (m_isPeerReplica) { + 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); + + tempFile = s.str(); + std::string origFile = m_peerListLookup->filename(); + m_peerListLookup->filename(tempFile); + m_peerListLookup->commit(true); + m_peerListLookup->filename(origFile); + } else { + tempFile = m_peerListLookup->filename(); } // read entire file into string buffer std::stringstream b; - std::ifstream stream(filename); + std::ifstream stream(tempFile); if (stream.is_open()) { while (stream.peek() != EOF) { b << (char)stream.get(); @@ -2512,6 +2568,9 @@ void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) stream.close(); } + if (m_isPeerReplica) + ::remove(tempFile.c_str()); + // convert to a byte array uint32_t len = b.str().size(); DECLARE_UINT8_ARRAY(buffer, len); diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 47d96f69..038a8588 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -265,6 +265,12 @@ namespace network */ bool resetPeer(uint32_t peerId); + /** + * @brief Helper to set the master is peer replica flag. + * @param peerReplica Flag indicating the master is a peer replica. + */ + void setPeerReplica(bool peerReplica); + private: friend class DiagNetwork; friend class callhandler::TagDMRData; @@ -289,6 +295,8 @@ namespace network std::string m_password; + bool m_isPeerReplica; + bool m_dmrEnabled; bool m_p25Enabled; bool m_nxdnEnabled; diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 1e16ecdc..82795ff9 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -45,6 +45,8 @@ PeerNetwork::PeerNetwork(const std::string& address, uint16_t port, uint16_t loc m_p25Callback(nullptr), m_nxdnCallback(nullptr), m_analogCallback(nullptr), + m_netTreeDiscCallback(nullptr), + m_peerReplicaCallback(nullptr), m_masterPeerId(0U), m_pidLookup(nullptr), m_peerReplica(false), @@ -312,6 +314,8 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco // flag this peer as replica enabled m_peerReplica = true; + if (m_peerReplicaCallback != nullptr) + m_peerReplicaCallback(this); // cleanup temporary file ::remove(filename.c_str()); @@ -368,7 +372,9 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco m_ridLookup->reload(); // flag this peer as replica enabled - m_peerReplica= true; + m_peerReplica = true; + if (m_peerReplicaCallback != nullptr) + m_peerReplicaCallback(this); // cleanup temporary file ::remove(filename.c_str()); @@ -426,6 +432,8 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco // flag this peer as replica enabled m_peerReplica = true; + if (m_peerReplicaCallback != nullptr) + m_peerReplicaCallback(this); // cleanup temporary file ::remove(filename.c_str()); diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index b710d8ac..7a8b96d3 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -136,6 +136,11 @@ namespace network * @param callback */ void setNetTreeDiscCallback(std::function&& callback) { m_netTreeDiscCallback = callback; } + /** + * @brief Helper to set the peer replica notification callback. + * @param callback + */ + void setNotifyPeerReplicaCallback(std::function&& callback) { m_peerReplicaCallback = callback; } /** * @brief Writes a complete update of this CFNE's active peer list to the network. @@ -206,6 +211,10 @@ namespace network * @brief Network Tree Disconnect Callback. */ std::function m_netTreeDiscCallback; + /** + * @brief Peer Replica Notification Callback. + */ + std::function m_peerReplicaCallback; /** * @brief User overrideable handler that allows user code to process network packets not handled by this class. From 1b9f70f5c274508401d02e04e41c50a83a71414c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 23 Oct 2025 10:15:35 -0400 Subject: [PATCH 086/200] deprecate the "external FNE" terming for upstream FNE connections and instead call them "neighbor FNE"; --- configs/fne-config.example.yml | 4 +- src/fne/network/DiagNetwork.cpp | 8 +- src/fne/network/FNENetwork.cpp | 79 +++++++++------- src/fne/network/FNENetwork.h | 16 ++-- src/fne/network/FNEPeerConnection.h | 10 +-- src/fne/network/PeerNetwork.cpp | 4 + src/fne/network/callhandler/TagAnalogData.cpp | 44 ++++----- src/fne/network/callhandler/TagAnalogData.h | 8 +- src/fne/network/callhandler/TagDMRData.cpp | 78 ++++++++-------- src/fne/network/callhandler/TagDMRData.h | 8 +- src/fne/network/callhandler/TagNXDNData.cpp | 72 +++++++-------- src/fne/network/callhandler/TagNXDNData.h | 8 +- src/fne/network/callhandler/TagP25Data.cpp | 90 +++++++++---------- src/fne/network/callhandler/TagP25Data.h | 12 +-- .../callhandler/packetdata/DMRPacketData.cpp | 24 ++--- .../callhandler/packetdata/DMRPacketData.h | 4 +- .../callhandler/packetdata/P25PacketData.cpp | 14 +-- .../callhandler/packetdata/P25PacketData.h | 6 +- 18 files changed, 253 insertions(+), 236 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index de636e32..c272e503 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -31,6 +31,7 @@ log: # # Master +# (This is the endpoint that downstream peers connect to for this FNE instance.) # master: # Network Peer ID @@ -203,7 +204,8 @@ master: time: 30 # -# FNE Neighbor Peers +# Upstream FNE Neighbor Peering +# (This is the list of connections to upstream FNEs this FNE instance should be connected to.) # peers: - name: EXAMPLEPEER diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index ea95909f..10508b81 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -210,7 +210,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_peers.find(req->rtpHeader.getSSRC()) != network->m_peers.end()) { FNEPeerConnection* connection = network->m_peers[req->rtpHeader.getSSRC()]; if (connection != nullptr) { - if (connection->isExternalFNEPeer() && connection->isPeerReplica()) { + if (connection->isNeighborFNEPeer() && connection->isPeerReplica()) { validPeerId = true; pktPeerId = req->rtpHeader.getSSRC(); } @@ -391,7 +391,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); // validate peer (simple validation really) - if (connection->connected() && connection->address() == ip && connection->isExternalFNEPeer() && + if (connection->connected() && connection->address() == ip && connection->isNeighborFNEPeer() && connection->isPeerReplica()) { DECLARE_UINT8_ARRAY(rawPayload, req->length); ::memcpy(rawPayload, req->buffer, req->length); @@ -492,7 +492,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); // validate peer (simple validation really) - if (connection->connected() && connection->address() == ip && connection->isExternalFNEPeer() && + if (connection->connected() && connection->address() == ip && connection->isNeighborFNEPeer() && connection->isPeerReplica()) { DECLARE_UINT8_ARRAY(rawPayload, req->length); ::memcpy(rawPayload, req->buffer, req->length); @@ -577,7 +577,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); // validate peer (simple validation really) - if (connection->connected() && connection->address() == ip && connection->isExternalFNEPeer()) { + if (connection->connected() && connection->address() == ip && connection->isNeighborFNEPeer()) { DECLARE_UINT8_ARRAY(rawPayload, req->length); ::memcpy(rawPayload, req->buffer, req->length); diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index f684bf3c..691f5f7e 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -199,7 +199,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) m_logSpanningTreeChanges = conf["logSpanningTreeChanges"].as(false); - // always force disable ADJ_STS_BCAST to external peers if the all option + // always force disable ADJ_STS_BCAST to neighbor FNE peers if the all option // is enabled if (m_disallowAdjStsBcast) { m_disallowExtAdjStsBcast = true; @@ -282,7 +282,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) } LogInfo(" Disable Packet Data: %s", m_disablePacketData ? "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(" Disable P25 ADJ_STS_BCAST to neighbor peers: %s", m_disallowExtAdjStsBcast ? "yes" : "no"); LogInfo(" Disable P25 TDULC call termination broadcasts to any peers: %s", m_disallowCallTerm ? "yes" : "no"); LogInfo(" Allow conventional sites to override affiliation and receive all traffic: %s", m_allowConvSiteAffOverride ? "yes" : "no"); LogInfo(" Enable In-Call Control: %s", m_enableInCallCtrl ? "yes" : "no"); @@ -462,7 +462,7 @@ void FNENetwork::clock(uint32_t ms) FNEPeerConnection* connection = peer.second; if (connection != nullptr) { uint64_t dt = 0U; - if (connection->isExternalFNEPeer() || connection->isPeerReplica()) + if (connection->isNeighborFNEPeer() || connection->isPeerReplica()) dt = connection->lastPing() + ((m_host->m_pingTime * 1000) * (m_host->m_maxMissedPings * 2U)); else dt = connection->lastPing() + ((m_host->m_pingTime * 1000) * m_host->m_maxMissedPings); @@ -475,9 +475,9 @@ void FNENetwork::clock(uint32_t ms) connection->connected(false); connection->connectionState(NET_STAT_INVALID); - // if the connection was an external FNE neighbor peer or a peer replica -- be noisy about a possible + // if the connection was an downstream FNE neighbor peer or a peer replica -- be noisy about a possible // netsplit - if (connection->isExternalFNEPeer() || connection->isPeerReplica()) { + if (connection->isNeighborFNEPeer() || connection->isPeerReplica()) { for (uint8_t i = 0U; i < 3U; i++) LogWarning(LOG_NET, "PEER %u (%s) downstream netsplit, dt = %u, now = %u", id, connection->identWithQualifier().c_str(), dt, now); @@ -494,7 +494,7 @@ void FNENetwork::clock(uint32_t ms) disconnectPeer(peerId, connection); } - // send peer updates to external FNE peers + // send peer updates to neighbor FNE peers if (m_host->m_peerNetworks.size() > 0) { for (auto peer : m_host->m_peerNetworks) { if (peer.second != nullptr) { @@ -1189,12 +1189,16 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogInfoEx(LOG_NET, "PEER %u >> SysView Peer", peerId); } - // is the peer reporting it is an external FNE neighbor peer? + // is the peer reporting it is an downstream FNE neighbor peer? + /* + ** bryanb: don't change externalPeer to neighborPeer -- this will break backward + ** compat with older FNE versions (we're stuck with this naming :() + */ if (peerConfig["externalPeer"].is()) { - bool external = peerConfig["externalPeer"].get(); - connection->isExternalFNEPeer(external); - if (external) - LogInfoEx(LOG_NET, "PEER %u >> External FNE Neighbor Peer", peerId); + bool neighbor = peerConfig["externalPeer"].get(); + connection->isNeighborFNEPeer(neighbor); + if (neighbor) + LogInfoEx(LOG_NET, "PEER %u >> Downstream Neighbor FNE Peer", peerId); uint32_t masterPeerId = 0U; if (peerConfig["masterPeerId"].is()) { @@ -1203,9 +1207,9 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogInfoEx(LOG_NET, "PEER %u >> Master Peer ID [%u]", peerId, masterPeerId); } - // master peer ID should never be zero for an external peer -- use the peer ID instead + // master peer ID should never be zero for an neighbor peer -- use the peer ID instead if (masterPeerId == 0U) { - LogWarning(LOG_NET, "PEER %u is an external FNE neighbor peer but has not supplied a valid masterPeerId, using own peerId as masterPeerId (old FNE perhaps?)", peerId); + LogWarning(LOG_NET, "PEER %u reports to be a downstream FNE neighbor peer but has not supplied a valid masterPeerId, using own peerId as masterPeerId (old FNE perhaps?)", peerId); masterPeerId = peerId; } @@ -1215,7 +1219,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (peerEntry.peerReplica()) { if (network->m_host->m_useAlternatePortForDiagnostics) { connection->isPeerReplica(true); - if (external) + if (neighbor) LogInfoEx(LOG_NET, "PEER %u >> Participates in Peer Replication", peerId); } else { LogError(LOG_NET, "PEER %u, Peer replication operations *require* the alternate diagnostics port option to be enabled.", peerId); @@ -1881,11 +1885,11 @@ void FNENetwork::disconnectPeer(uint32_t peerId, FNEPeerConnection* connection) void FNENetwork::erasePeer(uint32_t peerId) { - bool isExternalFNEPeer = false; + bool neighborFNE = false; { auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; }); if (it != m_peers.end()) { - isExternalFNEPeer = it->second->isExternalFNEPeer(); + neighborFNE = it->second->isNeighborFNEPeer(); m_peers.erase(peerId); } } @@ -1914,7 +1918,7 @@ void FNENetwork::erasePeer(uint32_t peerId) } } - if (isExternalFNEPeer && m_enableSpanningTree) { + if (neighborFNE && m_enableSpanningTree) { // erase this peer from the master tree MasterTree* tree = MasterTree::findByPeerID(peerId); if (tree != nullptr) { @@ -2020,15 +2024,22 @@ bool FNENetwork::resetPeer(uint32_t peerId) return false; } -/* Helper to set the master is peer replica flag. */ +/* Helper to set the master is upstream peer replica flag. */ void FNENetwork::setPeerReplica(bool peerReplica) { if (!m_isPeerReplica && peerReplica) { - LogInfoEx(LOG_NET, "MASTER set to peer replica, receiving ACL updates from upstream master"); + LogInfoEx(LOG_NET, "MASTER set to upstream peer replica, receiving ACL updates from upstream master"); } m_isPeerReplica = peerReplica; + + // be very noisy about being a peer replica and having multiple upstream peers + if (m_isPeerReplica) { + if (m_host->m_peerNetworks.size() > 1) { + LogWarning(LOG_NET, "We are a upstream peer replica, and have multiple upstream peers? This is a bad idea. Peer Replica FNEs should have a single upstream peer connection."); + } + } } /* Helper to resolve the peer ID to its identity string. */ @@ -2106,9 +2117,9 @@ void FNENetwork::taskMetadataUpdate(MetadataUpdateRequest* req) connection->lock(); uint32_t streamId = network->createStreamId(); - // if the connection is an external peer, and peer is participating in peer link, + // if the connection is a downstream neighbor FNE peer, and peer is participating in peer link, // send the peer proper configuration data - if (connection->isExternalFNEPeer() && connection->isPeerReplica()) { + if (connection->isNeighborFNEPeer() && connection->isPeerReplica()) { LogInfoEx(LOG_NET, "PEER %u (%s) sending replica network metadata updates", req->peerId, peerIdentity.c_str()); network->writeWhitelistRIDs(req->peerId, streamId, true); @@ -2138,12 +2149,12 @@ void FNENetwork::taskMetadataUpdate(MetadataUpdateRequest* req) /* Helper to send the list of whitelisted RIDs to the specified peer. */ -void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sendISSI) +void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sendReplica) { uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - // sending REPL style RID list to external peers - if (sendISSI) { + // sending REPL style RID list to replica neighbor FNE peers + if (sendReplica) { FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { // save out radio ID table to disk @@ -2338,14 +2349,14 @@ void FNENetwork::writeBlacklistRIDs(uint32_t peerId, uint32_t streamId) /* Helper to send the list of active TGIDs to the specified peer. */ -void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendISSI) +void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendReplica) { if (!m_tidLookup->sendTalkgroups()) { return; } - // sending REPL style TGID list to external peers - if (sendISSI) { + // sending REPL style TGID list to replica neighbor FNE peers + if (sendReplica) { FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { std::string tempFile; @@ -2537,7 +2548,7 @@ void FNENetwork::writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId) void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) { - // sending REPL style RID list to external peers + // sending REPL style PID list to replica neighbor FNE peers FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { std::string tempFile; @@ -2597,7 +2608,7 @@ void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) /* Helper to send the HA parameters to the specified peer. */ -void FNENetwork::writeHAParameters(uint32_t peerId, uint32_t streamId, bool sendISSI) +void FNENetwork::writeHAParameters(uint32_t peerId, uint32_t streamId, bool sendReplica) { if (!m_haEnabled) { return; @@ -2623,8 +2634,8 @@ void FNENetwork::writeHAParameters(uint32_t peerId, uint32_t streamId, bool send } m_peerReplicaHAParams.unlock(); - // sending REPL style HA parameters list to external peers - if (sendISSI) { + // sending REPL style HA parameters list to replica neighbor FNE peers + if (sendReplica) { FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, HA parameters, streamId = %u", peerId, connection->identWithQualifier().c_str(), streamId); @@ -2705,9 +2716,9 @@ bool FNENetwork::writePeer(uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePai if (m_maskOutboundPeerID) ssrc = m_peerId; // mask the source SSRC to our own peer ID else { - if ((connection->isExternalFNEPeer() && !connection->isPeerReplica()) && m_maskOutboundPeerIDForNonPL) { - // if the peer is an external FNE neighbor peer, and not a replica peer, we need to send the packet - // to the external FNE neighbor peer with our peer ID as the source instead of the originating peer + if ((connection->isNeighborFNEPeer() && !connection->isPeerReplica()) && m_maskOutboundPeerIDForNonPL) { + // if the peer is a downstream FNE neighbor peer, and not a replica peer, we need to send the packet + // to the neighbor FNE peer with our peer ID as the source instead of the originating peer // because we have routed it ssrc = m_peerId; } diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 038a8588..2214886e 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -256,7 +256,7 @@ namespace network * @param conn FNE Peer Connection. * @return json::object */ - json::object fneConnObject(uint32_t peerId, FNEPeerConnection *conn); + json::object fneConnObject(uint32_t peerId, FNEPeerConnection* conn); /** * @brief Helper to reset a peer connection. @@ -266,7 +266,7 @@ namespace network bool resetPeer(uint32_t peerId); /** - * @brief Helper to set the master is peer replica flag. + * @brief Helper to set the master is upstream peer replica flag. * @param peerReplica Flag indicating the master is a peer replica. */ void setPeerReplica(bool peerReplica); @@ -472,9 +472,9 @@ namespace network * @brief Helper to send the list of whitelisted RIDs to the specified peer. * @param peerId Peer ID. * @param streamId Stream ID for this message. - * @param sendISSI Flag indicating the RID transfer is to an external peer via ISSI. + * @param sendReplica Flag indicating the RID transfer is to an neighbor replica peer. */ - void writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sendISSI); + void writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sendReplica); /** * @brief Helper to send the list of blacklisted RIDs to the specified peer. * @param peerId Peer ID. @@ -485,9 +485,9 @@ namespace network * @brief Helper to send the list of active TGIDs to the specified peer. * @param peerId Peer ID. * @param streamId Stream ID for this message. - * @param sendISSI Flag indicating the TGID transfer is to an external peer via ISSI. + * @param sendReplica Flag indicating the TGID transfer is to an neighbor replica peer. */ - void writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendISSI); + void writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendReplica); /** * @brief Helper to send the list of deactivated TGIDs to the specified peer. * @param peerId Peer ID. @@ -504,9 +504,9 @@ namespace network * @brief Helper to send the HA parameters to the specified peer. * @param peerId Peer ID. * @param streamId Stream ID for this message. - * @param sendISSI Flag indicating the HA transfer is to an external peer via ISSI. + * @param sendReplica Flag indicating the HA transfer is to an neighbor replica peer. */ - void writeHAParameters(uint32_t peerId, uint32_t streamId, bool sendISSI); + void writeHAParameters(uint32_t peerId, uint32_t streamId, bool sendReplica); /** * @brief Helper to send a network tree disconnect to the specified peer. diff --git a/src/fne/network/FNEPeerConnection.h b/src/fne/network/FNEPeerConnection.h index 4f1a278c..0efc0fa8 100644 --- a/src/fne/network/FNEPeerConnection.h +++ b/src/fne/network/FNEPeerConnection.h @@ -54,7 +54,7 @@ namespace network m_pingsReceived(0U), m_lastPing(0U), m_missedMetadataUpdates(0U), - m_isExternalFNEPeer(false), + m_isNeighborFNEPeer(false), m_isConventionalPeer(false), m_isSysView(false), m_isPeerReplica(false), @@ -84,7 +84,7 @@ namespace network m_pingsReceived(0U), m_lastPing(0U), m_missedMetadataUpdates(0U), - m_isExternalFNEPeer(false), + m_isNeighborFNEPeer(false), m_isConventionalPeer(false), m_isSysView(false), m_isPeerReplica(false), @@ -107,7 +107,7 @@ namespace network return "@" + identity(); if (isPeerReplica()) return "%" + identity(); - if (isExternalFNEPeer()) + if (isNeighborFNEPeer()) return "+" + identity(); return " " + m_identity; @@ -187,9 +187,9 @@ namespace network DECLARE_PROPERTY_PLAIN(uint32_t, missedMetadataUpdates); /** - * @brief Flag indicating this connection is from an external neighbor FNE peer. + * @brief Flag indicating this connection is from an downstream neighbor FNE peer. */ - DECLARE_PROPERTY_PLAIN(bool, isExternalFNEPeer); + DECLARE_PROPERTY_PLAIN(bool, isNeighborFNEPeer); /** * @brief Flag indicating this connection is from an conventional peer. */ diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 82795ff9..ab43ceff 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -518,6 +518,10 @@ bool PeerNetwork::writeConfig() config["rcon"].set(rcon); // Flags + /* + ** bryanb: don't change externalPeer to neighborPeer -- this will break backward + ** compat with older FNE versions (we're stuck with this naming :() + */ bool external = true; config["externalPeer"].set(external); // External FNE Neighbor Peer Marker config["masterPeerId"].set(m_masterPeerId); // Master Peer ID diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 32569ce0..bf921a09 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -49,7 +49,7 @@ TagAnalogData::~TagAnalogData() = default; /* Process a data frame from the network. */ -bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool external) +bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool fromUpstream) { hrc::hrc_t pktTime = hrc::now(); @@ -83,14 +83,14 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // is the stream valid? if (validate(peerId, analogData, streamId)) { // is this peer ignored? - if (!isPeerPermitted(peerId, analogData, streamId, external)) { + if (!isPeerPermitted(peerId, analogData, streamId, fromUpstream)) { return false; } // is this the end of the call stream? if (frameType == AudioFrameType::TERMINATOR) { if (srcId == 0U && dstId == 0U) { - LogWarning(LOG_NET, "Analog, invalid TERMINATOR, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); + LogWarning(LOG_NET, "Analog, invalid TERMINATOR, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); return false; } @@ -117,8 +117,8 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } } - LogMessage(LOG_NET, "Analog, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, external = %u", - peerId, ssrc, srcId, dstId, duration / 1000, streamId, external); + LogMessage(LOG_NET, "Analog, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB if (m_network->m_enableInfluxDB) { @@ -141,7 +141,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // is this a new call stream? if (frameType == AudioFrameType::VOICE_START) { if (srcId == 0U && dstId == 0U) { - LogWarning(LOG_NET, "Analog, invalid call, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); + LogWarning(LOG_NET, "Analog, invalid call, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); return false; } @@ -167,8 +167,8 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status.unlock(); } else { - LogWarning(LOG_NET, "Analog, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", - peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + LogWarning(LOG_NET, "Analog, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); return false; } } else { @@ -205,7 +205,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status[dstId].activeCall = true; m_status.unlock(); - LogMessage(LOG_NET, "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); + LogMessage(LOG_NET, "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } } @@ -272,8 +272,8 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId, true); if (m_network->m_debug) { - LogDebug(LOG_NET, "Analog, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, len = %u, pktSeq = %u, stream = %u, external = %u", - ssrc, peerId, peer.first, seqNo, srcId, dstId, len, pktSeq, streamId, external); + LogDebug(LOG_NET, "Analog, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", + ssrc, peerId, peer.first, seqNo, srcId, dstId, len, pktSeq, streamId, fromUpstream); } i++; @@ -284,7 +284,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } /* - ** PEER TRAFFIC (e.g. networks this FNE is peered to) + ** PEER TRAFFIC (e.g. upstream networks this FNE is peered to) */ // repeat traffic to master nodes we have connected to as a peer @@ -293,7 +293,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee uint32_t dstPeerId = peer.second->getPeerId(); // don't try to repeat traffic to the source peer...if this traffic - // is coming from a external peer + // is coming from a neighbor FNE peer if (dstPeerId != peerId) { if (ssrc == dstPeerId) { // skip the peer if it is the source peer @@ -322,8 +322,8 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { - LogDebug(LOG_NET, "Analog, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, len = %u, pktSeq = %u, stream = %u, external = %u", - ssrc, peerId, dstPeerId, seqNo, srcId, dstId, len, pktSeq, streamId, external); + LogDebug(LOG_NET, "Analog, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", + ssrc, peerId, dstPeerId, seqNo, srcId, dstId, len, pktSeq, streamId, fromUpstream); } } } @@ -421,7 +421,7 @@ bool TagAnalogData::peerRewrite(uint32_t peerId, uint32_t& dstId, bool outbound) /* Helper to determine if the peer is permitted for traffic. */ -bool TagAnalogData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t streamId, bool external) +bool TagAnalogData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t streamId, bool fromUpstream) { if (!data.getGroup()) { if (m_network->m_disallowU2U) @@ -482,8 +482,8 @@ bool TagAnalogData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32 if (m_network->m_allowConvSiteAffOverride) { if (connection != nullptr) { if (connection->isConventionalPeer()) { - external = true; // we'll just set the external flag to disable the affiliation check - // for conventional peers + fromUpstream = true; // we'll just set the fromUpstream flag to disable the affiliation check + // for conventional peers } } } @@ -491,14 +491,14 @@ bool TagAnalogData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32 // is this peer a SysView peer? if (connection != nullptr) { if (connection->isSysView()) { - external = true; // we'll just set the external flag to disable the affiliation check - // for SysView peers + fromUpstream = true; // we'll just set the fromUpstream flag to disable the affiliation check + // for SysView peers } } // is this a TG that requires affiliations to repeat? - // NOTE: external peers *always* repeat traffic regardless of affiliation - if (tg.config().affiliated() && !external) { + // NOTE: neighbor FNE peers *always* repeat traffic regardless of affiliation + if (tg.config().affiliated() && !fromUpstream) { uint32_t lookupPeerId = peerId; if (connection != nullptr) { if (connection->ccPeerId() > 0U) diff --git a/src/fne/network/callhandler/TagAnalogData.h b/src/fne/network/callhandler/TagAnalogData.h index c7ae6624..6ddd3552 100644 --- a/src/fne/network/callhandler/TagAnalogData.h +++ b/src/fne/network/callhandler/TagAnalogData.h @@ -59,10 +59,10 @@ namespace network * @param ssrc RTP Synchronization Source ID. * @param pktSeq RTP packet sequence. * @param streamId Stream ID. - * @param external Flag indicating traffic is from an external peer. + * @param fromUpstream Flag indicating traffic is from a upstream master. * @returns bool True, if frame is processed, otherwise false. */ - bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool external = false); + bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool fromUpstream = false); /** * @brief Helper to playback a parrot frame to the network. @@ -177,10 +177,10 @@ namespace network * @param peerId Peer ID. * @param data Instance of data::NetData Analog data container class. * @param streamId Stream ID. - * @param external Flag indicating this traffic came from an external peer. + * @param fromUpstream Flag indicating traffic is from a upstream master. * @returns bool True, if valid, otherwise false. */ - bool isPeerPermitted(uint32_t peerId, analog::data::NetData& data, uint32_t streamId, bool external = false); + bool isPeerPermitted(uint32_t peerId, analog::data::NetData& data, uint32_t streamId, bool fromUpstream = false); /** * @brief Helper to validate the DMR call stream. * @param peerId Peer ID. diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 79bc699b..d551fa1e 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -59,7 +59,7 @@ TagDMRData::~TagDMRData() /* Process a data frame from the network. */ -bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool external) +bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool fromUpstream) { hrc::hrc_t pktTime = hrc::now(); @@ -110,7 +110,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId (dataType == DataType::RATE_1_DATA))) { if (m_network->m_disablePacketData) return false; - return m_packetData->processFrame(data, len, peerId, pktSeq, streamId, external); + return m_packetData->processFrame(data, len, peerId, pktSeq, streamId, fromUpstream); } uint8_t frame[DMR_FRAME_LENGTH_BYTES]; @@ -129,14 +129,14 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // is the stream valid? if (validate(peerId, dmrData, csbk.get(), streamId)) { // is this peer ignored? - if (!isPeerPermitted(peerId, dmrData, streamId, external)) { + if (!isPeerPermitted(peerId, dmrData, streamId, fromUpstream)) { return false; } // is this the end of the call stream? if (dataSync && (dataType == DataType::TERMINATOR_WITH_LC)) { if (srcId == 0U && dstId == 0U) { - LogWarning(LOG_NET, "DMR, invalid TERMINATOR, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, slotNo, streamId, external); + LogWarning(LOG_NET, "DMR, invalid TERMINATOR, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, streamId, fromUpstream); return false; } @@ -149,8 +149,8 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId return false; }); if (it == m_status.end()) { - LogError(LOG_NET, "DMR, tried to end call for non-existent call in progress?, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, streamId = %u, external = %u", - peerId, ssrc, srcId, dstId, slotNo, streamId, external); + LogError(LOG_NET, "DMR, tried to end call for non-existent call in progress?, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, slotNo, streamId, fromUpstream); } else { status = it->second; @@ -189,12 +189,12 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogMessage(LOG_NET, "DMR, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, external = %u", - peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, external); + LogMessage(LOG_NET, "DMR, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream); } else - LogMessage(LOG_NET, "DMR, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, external = %u", - peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, external); + LogMessage(LOG_NET, "DMR, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB if (m_network->m_enableInfluxDB) { @@ -218,7 +218,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // is this a new call stream? if (dataSync && (dataType == DataType::VOICE_LC_HEADER)) { if (srcId == 0U && dstId == 0U) { - LogWarning(LOG_NET, "DMR, invalid call, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); + LogWarning(LOG_NET, "DMR, invalid call, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); return false; } @@ -242,8 +242,8 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { - LogMessage(LOG_NET, "DMR, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, external = %u", - peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, external); + LogMessage(LOG_NET, "DMR, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, fromUpstream); m_status[dstId].srcId = srcId; } m_status.unlock(); @@ -260,8 +260,8 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_status[dstId].srcId = srcId; m_status.unlock(); } else { - LogWarning(LOG_NET, "DMR, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, external = %u", - peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, external); + LogWarning(LOG_NET, "DMR, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, fromUpstream); return false; } } else { @@ -317,11 +317,11 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogMessage(LOG_NET, "DMR, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", - peerId, ssrc, srcId, dstId, streamId, external); + LogMessage(LOG_NET, "DMR, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, streamId, fromUpstream); } else - LogMessage(LOG_NET, "DMR, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); + LogMessage(LOG_NET, "DMR, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } } @@ -376,14 +376,14 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId privateCallInProgress = false; // trick the system to repeat everywhere } else { // if this is a private call, check if the destination peer is one directly connected to us, if not - // flag the call so it only repeats to external peers + // flag the call so it only repeats to upstream neighbor peers if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { noConnectedPeerRepeat = true; for (auto peer : m_network->m_peers) { if (peerId != peer.first) { FNEPeerConnection* conn = peer.second; if (conn != nullptr) { - if (conn->isExternalFNEPeer()) { + if (conn->isNeighborFNEPeer()) { continue; } } @@ -418,14 +418,14 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } if (m_network->m_restrictPVCallToRegOnly) { - // is this peer an external peer? - bool external = false; + // is this peer an upstream neighbor peer? + bool neighbor = false; if (conn != nullptr) { - external = conn->isExternalFNEPeer(); + neighbor = conn->isNeighborFNEPeer(); } // is this a private call? - if ((flco == FLCO::PRIVATE) && !external) { + if ((flco == FLCO::PRIVATE) && !neighbor) { // is this a private call? if so only repeat to the peer that registered the unit auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { @@ -460,8 +460,8 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId, true); if (m_network->m_debug) { - LogDebug(LOG_NET, "DMR, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, flco = $%02X, slotNo = %u, len = %u, pktSeq = %u, stream = %u, external = %u", - ssrc, peerId, peer.first, seqNo, srcId, dstId, flco, slotNo, len, pktSeq, streamId, external); + LogDebug(LOG_NET, "DMR, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, flco = $%02X, slotNo = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", + ssrc, peerId, peer.first, seqNo, srcId, dstId, flco, slotNo, len, pktSeq, streamId, fromUpstream); } i++; @@ -472,13 +472,13 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } // if this is a private call, and we have already repeated to the connected peer that registered - // the unit, don't repeat to any external peers + // the unit, don't repeat to any neighbor FNE peers if (privateCallInProgress && !noConnectedPeerRepeat) { return true; } /* - ** PEER TRAFFIC (e.g. networks this FNE is peered to) + ** PEER TRAFFIC (e.g. upstream networks this FNE is peered to) */ // repeat traffic to master nodes we have connected to as a peer @@ -487,7 +487,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId uint32_t dstPeerId = peer.second->getPeerId(); // don't try to repeat traffic to the source peer...if this traffic - // is coming from a external peer + // is coming from a neighbor FNE peer if (dstPeerId != peerId) { if (ssrc == dstPeerId) { // skip the peer if it is the source peer @@ -516,8 +516,8 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { - LogDebug(LOG_NET, "DMR, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, flco = $%02X, slotNo = %u, len = %u, pktSeq = %u, stream = %u, external = %u", - ssrc, peerId, dstPeerId, seqNo, srcId, dstId, flco, slotNo, len, pktSeq, streamId, external); + LogDebug(LOG_NET, "DMR, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, flco = $%02X, slotNo = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", + ssrc, peerId, dstPeerId, seqNo, srcId, dstId, flco, slotNo, len, pktSeq, streamId, fromUpstream); } } } @@ -800,7 +800,7 @@ bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::NetDat /* Helper to determine if the peer is permitted for traffic. */ -bool TagDMRData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t streamId, bool external) +bool TagDMRData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t streamId, bool fromUpstream) { // promiscuous hub mode performs no ACL checking and will pass all traffic if (g_promiscuousHub) @@ -865,8 +865,8 @@ bool TagDMRData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t if (m_network->m_allowConvSiteAffOverride) { if (connection != nullptr) { if (connection->isConventionalPeer()) { - external = true; // we'll just set the external flag to disable the affiliation check - // for conventional peers + fromUpstream = true; // we'll just set the fromUpstream flag to disable the affiliation check + // for conventional peers } } } @@ -874,14 +874,14 @@ bool TagDMRData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t // is this peer a SysView peer? if (connection != nullptr) { if (connection->isSysView()) { - external = true; // we'll just set the external flag to disable the affiliation check - // for SysView peers + fromUpstream = true; // we'll just set the fromUpstream flag to disable the affiliation check + // for SysView peers } } // is this a TG that requires affiliations to repeat? - // NOTE: external peers *always* repeat traffic regardless of affiliation - if (tg.config().affiliated() && !external) { + // NOTE: neighbor FNE peers *always* repeat traffic regardless of affiliation + if (tg.config().affiliated() && !fromUpstream) { uint32_t lookupPeerId = peerId; if (connection != nullptr) { if (connection->ccPeerId() > 0U) @@ -1358,7 +1358,7 @@ void TagDMRData::write_CSBK(uint32_t peerId, uint8_t slot, lc::CSBK* csbk) m_network->m_frameQueue->flushQueue(); } - // repeat traffic to external peers + // repeat traffic to neighbor FNE 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(); diff --git a/src/fne/network/callhandler/TagDMRData.h b/src/fne/network/callhandler/TagDMRData.h index c1313b2e..1290efdf 100644 --- a/src/fne/network/callhandler/TagDMRData.h +++ b/src/fne/network/callhandler/TagDMRData.h @@ -59,10 +59,10 @@ namespace network * @param ssrc RTP Synchronization Source ID. * @param pktSeq RTP packet sequence. * @param streamId Stream ID. - * @param external Flag indicating traffic is from an external peer. + * @param fromUpstream Flag indicating traffic is from a upstream master. * @returns bool True, if frame is processed, otherwise false. */ - bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool external = false); + bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool fromUpstream = false); /** * @brief Process a grant request frame from the network. * @param srcId Source Radio ID. @@ -244,10 +244,10 @@ namespace network * @param peerId Peer ID. * @param data Instance of data::NetData DMR data container class. * @param streamId Stream ID. - * @param external Flag indicating this traffic came from an external peer. + * @param fromUpstream Flag indicating traffic is from a upstream master. * @returns bool True, if valid, otherwise false. */ - bool isPeerPermitted(uint32_t peerId, dmr::data::NetData& data, uint32_t streamId, bool external = false); + bool isPeerPermitted(uint32_t peerId, dmr::data::NetData& data, uint32_t streamId, bool fromUpstream = false); /** * @brief Helper to validate the DMR call stream. * @param peerId Peer ID. diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index f069ab10..588d5a36 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -57,7 +57,7 @@ TagNXDNData::~TagNXDNData() = default; /* Process a data frame from the network. */ -bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool external) +bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool fromUpstream) { hrc::hrc_t pktTime = hrc::now(); @@ -180,7 +180,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // is the stream valid? if (validate(peerId, lc, messageType, streamId)) { // is this peer ignored? - if (!isPeerPermitted(peerId, lc, messageType, streamId, external)) { + if (!isPeerPermitted(peerId, lc, messageType, streamId, fromUpstream)) { return false; } @@ -191,7 +191,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // is this the end of the call stream? if (messageType == MessageType::RTCH_TX_REL || messageType == MessageType::RTCH_TX_REL_EX) { if (srcId == 0U && dstId == 0U) { - LogWarning(LOG_NET, "NXDN, invalid TX_REL, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); + LogWarning(LOG_NET, "NXDN, invalid TX_REL, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); return false; } @@ -228,12 +228,12 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogMessage(LOG_NET, "NXDN, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, external = %u", - peerId, ssrc, srcId, dstId, duration / 1000, streamId, external); + LogMessage(LOG_NET, "NXDN, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); } else - LogMessage(LOG_NET, "NXDN, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, external = %u", - peerId, ssrc, srcId, dstId, duration / 1000, streamId, external); + LogMessage(LOG_NET, "NXDN, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB if (m_network->m_enableInfluxDB) { @@ -256,7 +256,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // is this a new call stream? if ((messageType != MessageType::RTCH_TX_REL && messageType != MessageType::RTCH_TX_REL_EX)) { if (srcId == 0U && dstId == 0U) { - LogWarning(LOG_NET, "NXDN, invalid call, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); + LogWarning(LOG_NET, "NXDN, invalid call, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); return false; } @@ -280,8 +280,8 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { - LogMessage(LOG_NET, "NXDN, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", - peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + LogMessage(LOG_NET, "NXDN, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); m_status[dstId].srcId = srcId; } m_status.unlock(); @@ -298,8 +298,8 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI m_status[dstId].srcId = srcId; m_status.unlock(); } else { - LogWarning(LOG_NET, "NXDN, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", - peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + LogWarning(LOG_NET, "NXDN, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); return false; } } else { @@ -352,12 +352,12 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogMessage(LOG_NET, "NXDN, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", - peerId, ssrc, srcId, dstId, streamId, external); + LogMessage(LOG_NET, "NXDN, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, streamId, fromUpstream); } else - LogMessage(LOG_NET, "NXDN, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", - peerId, ssrc, srcId, dstId, streamId, external); + LogMessage(LOG_NET, "NXDN, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, streamId, fromUpstream); } } } @@ -406,14 +406,14 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI privateCallInProgress = false; // trick the system to repeat everywhere } else { // if this is a private call, check if the destination peer is one directly connected to us, if not - // flag the call so it only repeats to external peers + // flag the call so it only repeats to neighbor FNE peers if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { noConnectedPeerRepeat = true; for (auto peer : m_network->m_peers) { if (peerId != peer.first) { FNEPeerConnection* conn = peer.second; if (conn != nullptr) { - if (conn->isExternalFNEPeer()) { + if (conn->isNeighborFNEPeer()) { continue; } } @@ -448,14 +448,14 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI } if (m_network->m_restrictPVCallToRegOnly) { - // is this peer an external peer? - bool external = false; + // is this peer an upstream neighbor peer? + bool neighbor = false; if (conn != nullptr) { - external = conn->isExternalFNEPeer(); + neighbor = conn->isNeighborFNEPeer(); } // is this a private call? - if (!group && !external) { + if (!group && !neighbor) { // is this a private call? if so only repeat to the peer that registered the unit auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { @@ -490,8 +490,8 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId, true); if (m_network->m_debug) { - LogDebug(LOG_NET, "NXDN, ssrc = %u, srcPeer = %u, dstPeer = %u, messageType = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, external = %u", - ssrc, peerId, peer.first, messageType, srcId, dstId, len, pktSeq, streamId, external); + LogDebug(LOG_NET, "NXDN, ssrc = %u, srcPeer = %u, dstPeer = %u, messageType = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", + ssrc, peerId, peer.first, messageType, srcId, dstId, len, pktSeq, streamId, fromUpstream); } i++; @@ -502,13 +502,13 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI } // if this is a private call, and we have already repeated to the connected peer that registered - // the unit, don't repeat to any external peers + // the unit, don't repeat to any neighbor FNE peers if (privateCallInProgress && !noConnectedPeerRepeat) { return true; } /* - ** PEER TRAFFIC (e.g. networks this FNE is peered to) + ** PEER TRAFFIC (e.g. upstream networks this FNE is peered to) */ // repeat traffic to master nodes we have connected to as a peer @@ -517,7 +517,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI uint32_t dstPeerId = peer.second->getPeerId(); // don't try to repeat traffic to the source peer...if this traffic - // is coming from a external peer + // is coming from a neighbor FNE peer if (dstPeerId != peerId) { if (ssrc == dstPeerId) { // skip the peer if it is the source peer @@ -546,8 +546,8 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { - LogDebug(LOG_NET, "NXDN, ssrc = %u, srcPeer = %u, dstPeer = %u, messageType = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, external = %u", - ssrc, peerId, dstPeerId, messageType, srcId, dstId, len, pktSeq, streamId, external); + LogDebug(LOG_NET, "NXDN, ssrc = %u, srcPeer = %u, dstPeer = %u, messageType = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", + ssrc, peerId, dstPeerId, messageType, srcId, dstId, len, pktSeq, streamId, fromUpstream); } } } @@ -692,7 +692,7 @@ bool TagNXDNData::peerRewrite(uint32_t peerId, uint32_t& dstId, bool outbound) /* Helper to determine if the peer is permitted for traffic. */ -bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, uint32_t streamId, bool external) +bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, uint32_t streamId, bool fromUpstream) { // promiscuous hub mode performs no ACL checking and will pass all traffic if (g_promiscuousHub) @@ -757,8 +757,8 @@ bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t message if (m_network->m_allowConvSiteAffOverride) { if (connection != nullptr) { if (connection->isConventionalPeer()) { - external = true; // we'll just set the external flag to disable the affiliation check - // for conventional peers + fromUpstream = true; // we'll just set the fromUpstream flag to disable the affiliation check + // for conventional peers } } } @@ -766,14 +766,14 @@ bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t message // is this peer a SysView peer? if (connection != nullptr) { if (connection->isSysView()) { - external = true; // we'll just set the external flag to disable the affiliation check - // for SysView peers + fromUpstream = true; // we'll just set the fromUpstream flag to disable the affiliation check + // for SysView peers } } // is this a TG that requires affiliations to repeat? - // NOTE: external peers *always* repeat traffic regardless of affiliation - if (tg.config().affiliated() && !external) { + // NOTE: neighbor FNE peers *always* repeat traffic regardless of affiliation + if (tg.config().affiliated() && !fromUpstream) { uint32_t lookupPeerId = peerId; if (connection != nullptr) { if (connection->ccPeerId() > 0U) diff --git a/src/fne/network/callhandler/TagNXDNData.h b/src/fne/network/callhandler/TagNXDNData.h index f48328cd..c5a3dec7 100644 --- a/src/fne/network/callhandler/TagNXDNData.h +++ b/src/fne/network/callhandler/TagNXDNData.h @@ -58,10 +58,10 @@ namespace network * @param ssrc RTP Synchronization Source ID. * @param pktSeq RTP packet sequence. * @param streamId Stream ID. - * @param external Flag indicating traffic is from an external peer. + * @param fromUpstream Flag indicating traffic is from a upstream master. * @returns bool True, if frame is processed, otherwise false. */ - bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool external = false); + bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool fromUpstream = false); /** * @brief Process a grant request frame from the network. * @param srcId Source Radio ID. @@ -194,10 +194,10 @@ namespace network * @param lc Instance of nxdn::lc::RTCH. * @param messageType Message Type. * @param streamId Stream ID. - * @param external Flag indicating this traffic came from an external peer. + * @param fromUpstream Flag indicating traffic is from a upstream master. * @returns bool True, if permitted, otherwise false. */ - bool isPeerPermitted(uint32_t peerId, nxdn::lc::RTCH& lc, uint8_t messageType, uint32_t streamId, bool external = false); + bool isPeerPermitted(uint32_t peerId, nxdn::lc::RTCH& lc, uint8_t messageType, uint32_t streamId, bool fromUpstream = false); /** * @brief Helper to validate the NXDN call stream. * @param peerId Peer ID. diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index ed67addf..ad0bdd80 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -66,7 +66,7 @@ TagP25Data::~TagP25Data() /* Process a data frame from the network. */ -bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool external) +bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool fromUpstream) { hrc::hrc_t pktTime = hrc::now(); @@ -100,7 +100,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (duid == DUID::PDU) { if (m_network->m_disablePacketData) return false; - return m_packetData->processFrame(data, len, peerId, pktSeq, streamId, external); + return m_packetData->processFrame(data, len, peerId, pktSeq, streamId, fromUpstream); } // perform TGID route rewrites if configured @@ -173,7 +173,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // is the stream valid? if (validate(peerId, control, duid, tsbk.get(), streamId)) { // is this peer ignored? - if (!isPeerPermitted(peerId, control, duid, streamId, external)) { + if (!isPeerPermitted(peerId, control, duid, streamId, fromUpstream)) { return false; } @@ -182,7 +182,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // is this the end of the call stream? if ((duid == DUID::TDU) || (duid == DUID::TDULC)) { if (srcId == 0U && dstId == 0U) { - LogWarning(LOG_NET, "P25, invalid TDU, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, ssrc, srcId, dstId, streamId, external); + LogWarning(LOG_NET, "P25, invalid TDU, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); return false; } @@ -209,8 +209,8 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_status.end()) { if (grantDemand && !switchOver) { - LogWarning(LOG_NET, "P25, Call Grant Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", - peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + LogWarning(LOG_NET, "P25, Call Grant Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); return false; } else { @@ -237,12 +237,12 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogMessage(LOG_NET, "P25, Private Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, external = %u", - peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, external); + LogMessage(LOG_NET, "P25, Private Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream); } else - LogMessage(LOG_NET, "P25, Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, external = %u", - peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, external); + LogMessage(LOG_NET, "P25, Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB if (m_network->m_enableInfluxDB) { @@ -266,7 +266,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // is this a new call stream? if ((duid != DUID::TDU) && (duid != DUID::TDULC)) { if (srcId == 0U && dstId == 0U) { - LogWarning(LOG_NET, "P25, invalid call, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, srcId, dstId, streamId, external); + LogWarning(LOG_NET, "P25, invalid call, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, srcId, dstId, streamId, fromUpstream); return false; } @@ -290,8 +290,8 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { - LogMessage(LOG_NET, "P25, Call Source Switched, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", - peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + LogMessage(LOG_NET, "P25, Call Source Switched, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); m_status[dstId].srcId = srcId; } m_status.unlock(); @@ -308,8 +308,8 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_status.unlock(); } else { - LogWarning(LOG_NET, "P25, Call Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, external = %u", - peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, external); + LogWarning(LOG_NET, "P25, Call Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); return false; } } else { @@ -362,12 +362,12 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogMessage(LOG_NET, "P25, Private Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, external = %u", - peerId, ssrc, sysId, netId, srcId, dstId, streamId, external); + LogMessage(LOG_NET, "P25, Private Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream); } else - LogMessage(LOG_NET, "P25, Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, external = %u", - peerId, ssrc, sysId, netId, srcId, dstId, streamId, external); + LogMessage(LOG_NET, "P25, Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream); } } } @@ -435,14 +435,14 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId privateCallInProgress = false; // trick the system to repeat everywhere } else { // if this is a private call, check if the destination peer is one directly connected to us, if not - // flag the call so it only repeats to external peers + // flag the call so it only repeats to neighbor FNE peers if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { noConnectedPeerRepeat = true; for (auto peer : m_network->m_peers) { if (peerId != peer.first) { FNEPeerConnection* conn = peer.second; if (conn != nullptr) { - if (conn->isExternalFNEPeer()) { + if (conn->isNeighborFNEPeer()) { continue; } } @@ -477,14 +477,14 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } if (m_network->m_restrictPVCallToRegOnly) { - // is this peer an external peer? - bool external = false; + // is this peer an upstream neighbor peer? + bool neighbor = false; if (conn != nullptr) { - external = conn->isExternalFNEPeer(); + neighbor = conn->isNeighborFNEPeer(); } // is this a private call? - if ((lco == LCO::PRIVATE) && !external) { + if ((lco == LCO::PRIVATE) && !neighbor) { // is this a private call? if so only repeat to the peer that registered the unit auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) { if (x.second.dstId == dstId) { @@ -524,8 +524,8 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId, true); if (m_network->m_debug) { - LogDebug(LOG_NET, "P25, ssrc = %u, srcPeer = %u, dstPeer = %u, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, external = %u", - ssrc, peerId, peer.first, duid, lco, MFId, srcId, dstId, len, pktSeq, streamId, external); + LogDebug(LOG_NET, "P25, ssrc = %u, srcPeer = %u, dstPeer = %u, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", + ssrc, peerId, peer.first, duid, lco, MFId, srcId, dstId, len, pktSeq, streamId, fromUpstream); } i++; @@ -536,13 +536,13 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } // if this is a private call, and we have already repeated to the connected peer that registered - // the unit, don't repeat to any external peers + // the unit, don't repeat to any neighbor FNE peers if (privateCallInProgress && !noConnectedPeerRepeat) { return true; } /* - ** PEER TRAFFIC (e.g. networks this FNE is peered to) + ** PEER TRAFFIC (e.g. upstream networks this FNE is peered to) */ // repeat traffic to master nodes we have connected to as a peer @@ -551,7 +551,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId uint32_t dstPeerId = peer.second->getPeerId(); // don't try to repeat traffic to the source peer...if this traffic - // is coming from a external peer + // is coming from a neighbor FNE peer if (dstPeerId != peerId) { if (ssrc == dstPeerId) { // skip the peer if it is the source peer @@ -574,16 +574,16 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // perform TGID route rewrites if configured routeRewrite(outboundPeerBuffer, dstPeerId, duid, dstId); - // process TSDUs going to external peers - if (processTSDUToExternal(outboundPeerBuffer, peerId, dstPeerId, duid)) { + // process TSDUs going to neighbor FNE peers + if (processTSDUToNeighbor(outboundPeerBuffer, peerId, dstPeerId, duid)) { // are we a replica peer? if (peer.second->isPeerReplica()) peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { - LogDebug(LOG_NET, "P25, ssrc = %u, srcPeer = %u, dstPeer = %u, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, external = %u", - ssrc, peerId, dstPeerId, duid, lco, MFId, srcId, dstId, len, pktSeq, streamId, external); + LogDebug(LOG_NET, "P25, ssrc = %u, srcPeer = %u, dstPeer = %u, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", + ssrc, peerId, dstPeerId, duid, lco, MFId, srcId, dstId, len, pktSeq, streamId, fromUpstream); } } } @@ -1061,9 +1061,9 @@ bool TagP25Data::processTSDUTo(uint8_t* buffer, uint32_t peerId, uint8_t duid) return true; } -/* Helper to process TSDUs being passed to an external peer. */ +/* Helper to process TSDUs being passed to a neighbor FNE peer. */ -bool TagP25Data::processTSDUToExternal(uint8_t* buffer, uint32_t srcPeerId, uint32_t dstPeerId, uint8_t duid) +bool TagP25Data::processTSDUToNeighbor(uint8_t* buffer, uint32_t srcPeerId, uint32_t dstPeerId, uint8_t duid) { // are we receiving a TSDU? if (duid == DUID::TSDU) { @@ -1079,7 +1079,7 @@ bool TagP25Data::processTSDUToExternal(uint8_t* buffer, uint32_t srcPeerId, uint case TSBKO::OSP_ADJ_STS_BCAST: { if (m_network->m_disallowExtAdjStsBcast) { - // LogWarning(LOG_NET, "PEER %u, passing ADJ_STS_BCAST to external peers is prohibited, dropping", dstPeerId); + // LogWarning(LOG_NET, "PEER %u, passing ADJ_STS_BCAST to neighbor peers is prohibited, dropping", dstPeerId); return false; } else { lc::tsbk::OSP_ADJ_STS_BCAST* osp = static_cast(tsbk.get()); @@ -1105,7 +1105,7 @@ bool TagP25Data::processTSDUToExternal(uint8_t* buffer, uint32_t srcPeerId, uint /* Helper to determine if the peer is permitted for traffic. */ -bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, DUID::E duid, uint32_t streamId, bool external) +bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, DUID::E duid, uint32_t streamId, bool fromUpstream) { // promiscuous hub mode performs no ACL checking and will pass all traffic if (g_promiscuousHub) @@ -1222,8 +1222,8 @@ bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, DUID::E duid, if (m_network->m_allowConvSiteAffOverride) { if (connection != nullptr) { if (connection->isConventionalPeer()) { - external = true; // we'll just set the external flag to disable the affiliation check - // for conventional peers + fromUpstream = true; // we'll just set the fromUpstream flag to disable the affiliation check + // for conventional peers } } } @@ -1231,14 +1231,14 @@ bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, DUID::E duid, // is this peer a SysView peer? if (connection != nullptr) { if (connection->isSysView()) { - external = true; // we'll just set the external flag to disable the affiliation check - // for SysView peers + fromUpstream = true; // we'll just set the fromUpstream flag to disable the affiliation check + // for SysView peers } } // is this a TG that requires affiliations to repeat? - // NOTE: external peers *always* repeat traffic regardless of affiliation - if (tg.config().affiliated() && !external) { + // NOTE: neighbor FNE peers *always* repeat traffic regardless of affiliation + if (tg.config().affiliated() && !fromUpstream) { uint32_t lookupPeerId = peerId; if (connection != nullptr) { if (connection->ccPeerId() > 0U) @@ -1779,7 +1779,7 @@ void TagP25Data::write_TSDU(uint32_t peerId, lc::TSBK* tsbk) m_network->m_frameQueue->flushQueue(); } - // repeat traffic to external peers + // repeat traffic to neighbor FNE 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(); diff --git a/src/fne/network/callhandler/TagP25Data.h b/src/fne/network/callhandler/TagP25Data.h index 12de5c16..63d4f661 100644 --- a/src/fne/network/callhandler/TagP25Data.h +++ b/src/fne/network/callhandler/TagP25Data.h @@ -64,10 +64,10 @@ namespace network * @param ssrc RTP Synchronization Source ID. * @param pktSeq RTP packet sequence. * @param streamId Stream ID. - * @param external Flag indicating traffic is from an external peer. + * @param fromUpstream Flag indicating traffic is from a upstream master. * @returns bool True, if frame is processed, otherwise false. */ - bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool external = false); + bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool fromUpstream = false); /** * @brief Process a grant request frame from the network. * @param srcId Source Radio ID. @@ -257,14 +257,14 @@ namespace network */ bool processTSDUTo(uint8_t* buffer, uint32_t peerId, uint8_t duid); /** - * @brief Helper to process TSDUs being passed to an external peer. + * @brief Helper to process TSDUs being passed to a neighbor FNE peer. * @param buffer Frame buffer. * @param srcPeerId Source Peer ID. * @param dstPeerID Destination Peer ID. * @param duid DUID. * @returns bool True, if allowed to pass, otherwise false. */ - bool processTSDUToExternal(uint8_t* buffer, uint32_t srcPeerId, uint32_t dstPeerId, uint8_t duid); + bool processTSDUToNeighbor(uint8_t* buffer, uint32_t srcPeerId, uint32_t dstPeerId, uint8_t duid); /** * @brief Helper to determine if the peer is permitted for traffic. @@ -272,10 +272,10 @@ namespace network * @param control Instance of p25::lc::LC. * @param duid DUID. * @param streamId Stream ID. - * @param external Flag indicating this traffic came from an external peer. + * @param fromUpstream Flag indicating traffic is from a upstream master. * @returns bool True, if permitted, otherwise false. */ - bool isPeerPermitted(uint32_t peerId, p25::lc::LC& control, P25DEF::DUID::E duid, uint32_t streamId, bool external = false); + bool isPeerPermitted(uint32_t peerId, p25::lc::LC& control, P25DEF::DUID::E duid, uint32_t streamId, bool fromUpstream = false); /** * @brief Helper to validate the P25 call stream. * @param peerId Peer ID. diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp index 86acf484..a16b4782 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp @@ -58,7 +58,7 @@ DMRPacketData::~DMRPacketData() = default; /* Process a data frame from the network. */ -bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool external) +bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool fromUpstream) { hrc::hrc_t pktTime = hrc::now(); @@ -102,14 +102,14 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee if (it != m_status.end()) { RxStatus* status = m_status[peerId]; if (streamId != status->streamId) { - LogWarning(LOG_NET, "DMR, Data Call Collision, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxSlotNo = %u, rxStreamId = %u, external = %u", - peerId, streamId, status->peerId, status->srcId, status->slotNo, status->streamId, external); + LogWarning(LOG_NET, "DMR, Data Call Collision, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", + peerId, streamId, status->peerId, status->srcId, status->slotNo, status->streamId, fromUpstream); uint64_t duration = hrc::diff(pktTime, status->callStartTime); if ((duration / 1000) > DATA_CALL_COLL_TIMEOUT) { - LogWarning(LOG_NET, "DMR, force clearing stuck data call, timeout, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxStreamId = %u, external = %u", - peerId, streamId, status->peerId, status->srcId, status->slotNo, status->streamId, external); + LogWarning(LOG_NET, "DMR, force clearing stuck data call, timeout, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, streamId, status->peerId, status->srcId, status->slotNo, status->streamId, fromUpstream); delete status; m_status.erase(peerId); @@ -157,7 +157,7 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status[peerId] = status; - LogMessage(LOG_NET, "DMR, Data Call Start, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, streamId = %u, external = %u", peerId, status->slotNo, status->srcId, status->dstId, gi, streamId, external); + LogMessage(LOG_NET, "DMR, Data Call Start, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, streamId = %u, fromUpstream = %u", peerId, status->slotNo, status->srcId, status->dstId, gi, streamId, fromUpstream); dispatchToFNE(peerId, dmrData, data, len, seqNo, pktSeq, streamId); return true; @@ -170,8 +170,8 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // a PDU header only with no blocks to follow is usually a response header if (status->header.getBlocksToFollow() == 0U) { - LogMessage(LOG_NET, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", - peerId, status->slotNo, status->srcId, status->dstId, streamId, external); + LogMessage(LOG_NET, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + peerId, status->slotNo, status->srcId, status->dstId, streamId, fromUpstream); delete status; m_status.erase(peerId); @@ -211,8 +211,8 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee bool gi = status->header.getGI(); uint32_t srcId = status->header.getSrcId(); uint32_t dstId = status->header.getDstId(); - LogMessage(LOG_NET, "P25, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, blocks = %u, duration = %u, streamId = %u, external = %u", - peerId, srcId, dstId, gi, status->header.getBlocksToFollow(), duration / 1000, streamId, external); + LogMessage(LOG_NET, "P25, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", + peerId, srcId, dstId, gi, status->header.getBlocksToFollow(), duration / 1000, streamId, fromUpstream); // report call event to InfluxDB if (m_network->m_enableInfluxDB) { @@ -295,13 +295,13 @@ void DMRPacketData::dispatchToFNE(uint32_t peerId, dmr::data::NetData& dmrData, m_network->m_frameQueue->flushQueue(); } - // repeat traffic to external peers + // repeat traffic to neighbor FNE 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(); // don't try to repeat traffic to the source peer...if this traffic - // is coming from a external peer + // is coming from a neighbor FNE peer if (dstPeerId != peerId) { // skip peer if it isn't enabled if (!peer.second->isEnabled()) { diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.h b/src/fne/network/callhandler/packetdata/DMRPacketData.h index d1481029..e8cd2266 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.h +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.h @@ -63,10 +63,10 @@ namespace network * @param peerId Peer ID. * @param pktSeq RTP packet sequence. * @param streamId Stream ID. - * @param external Flag indicating traffic is from an external peer. + * @param fromUpstream Flag indicating traffic is from a upstream master. * @returns bool True, if frame is processed, otherwise false. */ - bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool external = false); + bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool fromUpstream = false); private: FNENetwork* m_network; diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index f0cd19d4..0817c012 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -74,7 +74,7 @@ P25PacketData::~P25PacketData() = default; /* Process a data frame from the network. */ -bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool external) +bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool fromUpstream) { hrc::hrc_t pktTime = hrc::now(); @@ -154,7 +154,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return true; } - LogMessage(LOG_NET, "P25, Data Call Start, peer = %u, llId = %u, streamId = %u, external = %u", peerId, status->llId, streamId, external); + LogMessage(LOG_NET, "P25, Data Call Start, peer = %u, llId = %u, streamId = %u, fromUpstream = %u", peerId, status->llId, streamId, fromUpstream); return true; } @@ -298,8 +298,8 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee uint64_t duration = hrc::diff(pktTime, status->callStartTime); uint32_t srcId = (status->extendedAddress) ? status->header.getSrcLLId() : status->header.getLLId(); uint32_t dstId = status->header.getLLId(); - LogMessage(LOG_NET, "P25, Data Call End, peer = %u, srcId = %u, dstId = %u, blocks = %u, duration = %u, streamId = %u, external = %u", - peerId, srcId, dstId, status->header.getBlocksToFollow(), duration / 1000, streamId, external); + LogMessage(LOG_NET, "P25, Data Call End, peer = %u, srcId = %u, dstId = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", + peerId, srcId, dstId, status->header.getBlocksToFollow(), duration / 1000, streamId, fromUpstream); // report call event to InfluxDB if (m_network->m_enableInfluxDB) { @@ -807,13 +807,13 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) m_network->m_frameQueue->flushQueue(); } - // repeat traffic to external peers + // repeat traffic to neighbor FNE 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(); // don't try to repeat traffic to the source peer...if this traffic - // is coming from a external peer + // is coming from a neighbor FNE peer if (dstPeerId != peerId) { // skip peer if it isn't enabled if (!peer.second->isEnabled()) { @@ -830,7 +830,7 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) } } -/* Helper to dispatch PDU user data back to the local FNE network. (Will not transmit to external peers.) */ +/* Helper to dispatch PDU user data back to the local FNE network. (Will not transmit to neighbor FNE peers.) */ void P25PacketData::dispatchUserFrameToFNE(p25::data::DataHeader& dataHeader, bool extendedAddress, uint8_t* pduUserData) { diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index f8da4732..200ee508 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -64,10 +64,10 @@ namespace network * @param peerId Peer ID. * @param pktSeq RTP packet sequence. * @param streamId Stream ID. - * @param external Flag indicating traffic is from an external peer. + * @param fromUpstream Flag indicating traffic is from a upstream master. * @returns bool True, if frame is processed, otherwise false. */ - bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool external = false); + bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool fromUpstream = false); /** * @brief Process a data frame from the virtual IP network. @@ -209,7 +209,7 @@ namespace network */ void dispatchToFNE(uint32_t peerId); /** - * @brief Helper to dispatch PDU user data back to the local FNE network. (Will not transmit to external peers.) + * @brief Helper to dispatch PDU user data back to the local FNE network. (Will not transmit to neighbor FNE peers.) * @param dataHeader Instance of a PDU data header. * @param extendedAddress Flag indicating whether or not to extended addressing is in use. * @param pduUserData Buffer containing user data to transmit. From a082b8195b79075d0e62e9fa81f17ca2e2bf5740 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 23 Oct 2025 11:15:42 -0400 Subject: [PATCH 087/200] more code cleanup, simplify naming; --- src/fne/network/DiagNetwork.cpp | 12 ++--- src/fne/network/FNENetwork.cpp | 52 +++++++++---------- src/fne/network/FNENetwork.h | 6 +-- src/fne/network/FNEPeerConnection.h | 16 +++--- src/fne/network/PeerNetwork.h | 2 +- src/fne/network/callhandler/TagAnalogData.cpp | 4 +- src/fne/network/callhandler/TagDMRData.cpp | 4 +- src/fne/network/callhandler/TagNXDNData.cpp | 4 +- src/fne/network/callhandler/TagP25Data.cpp | 4 +- 9 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 10508b81..55d47f5c 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -210,7 +210,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_peers.find(req->rtpHeader.getSSRC()) != network->m_peers.end()) { FNEPeerConnection* connection = network->m_peers[req->rtpHeader.getSSRC()]; if (connection != nullptr) { - if (connection->isNeighborFNEPeer() && connection->isPeerReplica()) { + if (connection->isNeighborFNEPeer() && connection->isReplica()) { validPeerId = true; pktPeerId = req->rtpHeader.getSSRC(); } @@ -269,7 +269,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_ACTIVITY }, req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false, true, pktPeerId); } @@ -358,7 +358,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_STATUS }, req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false, true, pktPeerId); } @@ -392,7 +392,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) // validate peer (simple validation really) if (connection->connected() && connection->address() == ip && connection->isNeighborFNEPeer() && - connection->isPeerReplica()) { + connection->isReplica()) { DECLARE_UINT8_ARRAY(rawPayload, req->length); ::memcpy(rawPayload, req->buffer, req->length); @@ -493,7 +493,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) // validate peer (simple validation really) if (connection->connected() && connection->address() == ip && connection->isNeighborFNEPeer() && - connection->isPeerReplica()) { + connection->isReplica()) { DECLARE_UINT8_ARRAY(rawPayload, req->length); ::memcpy(rawPayload, req->buffer, req->length); @@ -544,7 +544,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->isReplica()) { std::vector haParams; network->m_peerReplicaHAParams.lock(false); for (auto entry : network->m_peerReplicaHAParams) { diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 691f5f7e..3c78f4b2 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -76,7 +76,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_address(address), m_port(port), m_password(password), - m_isPeerReplica(false), + m_isReplica(false), m_dmrEnabled(dmr), m_p25Enabled(p25), m_nxdnEnabled(nxdn), @@ -462,7 +462,7 @@ void FNENetwork::clock(uint32_t ms) FNEPeerConnection* connection = peer.second; if (connection != nullptr) { uint64_t dt = 0U; - if (connection->isNeighborFNEPeer() || connection->isPeerReplica()) + if (connection->isNeighborFNEPeer() || connection->isReplica()) dt = connection->lastPing() + ((m_host->m_pingTime * 1000) * (m_host->m_maxMissedPings * 2U)); else dt = connection->lastPing() + ((m_host->m_pingTime * 1000) * m_host->m_maxMissedPings); @@ -477,7 +477,7 @@ void FNENetwork::clock(uint32_t ms) // if the connection was an downstream FNE neighbor peer or a peer replica -- be noisy about a possible // netsplit - if (connection->isNeighborFNEPeer() || connection->isPeerReplica()) { + if (connection->isNeighborFNEPeer() || connection->isReplica()) { for (uint8_t i = 0U; i < 3U; i++) LogWarning(LOG_NET, "PEER %u (%s) downstream netsplit, dt = %u, now = %u", id, connection->identWithQualifier().c_str(), dt, now); @@ -506,7 +506,7 @@ void FNENetwork::clock(uint32_t ms) // perform peer replica maintainence tasks if (peer.second->isEnabled() && peer.second->getRemotePeerId() > 0U && - peer.second->isPeerReplica()) { + peer.second->isReplica()) { if (!peer.second->getAttachedKeyRSPHandler()) { peer.second->setAttachedKeyRSPHandler(true); // this is the only place this should happen peer.second->setKeyResponseCallback([=](p25::kmm::KeyItem ki, uint8_t algId, uint8_t keyLength) { @@ -546,7 +546,7 @@ void FNENetwork::clock(uint32_t ms) FNEPeerConnection* connection = peer.second; if (connection != nullptr) { // if this connection is a peer replica *always* send the update -- no stream checking - if (connection->connected() && connection->isPeerReplica()) { + if (connection->connected() && connection->isReplica()) { LogInfoEx(LOG_NET, "PEER %u (%s), Peer Replication, updating network metadata", id, connection->identWithQualifier().c_str()); peerMetadataUpdate(id); @@ -582,7 +582,7 @@ void FNENetwork::clock(uint32_t ms) if (m_host->m_peerNetworks.size() > 0) { for (auto peer : m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->isReplica()) { std::vector haParams; m_peerReplicaHAParams.lock(false); for (auto entry : m_peerReplicaHAParams) { @@ -1218,7 +1218,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (!peerEntry.peerDefault()) { if (peerEntry.peerReplica()) { if (network->m_host->m_useAlternatePortForDiagnostics) { - connection->isPeerReplica(true); + connection->isReplica(true); if (neighbor) LogInfoEx(LOG_NET, "PEER %u >> Participates in Peer Replication", peerId); } else { @@ -1496,7 +1496,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->isReplica()) { LogMessage(LOG_NET, "PEER %u (%s) no local key or container, requesting key from upstream master, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); @@ -1560,7 +1560,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_GRP_AFFIL }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); } @@ -1597,7 +1597,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_UNIT_REG }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false, 0U, ssrc); } @@ -1633,7 +1633,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_UNIT_DEREG }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); } @@ -1670,7 +1670,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_GRP_UNAFFIL }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); } @@ -1720,7 +1720,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_AFFILS }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); } @@ -1769,7 +1769,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_peerNetworks.size() > 0) { for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { - if (peer.second->isEnabled() && peer.second->isPeerReplica()) { + if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_SITE_VC }, req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); } @@ -2026,16 +2026,16 @@ bool FNENetwork::resetPeer(uint32_t peerId) /* Helper to set the master is upstream peer replica flag. */ -void FNENetwork::setPeerReplica(bool peerReplica) +void FNENetwork::setPeerReplica(bool replica) { - if (!m_isPeerReplica && peerReplica) { + if (!m_isReplica && replica) { LogInfoEx(LOG_NET, "MASTER set to upstream peer replica, receiving ACL updates from upstream master"); } - m_isPeerReplica = peerReplica; + m_isReplica = replica; // be very noisy about being a peer replica and having multiple upstream peers - if (m_isPeerReplica) { + if (m_isReplica) { if (m_host->m_peerNetworks.size() > 1) { LogWarning(LOG_NET, "We are a upstream peer replica, and have multiple upstream peers? This is a bad idea. Peer Replica FNEs should have a single upstream peer connection."); } @@ -2119,7 +2119,7 @@ void FNENetwork::taskMetadataUpdate(MetadataUpdateRequest* req) // if the connection is a downstream neighbor FNE peer, and peer is participating in peer link, // send the peer proper configuration data - if (connection->isNeighborFNEPeer() && connection->isPeerReplica()) { + if (connection->isNeighborFNEPeer() && connection->isReplica()) { LogInfoEx(LOG_NET, "PEER %u (%s) sending replica network metadata updates", req->peerId, peerIdentity.c_str()); network->writeWhitelistRIDs(req->peerId, streamId, true); @@ -2159,7 +2159,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sen if (connection != nullptr) { // save out radio ID table to disk std::string tempFile; - if (m_isPeerReplica) { + if (m_isReplica) { std::ostringstream s; std::random_device rd; std::mt19937 mt(rd()); @@ -2186,7 +2186,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sen stream.close(); } - if (m_isPeerReplica) + if (m_isReplica) ::remove(tempFile.c_str()); // convert to a byte array @@ -2360,7 +2360,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendReplica FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { std::string tempFile; - if (m_isPeerReplica) { + if (m_isReplica) { std::ostringstream s; std::random_device rd; std::mt19937 mt(rd()); @@ -2387,7 +2387,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendReplica stream.close(); } - if (m_isPeerReplica) + if (m_isReplica) ::remove(tempFile.c_str()); // convert to a byte array @@ -2552,7 +2552,7 @@ void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { std::string tempFile; - if (m_isPeerReplica) { + if (m_isReplica) { std::ostringstream s; std::random_device rd; std::mt19937 mt(rd()); @@ -2579,7 +2579,7 @@ void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) stream.close(); } - if (m_isPeerReplica) + if (m_isReplica) ::remove(tempFile.c_str()); // convert to a byte array @@ -2716,7 +2716,7 @@ bool FNENetwork::writePeer(uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePai if (m_maskOutboundPeerID) ssrc = m_peerId; // mask the source SSRC to our own peer ID else { - if ((connection->isNeighborFNEPeer() && !connection->isPeerReplica()) && m_maskOutboundPeerIDForNonPL) { + if ((connection->isNeighborFNEPeer() && !connection->isReplica()) && m_maskOutboundPeerIDForNonPL) { // if the peer is a downstream FNE neighbor peer, and not a replica peer, we need to send the packet // to the neighbor FNE peer with our peer ID as the source instead of the originating peer // because we have routed it diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 2214886e..df5a222c 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -267,9 +267,9 @@ namespace network /** * @brief Helper to set the master is upstream peer replica flag. - * @param peerReplica Flag indicating the master is a peer replica. + * @param replica Flag indicating the master is a peer replica. */ - void setPeerReplica(bool peerReplica); + void setPeerReplica(bool replica); private: friend class DiagNetwork; @@ -295,7 +295,7 @@ namespace network std::string m_password; - bool m_isPeerReplica; + bool m_isReplica; bool m_dmrEnabled; bool m_p25Enabled; diff --git a/src/fne/network/FNEPeerConnection.h b/src/fne/network/FNEPeerConnection.h index 0efc0fa8..d4b21a56 100644 --- a/src/fne/network/FNEPeerConnection.h +++ b/src/fne/network/FNEPeerConnection.h @@ -55,9 +55,9 @@ namespace network m_lastPing(0U), m_missedMetadataUpdates(0U), m_isNeighborFNEPeer(false), + m_isReplica(false), m_isConventionalPeer(false), m_isSysView(false), - m_isPeerReplica(false), m_config(), m_peerLockMtx() { @@ -85,9 +85,9 @@ namespace network m_lastPing(0U), m_missedMetadataUpdates(0U), m_isNeighborFNEPeer(false), + m_isReplica(false), m_isConventionalPeer(false), m_isSysView(false), - m_isPeerReplica(false), m_config(), m_peerLockMtx() { @@ -105,7 +105,7 @@ namespace network { if (isSysView()) return "@" + identity(); - if (isPeerReplica()) + if (isReplica()) return "%" + identity(); if (isNeighborFNEPeer()) return "+" + identity(); @@ -190,6 +190,11 @@ namespace network * @brief Flag indicating this connection is from an downstream neighbor FNE peer. */ DECLARE_PROPERTY_PLAIN(bool, isNeighborFNEPeer); + /** + * @brief Flag indicating this connection is from a neighbor FNE peer that is replica enabled. + */ + DECLARE_PROPERTY_PLAIN(bool, isReplica); + /** * @brief Flag indicating this connection is from an conventional peer. */ @@ -199,11 +204,6 @@ namespace network */ DECLARE_PROPERTY_PLAIN(bool, isSysView); - /** - * @brief Flag indicating this connection is from a neighbor FNE peer that is replica enabled. - */ - DECLARE_PROPERTY_PLAIN(bool, isPeerReplica); - /** * @brief JSON objecting containing peer configuration information. */ diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index 7a8b96d3..d00cb7a8 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -165,7 +165,7 @@ namespace network * @brief Returns flag indicating whether or not this peer connection is peer replication enabled. * @returns bool True, if peer replication enabled, otherwise false. */ - bool isPeerReplica() const { return m_peerReplica; } + bool isReplica() const { return m_peerReplica; } /** * @brief Enables the option that will save replicated ACL data to the local ACL files. diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index bf921a09..2068c93f 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -317,7 +317,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee routeRewrite(outboundPeerBuffer, dstPeerId, dstId); // are we a replica peer? - if (peer.second->isPeerReplica()) + if (peer.second->isReplica()) peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId); @@ -440,7 +440,7 @@ bool TagAnalogData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32 // is this peer a replica peer? if (connection != nullptr) { - if (connection->isPeerReplica()) { + if (connection->isReplica()) { return true; // replica peers are *always* allowed to receive traffic and no other rules may filter // these peers } diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index d551fa1e..a2ad01cb 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -511,7 +511,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId routeRewrite(outboundPeerBuffer, dstPeerId, dmrData, dataType, dstId, slotNo); // are we a replica peer? - if (peer.second->isPeerReplica()) + if (peer.second->isReplica()) peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId); @@ -823,7 +823,7 @@ bool TagDMRData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t // is this peer a replica peer? if (connection != nullptr) { - if (connection->isPeerReplica()) { + if (connection->isReplica()) { return true; // replica peers are *always* allowed to receive traffic and no other rules may filter // these peers } diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 588d5a36..f48d50ba 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -541,7 +541,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI routeRewrite(outboundPeerBuffer, dstPeerId, messageType, dstId); // are we a replica peer? - if (peer.second->isPeerReplica()) + if (peer.second->isReplica()) peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId); @@ -715,7 +715,7 @@ bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t message // is this peer a replica peer? if (connection != nullptr) { - if (connection->isPeerReplica()) { + if (connection->isReplica()) { return true; // replica peers are *always* allowed to receive traffic and no other rules may filter // these peers } diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index ad0bdd80..8c6fc8a9 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -577,7 +577,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // process TSDUs going to neighbor FNE peers if (processTSDUToNeighbor(outboundPeerBuffer, peerId, dstPeerId, duid)) { // are we a replica peer? - if (peer.second->isPeerReplica()) + if (peer.second->isReplica()) peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId); @@ -1135,7 +1135,7 @@ bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, DUID::E duid, // is this peer a replica peer? if (connection != nullptr) { - if (connection->isPeerReplica()) { + if (connection->isReplica()) { return true; // replica peers are *always* allowed to receive traffic and no other rules may filter // these peers } From 68ef40425de97793d2d30bb73b315149a07f1aef Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 23 Oct 2025 13:49:45 -0400 Subject: [PATCH 088/200] cleanup code; refactor log messages from the FNE to better categorize them; correct issue where a peer reconnecting may trip tree duplicate conn checking; --- src/common/Log.h | 1 + src/fne/Defines.h | 10 + src/fne/network/DiagNetwork.cpp | 30 +-- src/fne/network/FNENetwork.cpp | 216 +++++++++--------- src/fne/network/MasterTree.cpp | 6 +- src/fne/network/P25OTARService.cpp | 6 +- src/fne/network/PeerNetwork.cpp | 28 +-- src/fne/network/callhandler/TagAnalogData.cpp | 41 +++- src/fne/network/callhandler/TagDMRData.cpp | 64 +++--- src/fne/network/callhandler/TagNXDNData.cpp | 42 ++-- src/fne/network/callhandler/TagP25Data.cpp | 98 ++++---- .../callhandler/packetdata/DMRPacketData.cpp | 32 ++- .../callhandler/packetdata/P25PacketData.cpp | 127 +++++----- 13 files changed, 375 insertions(+), 326 deletions(-) diff --git a/src/common/Log.h b/src/common/Log.h index f1d63bf2..52735807 100644 --- a/src/common/Log.h +++ b/src/common/Log.h @@ -54,6 +54,7 @@ #define LOG_P25 "P25" #define LOG_NXDN "NXDN" #define LOG_DMR "DMR" +#define LOG_ANALOG "ANALOG" #define LOG_CAL "CAL" #define LOG_SETUP "SETUP" #define LOG_SERIAL "SERIAL" diff --git a/src/fne/Defines.h b/src/fne/Defines.h index f627a768..19217d06 100644 --- a/src/fne/Defines.h +++ b/src/fne/Defines.h @@ -38,4 +38,14 @@ #undef DEFAULT_LOCK_FILE #define DEFAULT_LOCK_FILE "/tmp/dvmfne.lock" +/** @cond */ + +#define LOG_MASTER "MSTR" +#define LOG_DIAG "DIAG" +#define LOG_PEER "PEER" +#define LOG_REPL "REPL" +#define LOG_STP "STP" + +/** @endcond */ + #endif // __DEFINES_H__ diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 55d47f5c..0820a5a4 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -117,7 +117,7 @@ void DiagNetwork::clock(uint32_t ms) bool DiagNetwork::open() { if (m_debug) - LogMessage(LOG_NET, "Opening Network"); + LogMessage(LOG_DIAG, "Opening Network"); m_threadPool.start(); @@ -146,7 +146,7 @@ bool DiagNetwork::open() void DiagNetwork::close() { if (m_debug) - LogMessage(LOG_NET, "Closing Network"); + LogMessage(LOG_DIAG, "Closing Network"); m_threadPool.stop(); m_threadPool.wait(); @@ -343,7 +343,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) uint32_t addrLen = peer.second->sockStorageLen(); if (network->m_debug) { - LogDebug(LOG_NET, "SysView, srcPeer = %u, dstPeer = %u, peer status message, len = %u", + LogDebug(LOG_DIAG, "SysView, srcPeer = %u, dstPeer = %u, peer status message, len = %u", pktPeerId, peer.first, req->length); } network->m_frameQueue->write(req->buffer, req->length, network->createStreamId(), pktPeerId, network->m_peerId, @@ -409,7 +409,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } else { DiagNetwork::PacketBufferEntry& pkt = diagNetwork->m_peerReplicaActPkt[peerId]; if (!pkt.locked && pkt.streamId != streamId) { - LogError(LOG_NET, "PEER %u (%s) Peer Replication, Active Peer List, stream ID mismatch, expected %u, got %u", peerId, + LogError(LOG_REPL, "PEER %u (%s) Peer Replication, Active Peer List, stream ID mismatch, expected %u, got %u", peerId, connection->identWithQualifier().c_str(), pkt.streamId, streamId); pkt.buffer->clear(); pkt.streamId = streamId; @@ -439,7 +439,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) json::value v; std::string err = json::parse(v, payload); if (!err.empty()) { - LogError(LOG_NET, "PEER %u (%s) error parsing active peer list, %s", peerId, connection->identWithQualifier().c_str(), err.c_str()); + LogError(LOG_REPL, "PEER %u (%s) error parsing active peer list, %s", peerId, connection->identWithQualifier().c_str(), err.c_str()); pkt.buffer->clear(); pkt.streamId = 0U; if (decompressed != nullptr) { @@ -451,7 +451,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) else { // ensure parsed JSON is an array if (!v.is()) { - LogError(LOG_NET, "PEER %u (%s) error parsing active peer list, data was not valid", peerId, connection->identWithQualifier().c_str()); + LogError(LOG_REPL, "PEER %u (%s) error parsing active peer list, data was not valid", peerId, connection->identWithQualifier().c_str()); pkt.buffer->clear(); delete pkt.buffer; pkt.streamId = 0U; @@ -463,7 +463,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } else { json::array arr = v.get(); - LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, Active Peer List, updating %u peer entries", peerId, connection->identWithQualifier().c_str(), arr.size()); + LogInfoEx(LOG_REPL, "PEER %u (%s) Peer Replication, Active Peer List, updating %u peer entries", peerId, connection->identWithQualifier().c_str(), arr.size()); network->m_peerReplicaPeers[peerId] = arr; } } @@ -531,13 +531,13 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_debug) { std::string address = __IP_FROM_UINT(rxEntry.masterIP); - LogDebugEx(LOG_NET, "DiagNetwork::taskNetworkRx", "PEER %u (%s) Peer Replication, HA Parameters, %s:%u", peerId, connection->identWithQualifier().c_str(), + LogDebugEx(LOG_REPL, "DiagNetwork::taskNetworkRx", "PEER %u (%s) Peer Replication, HA Parameters, %s:%u", peerId, connection->identWithQualifier().c_str(), address.c_str(), rxEntry.masterPort); } } if (receivedParams.size() > 0) { - LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, HA Parameters, updating %u entries, %u entries", peerId, connection->identWithQualifier().c_str(), receivedParams.size(), + LogInfoEx(LOG_REPL, "PEER %u (%s) Peer Replication, HA Parameters, updating %u entries, %u entries", peerId, connection->identWithQualifier().c_str(), receivedParams.size(), network->m_peerReplicaHAParams.size()); // send peer updates to replica peers @@ -594,7 +594,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } else { DiagNetwork::PacketBufferEntry& pkt = diagNetwork->m_peerTreeListPkt[peerId]; if (!pkt.locked && pkt.streamId != streamId) { - LogError(LOG_NET, "PEER %u (%s) Network Tree, Tree List, stream ID mismatch, expected %u, got %u", peerId, + LogError(LOG_STP, "PEER %u (%s) Network Tree, Tree List, stream ID mismatch, expected %u, got %u", peerId, connection->identWithQualifier().c_str(), pkt.streamId, streamId); pkt.buffer->clear(); pkt.streamId = streamId; @@ -624,7 +624,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) json::value v; std::string err = json::parse(v, payload); if (!err.empty()) { - LogError(LOG_NET, "PEER %u (%s) error parsing network tree list, %s", peerId, connection->identWithQualifier().c_str(), err.c_str()); + LogError(LOG_STP, "PEER %u (%s) error parsing network tree list, %s", peerId, connection->identWithQualifier().c_str(), err.c_str()); pkt.buffer->clear(); pkt.streamId = 0U; if (decompressed != nullptr) { @@ -636,7 +636,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) else { // ensure parsed JSON is an array if (!v.is()) { - LogError(LOG_NET, "PEER %u (%s) error parsing network tree list, data was not valid", peerId, connection->identWithQualifier().c_str()); + LogError(LOG_STP, "PEER %u (%s) error parsing network tree list, data was not valid", peerId, connection->identWithQualifier().c_str()); pkt.buffer->clear(); delete pkt.buffer; pkt.streamId = 0U; @@ -648,18 +648,18 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) } else { json::array arr = v.get(); - LogInfoEx(LOG_NET, "PEER %u (%s) Network Tree, Tree List, updating %u peer entries", peerId, connection->identWithQualifier().c_str(), arr.size()); + LogInfoEx(LOG_STP, "PEER %u (%s) Network Tree, Tree List, updating %u peer entries", peerId, connection->identWithQualifier().c_str(), arr.size()); std::vector duplicatePeers; MasterTree::deserializeTree(arr, network->m_fneTree, &duplicatePeers); if (network->m_logSpanningTreeChanges && network->m_fneTree->hasChildren()) { - LogInfoEx(LOG_NET, "PEER %u (%s) Network Tree, Tree Change, current tree:", peerId, connection->identWithQualifier().c_str()); + LogInfoEx(LOG_STP, "PEER %u (%s) Network Tree, Tree Change, Current Tree", peerId, connection->identWithQualifier().c_str()); MasterTree::visualizeTreeToLog(network->m_fneTree); } if (duplicatePeers.size() > 0U) { for (auto dupPeerId : duplicatePeers) { - LogWarning(LOG_NET, "PEER %u (%s) Network Tree, Tree Change, disconnecting duplicate peer connection for PEER %u to prevent network loop", + LogWarning(LOG_STP, "PEER %u (%s) Network Tree, Tree Change, disconnecting duplicate peer connection for PEER %u to prevent network loop", peerId, connection->identWithQualifier().c_str(), dupPeerId); network->writeTreeDisconnect(peerId, dupPeerId); } diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 3c78f4b2..95ecaee5 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -194,7 +194,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) m_enableSpanningTree = conf["enableSpanningTree"].as(true); if (!m_enableSpanningTree) { - LogWarning(LOG_NET, "WARNING: Disabling the peer spanning tree is not recommended! This can cause network loops and other issues in a multi-peer FNE network."); + LogWarning(LOG_MASTER, "WARNING: Disabling the peer spanning tree is not recommended! This can cause network loops and other issues in a multi-peer FNE network."); } m_logSpanningTreeChanges = conf["logSpanningTreeChanges"].as(false); @@ -224,13 +224,13 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) if (m_kmfServicesEnabled) { if (!m_p25OTARService->open(m_address, kmfOtarPort)) { m_kmfServicesEnabled = false; - LogError(LOG_P25, "FNE OTAR KMF services failed to start, OTAR service disabled."); + LogError(LOG_MASTER, "FNE OTAR KMF services failed to start, OTAR service disabled."); } } #else uint16_t kmfOtarPort = 64414U; // hardcoded m_kmfServicesEnabled = false; - LogWarning(LOG_P25, "FNE is compiled without OpenSSL support, KMF services are unavailable."); + LogWarning(LOG_MASTER, "FNE is compiled without OpenSSL support, KMF services are unavailable."); #endif // ENABLE_SSL m_callCollisionTimeout = conf["callCollisionTimeout"].as(5U); @@ -278,7 +278,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogInfo(" Log Spanning Tree Changes: %s", m_logSpanningTreeChanges ? "yes" : "no"); LogInfo(" Disable adjacent site broadcasts to any peers: %s", m_disallowAdjStsBcast ? "yes" : "no"); if (m_disallowAdjStsBcast) { - LogWarning(LOG_NET, "NOTICE: All P25 ADJ_STS_BCAST messages will be blocked and dropped!"); + LogWarning(LOG_MASTER, "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_dumpPacketData ? "yes" : "no"); @@ -294,7 +294,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) } LogInfo(" Call Collision Timeout: %us", m_callCollisionTimeout); if (m_callCollisionTimeout == 0U) { - LogWarning(LOG_NET, "Call Collisions are disabled because the call collision timeout is set to 0 seconds. This is not recommended, and can cause undesired behavior."); + LogWarning(LOG_MASTER, "Call Collisions are disabled because the call collision timeout is set to 0 seconds. This is not recommended, and can cause undesired behavior."); } LogInfo(" Restrict grant response by affiliation: %s", m_restrictGrantToAffOnly ? "yes" : "no"); LogInfo(" Restrict private call to registered units: %s", m_restrictPVCallToRegOnly ? "yes" : "no"); @@ -399,24 +399,24 @@ void FNENetwork::processNetworkTreeDisconnect(uint32_t peerId, uint32_t offendin } if (!m_enableSpanningTree) { - LogWarning(LOG_NET, "FNENetwork::processNetworkTreeDisconnect(), ignoring disconnect request for PEER %u, spanning tree is disabled", offendingPeerId); + LogWarning(LOG_STP, "FNENetwork::processNetworkTreeDisconnect(), ignoring disconnect request for PEER %u, spanning tree is disabled", offendingPeerId); return; } if (offendingPeerId > 0 && (m_peers.find(offendingPeerId) != m_peers.end())) { FNEPeerConnection* connection = m_peers[offendingPeerId]; if (connection != nullptr) { - LogWarning(LOG_NET, "PEER %u (%s) NAK, server already connected via upstream master, duplicate connection dropped, connectionState = %u", offendingPeerId, connection->identWithQualifier().c_str(), + LogWarning(LOG_STP, "PEER %u (%s) NAK, server already connected via upstream master, duplicate connection dropped, connectionState = %u", offendingPeerId, connection->identWithQualifier().c_str(), connection->connectionState()); writePeerNAK(offendingPeerId, createStreamId(), TAG_REPEATER_CONFIG, NET_CONN_NAK_FNE_DUPLICATE_CONN); disconnectPeer(offendingPeerId, connection); if (m_logSpanningTreeChanges && m_fneTree->hasChildren()) { - LogInfoEx(LOG_NET, "Network Tree, Tree Change, current tree:"); + LogInfoEx(LOG_STP, "Network Tree, Tree Change, Current Tree"); MasterTree::visualizeTreeToLog(m_fneTree); } } else { - LogError(LOG_NET, "Network Tree Disconnect, upstream master requested disconnect for PEER %u, but connection is null", offendingPeerId); + LogError(LOG_STP, "Network Tree Disconnect, upstream master requested disconnect for PEER %u, but connection is null", offendingPeerId); } } else { // is this perhaps a peer connection of ours? @@ -424,7 +424,7 @@ void FNENetwork::processNetworkTreeDisconnect(uint32_t peerId, uint32_t offendin for (auto peer : m_host->m_peerNetworks) { if (peer.second != nullptr) { if (peer.second->getPeerId() == peerId) { - LogWarning(LOG_NET, "PEER %u, upstream master requested disconnect for our peer connection, duplicate connection dropped", peerId); + LogWarning(LOG_STP, "PEER %u, upstream master requested disconnect for our peer connection, duplicate connection dropped", peerId); peer.second->close(); return; } @@ -432,7 +432,7 @@ void FNENetwork::processNetworkTreeDisconnect(uint32_t peerId, uint32_t offendin } } - LogError(LOG_NET, "Network Tree Disconnect, upstream master requested disconnect for unknown PEER %u", offendingPeerId); + LogError(LOG_STP, "Network Tree Disconnect, upstream master requested disconnect for unknown PEER %u", offendingPeerId); } } @@ -468,7 +468,7 @@ void FNENetwork::clock(uint32_t ms) dt = connection->lastPing() + ((m_host->m_pingTime * 1000) * m_host->m_maxMissedPings); if (dt < now) { - LogInfoEx(LOG_NET, "PEER %u (%s) timed out, dt = %u, now = %u", id, connection->identWithQualifier().c_str(), + LogInfoEx(LOG_MASTER, "PEER %u (%s) timed out, dt = %u, now = %u", id, connection->identWithQualifier().c_str(), dt, now); // set connection states for this stale connection @@ -479,7 +479,7 @@ void FNENetwork::clock(uint32_t ms) // netsplit if (connection->isNeighborFNEPeer() || connection->isReplica()) { for (uint8_t i = 0U; i < 3U; i++) - LogWarning(LOG_NET, "PEER %u (%s) downstream netsplit, dt = %u, now = %u", id, connection->identWithQualifier().c_str(), + LogWarning(LOG_MASTER, "PEER %u (%s) downstream netsplit, dt = %u, now = %u", id, connection->identWithQualifier().c_str(), dt, now); } @@ -547,7 +547,7 @@ void FNENetwork::clock(uint32_t ms) if (connection != nullptr) { // if this connection is a peer replica *always* send the update -- no stream checking if (connection->connected() && connection->isReplica()) { - LogInfoEx(LOG_NET, "PEER %u (%s), Peer Replication, updating network metadata", id, connection->identWithQualifier().c_str()); + LogInfoEx(LOG_MASTER, "PEER %u (%s), Peer Replication, updating network metadata", id, connection->identWithQualifier().c_str()); peerMetadataUpdate(id); connection->missedMetadataUpdates(0U); @@ -556,14 +556,14 @@ void FNENetwork::clock(uint32_t ms) if (connection->connected()) { if ((connection->streamCount() <= 1) || (connection->missedMetadataUpdates() > MAX_MISSED_ACL_UPDATES)) { - LogInfoEx(LOG_NET, "PEER %u (%s) updating ACL list", id, connection->identWithQualifier().c_str()); + LogInfoEx(LOG_MASTER, "PEER %u (%s) updating ACL list", id, connection->identWithQualifier().c_str()); peerMetadataUpdate(id); connection->missedMetadataUpdates(0U); } else { uint32_t missed = connection->missedMetadataUpdates(); missed++; - LogInfoEx(LOG_NET, "PEER %u (%s) skipped for metadata update, traffic in progress", id, connection->identWithQualifier().c_str()); + LogInfoEx(LOG_MASTER, "PEER %u (%s) skipped for metadata update, traffic in progress", id, connection->identWithQualifier().c_str()); connection->missedMetadataUpdates(missed); } } @@ -609,7 +609,7 @@ void FNENetwork::clock(uint32_t ms) bool FNENetwork::open() { if (m_debug) - LogMessage(LOG_NET, "Opening Network"); + LogMessage(LOG_MASTER, "Opening Network"); // start thread pool m_threadPool.start(); @@ -650,7 +650,7 @@ bool FNENetwork::open() void FNENetwork::close() { if (m_debug) - LogMessage(LOG_NET, "Closing Network"); + LogMessage(LOG_MASTER, "Closing Network"); if (m_status == NET_STAT_MST_RUNNING) { uint8_t buffer[1U]; @@ -791,7 +791,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) uint64_t dt = req->pktRxTime + PACKET_LATE_TIME; if (dt < now) { std::string peerIdentity = network->resolvePeerIdentity(peerId); - LogWarning(LOG_NET, "PEER %u (%s) packet processing latency >200ms, dt = %u, now = %u", peerId, peerIdentity.c_str(), + LogWarning(LOG_MASTER, "PEER %u (%s) packet processing latency >200ms, dt = %u, now = %u", peerId, peerIdentity.c_str(), dt, now); } @@ -805,11 +805,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) MULTIPLEX_RET_CODE ret = connection->verifyStream(streamId, pktSeq, req->fneHeader.getFunction(), &lastRxSeq); if (ret == MUX_LOST_FRAMES) { - LogError(LOG_NET, "PEER %u (%s) stream %u possible lost frames; got %u, expected %u", peerId, connection->identWithQualifier().c_str(), + LogError(LOG_MASTER, "PEER %u (%s) stream %u possible lost frames; got %u, expected %u", peerId, connection->identWithQualifier().c_str(), streamId, pktSeq, lastRxSeq); } else if (ret == MUX_OUT_OF_ORDER) { - LogError(LOG_NET, "PEER %u (%s) stream %u out-of-order; got %u, expected >%u", peerId, connection->identWithQualifier().c_str(), + LogError(LOG_MASTER, "PEER %u (%s) stream %u out-of-order; got %u, expected >%u", peerId, connection->identWithQualifier().c_str(), streamId, pktSeq, lastRxSeq); } } @@ -820,7 +820,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // if we don't have a stream ID and are receiving call data -- throw an error and discard if (streamId == 0U && req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) { std::string peerIdentity = network->resolvePeerIdentity(peerId); - LogError(LOG_NET, "PEER %u (%s) malformed packet (no stream ID for a call?)", peerId, peerIdentity.c_str()); + LogError(LOG_MASTER, "PEER %u (%s) malformed packet (no stream ID for a call?)", peerId, peerIdentity.c_str()); if (req->buffer != nullptr) delete[] req->buffer; @@ -950,13 +950,13 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) { if (peerId > 0 && (network->m_peers.find(peerId) == network->m_peers.end())) { if (network->m_peers.size() >= MAX_HARD_CONN_CAP) { - LogError(LOG_NET, "PEER %u attempted to connect with no more connections available, currConnections = %u", peerId, network->m_peers.size()); + LogError(LOG_MASTER, "PEER %u attempted to connect with no more connections available, currConnections = %u", peerId, network->m_peers.size()); network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_FNE_MAX_CONN, req->address, req->addrLen); break; } if (network->m_softConnLimit > 0U && network->m_peers.size() >= network->m_softConnLimit) { - LogError(LOG_NET, "PEER %u attempted to connect with no more connections available, maxConnections = %u, currConnections = %u", peerId, network->m_softConnLimit, network->m_peers.size()); + LogError(LOG_MASTER, "PEER %u attempted to connect with no more connections available, maxConnections = %u, currConnections = %u", peerId, network->m_softConnLimit, network->m_peers.size()); network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_FNE_MAX_CONN, req->address, req->addrLen); break; } @@ -969,11 +969,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // check if the peer is in the peer ACL list if (network->m_peerListLookup->getACL()) { if (network->m_peerListLookup->isPeerListEmpty()) { - LogWarning(LOG_NET, "Peer List ACL enabled, but we have an empty peer list? Passing all peers."); + LogWarning(LOG_MASTER, "Peer List ACL enabled, but we have an empty peer list? Passing all peers."); } if (!network->m_peerListLookup->isPeerAllowed(peerId) && !network->m_peerListLookup->isPeerListEmpty()) { - LogWarning(LOG_NET, "PEER %u RPTL, failed peer ACL check", peerId); + LogWarning(LOG_MASTER, "PEER %u RPTL, failed peer ACL check", peerId); network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_PEER_ACL, req->address, req->addrLen); network->disconnectPeer(peerId, connection); @@ -987,7 +987,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) FNEPeerConnection* connection = network->m_peers[peerId]; if (connection != nullptr) { if (connection->connectionState() == NET_STAT_RUNNING) { - LogMessage(LOG_NET, "PEER %u (%s) resetting peer connection, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + LogMessage(LOG_MASTER, "PEER %u (%s) resetting peer connection, connectionState = %u", peerId, connection->identWithQualifier().c_str(), connection->connectionState()); delete connection; @@ -1000,11 +1000,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // check if the peer is in the peer ACL list if (network->m_peerListLookup->getACL()) { if (network->m_peerListLookup->isPeerListEmpty()) { - LogWarning(LOG_NET, "Peer List ACL enabled, but we have an empty peer list? Passing all peers."); + LogWarning(LOG_MASTER, "Peer List ACL enabled, but we have an empty peer list? Passing all peers."); } if (!network->m_peerListLookup->isPeerAllowed(peerId) && !network->m_peerListLookup->isPeerListEmpty()) { - LogWarning(LOG_NET, "PEER %u RPTL, failed peer ACL check", peerId); + LogWarning(LOG_MASTER, "PEER %u RPTL, failed peer ACL check", peerId); network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_PEER_ACL, req->address, req->addrLen); network->disconnectPeer(peerId, connection); @@ -1013,14 +1013,14 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } else { network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); - LogWarning(LOG_NET, "PEER %u (%s) RPTL NAK, bad connection state, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + LogWarning(LOG_MASTER, "PEER %u (%s) RPTL NAK, bad connection state, connectionState = %u", peerId, connection->identWithQualifier().c_str(), connection->connectionState()); network->disconnectPeer(peerId, connection); } } else { network->writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); network->erasePeer(peerId); - LogWarning(LOG_NET, "PEER %u RPTL NAK, having no connection", peerId); + LogWarning(LOG_MASTER, "PEER %u RPTL NAK, having no connection", peerId); } } } @@ -1049,7 +1049,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) bool validAcl = true; if (network->m_peerListLookup->getACL()) { if (!network->m_peerListLookup->isPeerAllowed(peerId) && !network->m_peerListLookup->isPeerListEmpty()) { - LogWarning(LOG_NET, "PEER %u RPTK, failed peer ACL check", peerId); + LogWarning(LOG_MASTER, "PEER %u RPTK, failed peer ACL check", peerId); validAcl = false; } else { lookups::PeerId peerEntry = network->m_peerListLookup->find(peerId); @@ -1064,7 +1064,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } if (network->m_peerListLookup->isPeerListEmpty()) { - LogWarning(LOG_NET, "Peer List ACL enabled, but we have an empty peer list? Passing all peers."); + LogWarning(LOG_MASTER, "Peer List ACL enabled, but we have an empty peer list? Passing all peers."); validAcl = true; } } @@ -1097,11 +1097,11 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (validHash) { connection->connectionState(NET_STAT_WAITING_CONFIG); network->writePeerACK(peerId, streamId); - LogInfoEx(LOG_NET, "PEER %u RPTK ACK, completed the login exchange", peerId); + LogInfoEx(LOG_MASTER, "PEER %u RPTK ACK, completed the login exchange", peerId); network->m_peers[peerId] = connection; } else { - LogWarning(LOG_NET, "PEER %u RPTK NAK, failed the login exchange", peerId); + LogWarning(LOG_MASTER, "PEER %u RPTK NAK, failed the login exchange", peerId); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_FNE_UNAUTHORIZED, req->address, req->addrLen); network->disconnectPeer(peerId, connection); } @@ -1111,7 +1111,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } } else { - LogWarning(LOG_NET, "PEER %u RPTK NAK, login exchange while in an incorrect state, connectionState = %u", peerId, connection->connectionState()); + LogWarning(LOG_MASTER, "PEER %u RPTK NAK, login exchange while in an incorrect state, connectionState = %u", peerId, connection->connectionState()); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); network->disconnectPeer(peerId, connection); } @@ -1120,7 +1120,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) else { network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); network->erasePeer(peerId); - LogWarning(LOG_NET, "PEER %u RPTK NAK, having no connection", peerId); + LogWarning(LOG_MASTER, "PEER %u RPTK NAK, having no connection", peerId); } } break; @@ -1140,14 +1140,14 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) json::value v; std::string err = json::parse(v, payload); if (!err.empty()) { - LogWarning(LOG_NET, "PEER %u RPTC NAK, supplied invalid configuration data", peerId); + LogWarning(LOG_MASTER, "PEER %u RPTC NAK, supplied invalid configuration data", peerId); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_INVALID_CONFIG_DATA, req->address, req->addrLen); network->disconnectPeer(peerId, connection); } else { // ensure parsed JSON is an object if (!v.is()) { - LogWarning(LOG_NET, "PEER %u RPTC NAK, supplied invalid configuration data", peerId); + LogWarning(LOG_MASTER, "PEER %u RPTC NAK, supplied invalid configuration data", peerId); network->writePeerNAK(peerId, TAG_REPEATER_AUTH, NET_CONN_NAK_INVALID_CONFIG_DATA, req->address, req->addrLen); network->disconnectPeer(peerId, connection); } @@ -1173,12 +1173,12 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (peerConfig["identity"].is()) { std::string identity = peerConfig["identity"].get(); connection->identity(identity); - LogInfoEx(LOG_NET, "PEER %u >> Identity [%8s]", peerId, identity.c_str()); + LogInfoEx(LOG_MASTER, "PEER %u >> Identity [%8s]", peerId, identity.c_str()); } if (peerConfig["software"].is()) { std::string software = peerConfig["software"].get(); - LogInfoEx(LOG_NET, "PEER %u >> Software Version [%s]", peerId, software.c_str()); + LogInfoEx(LOG_MASTER, "PEER %u >> Software Version [%s]", peerId, software.c_str()); } // is the peer reporting it is a SysView peer? @@ -1186,7 +1186,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) bool sysView = peerConfig["sysView"].get(); connection->isSysView(sysView); if (sysView) - LogInfoEx(LOG_NET, "PEER %u >> SysView Peer", peerId); + LogInfoEx(LOG_MASTER, "PEER %u >> SysView Peer", peerId); } // is the peer reporting it is an downstream FNE neighbor peer? @@ -1198,18 +1198,18 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) bool neighbor = peerConfig["externalPeer"].get(); connection->isNeighborFNEPeer(neighbor); if (neighbor) - LogInfoEx(LOG_NET, "PEER %u >> Downstream Neighbor FNE Peer", peerId); + LogInfoEx(LOG_MASTER, "PEER %u >> Downstream Neighbor FNE Peer", peerId); uint32_t masterPeerId = 0U; if (peerConfig["masterPeerId"].is()) { masterPeerId = peerConfig["masterPeerId"].get(); connection->masterId(masterPeerId); - LogInfoEx(LOG_NET, "PEER %u >> Master Peer ID [%u]", peerId, masterPeerId); + LogInfoEx(LOG_MASTER, "PEER %u >> Master Peer ID [%u]", peerId, masterPeerId); } // master peer ID should never be zero for an neighbor peer -- use the peer ID instead if (masterPeerId == 0U) { - LogWarning(LOG_NET, "PEER %u reports to be a downstream FNE neighbor peer but has not supplied a valid masterPeerId, using own peerId as masterPeerId (old FNE perhaps?)", peerId); + LogWarning(LOG_MASTER, "PEER %u reports to be a downstream FNE neighbor peer but has not supplied a valid masterPeerId, using own peerId as masterPeerId (old FNE perhaps?)", peerId); masterPeerId = peerId; } @@ -1220,10 +1220,10 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_host->m_useAlternatePortForDiagnostics) { connection->isReplica(true); if (neighbor) - LogInfoEx(LOG_NET, "PEER %u >> Participates in Peer Replication", peerId); + LogInfoEx(LOG_MASTER, "PEER %u >> Participates in Peer Replication", peerId); } else { - LogError(LOG_NET, "PEER %u, Peer replication operations *require* the alternate diagnostics port option to be enabled.", peerId); - LogError(LOG_NET, "PEER %u, will not receive peer replication ACL updates.", peerId); + LogError(LOG_MASTER, "PEER %u, Peer replication operations *require* the alternate diagnostics port option to be enabled.", peerId); + LogError(LOG_MASTER, "PEER %u, will not receive peer replication ACL updates.", peerId); } } } @@ -1232,15 +1232,17 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // check if this peer is already connected via another peer MasterTree* tree = MasterTree::findByMasterID(masterPeerId); if (tree != nullptr) { - LogWarning(LOG_NET, "PEER %u (%s) RPTC NAK, server already connected via PEER %u, duplicate connection denied, connectionState = %u", peerId, connection->identWithQualifier().c_str(), - tree->id(), connection->connectionState()); - network->writePeerNAK(peerId, TAG_REPEATER_CONFIG, NET_CONN_NAK_FNE_DUPLICATE_CONN, req->address, req->addrLen); - network->disconnectPeer(peerId, connection); - break; + if (tree->id() != peerId && tree->masterId() == masterPeerId) { + LogWarning(LOG_MASTER, "PEER %u (%s) RPTC NAK, server already connected via PEER %u, duplicate connection denied, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + tree->id(), connection->connectionState()); + network->writePeerNAK(peerId, TAG_REPEATER_CONFIG, NET_CONN_NAK_FNE_DUPLICATE_CONN, req->address, req->addrLen); + network->disconnectPeer(peerId, connection); + break; + } } else { new MasterTree(peerId, masterPeerId, network->m_fneTree); if (network->m_logSpanningTreeChanges && network->m_fneTree->hasChildren()) { - LogInfoEx(LOG_NET, "PEER %u (%s) Network Tree, Tree Change, current tree:", peerId, connection->identWithQualifier().c_str()); + LogInfoEx(LOG_MASTER, "PEER %u (%s) Network Tree, Tree Change, Current Tree", peerId, connection->identWithQualifier().c_str()); MasterTree::visualizeTreeToLog(network->m_fneTree); } } @@ -1248,7 +1250,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } network->writePeerACK(peerId, streamId, buffer, 1U); - LogInfoEx(LOG_NET, "PEER %u RPTC ACK, completed the configuration exchange", peerId); + LogInfoEx(LOG_MASTER, "PEER %u RPTC ACK, completed the configuration exchange", peerId); // is the peer reporting it is a conventional peer? if (peerConfig["conventionalPeer"].is()) { @@ -1256,7 +1258,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) bool convPeer = peerConfig["conventionalPeer"].get(); connection->isConventionalPeer(convPeer); if (convPeer) - LogInfoEx(LOG_NET, "PEER %u >> Conventional Peer", peerId); + LogInfoEx(LOG_MASTER, "PEER %u >> Conventional Peer", peerId); } } @@ -1271,7 +1273,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } } else { - LogWarning(LOG_NET, "PEER %u (%s) RPTC NAK, login exchange while in an incorrect state, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + LogWarning(LOG_MASTER, "PEER %u (%s) RPTC NAK, login exchange while in an incorrect state, connectionState = %u", peerId, connection->identWithQualifier().c_str(), connection->connectionState()); network->writePeerNAK(peerId, TAG_REPEATER_CONFIG, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); network->disconnectPeer(peerId, connection); @@ -1280,7 +1282,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } else { network->writePeerNAK(peerId, TAG_REPEATER_CONFIG, NET_CONN_NAK_BAD_CONN_STATE, req->address, req->addrLen); - LogWarning(LOG_NET, "PEER %u RPTC NAK, having no connection", peerId); + LogWarning(LOG_MASTER, "PEER %u RPTC NAK, having no connection", peerId); } } break; @@ -1294,7 +1296,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // validate peer (simple validation really) if (connection->connected() && connection->address() == ip) { - LogInfoEx(LOG_NET, "PEER %u (%s) disconnected", peerId, connection->identWithQualifier().c_str()); + LogInfoEx(LOG_MASTER, "PEER %u (%s) disconnected", peerId, connection->identWithQualifier().c_str()); network->disconnectPeer(peerId, connection); } } @@ -1334,7 +1336,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) network->writePeerCommand(peerId, { NET_FUNC::PONG, NET_SUBFUNC::NOP }, payload, 8U, streamId, false); if (network->m_reportPeerPing) { - LogInfoEx(LOG_NET, "PEER %u (%s) ping, pingsReceived = %u, lastPing = %u, now = %u", peerId, connection->identWithQualifier().c_str(), + LogInfoEx(LOG_MASTER, "PEER %u (%s) ping, pingsReceived = %u, lastPing = %u, now = %u", peerId, connection->identWithQualifier().c_str(), connection->pingsReceived(), lastPing, now); } } @@ -1431,7 +1433,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) break; } else { if (!peerEntry.canRequestKeys()) { - LogError(LOG_NET, "PEER %u (%s) requested enc. key but is not allowed, no response", peerId, connection->identWithQualifier().c_str()); + LogError(LOG_MASTER, "PEER %u (%s) requested enc. key but is not allowed, no response", peerId, connection->identWithQualifier().c_str()); break; } } @@ -1439,7 +1441,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) std::unique_ptr frame = KMMFactory::create(req->buffer + 11U); if (frame == nullptr) { - LogWarning(LOG_NET, "PEER %u (%s), undecodable KMM frame from peer", peerId, connection->identWithQualifier().c_str()); + LogWarning(LOG_MASTER, "PEER %u (%s), undecodable KMM frame from peer", peerId, connection->identWithQualifier().c_str()); break; } @@ -1448,7 +1450,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) { KMMModifyKey* modifyKey = static_cast(frame.get()); if (modifyKey->getAlgId() > 0U && modifyKey->getKId() > 0U) { - LogMessage(LOG_NET, "PEER %u (%s) requested enc. key, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), + LogMessage(LOG_MASTER, "PEER %u (%s) requested enc. key, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); ::KeyItem keyItem = network->m_cryptoLookup->find(modifyKey->getKId()); if (!keyItem.isInvalid()) { @@ -1461,7 +1463,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) Utils::dump(1U, "FNENetwork::taskNetworkRx(), Key", key, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); } - LogMessage(LOG_NET, "PEER %u (%s) local enc. key, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), + LogMessage(LOG_MASTER, "PEER %u (%s) local enc. key, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); // build response buffer @@ -1497,7 +1499,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isReplica()) { - LogMessage(LOG_NET, "PEER %u (%s) no local key or container, requesting key from upstream master, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), + LogMessage(LOG_PEER, "PEER %u (%s) no local key or container, requesting key from upstream master, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); bool locked = network->m_keyQueueMutex.try_lock_for(std::chrono::milliseconds(60)); @@ -1545,7 +1547,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); + LogError(LOG_MASTER, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } @@ -1584,7 +1586,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); fne_lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); + LogError(LOG_MASTER, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } @@ -1620,7 +1622,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); + LogError(LOG_MASTER, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } @@ -1657,7 +1659,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) std::string ip = udp::Socket::address(req->address); lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); + LogError(LOG_MASTER, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } @@ -1697,7 +1699,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (connection->connected() && connection->address() == ip) { lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); + LogError(LOG_MASTER, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identWithQualifier().c_str()); network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } @@ -1714,7 +1716,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) aff->groupAff(srcId, dstId); offs += 8U; } - LogMessage(LOG_NET, "PEER %u (%s) announced %u affiliations", peerId, connection->identWithQualifier().c_str(), len); + LogMessage(LOG_MASTER, "PEER %u (%s) announced %u affiliations", peerId, connection->identWithQualifier().c_str(), len); // attempt to repeat traffic to replica masters if (network->m_host->m_peerNetworks.size() > 0) { @@ -1762,7 +1764,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } offs += 4U; } - LogMessage(LOG_NET, "PEER %u (%s) announced %u VCs", peerId, connection->identWithQualifier().c_str(), len); + LogMessage(LOG_MASTER, "PEER %u (%s) announced %u VCs", peerId, connection->identWithQualifier().c_str(), len); network->m_ccPeerMap[peerId] = vcPeers; // attempt to repeat traffic to replica masters @@ -1927,15 +1929,15 @@ void FNENetwork::erasePeer(uint32_t peerId) // netsplit be as noisy as possible about it... for (uint8_t i = 0U; i < 3U; i++) - LogWarning(LOG_NET, "PEER %u downstream netsplit, lost %u downstream connections", peerId, totalChildren); + LogWarning(LOG_MASTER, "PEER %u downstream netsplit, lost %u downstream connections", peerId, totalChildren); } - LogWarning(LOG_NET, "PEER %u downstream netsplit, disconnected", peerId); + LogWarning(LOG_MASTER, "PEER %u downstream netsplit, disconnected", peerId); MasterTree::erasePeer(peerId); } if (m_logSpanningTreeChanges && m_fneTree->hasChildren()) { - LogInfoEx(LOG_NET, "PEER %u Network Tree, Tree Change, current tree:", peerId); + LogInfoEx(LOG_STP, "PEER %u Network Tree, Tree Change, Current Tree", peerId); MasterTree::visualizeTreeToLog(m_fneTree); } } @@ -2010,7 +2012,7 @@ bool FNENetwork::resetPeer(uint32_t peerId) sockaddr_storage addr = connection->socketStorage(); uint32_t addrLen = connection->sockStorageLen(); - LogInfoEx(LOG_NET, "PEER %u (%s) resetting peer connection", peerId, connection->identWithQualifier().c_str()); + LogInfoEx(LOG_MASTER, "PEER %u (%s) resetting peer connection", peerId, connection->identWithQualifier().c_str()); writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_PEER_RESET, addr, addrLen); erasePeer(peerId); @@ -2020,7 +2022,7 @@ bool FNENetwork::resetPeer(uint32_t peerId) } } - LogWarning(LOG_NET, "PEER %u reset failed; peer not found.", peerId); + LogWarning(LOG_MASTER, "PEER %u reset failed; peer not found", peerId); return false; } @@ -2029,7 +2031,7 @@ bool FNENetwork::resetPeer(uint32_t peerId) void FNENetwork::setPeerReplica(bool replica) { if (!m_isReplica && replica) { - LogInfoEx(LOG_NET, "MASTER set to upstream peer replica, receiving ACL updates from upstream master"); + LogInfoEx(LOG_MASTER, "Set as upstream peer replica, receiving ACL updates from upstream master"); } m_isReplica = replica; @@ -2037,7 +2039,7 @@ void FNENetwork::setPeerReplica(bool replica) // be very noisy about being a peer replica and having multiple upstream peers if (m_isReplica) { if (m_host->m_peerNetworks.size() > 1) { - LogWarning(LOG_NET, "We are a upstream peer replica, and have multiple upstream peers? This is a bad idea. Peer Replica FNEs should have a single upstream peer connection."); + LogWarning(LOG_MASTER, "We are a upstream peer replica, and have multiple upstream peers? This is a bad idea. Peer Replica FNEs should have a single upstream peer connection."); } } } @@ -2064,7 +2066,7 @@ void FNENetwork::setupRepeaterLogin(uint32_t peerId, uint32_t streamId, FNEPeerC std::uniform_int_distribution dist(DVM_RAND_MIN, DVM_RAND_MAX); connection->salt(dist(m_random)); - LogInfoEx(LOG_NET, "PEER %u started login from, %s:%u", peerId, connection->address().c_str(), connection->port()); + LogInfoEx(LOG_MASTER, "PEER %u started login from, %s:%u", peerId, connection->address().c_str(), connection->port()); connection->connectionState(NET_STAT_WAITING_AUTHORISATION); m_peers[peerId] = connection; @@ -2075,7 +2077,7 @@ void FNENetwork::setupRepeaterLogin(uint32_t peerId, uint32_t streamId, FNEPeerC SET_UINT32(connection->salt(), salt, 0U); writePeerACK(peerId, streamId, salt, 4U); - LogInfoEx(LOG_NET, "PEER %u RPTL ACK, challenge response sent for login", peerId); + LogInfoEx(LOG_MASTER, "PEER %u RPTL ACK, challenge response sent for login", peerId); } /* Helper to send the network metadata to the specified peer in a separate thread. */ @@ -2120,7 +2122,7 @@ void FNENetwork::taskMetadataUpdate(MetadataUpdateRequest* req) // if the connection is a downstream neighbor FNE peer, and peer is participating in peer link, // send the peer proper configuration data if (connection->isNeighborFNEPeer() && connection->isReplica()) { - LogInfoEx(LOG_NET, "PEER %u (%s) sending replica network metadata updates", req->peerId, peerIdentity.c_str()); + LogInfoEx(LOG_MASTER, "PEER %u (%s) sending replica network metadata updates", req->peerId, peerIdentity.c_str()); network->writeWhitelistRIDs(req->peerId, streamId, true); network->writeTGIDs(req->peerId, streamId, true); @@ -2129,7 +2131,7 @@ void FNENetwork::taskMetadataUpdate(MetadataUpdateRequest* req) network->writeHAParameters(req->peerId, streamId, true); } else { - LogInfoEx(LOG_NET, "PEER %u (%s) sending network metadata updates", req->peerId, peerIdentity.c_str()); + LogInfoEx(LOG_MASTER, "PEER %u (%s) sending network metadata updates", req->peerId, peerIdentity.c_str()); network->writeWhitelistRIDs(req->peerId, streamId, false); network->writeBlacklistRIDs(req->peerId, streamId); @@ -2197,7 +2199,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sen PacketBuffer pkt(true, "Peer Replication, RID List"); pkt.encode((uint8_t*)buffer, len); - LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, RID List, blocks %u, streamId = %u", peerId, connection->identWithQualifier().c_str(), + LogInfoEx(LOG_REPL, "PEER %u (%s) Peer Replication, RID List, blocks %u, streamId = %u", peerId, connection->identWithQualifier().c_str(), pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { @@ -2262,7 +2264,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sen uint32_t id = ridWhitelist.at(j + (i * MAX_RID_LIST_CHUNK)); if (m_debug) - LogDebug(LOG_NET, "PEER %u (%s) whitelisting RID %u (%d / %d)", peerId, connection->identWithQualifier().c_str(), + LogDebug(LOG_MASTER, "PEER %u (%s) whitelisting RID %u (%d / %d)", peerId, connection->identWithQualifier().c_str(), id, i, j); SET_UINT32(id, payload, offs); @@ -2332,7 +2334,7 @@ void FNENetwork::writeBlacklistRIDs(uint32_t peerId, uint32_t streamId) uint32_t id = ridBlacklist.at(j + (i * MAX_RID_LIST_CHUNK)); if (m_debug) - LogDebug(LOG_NET, "PEER %u (%s) blacklisting RID %u (%d / %d)", peerId, connection->identWithQualifier().c_str(), + LogDebug(LOG_MASTER, "PEER %u (%s) blacklisting RID %u (%d / %d)", peerId, connection->identWithQualifier().c_str(), id, i, j); SET_UINT32(id, payload, offs); @@ -2398,7 +2400,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendReplica PacketBuffer pkt(true, "Peer Replication, TGID List"); pkt.encode((uint8_t*)buffer, len); - LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, TGID List, blocks %u, streamId = %u", peerId, connection->identWithQualifier().c_str(), + LogInfoEx(LOG_REPL, "PEER %u (%s) Peer Replication, TGID List, blocks %u, streamId = %u", peerId, connection->identWithQualifier().c_str(), pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { @@ -2424,7 +2426,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendReplica if (inclusion.size() > 0) { auto it = std::find(inclusion.begin(), inclusion.end(), peerId); if (it == inclusion.end()) { - // LogDebug(LOG_NET, "PEER %u TGID %u TS %u -- not included peer", peerId, entry.source().tgId(), entry.source().tgSlot()); + // LogDebug(LOG_MASTER, "PEER %u TGID %u TS %u -- not included peer", peerId, entry.source().tgId(), entry.source().tgSlot()); continue; } } @@ -2432,7 +2434,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendReplica if (exclusion.size() > 0) { auto it = std::find(exclusion.begin(), exclusion.end(), peerId); if (it != exclusion.end()) { - // LogDebug(LOG_NET, "PEER %u TGID %u TS %u -- excluded peer", peerId, entry.source().tgId(), entry.source().tgSlot()); + // LogDebug(LOG_MASTER, "PEER %u TGID %u TS %u -- excluded peer", peerId, entry.source().tgId(), entry.source().tgSlot()); continue; } } @@ -2474,7 +2476,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendReplica for (std::pair tg : tgidList) { if (m_debug) { std::string peerIdentity = resolvePeerIdentity(peerId); - LogDebug(LOG_NET, "PEER %u (%s) activating TGID %u TS %u", peerId, peerIdentity.c_str(), + LogDebug(LOG_MASTER, "PEER %u (%s) activating TGID %u TS %u", peerId, peerIdentity.c_str(), tg.first, tg.second); } SET_UINT32(tg.first, payload, offs); @@ -2503,7 +2505,7 @@ void FNENetwork::writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId) if (inclusion.size() > 0) { auto it = std::find(inclusion.begin(), inclusion.end(), peerId); if (it == inclusion.end()) { - // LogDebug(LOG_NET, "PEER %u TGID %u TS %u -- not included peer", peerId, entry.source().tgId(), entry.source().tgSlot()); + // LogDebug(LOG_MASTER, "PEER %u TGID %u TS %u -- not included peer", peerId, entry.source().tgId(), entry.source().tgSlot()); continue; } } @@ -2511,7 +2513,7 @@ void FNENetwork::writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId) if (exclusion.size() > 0) { auto it = std::find(exclusion.begin(), exclusion.end(), peerId); if (it != exclusion.end()) { - // LogDebug(LOG_NET, "PEER %u TGID %u TS %u -- excluded peer", peerId, entry.source().tgId(), entry.source().tgSlot()); + // LogDebug(LOG_MASTER, "PEER %u TGID %u TS %u -- excluded peer", peerId, entry.source().tgId(), entry.source().tgSlot()); continue; } } @@ -2532,7 +2534,7 @@ void FNENetwork::writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId) for (std::pair tg : tgidList) { if (m_debug) { std::string peerIdentity = resolvePeerIdentity(peerId); - LogDebug(LOG_NET, "PEER %u (%s) deactivating TGID %u TS %u", peerId, peerIdentity.c_str(), + LogDebug(LOG_MASTER, "PEER %u (%s) deactivating TGID %u TS %u", peerId, peerIdentity.c_str(), tg.first, tg.second); } SET_UINT32(tg.first, payload, offs); @@ -2590,7 +2592,7 @@ void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) PacketBuffer pkt(true, "Peer Replication, PID List"); pkt.encode((uint8_t*)buffer, len); - LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, PID List, blocks %u, streamId = %u", peerId, connection->identWithQualifier().c_str(), + LogInfoEx(LOG_REPL, "PEER %u (%s) Peer Replication, PID List, blocks %u, streamId = %u", peerId, connection->identWithQualifier().c_str(), pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { @@ -2638,7 +2640,7 @@ void FNENetwork::writeHAParameters(uint32_t peerId, uint32_t streamId, bool send if (sendReplica) { FNEPeerConnection* connection = m_peers[peerId]; if (connection != nullptr) { - LogInfoEx(LOG_NET, "PEER %u (%s) Peer Replication, HA parameters, streamId = %u", peerId, connection->identWithQualifier().c_str(), streamId); + LogInfoEx(LOG_REPL, "PEER %u (%s) Peer Replication, HA parameters, streamId = %u", peerId, connection->identWithQualifier().c_str(), streamId); writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_HA_PARAMS}, buffer, len, 0U, streamId, false, true, true); } @@ -2785,37 +2787,37 @@ void FNENetwork::logPeerNAKReason(uint32_t peerId, const char* tag, NET_CONN_NAK { switch (reason) { case NET_CONN_NAK_MODE_NOT_ENABLED: - LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; digital mode not enabled on FNE", peerId, tag, (uint16_t)reason); + LogWarning(LOG_MASTER, "PEER %u NAK %s, reason = %u; digital mode not enabled on FNE", peerId, tag, (uint16_t)reason); break; case NET_CONN_NAK_ILLEGAL_PACKET: - LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; illegal/unknown packet", peerId ,tag, (uint16_t)reason); + LogWarning(LOG_MASTER, "PEER %u NAK %s, reason = %u; illegal/unknown packet", peerId ,tag, (uint16_t)reason); break; case NET_CONN_NAK_FNE_UNAUTHORIZED: - LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; unauthorized", peerId, tag, (uint16_t)reason); + LogWarning(LOG_MASTER, "PEER %u NAK %s, reason = %u; unauthorized", peerId, tag, (uint16_t)reason); break; case NET_CONN_NAK_BAD_CONN_STATE: - LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; bad connection state", peerId ,tag, (uint16_t)reason); + LogWarning(LOG_MASTER, "PEER %u NAK %s, reason = %u; bad connection state", peerId ,tag, (uint16_t)reason); break; case NET_CONN_NAK_INVALID_CONFIG_DATA: - LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; invalid configuration data", peerId, tag, (uint16_t)reason); + LogWarning(LOG_MASTER, "PEER %u NAK %s, reason = %u; invalid configuration data", peerId, tag, (uint16_t)reason); break; case NET_CONN_NAK_FNE_MAX_CONN: - LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; FNE has reached maximum permitted connections", peerId, tag, (uint16_t)reason); + LogWarning(LOG_MASTER, "PEER %u NAK %s, reason = %u; FNE has reached maximum permitted connections", peerId, tag, (uint16_t)reason); break; case NET_CONN_NAK_PEER_RESET: - LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; FNE demanded connection reset", peerId, tag, (uint16_t)reason); + LogWarning(LOG_MASTER, "PEER %u NAK %s, reason = %u; FNE demanded connection reset", peerId, tag, (uint16_t)reason); break; case NET_CONN_NAK_PEER_ACL: - LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; ACL rejection", peerId, tag, (uint16_t)reason); + LogWarning(LOG_MASTER, "PEER %u NAK %s, reason = %u; ACL rejection", peerId, tag, (uint16_t)reason); break; case NET_CONN_NAK_FNE_DUPLICATE_CONN: - LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; duplicate connection drop", peerId, tag, (uint16_t)reason); + LogWarning(LOG_MASTER, "PEER %u NAK %s, reason = %u; duplicate connection drop", peerId, tag, (uint16_t)reason); break; case NET_CONN_NAK_GENERAL_FAILURE: default: - LogWarning(LOG_NET, "PEER %u NAK %s, reason = %u; general failure", peerId, tag, (uint16_t)reason); + LogWarning(LOG_MASTER, "PEER %u NAK %s, reason = %u; general failure", peerId, tag, (uint16_t)reason); break; } } @@ -2855,7 +2857,7 @@ bool FNENetwork::writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REA SET_UINT16((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_MASTER, "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); } @@ -2870,7 +2872,7 @@ void FNENetwork::processTEKResponse(p25::kmm::KeyItem* rspKi, uint8_t algId, uin if (rspKi == nullptr) return; - LogMessage(LOG_NET, "upstream master enc. key, algId = $%02X, kID = $%04X", algId, rspKi->kId()); + LogMessage(LOG_PEER, "upstream master enc. key, algId = $%02X, kID = $%04X", algId, rspKi->kId()); m_keyQueueMutex.lock(); diff --git a/src/fne/network/MasterTree.cpp b/src/fne/network/MasterTree.cpp index 49fb49ac..a7f8b86e 100644 --- a/src/fne/network/MasterTree.cpp +++ b/src/fne/network/MasterTree.cpp @@ -175,7 +175,7 @@ void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std existingNode = new MasterTree(id, masterId, parent); json::array childArray = obj["children"].get(); - //LogDebugEx(LOG_NET, "MasterTree::deserializeTree()", "peerId = %u, masterId = %u, childrenCnt = %u", + //LogDebugEx(LOG_STP, "MasterTree::deserializeTree()", "peerId = %u, masterId = %u, childrenCnt = %u", // existingNode->id(), existingNode->masterId(), childArray.size()); if (childArray.size() > 0U) deserializeTree(childArray, existingNode, duplicatePeers); @@ -193,7 +193,7 @@ void MasterTree::visualizeTreeToLog(MasterTree* node, uint32_t level) } if (level == 0U) - LogInfoEx(LOG_NET, "Peer ID: %u, Master Peer ID: %u, Children: %u, IsRoot: %u", + LogInfoEx(LOG_STP, "Peer ID: %u, Master Peer ID: %u, Children: %u, IsRoot: %u", node->id(), node->masterId(), node->m_children.size(), node->isRoot()); std::string indent; @@ -202,7 +202,7 @@ void MasterTree::visualizeTreeToLog(MasterTree* node, uint32_t level) } for (auto child : node->m_children) { - LogInfoEx(LOG_NET, "%s- Peer ID: %u, Master Peer ID: %u, Children: %u, IsRoot: %u", + LogInfoEx(LOG_STP, "%s- Peer ID: %u, Master Peer ID: %u, Children: %u, IsRoot: %u", indent.c_str(), child->id(), child->masterId(), child->m_children.size(), child->isRoot()); visualizeTreeToLog(child, level + 1U); } diff --git a/src/fne/network/P25OTARService.cpp b/src/fne/network/P25OTARService.cpp index ecd57273..cdf69c0c 100644 --- a/src/fne/network/P25OTARService.cpp +++ b/src/fne/network/P25OTARService.cpp @@ -36,7 +36,7 @@ using namespace p25::kmm; // Macro helper to verbose log a generic KMM. #define VERBOSE_LOG_KMM(_PCKT_STR, __LLID) \ if (m_verbose) { \ - LogMessage(LOG_NET, "KMM, %s, llId = %u", _PCKT_STR.c_str(), __LLID); \ + LogMessage(LOG_P25, "KMM, %s, llId = %u", _PCKT_STR.c_str(), __LLID); \ } // --------------------------------------------------------------------------- @@ -287,7 +287,7 @@ UInt8Array P25OTARService::cryptKMM(uint8_t algoId, uint16_t kid, uint8_t* mi, c uint8_t keyLength = keyItem.getKey(key); if (m_network->m_debug) { - LogDebugEx(LOG_HOST, "P25OTARService::cryptKMM()", "keyLength = %u", keyLength); + LogDebugEx(LOG_P25, "P25OTARService::cryptKMM()", "keyLength = %u", keyLength); Utils::dump(1U, "P25OTARService::cryptKMM(), Key", key, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); } @@ -300,7 +300,7 @@ UInt8Array P25OTARService::cryptKMM(uint8_t algoId, uint16_t kid, uint8_t* mi, c // TODO: implement handling AES-256 KMM encryption/decryption for data blocks return outBuffer; default: - LogError(LOG_HOST, "unsupported KEK algorithm, algoId = $%02X", algoId); + LogError(LOG_P25, "unsupported KEK algorithm, algoId = $%02X", algoId); break; } } diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index ab43ceff..9a1efda0 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -129,7 +129,7 @@ bool PeerNetwork::writePeerLinkPeers(json::array* peerList) pkt.encode((uint8_t*)buffer, len); uint32_t streamId = createStreamId(); - LogInfoEx(LOG_NET, "PEER %u Peer Replication, Active Peer List, blocks %u, streamId = %u", m_peerId, pkt.fragments.size(), streamId); + LogInfoEx(LOG_PEER, "PEER %u Peer Replication, Active Peer List, blocks %u, streamId = %u", m_peerId, pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writeMaster({ NET_FUNC::REPL, NET_SUBFUNC::REPL_ACT_PEER_LIST }, @@ -171,7 +171,7 @@ bool PeerNetwork::writeMasterTree(MasterTree* treeRoot) pkt.encode((uint8_t*)buffer, len); uint32_t streamId = createStreamId(); - LogInfoEx(LOG_NET, "PEER %u Network Tree, Tree List, blocks %u, streamId = %u", m_peerId, pkt.fragments.size(), streamId); + LogInfoEx(LOG_PEER, "PEER %u Network Tree, Tree List, blocks %u, streamId = %u", m_peerId, pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writeMaster({ NET_FUNC::NET_TREE, NET_SUBFUNC::NET_TREE_LIST }, @@ -253,7 +253,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco // enqueue the task if (!m_threadPool.enqueue(new_pooltask(taskNetworkRx, req))) { - LogError(LOG_NET, "Failed to task enqueue network packet request, peerId = %u", peerId); + LogError(LOG_PEER, "Failed to task enqueue network packet request, peerId = %u", peerId); if (req != nullptr) { if (req->buffer != nullptr) delete[] req->buffer; @@ -273,7 +273,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco if (m_tgidPkt.decode(data, &decompressed, &decompressedLen)) { if (m_tidLookup == nullptr) { - LogError(LOG_NET, "Talkgroup ID lookup not available yet."); + LogError(LOG_PEER, "Talkgroup ID lookup not available yet."); m_tgidPkt.clear(); delete[] decompressed; break; @@ -298,7 +298,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco 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()); + LogError(LOG_PEER, "Cannot open the talkgroup ID lookup file - %s", filename.c_str()); m_tgidPkt.clear(); delete[] decompressed; break; @@ -332,7 +332,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco if (m_ridPkt.decode(data, &decompressed, &decompressedLen)) { if (m_ridLookup == nullptr) { - LogError(LOG_NET, "Radio ID lookup not available yet."); + LogError(LOG_PEER, "Radio ID lookup not available yet."); m_ridPkt.clear(); delete[] decompressed; break; @@ -357,7 +357,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco 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()); + LogError(LOG_PEER, "Cannot open the radio ID lookup file - %s", filename.c_str()); m_ridPkt.clear(); delete[] decompressed; break; @@ -391,7 +391,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco if (m_pidPkt.decode(data, &decompressed, &decompressedLen)) { if (m_pidLookup == nullptr) { - LogError(LOG_NET, "Peer ID lookup not available yet."); + LogError(LOG_PEER, "Peer ID lookup not available yet."); m_pidPkt.clear(); delete[] decompressed; break; @@ -416,7 +416,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco 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()); + LogError(LOG_PEER, "Cannot open the peer ID lookup file - %s", filename.c_str()); m_pidPkt.clear(); delete[] decompressed; break; @@ -455,7 +455,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco case NET_SUBFUNC::NET_TREE_DISC: // Network Tree Disconnect { uint32_t offendingPeerId = GET_UINT32(data, 6U); - LogWarning(LOG_NET, "PEER %u Network Tree Disconnect, requested from upstream master, possible duplicate connection for PEER %u", m_peerId, offendingPeerId); + LogWarning(LOG_PEER, "PEER %u Network Tree Disconnect, requested from upstream master, possible duplicate connection for PEER %u", m_peerId, offendingPeerId); if (m_netTreeDiscCallback != nullptr) { m_netTreeDiscCallback(this, offendingPeerId); @@ -572,23 +572,23 @@ void PeerNetwork::taskNetworkRx(PeerPacketRequest* req) // determine if this packet is late (i.e. are we processing this packet more than 200ms after it was received?) uint64_t dt = req->pktRxTime + PACKET_LATE_TIME; if (dt < now) { - LogWarning(LOG_NET, "PEER %u packet processing latency >200ms, dt = %u, now = %u", req->peerId, dt, now); + LogWarning(LOG_PEER, "PEER %u packet processing latency >200ms, dt = %u, now = %u", req->peerId, dt, now); } uint16_t lastRxSeq = 0U; MULTIPLEX_RET_CODE ret = network->m_mux->verifyStream(req->streamId, req->rtpHeader.getSequence(), req->fneHeader.getFunction(), &lastRxSeq); if (ret == MUX_LOST_FRAMES) { - LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", req->fneHeader.getPeerId(), + LogError(LOG_PEER, "PEER %u stream %u possible lost frames; got %u, expected %u", req->fneHeader.getPeerId(), req->streamId, req->rtpHeader.getSequence(), lastRxSeq); } else if (ret == MUX_OUT_OF_ORDER) { - LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", req->fneHeader.getPeerId(), + LogError(LOG_PEER, "PEER %u stream %u out-of-order; got %u, expected >%u", req->fneHeader.getPeerId(), req->streamId, req->rtpHeader.getSequence(), lastRxSeq); } #if DEBUG_RTP_MUX else { - LogDebugEx(LOG_NET, "PeerNetwork::taskNetworkRx()", "PEER %u valid mux, seq = %u, streamId = %u", req->fneHeader.getPeerId(), req->rtpHeader.getSequence(), req->streamId); + LogDebugEx(LOG_PEER, "PeerNetwork::taskNetworkRx()", "PEER %u valid mux, seq = %u, streamId = %u", req->fneHeader.getPeerId(), req->rtpHeader.getSequence(), req->streamId); } #endif // process incomfing message subfunction opcodes diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 2068c93f..a86d796c 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -117,7 +117,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } } - LogMessage(LOG_NET, "Analog, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB @@ -159,7 +159,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee if (m_network->m_callCollisionTimeout > 0U) { uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { - LogWarning(LOG_NET, "Analog, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); m_status.lock(false); m_status[dstId].streamId = streamId; @@ -167,7 +167,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status.unlock(); } else { - LogWarning(LOG_NET, "Analog, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); return false; } @@ -205,7 +205,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status[dstId].activeCall = true; m_status.unlock(); - LogMessage(LOG_NET, "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } } @@ -272,7 +272,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId, true); if (m_network->m_debug) { - LogDebug(LOG_NET, "Analog, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", + LogDebugEx(LOG_ANALOG, "TagAnalogData::processFrame()", "Master, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", ssrc, peerId, peer.first, seqNo, srcId, dstId, len, pktSeq, streamId, fromUpstream); } @@ -322,7 +322,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { - LogDebug(LOG_NET, "Analog, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", + LogDebugEx(LOG_ANALOG, "TagAnalogData::processFrame()", "Peers, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", ssrc, peerId, dstPeerId, seqNo, srcId, dstId, len, pktSeq, streamId, fromUpstream); } } @@ -349,7 +349,7 @@ void TagAnalogData::playbackParrot() if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { - LogDebug(LOG_NET, "Analog, parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", + LogDebugEx(LOG_ANALOG, "TagAnalogData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", pkt.peerId, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } } @@ -358,7 +358,7 @@ void TagAnalogData::playbackParrot() for (auto peer : m_network->m_peers) { m_network->writePeer(peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { - LogDebug(LOG_NET, "Analog, parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", + LogDebugEx(LOG_ANALOG, "TagAnalogData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", peer.first, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } } @@ -545,6 +545,9 @@ bool TagAnalogData::validate(uint32_t peerId, data::NetData& data, uint32_t stre .requestAsync(m_network->m_influxServer); } + if (m_network->m_logDenials) + LogError(LOG_ANALOG, INFLUXDB_ERRSTR_DISABLED_SRC_RID ", peer = %u, srcId = %u, dstId = %u", peerId, data.getSrcId(), data.getDstId()); + // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG, NET_ICC::REJECT_TRAFFIC, data.getDstId()); return false; @@ -581,6 +584,9 @@ bool TagAnalogData::validate(uint32_t peerId, data::NetData& data, uint32_t stre .requestAsync(m_network->m_influxServer); } + if (m_network->m_logDenials) + LogError(LOG_ANALOG, INFLUXDB_ERRSTR_DISABLED_DST_RID ", peer = %u, srcId = %u, dstId = %u", peerId, data.getSrcId(), data.getDstId()); + return false; } } @@ -596,12 +602,13 @@ bool TagAnalogData::validate(uint32_t peerId, data::NetData& data, uint32_t stre .tag("streamId", std::to_string(streamId)) .tag("srcId", std::to_string(data.getSrcId())) .tag("dstId", std::to_string(data.getDstId())) - .field("message", std::string(INFLUXDB_ERRSTR_DISABLED_SRC_RID)) + .field("message", std::string(INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS)) .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) .requestAsync(m_network->m_influxServer); } - LogWarning(LOG_NET, "Analog, illegal/unknown RID attempted access, srcId = %u, dstId = %u", data.getSrcId(), data.getDstId()); + if (m_network->m_logDenials) + LogWarning(LOG_ANALOG, INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", data.getSrcId(), data.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG, NET_ICC::REJECT_TRAFFIC, data.getDstId()); @@ -627,6 +634,9 @@ bool TagAnalogData::validate(uint32_t peerId, data::NetData& data, uint32_t stre .requestAsync(m_network->m_influxServer); } + if (m_network->m_logDenials) + LogError(LOG_ANALOG, INFLUXDB_ERRSTR_INV_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", peerId, data.getSrcId(), data.getDstId()); + // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG, NET_ICC::REJECT_TRAFFIC, data.getDstId()); return false; @@ -653,12 +663,13 @@ bool TagAnalogData::validate(uint32_t peerId, data::NetData& data, uint32_t stre .tag("streamId", std::to_string(streamId)) .tag("srcId", std::to_string(data.getSrcId())) .tag("dstId", std::to_string(data.getDstId())) - .field("message", std::string(INFLUXDB_ERRSTR_DISABLED_SRC_RID)) + .field("message", std::string(INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS)) .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) .requestAsync(m_network->m_influxServer); } - LogWarning(LOG_NET, "Analog, illegal/unknown RID attempted access, srcId = %u, dstId = %u", data.getSrcId(), data.getDstId()); + if (m_network->m_logDenials) + LogWarning(LOG_ANALOG, INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", data.getSrcId(), data.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG, NET_ICC::REJECT_TRAFFIC, data.getDstId()); @@ -680,6 +691,9 @@ bool TagAnalogData::validate(uint32_t peerId, data::NetData& data, uint32_t stre .requestAsync(m_network->m_influxServer); } + if (m_network->m_logDenials) + LogError(LOG_ANALOG, INFLUXDB_ERRSTR_DISABLED_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", peerId, data.getSrcId(), data.getDstId()); + // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG, NET_ICC::REJECT_TRAFFIC, data.getDstId()); return false; @@ -705,6 +719,9 @@ bool TagAnalogData::validate(uint32_t peerId, data::NetData& data, uint32_t stre .requestAsync(m_network->m_influxServer); } + if (m_network->m_logDenials) + LogError(LOG_ANALOG, INFLUXDB_ERRSTR_RID_NOT_PERMITTED ", peer = %u, srcId = %u, dstId = %u", peerId, data.getSrcId(), data.getDstId()); + // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG, NET_ICC::REJECT_TRAFFIC, data.getDstId()); return false; diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index a2ad01cb..18c1d289 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -189,11 +189,11 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogMessage(LOG_NET, "DMR, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream); } else - LogMessage(LOG_NET, "DMR, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB @@ -242,7 +242,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { - LogMessage(LOG_NET, "DMR, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, fromUpstream); m_status[dstId].srcId = srcId; } @@ -253,14 +253,14 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (m_network->m_callCollisionTimeout > 0U) { uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { - LogWarning(LOG_NET, "DMR, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; m_status.unlock(); } else { - LogWarning(LOG_NET, "DMR, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, fromUpstream); return false; } @@ -317,11 +317,11 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogMessage(LOG_NET, "DMR, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } else - LogMessage(LOG_NET, "DMR, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } } @@ -460,7 +460,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId, true); if (m_network->m_debug) { - LogDebug(LOG_NET, "DMR, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, flco = $%02X, slotNo = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", + LogDebugEx(LOG_DMR, "TagDMRData::processFrame()", "Master, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, flco = $%02X, slotNo = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", ssrc, peerId, peer.first, seqNo, srcId, dstId, flco, slotNo, len, pktSeq, streamId, fromUpstream); } @@ -516,7 +516,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { - LogDebug(LOG_NET, "DMR, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, flco = $%02X, slotNo = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", + LogDebugEx(LOG_DMR, "TagDMRData::processFrame()", "Peers, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, flco = $%02X, slotNo = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", ssrc, peerId, dstPeerId, seqNo, srcId, dstId, flco, slotNo, len, pktSeq, streamId, fromUpstream); } } @@ -590,7 +590,7 @@ void TagDMRData::playbackParrot() if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { - LogDebug(LOG_NET, "DMR, parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", + LogDebugEx(LOG_DMR, "TagDMRData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", pkt.peerId, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } } @@ -599,7 +599,7 @@ void TagDMRData::playbackParrot() for (auto peer : m_network->m_peers) { m_network->writePeer(peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { - LogDebug(LOG_NET, "DMR, parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", + LogDebugEx(LOG_DMR, "TagDMRData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", peer.first, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } } @@ -621,7 +621,7 @@ void TagDMRData::write_Ext_Func(uint32_t peerId, uint8_t slot, uint32_t func, ui csbk->setSrcId(arg); csbk->setDstId(dstId); - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, %s, op = $%02X, arg = %u, tgt = %u", + LogMessage(LOG_DMR, "DMR Slot %u, DT_CSBK, %s, op = $%02X, arg = %u, tgt = %u", slot, csbk->toString().c_str(), func, arg, dstId); write_CSBK(peerId, slot, csbk.get()); @@ -636,7 +636,7 @@ void TagDMRData::write_Call_Alrt(uint32_t peerId, uint8_t slot, uint32_t srcId, csbk->setSrcId(srcId); csbk->setDstId(dstId); - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, %s, srcId = %u, dstId = %u", + LogMessage(LOG_DMR, "DMR Slot %u, DT_CSBK, %s, srcId = %u, dstId = %u", slot, csbk->toString().c_str(), srcId, dstId); write_CSBK(peerId, slot, csbk.get()); @@ -673,7 +673,7 @@ void TagDMRData::routeRewrite(uint8_t* buffer, uint32_t peerId, dmr::data::NetDa lc::FullLC fullLC; std::unique_ptr lc = fullLC.decode(data + 2U, dataType); if (lc == nullptr) { - LogWarning(LOG_NET, "DMR Slot %u, bad LC received from the network, replacing", slotNo); + LogWarning(LOG_DMR, "DMR Slot %u, bad LC received from the network, replacing", slotNo); lc = std::make_unique(dmrData.getFLCO(), dmrData.getSrcId(), rewriteDstId); } @@ -688,7 +688,7 @@ void TagDMRData::routeRewrite(uint8_t* buffer, uint32_t peerId, dmr::data::NetDa lc::FullLC fullLC; std::unique_ptr lc = fullLC.decodePI(data + 2U); if (lc == nullptr) { - LogWarning(LOG_NET, "DMR Slot %u, DT_VOICE_PI_HEADER, bad LC received, replacing", slotNo); + LogWarning(LOG_DMR, "DMR Slot %u, DT_VOICE_PI_HEADER, bad LC received, replacing", slotNo); lc = std::make_unique(); } @@ -775,11 +775,11 @@ bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::NetDat lc::csbk::CSBK_BROADCAST* osp = static_cast(csbk.get()); if (osp->getAnncType() == BroadcastAnncType::ANN_WD_TSCC) { if (m_network->m_disallowAdjStsBcast) { - // LogWarning(LOG_NET, "PEER %u, passing BroadcastAnncType::ANN_WD_TSCC to internal peers is prohibited, dropping", peerId); + // LogWarning(LOG_DMR, "PEER %u, passing BroadcastAnncType::ANN_WD_TSCC to internal peers is prohibited, dropping", peerId); return false; } else { if (m_network->m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, %s, sysId = $%03X, chNo = %u, peerId = %u", dmrData.getSlotNo(), csbk->toString().c_str(), + LogMessage(LOG_DMR, "DMR Slot %u, DT_CSBK, %s, sysId = $%03X, chNo = %u, peerId = %u", dmrData.getSlotNo(), csbk->toString().c_str(), osp->getSystemId(), osp->getLogicalCh1(), peerId); } } @@ -791,7 +791,7 @@ bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::NetDat } } else { std::string peerIdentity = m_network->resolvePeerIdentity(peerId); - LogWarning(LOG_NET, "PEER %u (%s), passing CSBK that failed to decode? csbk == nullptr", peerId, peerIdentity.c_str()); + LogWarning(LOG_DMR, "PEER %u (%s), passing CSBK that failed to decode? csbk == nullptr", peerId, peerIdentity.c_str()); } } @@ -934,7 +934,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, lc::CSBK* csbk, } if (m_network->m_logDenials) - LogError(LOG_NET, "DMR Slot %u, " INFLUXDB_ERRSTR_DISABLED_SRC_RID ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); + LogError(LOG_DMR, "DMR Slot %u, " INFLUXDB_ERRSTR_DISABLED_SRC_RID ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo()); @@ -998,7 +998,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, lc::CSBK* csbk, case ExtendedFunctions::UNINHIBIT: { if (!pid.peerDefault() && !pid.canIssueInhibit()) { - LogWarning(LOG_NET, "DMR, PEER %u attempted inhibit/unhibit, not authorized", peerId); + LogWarning(LOG_DMR, "DMR, PEER %u attempted inhibit/unhibit, not authorized", peerId); return false; } } @@ -1035,7 +1035,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, lc::CSBK* csbk, } if (m_network->m_logDenials) - LogError(LOG_NET, "DMR Slot %u, " INFLUXDB_ERRSTR_DISABLED_DST_RID ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); + LogError(LOG_DMR, "DMR Slot %u, " INFLUXDB_ERRSTR_DISABLED_DST_RID ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); return false; } @@ -1059,7 +1059,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, lc::CSBK* csbk, } if (m_network->m_logDenials) - LogWarning(LOG_NET, "DMR slot %s, " INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", data.getSlotNo(), data.getSrcId(), data.getDstId()); + LogWarning(LOG_DMR, "DMR slot %s, " INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", data.getSlotNo(), data.getSrcId(), data.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo()); @@ -1087,7 +1087,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, lc::CSBK* csbk, } if (m_network->m_logDenials) - LogError(LOG_NET, "DMR Slot %u, " INFLUXDB_ERRSTR_INV_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); + LogError(LOG_DMR, "DMR Slot %u, " INFLUXDB_ERRSTR_INV_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo()); @@ -1122,7 +1122,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, lc::CSBK* csbk, } if (m_network->m_logDenials) - LogWarning(LOG_NET, "DMR slot %s, " INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", data.getSlotNo(), data.getSrcId(), data.getDstId()); + LogWarning(LOG_DMR, "DMR slot %s, " INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", data.getSlotNo(), data.getSrcId(), data.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo()); @@ -1146,7 +1146,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, lc::CSBK* csbk, } if (m_network->m_logDenials) - LogError(LOG_NET, "DMR Slot %u, " INFLUXDB_ERRSTR_INV_SLOT ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); + LogError(LOG_DMR, "DMR Slot %u, " INFLUXDB_ERRSTR_INV_SLOT ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo()); @@ -1170,7 +1170,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, lc::CSBK* csbk, } if (m_network->m_logDenials) - LogError(LOG_NET, "DMR Slot %u, " INFLUXDB_ERRSTR_DISABLED_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); + LogError(LOG_DMR, "DMR Slot %u, " INFLUXDB_ERRSTR_DISABLED_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo()); @@ -1198,7 +1198,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, lc::CSBK* csbk, } if (m_network->m_logDenials) - LogError(LOG_NET, "DMR Slot %u, " INFLUXDB_ERRSTR_RID_NOT_PERMITTED ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); + LogError(LOG_DMR, "DMR Slot %u, " INFLUXDB_ERRSTR_RID_NOT_PERMITTED ", peer = %u, srcId = %u, dstId = %u", data.getSlotNo(), peerId, data.getSrcId(), data.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo()); @@ -1230,7 +1230,7 @@ bool TagDMRData::write_CSBK_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstI lookups::AffiliationLookup* aff = m_network->m_peerAffiliations[peerId]; if (aff == nullptr) { std::string peerIdentity = m_network->resolvePeerIdentity(peerId); - LogError(LOG_NET, "PEER %u (%s) has an invalid affiliations lookup? This shouldn't happen BUGBUG.", peerId, peerIdentity.c_str()); + LogError(LOG_MASTER, "PEER %u (%s) has an invalid affiliations lookup? This shouldn't happen BUGBUG.", peerId, peerIdentity.c_str()); return false; // this will cause no traffic to pass for this peer now...I'm not sure this is good behavior } else { @@ -1247,7 +1247,7 @@ bool TagDMRData::write_CSBK_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstI csbk->setSlotNo(slot); if (m_network->m_verbose) { - LogMessage(LOG_NET, "DMR, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u, peerId = %u", + LogMessage(LOG_DMR, "DMR, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u, peerId = %u", csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId, peerId); } @@ -1263,7 +1263,7 @@ bool TagDMRData::write_CSBK_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstI csbk->setSlotNo(slot); if (m_network->m_verbose) { - LogMessage(LOG_NET, "DMR, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u, peerId = %u", + LogMessage(LOG_DMR, "DMR, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u, peerId = %u", csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId, peerId); } @@ -1350,7 +1350,7 @@ void TagDMRData::write_CSBK(uint32_t peerId, uint8_t slot, lc::CSBK* csbk) m_network->writePeer(peer.first, m_network->m_peerId, { 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", + LogDebugEx(LOG_DMR, "TagDMRData::write_CSBK()", "peer = %u, slotNo = %u, len = %u, stream = %u", peer.first, slot, messageLength, streamId); } i++; @@ -1365,7 +1365,7 @@ void TagDMRData::write_CSBK(uint32_t peerId, uint8_t slot, lc::CSBK* csbk) 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", + LogDebugEx(LOG_DMR, "TagDMRData::write_CSBK()", "peer = %u, slotNo = %u, len = %u, stream = %u", dstPeerId, slot, messageLength, streamId); } } diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index f48d50ba..ec59e575 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -228,11 +228,11 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogMessage(LOG_NET, "NXDN, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); } else - LogMessage(LOG_NET, "NXDN, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB @@ -280,7 +280,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { - LogMessage(LOG_NET, "NXDN, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); m_status[dstId].srcId = srcId; } @@ -291,14 +291,14 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI if (m_network->m_callCollisionTimeout > 0U) { uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { - LogWarning(LOG_NET, "NXDN, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; m_status.unlock(); } else { - LogWarning(LOG_NET, "NXDN, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); return false; } @@ -352,11 +352,11 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogMessage(LOG_NET, "NXDN, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } else - LogMessage(LOG_NET, "NXDN, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } } @@ -490,7 +490,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId, true); if (m_network->m_debug) { - LogDebug(LOG_NET, "NXDN, ssrc = %u, srcPeer = %u, dstPeer = %u, messageType = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", + LogDebugEx(LOG_NXDN, "TagNXDNData::processFrame()", "Master, ssrc = %u, srcPeer = %u, dstPeer = %u, messageType = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", ssrc, peerId, peer.first, messageType, srcId, dstId, len, pktSeq, streamId, fromUpstream); } @@ -546,7 +546,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { - LogDebug(LOG_NET, "NXDN, ssrc = %u, srcPeer = %u, dstPeer = %u, messageType = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", + LogDebugEx(LOG_NXDN, "TagNXDNData::processFrame()", "Peers, ssrc = %u, srcPeer = %u, dstPeer = %u, messageType = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", ssrc, peerId, dstPeerId, messageType, srcId, dstId, len, pktSeq, streamId, fromUpstream); } } @@ -620,7 +620,7 @@ void TagNXDNData::playbackParrot() if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { - LogDebug(LOG_NET, "NXDN, parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", + LogDebugEx(LOG_NXDN, "TagNXDNData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", pkt.peerId, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } } @@ -629,7 +629,7 @@ void TagNXDNData::playbackParrot() for (auto peer : m_network->m_peers) { m_network->writePeer(peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { - LogDebug(LOG_NET, "NXDN, parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", + LogDebugEx(LOG_NXDN, "TagNXDNData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", peer.first, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } } @@ -825,7 +825,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u } if (m_network->m_logDenials) - LogError(LOG_NET, "NXDN, " INFLUXDB_ERRSTR_DISABLED_SRC_RID ", peer = %u, srcId = %u, dstId = %u", peerId, lc.getSrcId(), lc.getDstId()); + LogError(LOG_NXDN, INFLUXDB_ERRSTR_DISABLED_SRC_RID ", peer = %u, srcId = %u, dstId = %u", peerId, lc.getSrcId(), lc.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId()); @@ -864,7 +864,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u } if (m_network->m_logDenials) - LogError(LOG_NET, "NXDN, " INFLUXDB_ERRSTR_DISABLED_DST_RID ", peer = %u, srcId = %u, dstId = %u", peerId, lc.getSrcId(), lc.getDstId()); + LogError(LOG_NXDN, INFLUXDB_ERRSTR_DISABLED_DST_RID ", peer = %u, srcId = %u, dstId = %u", peerId, lc.getSrcId(), lc.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId()); @@ -889,7 +889,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u } if (m_network->m_logDenials) - LogWarning(LOG_NET, "NXDN, " INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", lc.getSrcId(), lc.getDstId()); + LogWarning(LOG_NXDN, INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", lc.getSrcId(), lc.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId()); @@ -918,7 +918,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u } if (m_network->m_logDenials) - LogError(LOG_NET, "NXDN, " INFLUXDB_ERRSTR_INV_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", peerId, lc.getSrcId(), lc.getDstId()); + LogError(LOG_NXDN, INFLUXDB_ERRSTR_INV_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", peerId, lc.getSrcId(), lc.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId()); @@ -952,7 +952,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u } if (m_network->m_logDenials) - LogWarning(LOG_NET, "NXDN, " INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", lc.getSrcId(), lc.getDstId()); + LogWarning(LOG_NXDN, INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", lc.getSrcId(), lc.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId()); @@ -975,7 +975,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u } if (m_network->m_logDenials) - LogError(LOG_NET, "NXDN, " INFLUXDB_ERRSTR_DISABLED_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", peerId, lc.getSrcId(), lc.getDstId()); + LogError(LOG_NXDN, INFLUXDB_ERRSTR_DISABLED_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", peerId, lc.getSrcId(), lc.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId()); @@ -1003,7 +1003,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u } if (m_network->m_logDenials) - LogError(LOG_NET, "NXDN, " INFLUXDB_ERRSTR_RID_NOT_PERMITTED ", peer = %u, srcId = %u, dstId = %u", peerId, lc.getSrcId(), lc.getDstId()); + LogError(LOG_NXDN, INFLUXDB_ERRSTR_RID_NOT_PERMITTED ", peer = %u, srcId = %u, dstId = %u", peerId, lc.getSrcId(), lc.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId()); @@ -1029,7 +1029,7 @@ bool TagNXDNData::write_Message_Grant(uint32_t peerId, uint32_t srcId, uint32_t lookups::AffiliationLookup* aff = m_network->m_peerAffiliations[peerId]; if (aff == nullptr) { std::string peerIdentity = m_network->resolvePeerIdentity(peerId); - LogError(LOG_NET, "PEER %u (%s) has an invalid affiliations lookup? This shouldn't happen BUGBUG.", peerId, peerIdentity.c_str()); + LogError(LOG_MASTER, "PEER %u (%s) has an invalid affiliations lookup? This shouldn't happen BUGBUG.", peerId, peerIdentity.c_str()); return false; // this will cause no traffic to pass for this peer now...I'm not sure this is good behavior } else { @@ -1049,7 +1049,7 @@ bool TagNXDNData::write_Message_Grant(uint32_t peerId, uint32_t srcId, uint32_t rcch->setPriority(priority); if (m_network->m_verbose) { - LogMessage(LOG_NET, "NXDN, %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u, peerId = %u", + LogMessage(LOG_NXDN, "%s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u, peerId = %u", rcch->toString().c_str(), rcch->getEmergency(), rcch->getEncrypted(), rcch->getPriority(), rcch->getGrpVchNo(), rcch->getSrcId(), rcch->getDstId(), peerId); } @@ -1076,7 +1076,7 @@ void TagNXDNData::write_Message_Deny(uint32_t peerId, uint32_t srcId, uint32_t d rcch->setDstId(dstId); if (m_network->m_verbose) { - LogMessage(LOG_RF, "NXDN, MSG_DENIAL (Message Denial), reason = $%02X (%s), service = $%02X, srcId = %u, dstId = %u", + LogMessage(LOG_NXDN, "MSG_DENIAL (Message Denial), reason = $%02X (%s), service = $%02X, srcId = %u, dstId = %u", reason, NXDNUtils::causeToString(reason).c_str(), service, srcId, dstId); } diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 8c6fc8a9..6bb4bb27 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -131,7 +131,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } if (m_debug) { - LogDebug(LOG_NET, P25_HDU_STR ", HDU_BSDWNACT, dstId = %u, algo = $%02X, kid = $%04X", dstId, algId, kid); + LogDebug((fromUpstream) ? LOG_PEER : LOG_MASTER, P25_HDU_STR ", HDU_BSDWNACT, dstId = %u, algo = $%02X, kid = $%04X", dstId, algId, kid); if (algId != ALGO_UNENCRYPT) { LogDebug(LOG_NET, P25_HDU_STR ", Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", @@ -209,7 +209,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_status.end()) { if (grantDemand && !switchOver) { - LogWarning(LOG_NET, "P25, Call Grant Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Grant Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); return false; } @@ -237,11 +237,11 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogMessage(LOG_NET, "P25, Private Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Private Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream); } else - LogMessage(LOG_NET, "P25, Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB @@ -290,7 +290,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { - LogMessage(LOG_NET, "P25, Call Source Switched, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Source Switched, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); m_status[dstId].srcId = srcId; } @@ -300,7 +300,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (m_network->m_callCollisionTimeout > 0U) { uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { - LogWarning(LOG_NET, "P25, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); m_status.lock(false); m_status[dstId].streamId = streamId; @@ -308,7 +308,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_status.unlock(); } else { - LogWarning(LOG_NET, "P25, Call Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Collision, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); return false; } @@ -362,11 +362,11 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogMessage(LOG_NET, "P25, Private Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Private Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream); } else - LogMessage(LOG_NET, "P25, Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream); } } @@ -524,7 +524,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId, true); if (m_network->m_debug) { - LogDebug(LOG_NET, "P25, ssrc = %u, srcPeer = %u, dstPeer = %u, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", + LogDebugEx(LOG_P25, "TagP25Data::processFrame()", "Master, ssrc = %u, srcPeer = %u, dstPeer = %u, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", ssrc, peerId, peer.first, duid, lco, MFId, srcId, dstId, len, pktSeq, streamId, fromUpstream); } @@ -582,7 +582,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { - LogDebug(LOG_NET, "P25, ssrc = %u, srcPeer = %u, dstPeer = %u, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", + LogDebugEx(LOG_P25, "TagP25Data::processFrame()", "Peers, ssrc = %u, srcPeer = %u, dstPeer = %u, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", ssrc, peerId, dstPeerId, duid, lco, MFId, srcId, dstId, len, pktSeq, streamId, fromUpstream); } } @@ -675,13 +675,13 @@ void TagP25Data::playbackParrot() UInt8Array message = m_network->createP25_TDUMessage(messageLength, control, lsd, controlByte); if (message != nullptr) { if (m_network->m_parrotOnlyOriginating) { - LogMessage(LOG_NET, "P25, Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", pkt.peerId, srcId, dstId); + LogMessage(LOG_P25, "Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", pkt.peerId, srcId, dstId); m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, m_network->createStreamId(), false); } else { // repeat traffic to the connected peers for (auto peer : m_network->m_peers) { - LogMessage(LOG_NET, "P25, Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", peer.first, srcId, dstId); + LogMessage(LOG_P25, "Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", peer.first, srcId, dstId); m_network->writePeer(peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, m_network->createStreamId(), false); } @@ -695,7 +695,7 @@ void TagP25Data::playbackParrot() if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { - LogDebug(LOG_NET, "P25, parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", + LogDebugEx(LOG_P25, "TagP25Data::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", pkt.peerId, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } } else { @@ -703,7 +703,7 @@ void TagP25Data::playbackParrot() for (auto peer : m_network->m_peers) { m_network->writePeer(peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { - LogDebug(LOG_NET, "P25, parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", + LogDebug(LOG_P25, "TagP25Data::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", peer.first, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } } @@ -723,7 +723,7 @@ void TagP25Data::write_TSDU_Call_Alrt(uint32_t peerId, uint32_t srcId, uint32_t iosp->setSrcId(srcId); iosp->setDstId(dstId); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", iosp->toString().c_str(), srcId, dstId); + LogMessage(LOG_P25, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", iosp->toString().c_str(), srcId, dstId); write_TSDU(peerId, iosp.get()); } @@ -737,7 +737,7 @@ void TagP25Data::write_TSDU_Radio_Mon(uint32_t peerId, uint32_t srcId, uint32_t iosp->setDstId(dstId); iosp->setTxMult(txMult); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", iosp->toString().c_str(), srcId, dstId, txMult); + LogMessage(LOG_P25, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", iosp->toString().c_str(), srcId, dstId, txMult); write_TSDU(peerId, iosp.get()); } @@ -756,7 +756,7 @@ void TagP25Data::write_TSDU_Ext_Func(uint32_t peerId, uint32_t func, uint32_t ar iosp->setMFId(MFG_MOT); } - LogMessage(LOG_NET, P25_TSDU_STR ", %s, mfId = $%02X, op = $%02X, arg = %u, tgt = %u", + LogMessage(LOG_P25, P25_TSDU_STR ", %s, mfId = $%02X, op = $%02X, arg = %u, tgt = %u", iosp->toString().c_str(), iosp->getMFId(), iosp->getExtendedFunction(), iosp->getSrcId(), iosp->getDstId()); write_TSDU(peerId, iosp.get()); @@ -770,7 +770,7 @@ void TagP25Data::write_TSDU_Grp_Aff_Q(uint32_t peerId, uint32_t dstId) osp->setSrcId(WUID_FNE); osp->setDstId(dstId); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, dstId = %u", osp->toString().c_str(), dstId); + LogMessage(LOG_P25, P25_TSDU_STR ", %s, dstId = %u", osp->toString().c_str(), dstId); write_TSDU(peerId, osp.get()); } @@ -783,7 +783,7 @@ void TagP25Data::write_TSDU_U_Reg_Cmd(uint32_t peerId, uint32_t dstId) osp->setSrcId(WUID_FNE); osp->setDstId(dstId); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, dstId = %u", osp->toString().c_str(), dstId); + LogMessage(LOG_P25, P25_TSDU_STR ", %s, dstId = %u", osp->toString().c_str(), dstId); write_TSDU(peerId, osp.get()); } @@ -817,7 +817,7 @@ void TagP25Data::routeRewrite(uint8_t* buffer, uint32_t peerId, uint8_t duid, ui switch (tsbk->getLCO()) { case TSBKO::IOSP_GRP_VCH: { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", + LogMessage(LOG_P25, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", tsbk->toString(true).c_str(), tsbk->getEmergency(), tsbk->getEncrypted(), tsbk->getPriority(), tsbk->getGrpVchId(), tsbk->getGrpVchNo(), srcId, rewriteDstId); tsbk->setDstId(rewriteDstId); @@ -926,13 +926,13 @@ bool TagP25Data::processTSDUFrom(uint8_t* buffer, uint32_t peerId, uint8_t duid) case TSBKO::OSP_ADJ_STS_BCAST: { if (m_network->m_disallowAdjStsBcast) { - // LogWarning(LOG_NET, "PEER %u, passing ADJ_STS_BCAST to internal peers is prohibited, dropping", peerId); + // LogWarning(LOG_P25, "PEER %u, passing ADJ_STS_BCAST to internal peers is prohibited, dropping", peerId); return false; } else { lc::tsbk::OSP_ADJ_STS_BCAST* osp = static_cast(tsbk.get()); if (m_network->m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X, peerId = %u", tsbk->toString().c_str(), + LogMessage(LOG_P25, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X, peerId = %u", tsbk->toString().c_str(), osp->getAdjSiteSysId(), osp->getAdjSiteRFSSId(), osp->getAdjSiteId(), osp->getAdjSiteChnId(), osp->getAdjSiteChnNo(), osp->getAdjSiteSvcClass(), peerId); } @@ -940,7 +940,7 @@ bool TagP25Data::processTSDUFrom(uint8_t* buffer, uint32_t peerId, uint8_t duid) lookups::AdjPeerMapEntry adjPeerMap = m_network->m_adjSiteMapLookup->find(peerId); if (!adjPeerMap.isEmpty()) { if (!adjPeerMap.active()) { - // LogWarning(LOG_NET, "PEER %u, passing ADJ_STS_BCAST to other peers is disabled, dropping", peerId); + // LogWarning(LOG_P25, "PEER %u, passing ADJ_STS_BCAST to other peers is disabled, dropping", peerId); return false; } else { // if the peer is mapped, we can repeat the ADJ_STS_BCAST to other peers @@ -964,7 +964,7 @@ bool TagP25Data::processTSDUFrom(uint8_t* buffer, uint32_t peerId, uint8_t duid) } } else { std::string peerIdentity = m_network->resolvePeerIdentity(peerId); - LogWarning(LOG_NET, "PEER %u (%s), passing TSBK that failed to decode? tsbk == nullptr", peerId, peerIdentity.c_str()); + LogWarning(LOG_P25, "PEER %u (%s), passing TSBK that failed to decode? tsbk == nullptr", peerId, peerIdentity.c_str()); } } @@ -988,7 +988,7 @@ bool TagP25Data::processTSDUFrom(uint8_t* buffer, uint32_t peerId, uint8_t duid) } else { // bryanb: should these be logged? //std::string peerIdentity = m_network->resolvePeerIdentity(peerId); - //LogWarning(LOG_NET, "PEER %u (%s), passing TDULC that failed to decode? tdulc == nullptr", peerId, peerIdentity.c_str()); + //LogWarning(LOG_P25, "PEER %u (%s), passing TDULC that failed to decode? tdulc == nullptr", peerId, peerIdentity.c_str()); } } @@ -1033,14 +1033,14 @@ bool TagP25Data::processTSDUTo(uint8_t* buffer, uint32_t peerId, uint8_t duid) lookups::AffiliationLookup* aff = m_network->m_peerAffiliations[lookupPeerId]; if (aff == nullptr) { std::string peerIdentity = m_network->resolvePeerIdentity(lookupPeerId); - //LogError(LOG_NET, "PEER %u (%s) has an invalid affiliations lookup? This shouldn't happen BUGBUG.", lookupPeerId, peerIdentity.c_str()); + //LogError(LOG_P25, "PEER %u (%s) has an invalid affiliations lookup? This shouldn't happen BUGBUG.", lookupPeerId, peerIdentity.c_str()); return false; // this will cause no TSDU to pass for this peer now...I'm not sure this is good behavior } else { if (!aff->hasGroupAff(dstId)) { if (m_debug) { std::string peerIdentity = m_network->resolvePeerIdentity(lookupPeerId); - LogDebug(LOG_NET, "PEER %u (%s) can fuck off there's no affiliations.", lookupPeerId, peerIdentity.c_str()); // just so Faulty can see more "salty" log messages + LogDebug(LOG_P25, "PEER %u (%s) can fuck off there's no affiliations.", lookupPeerId, peerIdentity.c_str()); // just so Faulty can see more "salty" log messages } return false; } @@ -1054,7 +1054,7 @@ bool TagP25Data::processTSDUTo(uint8_t* buffer, uint32_t peerId, uint8_t duid) } } else { std::string peerIdentity = m_network->resolvePeerIdentity(peerId); - LogWarning(LOG_NET, "PEER %u (%s), passing TSBK that failed to decode? tsbk == nullptr", peerId, peerIdentity.c_str()); + LogWarning(LOG_P25, "PEER %u (%s), passing TSBK that failed to decode? tsbk == nullptr", peerId, peerIdentity.c_str()); } } @@ -1085,7 +1085,7 @@ bool TagP25Data::processTSDUToNeighbor(uint8_t* buffer, uint32_t srcPeerId, uint lc::tsbk::OSP_ADJ_STS_BCAST* osp = static_cast(tsbk.get()); if (m_network->m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X, peerId = %u", tsbk->toString().c_str(), + LogMessage(LOG_P25, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X, peerId = %u", tsbk->toString().c_str(), osp->getAdjSiteSysId(), osp->getAdjSiteRFSSId(), osp->getAdjSiteId(), osp->getAdjSiteChnId(), osp->getAdjSiteChnNo(), osp->getAdjSiteSvcClass(), srcPeerId); } } @@ -1096,7 +1096,7 @@ bool TagP25Data::processTSDUToNeighbor(uint8_t* buffer, uint32_t srcPeerId, uint } } else { std::string peerIdentity = m_network->resolvePeerIdentity(srcPeerId); - LogWarning(LOG_NET, "PEER %u (%s), passing TSBK that failed to decode? tsbk == nullptr", srcPeerId, peerIdentity.c_str()); + LogWarning(LOG_P25, "PEER %u (%s), passing TSBK that failed to decode? tsbk == nullptr", srcPeerId, peerIdentity.c_str()); } } @@ -1275,7 +1275,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const skipRidCheck = true; } - //LogDebug(LOG_NET, "P25, duid = $%02X, mfId = $%02X, lco = $%02X, srcId = %u, dstId = %u", duid, control.getMFId(), control.getLCO(), control.getSrcId(), control.getDstId()); + //LogDebugEx(LOG_P25, "TagP25Data::validate()", "duid = $%02X, mfId = $%02X, lco = $%02X, srcId = %u, dstId = %u", duid, control.getMFId(), control.getLCO(), control.getSrcId(), control.getDstId()); // is the source ID a blacklisted ID? bool rejectUnknownBadCall = false; @@ -1297,7 +1297,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const } if (m_network->m_logDenials) - LogError(LOG_NET, "P25, " INFLUXDB_ERRSTR_DISABLED_SRC_RID ", peer = %u, srcId = %u, dstId = %u", peerId, control.getSrcId(), control.getDstId()); + LogError(LOG_P25, INFLUXDB_ERRSTR_DISABLED_SRC_RID ", peer = %u, srcId = %u, dstId = %u", peerId, control.getSrcId(), control.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId()); @@ -1343,7 +1343,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const return true; } - //LogDebugEx(LOG_NET, "TagP25Data::validate()", "TDU for invalid destination, dropped, dstId = %u", control.getDstId()); + //LogDebugEx(LOG_P25, "TagP25Data::validate()", "TDU for invalid destination, dropped, dstId = %u", control.getDstId()); return false; } @@ -1390,7 +1390,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const } if (m_network->m_logDenials) - LogError(LOG_NET, "P25, " INFLUXDB_ERRSTR_DISABLED_DST_RID ", peer = %u, srcId = %u, dstId = %u", peerId, control.getSrcId(), control.getDstId()); + LogError(LOG_P25, INFLUXDB_ERRSTR_DISABLED_DST_RID ", peer = %u, srcId = %u, dstId = %u", peerId, control.getSrcId(), control.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId()); @@ -1415,7 +1415,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const } if (m_network->m_logDenials) - LogWarning(LOG_NET, "P25, " INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", control.getSrcId(), control.getDstId()); + LogWarning(LOG_P25, INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", control.getSrcId(), control.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId()); @@ -1459,7 +1459,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const case ExtendedFunctions::UNINHIBIT: { if (!pid.peerDefault() && !pid.canIssueInhibit()) { - LogWarning(LOG_NET, "P25, PEER %u attempted inhibit/unhibit, not authorized", peerId); + LogWarning(LOG_P25, "PEER %u attempted inhibit/unhibit, not authorized", peerId); return false; } } @@ -1512,7 +1512,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const } if (m_network->m_logDenials) - LogError(LOG_NET, "P25, " INFLUXDB_ERRSTR_INV_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", peerId, control.getSrcId(), control.getDstId()); + LogError(LOG_P25, INFLUXDB_ERRSTR_INV_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", peerId, control.getSrcId(), control.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId()); @@ -1546,7 +1546,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const } if (m_network->m_logDenials) - LogWarning(LOG_NET, "P25, " INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", control.getSrcId(), control.getDstId()); + LogWarning(LOG_P25, INFLUXDB_ERRSTR_ILLEGAL_RID_ACCESS ", srcId = %u, dstId = %u", control.getSrcId(), control.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId()); @@ -1569,7 +1569,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const } if (m_network->m_logDenials) - LogError(LOG_NET, "P25, " INFLUXDB_ERRSTR_DISABLED_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", peerId, control.getSrcId(), control.getDstId()); + LogError(LOG_P25, INFLUXDB_ERRSTR_DISABLED_TALKGROUP ", peer = %u, srcId = %u, dstId = %u", peerId, control.getSrcId(), control.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId()); @@ -1597,7 +1597,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const } if (m_network->m_logDenials) - LogError(LOG_NET, "P25, " INFLUXDB_ERRSTR_RID_NOT_PERMITTED ", peer = %u, srcId = %u, dstId = %u", peerId, control.getSrcId(), control.getDstId()); + LogError(LOG_P25, INFLUXDB_ERRSTR_RID_NOT_PERMITTED ", peer = %u, srcId = %u, dstId = %u", peerId, control.getSrcId(), control.getDstId()); // report In-Call Control to the peer sending traffic m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId()); @@ -1625,7 +1625,7 @@ bool TagP25Data::write_TSDU_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstI lookups::AffiliationLookup* aff = m_network->m_peerAffiliations[peerId]; if (aff == nullptr) { std::string peerIdentity = m_network->resolvePeerIdentity(peerId); - LogError(LOG_NET, "PEER %u (%s) has an invalid affiliations lookup? This shouldn't happen BUGBUG.", peerId, peerIdentity.c_str()); + LogError(LOG_MASTER, "PEER %u (%s) has an invalid affiliations lookup? This shouldn't happen BUGBUG.", peerId, peerIdentity.c_str()); return false; // this will cause no traffic to pass for this peer now...I'm not sure this is good behavior } else { @@ -1645,7 +1645,7 @@ bool TagP25Data::write_TSDU_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstI iosp->setPriority(priority); if (m_network->m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u, peerId = %u", + LogMessage(LOG_P25, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u, peerId = %u", iosp->toString().c_str(), iosp->getEmergency(), iosp->getEncrypted(), iosp->getPriority(), iosp->getGrpVchId(), iosp->getGrpVchNo(), iosp->getSrcId(), iosp->getDstId(), peerId); } @@ -1662,7 +1662,7 @@ bool TagP25Data::write_TSDU_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstI iosp->setPriority(priority); if (m_network->m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u, peerId = %u", + LogMessage(LOG_P25, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u, peerId = %u", iosp->toString().c_str(), iosp->getEmergency(), iosp->getEncrypted(), iosp->getPriority(), iosp->getGrpVchId(), iosp->getGrpVchNo(), iosp->getSrcId(), iosp->getDstId(), peerId); } @@ -1685,7 +1685,7 @@ void TagP25Data::write_TSDU_Deny(uint32_t peerId, uint32_t srcId, uint32_t dstId osp->setGroup(grp); if (m_network->m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", + LogMessage(LOG_P25, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", osp->toString().c_str(), osp->getAIV(), reason, P25Utils::denyRsnToString(reason).c_str(), osp->getSrcId(), osp->getDstId()); } @@ -1706,7 +1706,7 @@ void TagP25Data::write_TSDU_Queue(uint32_t peerId, uint32_t srcId, uint32_t dstI osp->setGroup(grp); if (m_network->m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", + LogMessage(LOG_P25, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", osp->toString().c_str(), osp->getAIV(), reason, P25Utils::queueRsnToString(reason).c_str(), osp->getSrcId(), osp->getDstId()); } @@ -1735,7 +1735,7 @@ void TagP25Data::write_TSDU(uint32_t peerId, lc::TSBK* tsbk) P25Utils::setStatusBitsStartIdle(data); if (m_debug) { - LogDebug(LOG_RF, P25_TSDU_STR ", lco = $%02X, mfId = $%02X, lastBlock = %u, AIV = %u, EX = %u, srcId = %u, dstId = %u, sysId = $%03X, netId = $%05X", + LogDebug(LOG_P25, P25_TSDU_STR ", lco = $%02X, mfId = $%02X, lastBlock = %u, AIV = %u, EX = %u, srcId = %u, dstId = %u, sysId = $%03X, netId = $%05X", tsbk->getLCO(), tsbk->getMFId(), tsbk->getLastBlock(), tsbk->getAIV(), tsbk->getEX(), tsbk->getSrcId(), tsbk->getDstId(), tsbk->getSysId(), tsbk->getNetId()); @@ -1771,7 +1771,7 @@ void TagP25Data::write_TSDU(uint32_t peerId, lc::TSBK* tsbk) m_network->writePeer(peer.first, m_network->m_peerId, { 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", + LogDebugEx(LOG_P25, "TagP25Data::write_TSDU()", "P25, peer = %u, len = %u, streamId = %u", peer.first, messageLength, streamId); } i++; @@ -1785,7 +1785,7 @@ void TagP25Data::write_TSDU(uint32_t peerId, lc::TSBK* tsbk) 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", + LogDebugEx(LOG_P25, "TagP25Data::write_TSDU()", "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 a16b4782..23a5dad3 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp @@ -130,7 +130,7 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee bool ret = status->header.decode(frame); if (!ret) { - LogError(LOG_NET, "DMR Slot %u, DataType::DATA_HEADER, unable to decode the network data header", status->slotNo); + LogError(LOG_DMR, "DMR Slot %u, DataType::DATA_HEADER, unable to decode the network data header", status->slotNo); Utils::dump(1U, "DMR, Unfixable PDU Data", frame, DMR_FRAME_LENGTH_BYTES); delete status; @@ -145,19 +145,19 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee uint32_t srcId = status->header.getSrcId(); uint32_t dstId = status->header.getDstId(); - LogMessage(LOG_NET, DMR_DT_DATA_HEADER ", peerId = %u, slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", + LogMessage(LOG_DMR, DMR_DT_DATA_HEADER ", peerId = %u, slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", peerId, status->slotNo, status->header.getDPF(), status->header.getA(), status->header.getSAP(), status->header.getFullMesage(), status->header.getBlocksToFollow(), status->header.getPadLength(), status->header.getPacketLength(), status->header.getFSN(), dstId, srcId, gi); // make sure we don't get a PDU with more blocks then we support if (status->header.getBlocksToFollow() >= MAX_PDU_COUNT) { - LogError(LOG_NET, P25_PDU_STR ", too many PDU blocks to process, %u > %u", status->header.getBlocksToFollow(), MAX_PDU_COUNT); + LogError(LOG_DMR, DMR_DT_DATA_HEADER ", too many PDU blocks to process, %u > %u", status->header.getBlocksToFollow(), MAX_PDU_COUNT); return false; } m_status[peerId] = status; - LogMessage(LOG_NET, "DMR, Data Call Start, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, streamId = %u, fromUpstream = %u", peerId, status->slotNo, status->srcId, status->dstId, gi, streamId, fromUpstream); + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call Start, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, streamId = %u, fromUpstream = %u", peerId, status->slotNo, status->srcId, status->dstId, gi, streamId, fromUpstream); dispatchToFNE(peerId, dmrData, data, len, seqNo, pktSeq, streamId); return true; @@ -170,7 +170,7 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // a PDU header only with no blocks to follow is usually a response header if (status->header.getBlocksToFollow() == 0U) { - LogMessage(LOG_NET, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, status->slotNo, status->srcId, status->dstId, streamId, fromUpstream); delete status; @@ -191,12 +191,12 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee dataBlock.setLastBlock(true); if (dataType == DataType::RATE_34_DATA) { - LogMessage(LOG_NET, DMR_DT_RATE_34_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); + LogMessage(LOG_DMR, DMR_DT_RATE_34_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); } else if (dataType == DataType::RATE_12_DATA) { - LogMessage(LOG_NET, DMR_DT_RATE_12_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); + LogMessage(LOG_DMR, DMR_DT_RATE_12_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); } else { - LogMessage(LOG_NET, DMR_DT_RATE_1_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); + LogMessage(LOG_DMR, DMR_DT_RATE_1_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); } dispatchToFNE(peerId, dmrData, data, len, seqNo, pktSeq, streamId); @@ -211,7 +211,7 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee bool gi = status->header.getGI(); uint32_t srcId = status->header.getSrcId(); uint32_t dstId = status->header.getDstId(); - LogMessage(LOG_NET, "P25, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, srcId, dstId, gi, status->header.getBlocksToFollow(), duration / 1000, streamId, fromUpstream); // report call event to InfluxDB @@ -250,7 +250,7 @@ void DMRPacketData::dispatch(uint32_t peerId, dmr::data::NetData& dmrData, const if (status->header.getBlocksToFollow() > 0U && status->frames == 0U) { bool crcRet = edac::CRC::checkCRC32(status->pduUserData, status->pduDataOffset); if (!crcRet) { - LogWarning(LOG_NET, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", status->header.getBlocksToFollow(), status->pduDataOffset); + LogWarning(LOG_DMR, "DMR Data, failed CRC-32 check, blocks %u, len %u", status->header.getBlocksToFollow(), status->pduDataOffset); } if (m_network->m_dumpPacketData) { @@ -268,6 +268,10 @@ void DMRPacketData::dispatchToFNE(uint32_t peerId, dmr::data::NetData& dmrData, uint32_t srcId = status->header.getSrcId(); uint32_t dstId = status->header.getDstId(); + /* + ** MASTER TRAFFIC + */ + // repeat traffic to the connected peers if (m_network->m_peers.size() > 0U) { uint32_t i = 0U; @@ -285,7 +289,7 @@ void DMRPacketData::dispatchToFNE(uint32_t peerId, dmr::data::NetData& dmrData, m_network->writePeer(peer.first, peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, data, len, pktSeq, streamId, true); if (m_network->m_debug) { - LogDebug(LOG_NET, "DMR, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, slotNo = %u, len = %u, pktSeq = %u, stream = %u", + LogDebugEx(LOG_DMR, "DMRPacketData::dispatchToFNE()", "Master, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, slotNo = %u, len = %u, pktSeq = %u, stream = %u", peerId, peer.first, seqNo, srcId, dstId, status->slotNo, len, pktSeq, streamId); } @@ -295,6 +299,10 @@ void DMRPacketData::dispatchToFNE(uint32_t peerId, dmr::data::NetData& dmrData, m_network->m_frameQueue->flushQueue(); } + /* + ** PEER TRAFFIC (e.g. upstream networks this FNE is peered to) + */ + // repeat traffic to neighbor FNE peers if (m_network->m_host->m_peerNetworks.size() > 0U) { for (auto peer : m_network->m_host->m_peerNetworks) { @@ -315,7 +323,7 @@ void DMRPacketData::dispatchToFNE(uint32_t peerId, dmr::data::NetData& dmrData, peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, data, len, pktSeq, streamId); if (m_network->m_debug) { - LogDebug(LOG_NET, "DMR, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, slotNo = %u, len = %u, pktSeq = %u, stream = %u", + LogDebugEx(LOG_DMR, "DMRPacketData::dispatchToFNE()", "Peers, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, slotNo = %u, len = %u, pktSeq = %u, stream = %u", peerId, dstPeerId, seqNo, srcId, dstId, status->slotNo, len, pktSeq, streamId); } } diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index 0817c012..0d226bad 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -107,7 +107,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // make sure we don't get a PDU with more blocks then we support if (currentBlock >= P25_MAX_PDU_BLOCKS) { - LogError(LOG_NET, P25_PDU_STR ", too many PDU blocks to process, %u > %u", currentBlock, P25_MAX_PDU_BLOCKS); + LogError(LOG_P25, P25_PDU_STR ", too many PDU blocks to process, %u > %u", currentBlock, P25_MAX_PDU_BLOCKS); delete status; m_status.erase(peerId); @@ -118,7 +118,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee if (currentBlock == 0U) { bool ret = status->header.decode(buffer); if (!ret) { - LogWarning(LOG_NET, P25_PDU_STR ", unfixable RF 1/2 rate header data"); + LogWarning(LOG_P25, P25_PDU_STR ", unfixable RF 1/2 rate header data"); Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); delete status; @@ -126,14 +126,14 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return true; } - LogMessage(LOG_NET, P25_PDU_STR ", peerId = %u, ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, hdrOffset = %u, llId = %u", + LogMessage(LOG_P25, P25_PDU_STR ", peerId = %u, ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, hdrOffset = %u, llId = %u", peerId, status->header.getAckNeeded(), status->header.getOutbound(), status->header.getFormat(), status->header.getSAP(), status->header.getFullMessage(), status->header.getBlocksToFollow(), status->header.getPadLength(), status->header.getPacketLength(), status->header.getSynchronize(), status->header.getNs(), status->header.getFSN(), status->header.getHeaderOffset(), status->header.getLLId()); // make sure we don't get a PDU with more blocks then we support if (status->header.getBlocksToFollow() >= P25_MAX_PDU_BLOCKS) { - LogError(LOG_NET, P25_PDU_STR ", too many PDU blocks to process, %u > %u", status->header.getBlocksToFollow(), P25_MAX_PDU_BLOCKS); + LogError(LOG_P25, P25_PDU_STR ", too many PDU blocks to process, %u > %u", status->header.getBlocksToFollow(), P25_MAX_PDU_BLOCKS); delete status; m_status.erase(peerId); @@ -154,7 +154,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return true; } - LogMessage(LOG_NET, "P25, Data Call Start, peer = %u, llId = %u, streamId = %u, fromUpstream = %u", peerId, status->llId, streamId, fromUpstream); + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Data Call Start, peer = %u, llId = %u, streamId = %u, fromUpstream = %u", peerId, status->llId, streamId, fromUpstream); return true; } @@ -207,7 +207,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee bool ret = status->header.decodeExtAddr(buffer); if (!ret) { - LogWarning(LOG_NET, P25_PDU_STR ", unfixable RF 1/2 rate second header data"); + LogWarning(LOG_P25, P25_PDU_STR ", unfixable RF 1/2 rate second header data"); Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_HEADER_LENGTH_BYTES); delete status; @@ -216,7 +216,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return false; } - LogMessage(LOG_NET, P25_PDU_STR ", ISP, extended address, sap = $%02X, srcLlId = %u", + LogMessage(LOG_P25, P25_PDU_STR ", ISP, extended address, sap = $%02X, srcLlId = %u", status->header.getEXSAP(), status->header.getSrcLLId()); status->extendedAddress = true; @@ -254,14 +254,14 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee status->blockData[i].getData(secondHeader); status->header.decodeExtAddr(secondHeader); - LogMessage(LOG_NET, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", + LogMessage(LOG_P25, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", status->blockData[i].getSerialNo(), status->blockData[i].getFormat(), status->blockData[i].getLastBlock(), status->header.getEXSAP(), status->header.getSrcLLId()); status->extendedAddress = true; } else { - LogMessage(LOG_NET, P25_PDU_STR ", peerId = %u, block %u, fmt = $%02X, lastBlock = %u", + LogMessage(LOG_P25, P25_PDU_STR ", peerId = %u, block %u, fmt = $%02X, lastBlock = %u", peerId, (status->header.getFormat() == PDUFormatType::CONFIRMED) ? status->blockData[i].getSerialNo() : status->dataBlockCnt, status->blockData[i].getFormat(), status->blockData[i].getLastBlock()); } @@ -274,9 +274,9 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } else { if (status->blockData[i].getFormat() == PDUFormatType::CONFIRMED) - LogWarning(LOG_NET, P25_PDU_STR ", unfixable PDU data (3/4 rate or CRC), block %u", i); + LogWarning(LOG_P25, P25_PDU_STR ", unfixable PDU data (3/4 rate or CRC), block %u", i); else - LogWarning(LOG_NET, P25_PDU_STR ", unfixable PDU data (1/2 rate or CRC), block %u", i); + LogWarning(LOG_P25, P25_PDU_STR ", unfixable PDU data (1/2 rate or CRC), block %u", i); if (m_network->m_dumpPacketData) { Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); @@ -287,7 +287,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } if (status->dataBlockCnt < blocksToFollow) { - LogWarning(LOG_NET, P25_PDU_STR ", incomplete PDU (%d / %d blocks), peerId = %u, llId = %u", status->dataBlockCnt, blocksToFollow, peerId, status->llId); + LogWarning(LOG_P25, P25_PDU_STR ", incomplete PDU (%d / %d blocks), peerId = %u, llId = %u", status->dataBlockCnt, blocksToFollow, peerId, status->llId); } // dispatch the PDU data @@ -298,7 +298,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee uint64_t duration = hrc::diff(pktTime, status->callStartTime); uint32_t srcId = (status->extendedAddress) ? status->header.getSrcLLId() : status->header.getLLId(); uint32_t dstId = status->header.getLLId(); - LogMessage(LOG_NET, "P25, Data Call End, peer = %u, srcId = %u, dstId = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Data Call End, peer = %u, srcId = %u, dstId = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, srcId, dstId, status->header.getBlocksToFollow(), duration / 1000, streamId, fromUpstream); // report call event to InfluxDB @@ -352,7 +352,7 @@ void P25PacketData::processPacketFrame(const uint8_t* data, uint32_t len, bool a std::string srcIpStr = __IP_FROM_UINT(srcProtoAddr); std::string tgtIpStr = __IP_FROM_UINT(tgtProtoAddr); - LogMessage(LOG_NET, "P25, VTUN -> PDU IP Data, srcIp = %s (%u), dstIp = %s (%u), pktLen = %u, proto = %02X", + LogMessage(LOG_P25, "VTUN -> PDU IP Data, srcIp = %s (%u), dstIp = %s (%u), pktLen = %u, proto = %02X", srcIpStr.c_str(), WUID_FNE, tgtIpStr.c_str(), llId, pktLen, proto); // assemble a P25 PDU frame header for transport... @@ -368,7 +368,7 @@ void P25PacketData::processPacketFrame(const uint8_t* data, uint32_t len, bool a pktHeader->calculateLength(pktLen); uint32_t pduLength = pktHeader->getPDULength(); if (pduLength < pktLen) { - LogWarning(LOG_NET, "P25, VTUN, data truncated!"); + LogWarning(LOG_P25, "VTUN, data truncated!"); pktLen = pduLength; // don't overflow the buffer } @@ -467,25 +467,25 @@ void P25PacketData::clock(uint32_t ms) processed = true; if (frame->retryCnt >= MAX_PKT_RETRY_CNT && !frame->extendRetry) { - LogWarning(LOG_NET, "P25, max packet retry count exceeded, dropping packet, dstIp = %s", __IP_FROM_UINT(frame->tgtProtoAddr).c_str()); + LogWarning(LOG_P25, P25_PDU_STR ", max packet retry count exceeded, dropping packet, dstIp = %s", __IP_FROM_UINT(frame->tgtProtoAddr).c_str()); goto pkt_clock_abort; } if (frame->retryCnt >= (MAX_PKT_RETRY_CNT * 2U) && frame->extendRetry) { - LogWarning(LOG_NET, "P25, max packet retry count exceeded, dropping packet, dstIp = %s", __IP_FROM_UINT(frame->tgtProtoAddr).c_str()); + LogWarning(LOG_P25, P25_PDU_STR ", max packet retry count exceeded, dropping packet, dstIp = %s", __IP_FROM_UINT(frame->tgtProtoAddr).c_str()); m_readyForNextPkt[frame->llId] = true; // force ready for next packet goto pkt_clock_abort; } std::string tgtIpStr = __IP_FROM_UINT(frame->tgtProtoAddr); - LogMessage(LOG_NET, "P25, VTUN -> PDU IP Data, dstIp = %s (%u), userDataLen = %u, retries = %u", + LogMessage(LOG_P25, "VTUN -> PDU IP Data, dstIp = %s (%u), userDataLen = %u, retries = %u", tgtIpStr.c_str(), frame->llId, frame->userDataLen, frame->retryCnt); // do we have a valid target address? if (frame->llId == 0U) { frame->llId = getLLIdAddress(frame->tgtProtoAddr); if (frame->llId == 0U) { - LogWarning(LOG_NET, "P25, no ARP entry for, dstIp = %s", tgtIpStr.c_str()); + LogWarning(LOG_P25, P25_PDU_STR ", no ARP entry for, dstIp = %s", tgtIpStr.c_str()); write_PDU_ARP(frame->tgtProtoAddr); processed = false; @@ -502,7 +502,7 @@ void P25PacketData::clock(uint32_t ms) auto ready = std::find_if(m_readyForNextPkt.begin(), m_readyForNextPkt.end(), [=](ReadyForNextPktPair x) { return x.first == frame->llId; }); if (ready != m_readyForNextPkt.end()) { if (!ready->second) { - LogWarning(LOG_NET, "P25, subscriber not ready, dstIp = %s", tgtIpStr.c_str()); + LogWarning(LOG_P25, P25_PDU_STR ", subscriber not ready, dstIp = %s", tgtIpStr.c_str()); processed = false; frame->timestamp = now + SUBSCRIBER_READY_RETRY_MS; frame->extendRetry = true; @@ -541,21 +541,21 @@ void P25PacketData::dispatch(uint32_t peerId) RxStatus* status = m_status[peerId]; if (status == nullptr) { - LogError(LOG_NET, P25_PDU_STR ", illegal PDU packet state, status shouldn't be null"); + LogError(LOG_P25, P25_PDU_STR ", illegal PDU packet state, status shouldn't be null"); return; } bool crcValid = false; if (status->header.getBlocksToFollow() > 0U) { if (status->pduUserDataLength < 4U) { - LogError(LOG_NET, P25_PDU_STR ", illegal PDU packet length, peer = %u, llId = %u, blocks %u, len %u", + LogError(LOG_P25, P25_PDU_STR ", illegal PDU packet length, peer = %u, llId = %u, blocks %u, len %u", peerId, status->header.getLLId(), status->header.getBlocksToFollow(), status->pduUserDataLength); return; } crcValid = edac::CRC::checkCRC32(status->pduUserData, status->pduUserDataLength); if (!crcValid) { - LogError(LOG_NET, P25_PDU_STR ", failed CRC-32 check, peer = %u, llId = %u, blocks %u, len %u", + LogError(LOG_P25, P25_PDU_STR ", failed CRC-32 check, peer = %u, llId = %u, blocks %u, len %u", peerId, status->header.getLLId(), status->header.getBlocksToFollow(), status->pduUserDataLength); return; } @@ -566,7 +566,7 @@ void P25PacketData::dispatch(uint32_t peerId) } if (status->header.getFormat() == PDUFormatType::RSP) { - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, peer = %u, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", + LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, peer = %u, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", peerId, status->header.getFormat(), status->header.getResponseClass(), status->header.getResponseType(), status->header.getResponseStatus(), status->header.getLLId(), status->header.getSrcLLId()); @@ -574,26 +574,26 @@ void P25PacketData::dispatch(uint32_t peerId) m_readyForNextPkt[status->header.getSrcLLId()] = true; if (status->header.getResponseClass() == PDUAckClass::ACK && status->header.getResponseType() == PDUAckType::ACK) { - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, OSP ACK, peer = %u, llId = %u, all blocks received OK, n = %u", + LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, OSP ACK, peer = %u, llId = %u, all blocks received OK, n = %u", peerId, status->header.getLLId(), status->header.getResponseStatus()); } else { if (status->header.getResponseClass() == PDUAckClass::NACK) { switch (status->header.getResponseType()) { case PDUAckType::NACK_ILLEGAL: - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, OSP NACK, illegal format, peer = %u, llId = %u", + LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, illegal format, peer = %u, llId = %u", peerId, status->header.getLLId()); break; case PDUAckType::NACK_PACKET_CRC: - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, OSP NACK, packet CRC error, peer = %u, llId = %u, n = %u", + LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet CRC error, peer = %u, llId = %u, n = %u", peerId, status->header.getLLId(), status->header.getResponseStatus()); break; case PDUAckType::NACK_SEQ: case PDUAckType::NACK_OUT_OF_SEQ: - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, OSP NACK, packet out of sequence, peer = %u, llId = %u, seqNo = %u", + LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet out of sequence, peer = %u, llId = %u, seqNo = %u", peerId, status->header.getLLId(), status->header.getResponseStatus()); break; case PDUAckType::NACK_UNDELIVERABLE: - LogMessage(LOG_NET, P25_PDU_STR ", ISP, response, OSP NACK, packet undeliverable, peer = %u, llId = %u, n = %u", + LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet undeliverable, peer = %u, llId = %u, n = %u", peerId, status->header.getLLId(), status->header.getResponseStatus()); break; @@ -634,16 +634,16 @@ void P25PacketData::dispatch(uint32_t peerId) uint32_t tgtProtoAddr = GET_UINT32(arpPacket, 18U); if (opcode == P25_PDU_ARP_REQUEST) { - LogMessage(LOG_NET, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); + LogMessage(LOG_P25, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); if (fneIPv4 == tgtProtoAddr) { write_PDU_ARP_Reply(fneIPv4, srcHWAddr, srcProtoAddr, WUID_FNE); } else { write_PDU_ARP_Reply(tgtProtoAddr, srcHWAddr, srcProtoAddr); } } else if (opcode == P25_PDU_ARP_REPLY) { - LogMessage(LOG_NET, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); + LogMessage(LOG_P25, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); if (fneIPv4 == srcProtoAddr) { - LogWarning(LOG_NET, P25_PDU_STR ", ARP reply, %u is trying to masquerade as us...", srcHWAddr); + LogWarning(LOG_P25, P25_PDU_STR ", ARP reply, %u is trying to masquerade as us...", srcHWAddr); } else { m_arpTable[srcHWAddr] = srcProtoAddr; @@ -690,7 +690,7 @@ void P25PacketData::dispatch(uint32_t peerId) // 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)", + LogMessage(LOG_P25, "PDU -> VTUN, IP Data, repeated to CAI, broadcast packet, dstIp = %s (%u)", dstIp, status->header.getLLId()); dispatchUserFrameToFNE(status->header, status->extendedAddress, status->pduUserData); @@ -700,7 +700,7 @@ void P25PacketData::dispatch(uint32_t peerId) 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()); + LogMessage(LOG_P25, 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); } } @@ -708,7 +708,7 @@ void P25PacketData::dispatch(uint32_t peerId) // 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)", + LogMessage(LOG_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); @@ -718,13 +718,13 @@ void P25PacketData::dispatch(uint32_t peerId) 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()); + LogMessage(LOG_P25, 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", + LogMessage(LOG_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); DECLARE_UINT8_ARRAY(ipFrame, pktLen); @@ -733,7 +733,7 @@ void P25PacketData::dispatch(uint32_t peerId) Utils::dump(1U, "P25, P25PacketData::dispatch(), ipFrame", ipFrame, pktLen); #endif if (!m_network->m_host->m_tun->write(ipFrame, pktLen)) { - LogError(LOG_NET, P25_PDU_STR ", failed to write IP frame to virtual tunnel, len %u", pktLen); + LogError(LOG_P25, P25_PDU_STR ", failed to write IP frame to virtual tunnel, len %u", pktLen); } // if the packet is unhandled and sent off to VTUN; ack the packet so the sender knows we received it @@ -746,7 +746,7 @@ void P25PacketData::dispatch(uint32_t peerId) break; case PDUSAP::CONV_DATA_REG: { - LogMessage(LOG_NET, P25_PDU_STR ", CONV_DATA_REG (Conventional Data Registration), peer = %u, blocksToFollow = %u", + LogMessage(LOG_P25, P25_PDU_STR ", CONV_DATA_REG (Conventional Data Registration), peer = %u, blocksToFollow = %u", peerId, status->header.getBlocksToFollow()); processConvDataReg(status); @@ -754,7 +754,7 @@ void P25PacketData::dispatch(uint32_t peerId) break; case PDUSAP::SNDCP_CTRL_DATA: { - LogMessage(LOG_NET, P25_PDU_STR ", SNDCP_CTRL_DATA (SNDCP Control Data), peer = %u, blocksToFollow = %u", + LogMessage(LOG_P25, P25_PDU_STR ", SNDCP_CTRL_DATA (SNDCP Control Data), peer = %u, blocksToFollow = %u", peerId, status->header.getBlocksToFollow()); processSNDCPControl(status); @@ -763,7 +763,7 @@ void P25PacketData::dispatch(uint32_t peerId) case PDUSAP::UNENC_KMM: case PDUSAP::ENC_KMM: { - LogMessage(LOG_NET, P25_PDU_STR ", KMM (Key Management Message), peer = %u, blocksToFollow = %u", + LogMessage(LOG_P25, P25_PDU_STR ", KMM (Key Management Message), peer = %u, blocksToFollow = %u", peerId, status->header.getBlocksToFollow()); bool encrypted = (sap == PDUSAP::ENC_KMM); @@ -785,6 +785,10 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) uint32_t srcId = (status->extendedAddress) ? status->header.getSrcLLId() : status->header.getLLId(); uint32_t dstId = status->header.getLLId(); + /* + ** MASTER TRAFFIC + */ + // repeat traffic to the connected peers if (m_network->m_peers.size() > 0U) { uint32_t i = 0U; @@ -797,7 +801,7 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) write_PDU_User(peer.first, peerId, nullptr, status->header, status->extendedAddress, status->pduUserData, true); if (m_network->m_debug) { - LogDebug(LOG_NET, "P25, srcPeer = %u, dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", + LogDebug(LOG_P25, "srcPeer = %u, dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", peerId, peer.first, DUID::PDU, srcId, dstId); } @@ -807,6 +811,10 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) m_network->m_frameQueue->flushQueue(); } + /* + ** PEER TRAFFIC (e.g. upstream networks this FNE is peered to) + */ + // repeat traffic to neighbor FNE peers if (m_network->m_host->m_peerNetworks.size() > 0U) { for (auto peer : m_network->m_host->m_peerNetworks) { @@ -822,7 +830,7 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) write_PDU_User(dstPeerId, peerId, peer.second, status->header, status->extendedAddress, status->pduUserData); if (m_network->m_debug) { - LogDebug(LOG_NET, "P25, srcPeer = %u, dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", + LogDebug(LOG_P25, "srcPeer = %u, dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", peerId, dstPeerId, DUID::PDU, srcId, dstId); } } @@ -847,6 +855,10 @@ void P25PacketData::dispatchUserFrameToFNE(p25::data::DataHeader& dataHeader, bo dataHeader.setNs(m_suSendSeq[srcId]); + /* + ** MASTER TRAFFIC + */ + // repeat traffic to the connected peers if (m_network->m_peers.size() > 0U) { uint32_t i = 0U; @@ -858,7 +870,7 @@ void P25PacketData::dispatchUserFrameToFNE(p25::data::DataHeader& dataHeader, bo write_PDU_User(peer.first, m_network->m_peerId, nullptr, dataHeader, extendedAddress, pduUserData, true); if (m_network->m_debug) { - LogDebug(LOG_NET, "P25, dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", + LogDebug(LOG_P25, "dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", peer.first, DUID::PDU, srcId, dstId); } @@ -868,7 +880,6 @@ void P25PacketData::dispatchUserFrameToFNE(p25::data::DataHeader& dataHeader, bo } } - /* Helper used to process conventional data registration from PDU data. */ bool P25PacketData::processConvDataReg(RxStatus* status) @@ -881,11 +892,11 @@ bool P25PacketData::processConvDataReg(RxStatus* status) uint32_t ipAddr = (status->pduUserData[8U] << 24) + (status->pduUserData[9U] << 16) + (status->pduUserData[10U] << 8) + status->pduUserData[11U]; if (ipAddr == 0U) { - LogWarning(LOG_NET, P25_PDU_STR ", CONNECT (Registration Request Connect) with zero IP address, llId = %u", llId); + LogWarning(LOG_P25, P25_PDU_STR ", CONNECT (Registration Request Connect) with zero IP address, llId = %u", llId); ipAddr = getIPAddress(llId); } - LogMessage(LOG_NET, P25_PDU_STR ", CONNECT (Registration Request Connect), llId = %u, ipAddr = %s", llId, __IP_FROM_UINT(ipAddr).c_str()); + LogMessage(LOG_P25, P25_PDU_STR ", CONNECT (Registration Request Connect), llId = %u, ipAddr = %s", llId, __IP_FROM_UINT(ipAddr).c_str()); m_arpTable[llId] = ipAddr; // update ARP table } break; @@ -893,13 +904,13 @@ bool P25PacketData::processConvDataReg(RxStatus* status) { uint32_t llId = (status->pduUserData[1U] << 16) + (status->pduUserData[2U] << 8) + status->pduUserData[3U]; - LogMessage(LOG_NET, P25_PDU_STR ", DISCONNECT (Registration Request Disconnect), llId = %u", llId); + LogMessage(LOG_P25, P25_PDU_STR ", DISCONNECT (Registration Request Disconnect), llId = %u", llId); m_arpTable.erase(llId); } break; default: - LogError(LOG_RF, "P25 unhandled PDU registration type, regType = $%02X", regType); + LogError(LOG_P25, "P25 unhandled PDU registration type, regType = $%02X", regType); break; } @@ -912,7 +923,7 @@ bool P25PacketData::processSNDCPControl(RxStatus* status) { std::unique_ptr packet = SNDCPFactory::create(status->pduUserData); if (packet == nullptr) { - LogWarning(LOG_NET, P25_PDU_STR ", undecodable SNDCP packet"); + LogWarning(LOG_P25, P25_PDU_STR ", undecodable SNDCP packet"); return false; } @@ -922,7 +933,7 @@ bool P25PacketData::processSNDCPControl(RxStatus* status) case SNDCP_PDUType::ACT_TDS_CTX: { SNDCPCtxActRequest* isp = static_cast(packet.get()); - LogMessage(LOG_NET, P25_PDU_STR ", SNDCP context activation request, llId = %u, nsapi = %u, ipAddr = %s, nat = $%02X, dsut = $%02X, mdpco = $%02X", llId, + LogMessage(LOG_P25, P25_PDU_STR ", SNDCP context activation request, llId = %u, nsapi = %u, ipAddr = %s, nat = $%02X, dsut = $%02X, mdpco = $%02X", llId, isp->getNSAPI(), __IP_FROM_UINT(isp->getIPAddress()).c_str(), isp->getNAT(), isp->getDSUT(), isp->getMDPCO()); m_arpTable[llId] = isp->getIPAddress(); @@ -932,7 +943,7 @@ bool P25PacketData::processSNDCPControl(RxStatus* status) case SNDCP_PDUType::DEACT_TDS_CTX_REQ: { SNDCPCtxDeactivation* isp = static_cast(packet.get()); - LogMessage(LOG_NET, P25_PDU_STR ", SNDCP context deactivation request, llId = %u, deactType = %02X", llId, + LogMessage(LOG_P25, P25_PDU_STR ", SNDCP context deactivation request, llId = %u, deactType = %02X", llId, isp->getDeactType()); m_arpTable.erase(llId); @@ -972,7 +983,7 @@ void P25PacketData::write_PDU_ARP(uint32_t addr) #if DEBUG_P25_PDU_DATA Utils::dump(1U, "P25, P25PacketData::write_PDU_ARP(), arpPacket", arpPacket, P25_PDU_ARP_PCKT_LENGTH); #endif - LogMessage(LOG_NET, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(addr).c_str(), fneIPv4.c_str(), WUID_FNE); + LogMessage(LOG_P25, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(addr).c_str(), fneIPv4.c_str(), WUID_FNE); // assemble a P25 PDU frame header for transport... data::DataHeader rspHeader = data::DataHeader(); @@ -1028,7 +1039,7 @@ void P25PacketData::write_PDU_ARP_Reply(uint32_t targetAddr, uint32_t requestorL #if DEBUG_P25_PDU_DATA Utils::dump(1U, "P25, P25PacketData::write_PDU_ARP_Reply(), arpPacket", arpPacket, P25_PDU_ARP_PCKT_LENGTH); #endif - LogMessage(LOG_NET, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(targetAddr).c_str(), tgtLlid); + LogMessage(LOG_P25, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(targetAddr).c_str(), tgtLlid); // assemble a P25 PDU frame header for transport... data::DataHeader rspHeader = data::DataHeader(); @@ -1066,7 +1077,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); if (m_network->m_verbosePacketData) - LogMessage(LOG_NET, P25_PDU_STR ", OSP, peerId = %u, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", + LogMessage(LOG_P25, P25_PDU_STR ", OSP, peerId = %u, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", peerId, dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(), dataHeader.getSynchronize(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), dataHeader.getHeaderOffset(), dataHeader.getLLId()); @@ -1100,7 +1111,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: networkBlock++; if (m_network->m_verbosePacketData) - LogMessage(LOG_NET, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", + LogMessage(LOG_P25, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } @@ -1109,7 +1120,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: dataHeader.encodeExtAddr(pduUserData); if (m_network->m_verbosePacketData) - LogMessage(LOG_NET, P25_PDU_STR ", OSP, sap = $%02X, srcLlId = %u", + LogMessage(LOG_P25, P25_PDU_STR ", OSP, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } @@ -1129,7 +1140,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: dataBlock.setData(pduUserData + dataOffset); if (m_network->m_verbosePacketData) - LogMessage(LOG_NET, P25_PDU_STR ", OSP, peerId = %u, block %u, fmt = $%02X, lastBlock = %u", + LogMessage(LOG_P25, P25_PDU_STR ", OSP, peerId = %u, block %u, fmt = $%02X, lastBlock = %u", peerId, (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? dataBlock.getSerialNo() : i, dataBlock.getFormat(), dataBlock.getLastBlock()); From da15b054754f649cd9897afddc49d3be7fe0afcf Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 23 Oct 2025 15:09:32 -0400 Subject: [PATCH 089/200] enhance STP peer reconnect logic to allow peers announcing the same peerId and masterId to reconnect between spanning tree updates; --- configs/fne-config.example.yml | 4 ++++ src/fne/network/FNENetwork.cpp | 15 ++++++++++++--- src/fne/network/FNENetwork.h | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index c272e503..05cf2a0f 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -78,6 +78,10 @@ master: enableSpanningTree: true # Flag indicating whether or not spanning tree changes will be logged. logSpanningTreeChanges: false + # Flag indicating whether or not the spanning tree allows fast peer reconnects. + # (This is mainly useful for a peer announcing the same master to reconnect rapidly, inbetween + # spanning tree updates.) + spanningTreeFastReconnect: true # Flag indicating whether or not peer pinging will be reported. reportPeerPing: true diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 95ecaee5..52cf3bee 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -106,6 +106,9 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_updateLookupTimer(1000U, (updateLookupTime * 60U)), m_haUpdateTimer(1000U, FIXED_HA_UPDATE_INTERVAL), m_softConnLimit(0U), + m_enableSpanningTree(true), + m_logSpanningTreeChanges(false), + m_spanningTreeFastReconnect(true), m_callCollisionTimeout(5U), m_disallowAdjStsBcast(false), m_disallowExtAdjStsBcast(true), @@ -198,6 +201,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) } m_logSpanningTreeChanges = conf["logSpanningTreeChanges"].as(false); + m_spanningTreeFastReconnect = conf["spanningTreeFastReconnect"].as(true); // always force disable ADJ_STS_BCAST to neighbor FNE peers if the all option // is enabled @@ -276,6 +280,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogInfo(" Maximum Permitted Connections: %u", m_softConnLimit); LogInfo(" Enable Peer Spanning Tree: %s", m_enableSpanningTree ? "yes" : "no"); LogInfo(" Log Spanning Tree Changes: %s", m_logSpanningTreeChanges ? "yes" : "no"); + LogInfo(" Spanning Tree Allow Fast Reconnect: %s", m_spanningTreeFastReconnect ? "yes" : "no"); LogInfo(" Disable adjacent site broadcasts to any peers: %s", m_disallowAdjStsBcast ? "yes" : "no"); if (m_disallowAdjStsBcast) { LogWarning(LOG_MASTER, "NOTICE: All P25 ADJ_STS_BCAST messages will be blocked and dropped!"); @@ -1232,9 +1237,13 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // check if this peer is already connected via another peer MasterTree* tree = MasterTree::findByMasterID(masterPeerId); if (tree != nullptr) { - if (tree->id() != peerId && tree->masterId() == masterPeerId) { - LogWarning(LOG_MASTER, "PEER %u (%s) RPTC NAK, server already connected via PEER %u, duplicate connection denied, connectionState = %u", peerId, connection->identWithQualifier().c_str(), - tree->id(), connection->connectionState()); + if ((tree->id() == peerId && tree->masterId() == masterPeerId) && + network->m_spanningTreeFastReconnect) { + LogWarning(LOG_MASTER, "PEER %u (%s) RPTC NAK, server already announced in server tree, fast peer reconnect, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + peerId, masterPeerId, tree->id(), tree->masterId(), connection->connectionState()); + } else { + LogWarning(LOG_MASTER, "PEER %u (%s) RPTC NAK, server already connected via PEER %u, duplicate connection denied, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + peerId, masterPeerId, tree->id(), tree->masterId(), tree->id(), connection->connectionState()); network->writePeerNAK(peerId, TAG_REPEATER_CONFIG, NET_CONN_NAK_FNE_DUPLICATE_CONN, req->address, req->addrLen); network->disconnectPeer(peerId, connection); break; diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index df5a222c..9fe73e56 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -342,6 +342,7 @@ namespace network bool m_enableSpanningTree; bool m_logSpanningTreeChanges; + bool m_spanningTreeFastReconnect; uint32_t m_callCollisionTimeout; From 5ed30e63905e264d62d1f0c516e0f753c3dc68ca Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 23 Oct 2025 15:23:04 -0400 Subject: [PATCH 090/200] update log colorizer to match new logging categories in the FNE; --- tools/colorize-fne.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/colorize-fne.sh b/tools/colorize-fne.sh index bee978b3..d9c8982b 100755 --- a/tools/colorize-fne.sh +++ b/tools/colorize-fne.sh @@ -33,4 +33,9 @@ AFF_COLOR="s#Affiliations#\x1b[1m\x1b[36m&#; s#Affiliation#\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}; ${DMR_COLOR}; ${P25_COLOR}; ${NXDN_COLOR}; ${AFF_COLOR}" \ No newline at end of file +MASTER_HIGHLIGHT="s#(MSTR)#\x1b[1m\x1b[34m&\x1b[0m#;" +PEER_HIGHLIGHT="s#(PEER)#\x1b[1m\x1b[36m&\x1b[0m#;" +STP_HIGHLIGHT="s#(STP)#\x1b[1m\x1b[32m&\x1b[0m#;" +REPL_HIGHLIGHT="s#(REPL)#\x1b[1m\x1b[33m&\x1b[0m#;" + +sed "${LOG_COLOR}; ${RF_HIGHLIGHT}; ${NET_HIGHLIGHT}; ${MASTER_HIGHLIGHT}; ${PEER_HIGHLIGHT}; ${STP_HIGHLIGHT}; ${REPL_HIGHLIGHT}; ${DMR_COLOR}; ${P25_COLOR}; ${NXDN_COLOR}; ${AFF_COLOR}" \ No newline at end of file From 6c263e0d630b1a94ae0555761ac091107739c4df Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 23 Oct 2025 15:28:13 -0400 Subject: [PATCH 091/200] update log colorizer to match new logging categories in the FNE (round 2); --- tools/colorize-fne.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/colorize-fne.sh b/tools/colorize-fne.sh index d9c8982b..99c95eaf 100755 --- a/tools/colorize-fne.sh +++ b/tools/colorize-fne.sh @@ -30,6 +30,8 @@ P25_COLOR="s#LDU#\x1b[36m&#; s#TDU#\x1b[0m\x1b[32m&#; s#HDU#\x1b[0m\x1b[32m&#; s NXDN_COLOR="s#VCALL#\x1b[36m&#; s#TX_REL#\x1b[0m\x1b[32m&#" AFF_COLOR="s#Affiliations#\x1b[1m\x1b[36m&#; s#Affiliation#\x1b[1m\x1b[36m&#;" +HOST_HIGHLIGHT="s#(HOST)#\x1b[1m\x1b[37m&\x1b[0m#;" + RF_HIGHLIGHT="s#(RF)#\x1b[1m\x1b[34m&\x1b[0m#;" NET_HIGHLIGHT="s#(NET)#\x1b[1m\x1b[36m&\x1b[0m#;" @@ -38,4 +40,4 @@ PEER_HIGHLIGHT="s#(PEER)#\x1b[1m\x1b[36m&\x1b[0m#;" STP_HIGHLIGHT="s#(STP)#\x1b[1m\x1b[32m&\x1b[0m#;" REPL_HIGHLIGHT="s#(REPL)#\x1b[1m\x1b[33m&\x1b[0m#;" -sed "${LOG_COLOR}; ${RF_HIGHLIGHT}; ${NET_HIGHLIGHT}; ${MASTER_HIGHLIGHT}; ${PEER_HIGHLIGHT}; ${STP_HIGHLIGHT}; ${REPL_HIGHLIGHT}; ${DMR_COLOR}; ${P25_COLOR}; ${NXDN_COLOR}; ${AFF_COLOR}" \ No newline at end of file +sed "${LOG_COLOR}; ${HOST_HIGHLIGHT}; ${RF_HIGHLIGHT}; ${NET_HIGHLIGHT}; ${MASTER_HIGHLIGHT}; ${PEER_HIGHLIGHT}; ${STP_HIGHLIGHT}; ${REPL_HIGHLIGHT}; ${DMR_COLOR}; ${P25_COLOR}; ${NXDN_COLOR}; ${AFF_COLOR}" From 6e692ea434437d88e14a600c7717c7e1c2d4c03b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 23 Oct 2025 15:56:44 -0400 Subject: [PATCH 092/200] update log colorizer to match new logging categories in the FNE (round 3); --- tools/colorize-fne.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/colorize-fne.sh b/tools/colorize-fne.sh index 99c95eaf..16692bc7 100755 --- a/tools/colorize-fne.sh +++ b/tools/colorize-fne.sh @@ -23,13 +23,16 @@ #* 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&#;" +CLEAR_ID="s/ID: /ID /g;" +LOG_COLOR="s#W:#\x1b[0m\x1b[1m\x1b[33m&\x1b[0m#; s#E:#\x1b[0m\x1b[1m\x1b[31m&\x1b[0m#; s#M:#\x1b[0m&#; s#I:#\x1b[0m&#; s#D:#\x1b[1m\x1b[34m&\x1b[0m#; s#U:#\x1b[44m\x1b[1m\x1b[33m&#;" DMR_COLOR="s#VOICE#\x1b[36m&#; s#TERMINATOR_WITH_LC#\x1b[0m\x1b[32m&#; s#CSBK#\x1b[0m\x1b[35m&#" P25_COLOR="s#LDU#\x1b[36m&#; s#TDU#\x1b[0m\x1b[32m&#; s#HDU#\x1b[0m\x1b[32m&#; s#TSDU#\x1b[0m\x1b[35m&#" NXDN_COLOR="s#VCALL#\x1b[36m&#; s#TX_REL#\x1b[0m\x1b[32m&#" AFF_COLOR="s#Affiliations#\x1b[1m\x1b[36m&#; s#Affiliation#\x1b[1m\x1b[36m&#;" +NAK_COLOR="s#NAK#\x1b[0m\x1b[1m\x1b[31m&\x1b[0m#;" + HOST_HIGHLIGHT="s#(HOST)#\x1b[1m\x1b[37m&\x1b[0m#;" RF_HIGHLIGHT="s#(RF)#\x1b[1m\x1b[34m&\x1b[0m#;" @@ -40,4 +43,4 @@ PEER_HIGHLIGHT="s#(PEER)#\x1b[1m\x1b[36m&\x1b[0m#;" STP_HIGHLIGHT="s#(STP)#\x1b[1m\x1b[32m&\x1b[0m#;" REPL_HIGHLIGHT="s#(REPL)#\x1b[1m\x1b[33m&\x1b[0m#;" -sed "${LOG_COLOR}; ${HOST_HIGHLIGHT}; ${RF_HIGHLIGHT}; ${NET_HIGHLIGHT}; ${MASTER_HIGHLIGHT}; ${PEER_HIGHLIGHT}; ${STP_HIGHLIGHT}; ${REPL_HIGHLIGHT}; ${DMR_COLOR}; ${P25_COLOR}; ${NXDN_COLOR}; ${AFF_COLOR}" +sed "${CLEAR_ID}; ${LOG_COLOR}; ${NAK_COLOR}; ${HOST_HIGHLIGHT}; ${RF_HIGHLIGHT}; ${NET_HIGHLIGHT}; ${MASTER_HIGHLIGHT}; ${PEER_HIGHLIGHT}; ${STP_HIGHLIGHT}; ${REPL_HIGHLIGHT}; ${DMR_COLOR}; ${P25_COLOR}; ${NXDN_COLOR}; ${AFF_COLOR}" From 468a13a8ee38925d52a1eec7dc14d3951f047050 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 23 Oct 2025 16:10:39 -0400 Subject: [PATCH 093/200] update log colorizer to match new logging categories in the FNE (round 4); --- tools/colorize-fne.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/colorize-fne.sh b/tools/colorize-fne.sh index 16692bc7..b0bbb54b 100755 --- a/tools/colorize-fne.sh +++ b/tools/colorize-fne.sh @@ -24,6 +24,7 @@ #* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #*/ CLEAR_ID="s/ID: /ID /g;" +EOL_CLEAR="s/$/\x1b[0m/;" LOG_COLOR="s#W:#\x1b[0m\x1b[1m\x1b[33m&\x1b[0m#; s#E:#\x1b[0m\x1b[1m\x1b[31m&\x1b[0m#; s#M:#\x1b[0m&#; s#I:#\x1b[0m&#; s#D:#\x1b[1m\x1b[34m&\x1b[0m#; s#U:#\x1b[44m\x1b[1m\x1b[33m&#;" DMR_COLOR="s#VOICE#\x1b[36m&#; s#TERMINATOR_WITH_LC#\x1b[0m\x1b[32m&#; s#CSBK#\x1b[0m\x1b[35m&#" @@ -43,4 +44,4 @@ PEER_HIGHLIGHT="s#(PEER)#\x1b[1m\x1b[36m&\x1b[0m#;" STP_HIGHLIGHT="s#(STP)#\x1b[1m\x1b[32m&\x1b[0m#;" REPL_HIGHLIGHT="s#(REPL)#\x1b[1m\x1b[33m&\x1b[0m#;" -sed "${CLEAR_ID}; ${LOG_COLOR}; ${NAK_COLOR}; ${HOST_HIGHLIGHT}; ${RF_HIGHLIGHT}; ${NET_HIGHLIGHT}; ${MASTER_HIGHLIGHT}; ${PEER_HIGHLIGHT}; ${STP_HIGHLIGHT}; ${REPL_HIGHLIGHT}; ${DMR_COLOR}; ${P25_COLOR}; ${NXDN_COLOR}; ${AFF_COLOR}" +sed "${CLEAR_ID}; ${LOG_COLOR}; ${NAK_COLOR}; ${HOST_HIGHLIGHT}; ${RF_HIGHLIGHT}; ${NET_HIGHLIGHT}; ${MASTER_HIGHLIGHT}; ${PEER_HIGHLIGHT}; ${STP_HIGHLIGHT}; ${REPL_HIGHLIGHT}; ${DMR_COLOR}; ${P25_COLOR}; ${NXDN_COLOR}; ${AFF_COLOR}; ${EOL_COLOR};" From cec396d074eaffc1f6ad8736ba18b3d2718bafbf Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 23 Oct 2025 22:01:37 -0400 Subject: [PATCH 094/200] bump major version numbering in a preliminary fashion, at least until group talks about it are done (so this could be permenant); --- src/CMakeLists.txt | 2 +- src/common/Defines.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 75cb28d5..1b143996 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -61,7 +61,7 @@ set(CPACK_PACKAGE_VENDOR "DVMProject") set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "The DVM Host software provides the host computer implementation of a mixed-mode DMR, P25 and/or NXDN or dedicated-mode DMR, P25 or NXDN repeater system that talks to the actual modem hardware. The host software; is the portion of a complete Over-The-Air modem implementation that performs the data processing, decision making and FEC correction for a digital repeater.") set(CPACK_DEBIAN_PACKAGE_MAINTAINER "DVMProject Authors") -set(CPACK_DEBIAN_PACKAGE_VERSION "R04Jxx") +set(CPACK_DEBIAN_PACKAGE_VERSION "R05A") set(CPACK_DEBIAN_PACKAGE_RELEASE "0") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/dvmproject") diff --git a/src/common/Defines.h b/src/common/Defines.h index eca5a11c..7f90228b 100644 --- a/src/common/Defines.h +++ b/src/common/Defines.h @@ -116,9 +116,9 @@ typedef unsigned long long ulong64_t; #define __PROG_NAME__ "" #define __EXE_NAME__ "" -#define VERSION_MAJOR "04" -#define VERSION_MINOR "34" -#define VERSION_REV "K" +#define VERSION_MAJOR "05" +#define VERSION_MINOR "02" +#define VERSION_REV "A" #define __NETVER__ "DVM_R" VERSION_MAJOR VERSION_REV VERSION_MINOR #define __VER__ VERSION_MAJOR "." VERSION_MINOR VERSION_REV " (R" VERSION_MAJOR VERSION_REV VERSION_MINOR " " __GIT_VER__ ")" From 1966073966a9ce0710bb523c4b3629b5f8434c6b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 23 Oct 2025 22:06:14 -0400 Subject: [PATCH 095/200] fix messaging for fast peer reconnect (it was misleading as a RPTC NAK which it isnt); --- 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 52cf3bee..4347e380 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1239,7 +1239,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (tree != nullptr) { if ((tree->id() == peerId && tree->masterId() == masterPeerId) && network->m_spanningTreeFastReconnect) { - LogWarning(LOG_MASTER, "PEER %u (%s) RPTC NAK, server already announced in server tree, fast peer reconnect, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + LogWarning(LOG_MASTER, "PEER %u (%s) server already announced in server tree, fast peer reconnect, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), peerId, masterPeerId, tree->id(), tree->masterId(), connection->connectionState()); } else { LogWarning(LOG_MASTER, "PEER %u (%s) RPTC NAK, server already connected via PEER %u, duplicate connection denied, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), From 0015625bcc16ba5ec741d888b3e6c8d1f26b9725 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 23 Oct 2025 23:09:54 -0400 Subject: [PATCH 096/200] allow reparenting of a STP node if it moves from one tree node to another; --- src/fne/network/DiagNetwork.cpp | 5 +-- src/fne/network/FNENetwork.cpp | 42 +++++++++++++------- src/fne/network/FNENetwork.h | 6 +++ src/fne/network/MasterTree.cpp | 70 ++++++++++++++++++++++++++++++++- src/fne/network/MasterTree.h | 10 +++++ 5 files changed, 113 insertions(+), 20 deletions(-) diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 0820a5a4..f6269b05 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -652,10 +652,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) std::vector duplicatePeers; MasterTree::deserializeTree(arr, network->m_fneTree, &duplicatePeers); - if (network->m_logSpanningTreeChanges && network->m_fneTree->hasChildren()) { - LogInfoEx(LOG_STP, "PEER %u (%s) Network Tree, Tree Change, Current Tree", peerId, connection->identWithQualifier().c_str()); - MasterTree::visualizeTreeToLog(network->m_fneTree); - } + network->logSpanningTree(connection); if (duplicatePeers.size() > 0U) { for (auto dupPeerId : duplicatePeers) { diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 4347e380..26a6f43e 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -151,6 +151,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_p25OTARService = new P25OTARService(this, m_tagP25->packetData(), kmfDebug, verbose); + MasterTree::m_maxUpdatesBeforeReparent = (uint8_t)host->m_maxMissedPings; m_fneTree = new MasterTree(peerId, peerId, nullptr); /* @@ -415,11 +416,7 @@ void FNENetwork::processNetworkTreeDisconnect(uint32_t peerId, uint32_t offendin connection->connectionState()); writePeerNAK(offendingPeerId, createStreamId(), TAG_REPEATER_CONFIG, NET_CONN_NAK_FNE_DUPLICATE_CONN); disconnectPeer(offendingPeerId, connection); - - if (m_logSpanningTreeChanges && m_fneTree->hasChildren()) { - LogInfoEx(LOG_STP, "Network Tree, Tree Change, Current Tree"); - MasterTree::visualizeTreeToLog(m_fneTree); - } + logSpanningTree(); } else { LogError(LOG_STP, "Network Tree Disconnect, upstream master requested disconnect for PEER %u, but connection is null", offendingPeerId); } @@ -1237,12 +1234,17 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) // check if this peer is already connected via another peer MasterTree* tree = MasterTree::findByMasterID(masterPeerId); if (tree != nullptr) { + // are we allowing a fast reconnect? (this happens when a connecting peer + // uses the same peer ID and master ID already announced in the tree, but + // the tree entry wasn't yet erased) if ((tree->id() == peerId && tree->masterId() == masterPeerId) && network->m_spanningTreeFastReconnect) { - LogWarning(LOG_MASTER, "PEER %u (%s) server already announced in server tree, fast peer reconnect, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + LogWarning(LOG_STP, "PEER %u (%s) server already announced in server tree, fast peer reconnect, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), peerId, masterPeerId, tree->id(), tree->masterId(), connection->connectionState()); + MasterTree::moveParent(tree, network->m_fneTree); + network->logSpanningTree(connection); } else { - LogWarning(LOG_MASTER, "PEER %u (%s) RPTC NAK, server already connected via PEER %u, duplicate connection denied, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + LogWarning(LOG_STP, "PEER %u (%s) RPTC NAK, server already connected via PEER %u, duplicate connection denied, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), peerId, masterPeerId, tree->id(), tree->masterId(), tree->id(), connection->connectionState()); network->writePeerNAK(peerId, TAG_REPEATER_CONFIG, NET_CONN_NAK_FNE_DUPLICATE_CONN, req->address, req->addrLen); network->disconnectPeer(peerId, connection); @@ -1250,10 +1252,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } } else { new MasterTree(peerId, masterPeerId, network->m_fneTree); - if (network->m_logSpanningTreeChanges && network->m_fneTree->hasChildren()) { - LogInfoEx(LOG_MASTER, "PEER %u (%s) Network Tree, Tree Change, Current Tree", peerId, connection->identWithQualifier().c_str()); - MasterTree::visualizeTreeToLog(network->m_fneTree); - } + network->logSpanningTree(connection); } } } @@ -1828,6 +1827,22 @@ bool FNENetwork::checkU2UDroppedPeer(uint32_t peerId) return false; } +/* Helper to dump the current spanning tree configuration to the log. */ + +void FNENetwork::logSpanningTree(FNEPeerConnection* connection) +{ + if (!m_enableSpanningTree) + return; + + if (m_logSpanningTreeChanges && m_fneTree->hasChildren()) { + if (connection != nullptr) + LogInfoEx(LOG_STP, "PEER %u (%s) Network Tree, Tree Change, Current Tree", connection->id(), connection->identWithQualifier().c_str()); + else + LogInfoEx(LOG_STP, "PEER %u Network Tree, Tree Display, Current Tree", m_peerId); + MasterTree::visualizeTreeToLog(m_fneTree); + } +} + /* Erases a stream ID from the given peer ID connection. */ void FNENetwork::eraseStreamPktSeq(uint32_t peerId, uint32_t streamId) @@ -1945,10 +1960,7 @@ void FNENetwork::erasePeer(uint32_t peerId) MasterTree::erasePeer(peerId); } - if (m_logSpanningTreeChanges && m_fneTree->hasChildren()) { - LogInfoEx(LOG_STP, "PEER %u Network Tree, Tree Change, Current Tree", peerId); - MasterTree::visualizeTreeToLog(m_fneTree); - } + logSpanningTree(); } // cleanup peer affiliations diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 9fe73e56..3c8e147f 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -403,6 +403,12 @@ namespace network */ bool checkU2UDroppedPeer(uint32_t peerId); + /** + * @brief Helper to dump the current spanning tree configuration to the log. + * @param connection Instance of the FNEPeerConnection class. + */ + void logSpanningTree(FNEPeerConnection* connection = nullptr); + /** * @brief Erases a stream ID from the given peer ID connection. * @param peerId Peer ID. diff --git a/src/fne/network/MasterTree.cpp b/src/fne/network/MasterTree.cpp index a7f8b86e..ebe0d605 100644 --- a/src/fne/network/MasterTree.cpp +++ b/src/fne/network/MasterTree.cpp @@ -18,6 +18,7 @@ using namespace network; // --------------------------------------------------------------------------- std::unordered_map MasterTree::m_masterTrees = std::unordered_map(); +uint8_t MasterTree::m_maxUpdatesBeforeReparent = 5U; // --------------------------------------------------------------------------- // Public Class Members @@ -29,7 +30,8 @@ MasterTree::MasterTree(uint32_t id, uint32_t masterId, MasterTree* parent) : m_parent(parent), m_children(), m_id(id), - m_masterId(masterId) + m_masterId(masterId), + m_updatesBeforeReparent(0U) { m_masterTrees[id] = this; if (m_parent != nullptr) @@ -163,6 +165,9 @@ void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std // check if this peer is already connected via another peer MasterTree* tree = MasterTree::findByMasterID(masterId); if (tree != nullptr) { + // is this a fast reconnect? (this happens when a connecting peer + // uses the same peer ID and master ID already announced in the tree, but + // the tree entry wasn't yet erased) if (tree->id() != id) { if (duplicatePeers != nullptr) duplicatePeers->push_back(id); @@ -173,6 +178,16 @@ void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std MasterTree* existingNode = findByPeerID(id); if (existingNode == nullptr) existingNode = new MasterTree(id, masterId, parent); + else { + if (existingNode->m_updatesBeforeReparent >= m_maxUpdatesBeforeReparent) { + existingNode->m_updatesBeforeReparent = 0U; + + // reparent the node if necessary + moveParent(existingNode, parent); + } else { + existingNode->m_updatesBeforeReparent++; + } + } json::array childArray = obj["children"].get(); //LogDebugEx(LOG_STP, "MasterTree::deserializeTree()", "peerId = %u, masterId = %u, childrenCnt = %u", @@ -182,6 +197,59 @@ void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std } } +/* Helper to move the master tree node to a different parent master tree node. */ + +void MasterTree::moveParent(MasterTree* node, MasterTree* parent) +{ + if (node == nullptr) + return; + if (parent == nullptr) + return; + + // the root node cannot be moved + if (node->m_parent == nullptr) { + LogError(LOG_STP, "PEER %u is a root tree node, can't be moved. BUGBUG.", node->id()); + return; + } + + if (node->m_parent != parent) { + // find the node in the parent children and remove it + MasterTree* nodeParent = node->m_parent; + uint32_t nodeParentId = 0U; + bool hasReleasedFromParent = false; + + if (nodeParent != nullptr) { + nodeParentId = nodeParent->id(); + auto it = std::find_if(nodeParent->m_children.begin(), nodeParent->m_children.end(), [&](MasterTree* childNode) { + if (childNode == node) + return true; + return false; + }); + if (it != nodeParent->m_children.end()) { + nodeParent->m_children.erase(it); + hasReleasedFromParent = true; + } else { + LogError(LOG_STP, "PEER %u failed to release ownership from PEER %u, tree is potentially inconsistent", + node->id(), nodeParent->id()); + } + } + + if (hasReleasedFromParent) { + // reparent existing node + node->m_parent = parent; + if (node->m_parent != nullptr) + node->m_parent->m_children.push_back(node); + + // reset update counter + if (node->m_updatesBeforeReparent > 0U) + node->m_updatesBeforeReparent = 0U; + + LogWarning(LOG_STP, "PEER %u ownership has changed from PEER %u to PEER %u; this normally shouldn't happen", + node->id(), nodeParentId, parent->id()); + } + } +} + /* Debug helper to visualize the tree structure in the log. */ void MasterTree::visualizeTreeToLog(MasterTree* node, uint32_t level) diff --git a/src/fne/network/MasterTree.h b/src/fne/network/MasterTree.h index 7a9f087b..6ba01f22 100644 --- a/src/fne/network/MasterTree.h +++ b/src/fne/network/MasterTree.h @@ -132,6 +132,13 @@ namespace network */ static void deserializeTree(json::array& jsonArray, MasterTree* parent, std::vector* duplicatePeers); + /** + * @brief Helper to move the master tree node to a different parent master tree node. + * @param node Pointer to a MasterTree node. + * @param parent Pointer to new parent MasterTree Node. + */ + static void moveParent(MasterTree* node, MasterTree* parent); + /** * @brief Helper to visualize the tree structure in the log. * @param node Pointer to MasterTree node. @@ -144,6 +151,7 @@ namespace network std::vector m_children; //!< Child master tree nodes. (i.e. peer FNEs below this) static std::unordered_map m_masterTrees; //!< Static map of all master trees by peer ID. + static uint8_t m_maxUpdatesBeforeReparent; //!< Maximum count of updates before allowing node reparenting. /** * @brief Peer ID. @@ -155,6 +163,8 @@ namespace network DECLARE_PROPERTY_PLAIN(uint32_t, masterId); private: + uint8_t m_updatesBeforeReparent; + /** * @brief Helper to erase all children of a master tree node. * @param node Pointer to MasterTree node. From f12925e999c3f79866c77d5d531821ef7a1e6c33 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 24 Oct 2025 00:17:28 -0400 Subject: [PATCH 097/200] implement tree cleanups if the downstream announcement removes child leaves or reports no children at all; --- src/fne/network/MasterTree.cpp | 81 +++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/src/fne/network/MasterTree.cpp b/src/fne/network/MasterTree.cpp index ebe0d605..c3fbc124 100644 --- a/src/fne/network/MasterTree.cpp +++ b/src/fne/network/MasterTree.cpp @@ -179,21 +179,82 @@ void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std if (existingNode == nullptr) existingNode = new MasterTree(id, masterId, parent); else { - if (existingNode->m_updatesBeforeReparent >= m_maxUpdatesBeforeReparent) { - existingNode->m_updatesBeforeReparent = 0U; - - // reparent the node if necessary - moveParent(existingNode, parent); - } else { - existingNode->m_updatesBeforeReparent++; + // are the parents different? if so, start counting down to reparenting + if (existingNode->m_parent != parent) { + if (existingNode->m_updatesBeforeReparent >= m_maxUpdatesBeforeReparent) { + existingNode->m_updatesBeforeReparent = 0U; + + // reparent the node if necessary + moveParent(existingNode, parent); + } else { + existingNode->m_updatesBeforeReparent++; + } } } + // process children json::array childArray = obj["children"].get(); - //LogDebugEx(LOG_STP, "MasterTree::deserializeTree()", "peerId = %u, masterId = %u, childrenCnt = %u", - // existingNode->id(), existingNode->masterId(), childArray.size()); - if (childArray.size() > 0U) + //LogDebugEx(LOG_STP, "MasterTree::deserializeTree()", "peerId = %u, masterId = %u, parent = %u, childrenCnt = %u", + // existingNode->id(), existingNode->masterId(), parent->id(), childArray.size()); + if (childArray.size() > 0U) { deserializeTree(childArray, existingNode, duplicatePeers); + + // does the announced array disagree with our current count of children? + if (childArray.size() < existingNode->m_children.size()) { + /* + ** bryanb: this is doing some array comparision/differencing non-sense and IMHO is probably + ** bad and slow as balls... + */ + + // enumerate the peer IDs in the announced children + std::vector announcedChildren; + for (auto& child : childArray) { + if (!child.is()) + continue; + + json::object obj = child.get(); + if (!obj["id"].is()) + continue; + + uint32_t childId = obj["id"].get(); + announcedChildren.push_back(childId); + } + + // iterate over the nodes children and remove entries + std::vector childrenToErase; + for (auto child : existingNode->m_children) { + if (child != nullptr) { + auto it = std::find(announcedChildren.begin(), announcedChildren.end(), child->id()); + if (it == announcedChildren.end()) { + childrenToErase.push_back(child); + } + } + } + + if (childrenToErase.size() > 0) { + for (auto child : childrenToErase) { + if (child != nullptr) { + auto it = std::find_if(existingNode->m_children.begin(), existingNode->m_children.end(), + [&](MasterTree* x) { + if (x != nullptr) { + return x->id() == child->id(); + } + + return false; + }); + if (it != existingNode->m_children.end()) { + existingNode->m_children.erase(it); + } + } + } + } + } + } + else { + // did the node report children atsome point and is no longer reporting them? + if (childArray.size() == 0U && existingNode->hasChildren()) + eraseChildren(existingNode); + } } } From 7f2878fd02bce7d1511d7852f599dff62b5ff13a Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 24 Oct 2025 09:28:51 -0400 Subject: [PATCH 098/200] relabel MasterTree to SpanningTree proper; --- src/fne/network/DiagNetwork.cpp | 2 +- src/fne/network/FNENetwork.cpp | 22 ++--- src/fne/network/FNENetwork.h | 4 +- src/fne/network/PeerNetwork.cpp | 6 +- src/fne/network/PeerNetwork.h | 6 +- src/fne/network/RESTAPI.cpp | 10 +- src/fne/network/RESTAPI.h | 4 +- src/fne/network/RESTDefines.h | 2 +- .../{MasterTree.cpp => SpanningTree.cpp} | 80 ++++++++-------- .../network/{MasterTree.h => SpanningTree.h} | 95 +++++++++---------- src/remote/RESTClientMain.cpp | 8 +- 11 files changed, 119 insertions(+), 120 deletions(-) rename src/fne/network/{MasterTree.cpp => SpanningTree.cpp} (80%) rename src/fne/network/{MasterTree.h => SpanningTree.h} (52%) diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index f6269b05..eee94061 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -650,7 +650,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) json::array arr = v.get(); LogInfoEx(LOG_STP, "PEER %u (%s) Network Tree, Tree List, updating %u peer entries", peerId, connection->identWithQualifier().c_str(), arr.size()); std::vector duplicatePeers; - MasterTree::deserializeTree(arr, network->m_fneTree, &duplicatePeers); + SpanningTree::deserializeTree(arr, network->m_treeRoot, &duplicatePeers); network->logSpanningTree(connection); diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 26a6f43e..70659456 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -97,7 +97,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_peerAffiliations(), m_ccPeerMap(), m_peerReplicaKeyQueue(), - m_fneTree(nullptr), + m_treeRoot(nullptr), m_peerReplicaHAParams(), m_advertisedHAAddress(), m_advertisedHAPort(TRAFFIC_DEFAULT_PORT), @@ -151,8 +151,8 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_p25OTARService = new P25OTARService(this, m_tagP25->packetData(), kmfDebug, verbose); - MasterTree::m_maxUpdatesBeforeReparent = (uint8_t)host->m_maxMissedPings; - m_fneTree = new MasterTree(peerId, peerId, nullptr); + SpanningTree::m_maxUpdatesBeforeReparent = (uint8_t)host->m_maxMissedPings; + m_treeRoot = new SpanningTree(peerId, peerId, nullptr); /* ** Initialize Threads @@ -503,7 +503,7 @@ void FNENetwork::clock(uint32_t ms) // perform master tree maintainence tasks if (peer.second->isEnabled() && peer.second->getRemotePeerId() > 0U && m_enableSpanningTree) { - peer.second->writeMasterTree(m_fneTree); + peer.second->writeSpanningTree(m_treeRoot); } // perform peer replica maintainence tasks @@ -1232,7 +1232,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (network->m_enableSpanningTree && !connection->isSysView()) { // check if this peer is already connected via another peer - MasterTree* tree = MasterTree::findByMasterID(masterPeerId); + SpanningTree* tree = SpanningTree::findByMasterID(masterPeerId); if (tree != nullptr) { // are we allowing a fast reconnect? (this happens when a connecting peer // uses the same peer ID and master ID already announced in the tree, but @@ -1241,7 +1241,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) network->m_spanningTreeFastReconnect) { LogWarning(LOG_STP, "PEER %u (%s) server already announced in server tree, fast peer reconnect, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), peerId, masterPeerId, tree->id(), tree->masterId(), connection->connectionState()); - MasterTree::moveParent(tree, network->m_fneTree); + SpanningTree::moveParent(tree, network->m_treeRoot); network->logSpanningTree(connection); } else { LogWarning(LOG_STP, "PEER %u (%s) RPTC NAK, server already connected via PEER %u, duplicate connection denied, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), @@ -1251,7 +1251,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) break; } } else { - new MasterTree(peerId, masterPeerId, network->m_fneTree); + new SpanningTree(peerId, masterPeerId, network->m_treeRoot); network->logSpanningTree(connection); } } @@ -1834,12 +1834,12 @@ void FNENetwork::logSpanningTree(FNEPeerConnection* connection) if (!m_enableSpanningTree) return; - if (m_logSpanningTreeChanges && m_fneTree->hasChildren()) { + if (m_logSpanningTreeChanges && m_treeRoot->hasChildren()) { if (connection != nullptr) LogInfoEx(LOG_STP, "PEER %u (%s) Network Tree, Tree Change, Current Tree", connection->id(), connection->identWithQualifier().c_str()); else LogInfoEx(LOG_STP, "PEER %u Network Tree, Tree Display, Current Tree", m_peerId); - MasterTree::visualizeTreeToLog(m_fneTree); + SpanningTree::visualizeTreeToLog(m_treeRoot); } } @@ -1946,7 +1946,7 @@ void FNENetwork::erasePeer(uint32_t peerId) if (neighborFNE && m_enableSpanningTree) { // erase this peer from the master tree - MasterTree* tree = MasterTree::findByPeerID(peerId); + SpanningTree* tree = SpanningTree::findByPeerID(peerId); if (tree != nullptr) { if (tree->hasChildren()) { uint32_t totalChildren = tree->countChildren(tree); @@ -1957,7 +1957,7 @@ void FNENetwork::erasePeer(uint32_t peerId) } LogWarning(LOG_MASTER, "PEER %u downstream netsplit, disconnected", peerId); - MasterTree::erasePeer(peerId); + SpanningTree::erasePeer(peerId); } logSpanningTree(); diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 3c8e147f..8c8d9301 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -39,7 +39,7 @@ #include "fne/lookups/AffiliationLookup.h" #include "fne/network/influxdb/InfluxDB.h" #include "fne/network/FNEPeerConnection.h" -#include "fne/network/MasterTree.h" +#include "fne/network/SpanningTree.h" #include "fne/network/HAParameters.h" #include "fne/CryptoContainer.h" @@ -327,7 +327,7 @@ namespace network static std::timed_mutex m_keyQueueMutex; std::unordered_map m_peerReplicaKeyQueue; - MasterTree* m_fneTree; + SpanningTree* m_treeRoot; concurrent::vector m_peerReplicaHAParams; std::string m_advertisedHAAddress; diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 9a1efda0..61746cc4 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -145,9 +145,9 @@ bool PeerNetwork::writePeerLinkPeers(json::array* peerList) return false; } -/* Writes a complete update of this CFNE's known master FNE tree upstream to the network. */ +/* Writes a complete update of this CFNE's known spanning tree upstream to the network. */ -bool PeerNetwork::writeMasterTree(MasterTree* treeRoot) +bool PeerNetwork::writeSpanningTree(SpanningTree* treeRoot) { if (treeRoot == nullptr) return false; @@ -156,7 +156,7 @@ bool PeerNetwork::writeMasterTree(MasterTree* treeRoot) if (treeRoot->m_children.size() > 0) { json::array jsonArray; - MasterTree::serializeTree(treeRoot, jsonArray); + SpanningTree::serializeTree(treeRoot, jsonArray); json::value v = json::value(jsonArray); std::string json = std::string(v.serialize()); diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index d00cb7a8..d7be69ab 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -21,7 +21,7 @@ #include "common/network/Network.h" #include "common/network/PacketBuffer.h" #include "common/ThreadPool.h" -#include "fne/network/MasterTree.h" +#include "fne/network/SpanningTree.h" #include "fne/network/HAParameters.h" #include @@ -149,11 +149,11 @@ namespace network */ bool writePeerLinkPeers(json::array* peerList); /** - * @brief Writes a complete update of this CFNE's known master FNE tree upstream to the network. + * @brief Writes a complete update of this CFNE's known spanning tree upstream to the network. * @param treeRoot Root of the master tree. * @returns bool True, if list was sent, otherwise false. */ - bool writeMasterTree(MasterTree* treeRoot); + bool writeSpanningTree(SpanningTree* treeRoot); /** * @brief Writes a complete update of this CFNE's HA parameters to the network. * @param haParams List of HA parameters. diff --git a/src/fne/network/RESTAPI.cpp b/src/fne/network/RESTAPI.cpp index ab349c5e..003b9cbb 100644 --- a/src/fne/network/RESTAPI.cpp +++ b/src/fne/network/RESTAPI.cpp @@ -17,7 +17,7 @@ #include "fne/network/callhandler/TagDMRData.h" #include "fne/network/callhandler/TagP25Data.h" #include "fne/network/RESTAPI.h" -#include "fne/network/MasterTree.h" +#include "fne/network/SpanningTree.h" #include "HostFNE.h" using namespace network; @@ -668,7 +668,7 @@ void RESTAPI::initializeEndpoints() m_dispatcher.match(FNE_GET_AFF_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetAffList, this)); - m_dispatcher.match(FNE_GET_MASTER_TREE).get(REST_API_BIND(RESTAPI::restAPI_GetMasterTree, this)); + m_dispatcher.match(FNE_GET_SPANNING_TREE).get(REST_API_BIND(RESTAPI::restAPI_GetSpanningTree, this)); /* ** Digital Mobile Radio @@ -1619,9 +1619,9 @@ void RESTAPI::restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, reply.payload(response); } -/* REST API endpoint; implements get master tree list request. */ +/* REST API endpoint; implements get spanning tree list request. */ -void RESTAPI::restAPI_GetMasterTree(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +void RESTAPI::restAPI_GetSpanningTree(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1632,7 +1632,7 @@ void RESTAPI::restAPI_GetMasterTree(const HTTPPayload& request, HTTPPayload& rep json::array tree = json::array(); if (m_network != nullptr) { - MasterTree::serializeTree(m_network->m_fneTree, tree); + SpanningTree::serializeTree(m_network->m_treeRoot, tree); } response["masterTree"].set(tree); diff --git a/src/fne/network/RESTAPI.h b/src/fne/network/RESTAPI.h index 8084855c..c64f4685 100644 --- a/src/fne/network/RESTAPI.h +++ b/src/fne/network/RESTAPI.h @@ -342,12 +342,12 @@ class HOST_SW_API RESTAPI : private Thread { void restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); /** - * @brief REST API endpoint; implements get master tree list request. + * @brief REST API endpoint; implements get spanning tree list request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetMasterTree(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetSpanningTree(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); /* ** Digital Mobile Radio diff --git a/src/fne/network/RESTDefines.h b/src/fne/network/RESTDefines.h index a95590f3..41c1f36f 100644 --- a/src/fne/network/RESTDefines.h +++ b/src/fne/network/RESTDefines.h @@ -57,6 +57,6 @@ #define FNE_GET_AFF_LIST "/report-affiliations" -#define FNE_GET_MASTER_TREE "/report-master-tree" +#define FNE_GET_SPANNING_TREE "/spanning-tree" #endif // __FNE_REST_DEFINES_H__ diff --git a/src/fne/network/MasterTree.cpp b/src/fne/network/SpanningTree.cpp similarity index 80% rename from src/fne/network/MasterTree.cpp rename to src/fne/network/SpanningTree.cpp index c3fbc124..315880e5 100644 --- a/src/fne/network/MasterTree.cpp +++ b/src/fne/network/SpanningTree.cpp @@ -9,7 +9,7 @@ */ #include "fne/Defines.h" #include "common/Log.h" -#include "network/MasterTree.h" +#include "network/SpanningTree.h" using namespace network; @@ -17,30 +17,30 @@ using namespace network; // Static Class Members // --------------------------------------------------------------------------- -std::unordered_map MasterTree::m_masterTrees = std::unordered_map(); -uint8_t MasterTree::m_maxUpdatesBeforeReparent = 5U; +std::unordered_map SpanningTree::m_SpanningTrees = std::unordered_map(); +uint8_t SpanningTree::m_maxUpdatesBeforeReparent = 5U; // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- -/* Initializes a new instance of the MasterTree class. */ +/* Initializes a new instance of the SpanningTree class. */ -MasterTree::MasterTree(uint32_t id, uint32_t masterId, MasterTree* parent) : +SpanningTree::SpanningTree(uint32_t id, uint32_t masterId, SpanningTree* parent) : m_parent(parent), m_children(), m_id(id), m_masterId(masterId), m_updatesBeforeReparent(0U) { - m_masterTrees[id] = this; + m_SpanningTrees[id] = this; if (m_parent != nullptr) m_parent->m_children.push_back(this); } -/* Finalizes a instance of the MasterTree class. */ +/* Finalizes a instance of the SpanningTree class. */ -MasterTree::~MasterTree() +SpanningTree::~SpanningTree() { for (auto child : m_children) { if (child != nullptr) { @@ -51,24 +51,24 @@ MasterTree::~MasterTree() m_children.clear(); } -/* Find a peer master tree by peer ID. */ +/* Find a peer tree by peer ID. */ -MasterTree* MasterTree::findByPeerID(const uint32_t peerId) +SpanningTree* SpanningTree::findByPeerID(const uint32_t peerId) { - auto it = m_masterTrees.find(peerId); - if (it != m_masterTrees.end()) { + auto it = m_SpanningTrees.find(peerId); + if (it != m_SpanningTrees.end()) { return it->second; } return nullptr; } -/* Find a peer master tree by master peer ID. */ +/* Find a peer tree by master peer ID. */ -MasterTree* MasterTree::findByMasterID(const uint32_t masterId) +SpanningTree* SpanningTree::findByMasterID(const uint32_t masterId) { - for (auto it : m_masterTrees) { - MasterTree* tree = it.second; + for (auto it : m_SpanningTrees) { + SpanningTree* tree = it.second; if (tree != nullptr) { if (tree->masterId() == masterId) { return tree; @@ -79,9 +79,9 @@ MasterTree* MasterTree::findByMasterID(const uint32_t masterId) return nullptr; } -/* Count all children of a master tree node. */ +/* Count all children of a tree node. */ -uint32_t MasterTree::countChildren(MasterTree* node) +uint32_t SpanningTree::countChildren(SpanningTree* node) { if (node == nullptr) return 0U; @@ -99,11 +99,11 @@ uint32_t MasterTree::countChildren(MasterTree* node) /* Erase a peer from the tree. */ -void MasterTree::erasePeer(const uint32_t peerId) +void SpanningTree::erasePeer(const uint32_t peerId) { - auto it = m_masterTrees.find(peerId); - if (it != m_masterTrees.end()) { - MasterTree* tree = it->second; + auto it = m_SpanningTrees.find(peerId); + if (it != m_SpanningTrees.end()) { + SpanningTree* tree = it->second; if (tree != nullptr) { if (tree->m_parent != nullptr) { auto& siblings = tree->m_parent->m_children; @@ -117,13 +117,13 @@ void MasterTree::erasePeer(const uint32_t peerId) delete tree; } - m_masterTrees.erase(it); + m_SpanningTrees.erase(it); } } -/* Helper to recursively serialize master tree node to JSON array. */ +/* Helper to recursively serialize tree node to JSON array. */ -void MasterTree::serializeTree(MasterTree* node, json::array& jsonArray) +void SpanningTree::serializeTree(SpanningTree* node, json::array& jsonArray) { if (node == nullptr) return; @@ -143,9 +143,9 @@ void MasterTree::serializeTree(MasterTree* node, json::array& jsonArray) jsonArray.push_back(json::value(obj)); } -/* Helper to recursively deserialize master tree node from JSON array. */ +/* Helper to recursively deserialize tree node from JSON array. */ -void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std::vector* duplicatePeers) +void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers) { for (auto& v : jsonArray) { if (!v.is()) @@ -163,7 +163,7 @@ void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std uint32_t masterId = obj["masterId"].get(); // check if this peer is already connected via another peer - MasterTree* tree = MasterTree::findByMasterID(masterId); + SpanningTree* tree = SpanningTree::findByMasterID(masterId); if (tree != nullptr) { // is this a fast reconnect? (this happens when a connecting peer // uses the same peer ID and master ID already announced in the tree, but @@ -175,9 +175,9 @@ void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std } } - MasterTree* existingNode = findByPeerID(id); + SpanningTree* existingNode = findByPeerID(id); if (existingNode == nullptr) - existingNode = new MasterTree(id, masterId, parent); + existingNode = new SpanningTree(id, masterId, parent); else { // are the parents different? if so, start counting down to reparenting if (existingNode->m_parent != parent) { @@ -194,7 +194,7 @@ void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std // process children json::array childArray = obj["children"].get(); - //LogDebugEx(LOG_STP, "MasterTree::deserializeTree()", "peerId = %u, masterId = %u, parent = %u, childrenCnt = %u", + //LogDebugEx(LOG_STP, "SpanningTree::deserializeTree()", "peerId = %u, masterId = %u, parent = %u, childrenCnt = %u", // existingNode->id(), existingNode->masterId(), parent->id(), childArray.size()); if (childArray.size() > 0U) { deserializeTree(childArray, existingNode, duplicatePeers); @@ -221,7 +221,7 @@ void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std } // iterate over the nodes children and remove entries - std::vector childrenToErase; + std::vector childrenToErase; for (auto child : existingNode->m_children) { if (child != nullptr) { auto it = std::find(announcedChildren.begin(), announcedChildren.end(), child->id()); @@ -235,7 +235,7 @@ void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std for (auto child : childrenToErase) { if (child != nullptr) { auto it = std::find_if(existingNode->m_children.begin(), existingNode->m_children.end(), - [&](MasterTree* x) { + [&](SpanningTree* x) { if (x != nullptr) { return x->id() == child->id(); } @@ -258,9 +258,9 @@ void MasterTree::deserializeTree(json::array& jsonArray, MasterTree* parent, std } } -/* Helper to move the master tree node to a different parent master tree node. */ +/* Helper to move the tree node to a different parent tree node. */ -void MasterTree::moveParent(MasterTree* node, MasterTree* parent) +void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) { if (node == nullptr) return; @@ -275,13 +275,13 @@ void MasterTree::moveParent(MasterTree* node, MasterTree* parent) if (node->m_parent != parent) { // find the node in the parent children and remove it - MasterTree* nodeParent = node->m_parent; + SpanningTree* nodeParent = node->m_parent; uint32_t nodeParentId = 0U; bool hasReleasedFromParent = false; if (nodeParent != nullptr) { nodeParentId = nodeParent->id(); - auto it = std::find_if(nodeParent->m_children.begin(), nodeParent->m_children.end(), [&](MasterTree* childNode) { + auto it = std::find_if(nodeParent->m_children.begin(), nodeParent->m_children.end(), [&](SpanningTree* childNode) { if (childNode == node) return true; return false; @@ -313,7 +313,7 @@ void MasterTree::moveParent(MasterTree* node, MasterTree* parent) /* Debug helper to visualize the tree structure in the log. */ -void MasterTree::visualizeTreeToLog(MasterTree* node, uint32_t level) +void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) { if (node == nullptr) return; @@ -341,9 +341,9 @@ void MasterTree::visualizeTreeToLog(MasterTree* node, uint32_t level) // Private Static Class Members // --------------------------------------------------------------------------- -/* Erase all children of a master tree node. */ +/* Erase all children of a spanning tree node. */ -void MasterTree::eraseChildren(MasterTree* node) +void SpanningTree::eraseChildren(SpanningTree* node) { if (node == nullptr) return; diff --git a/src/fne/network/MasterTree.h b/src/fne/network/SpanningTree.h similarity index 52% rename from src/fne/network/MasterTree.h rename to src/fne/network/SpanningTree.h index 6ba01f22..884ca122 100644 --- a/src/fne/network/MasterTree.h +++ b/src/fne/network/SpanningTree.h @@ -8,13 +8,13 @@ * */ /** - * @file MasterTree.h + * @file SpanningTree.h * @ingroup fne_network - * @file MasterTree.cpp + * @file SpanningTree.cpp * @ingroup fne_network */ -#if !defined(__MASTER_TREE_H__) -#define __MASTER_TREE_H__ +#if !defined(__SPANNING_TREE_H__) +#define __SPANNING_TREE_H__ #include "fne/Defines.h" #include "common/network/json/json.h" @@ -34,9 +34,9 @@ namespace network * @ingroup fne_network * @remarks * This class implements a extremely rudimentary spanning tree structure to represent - * the master FNE tree topology. + * the linked FNE tree topology. * - * Each MasterTree node represents a single FNE in the tree. The root node is the master FNE + * Each node represents a master FNE in the tree. The root node is the master FNE * at the top of the tree. Each node contains a list of child nodes that are directly connected * to it downstream. * @@ -56,29 +56,28 @@ namespace network * Child nodes always send their data upstream to their parent node. The tree is always a top-down * structure, with data flowing from the leaves up to the root. The root node does not have a parent. * - * Root nodes can have multiple child nodes, and child nodes can have their own children, forming a hierarchical tree. - * Root nodes determine duplicate connections and enforce tree integrity. - * - * Each node in the tree assumes it is a root of its own subtree. For instance, B considers itself - * the root of the subtree containing B, D, E, and G. This allows for easy traversal and management of - * the tree structure. + * - Nodes can have multiple child nodes, and child nodes can have their own children, forming a hierarchical tree. + * - Nodes with child nodes can determine duplicate connections and enforce tree integrity. * + * - Each node in the tree assumes it is the root of its own subtree. For instance, B considers itself + * the root of the subtree containing B, D, E, and G. This allows for easy traversal and management of + * the tree structure. */ - class HOST_SW_API MasterTree { + class HOST_SW_API SpanningTree { public: - auto operator=(MasterTree&) -> MasterTree& = delete; - auto operator=(MasterTree&&) -> MasterTree& = delete; - MasterTree(MasterTree&) = delete; + auto operator=(SpanningTree&) -> SpanningTree& = delete; + auto operator=(SpanningTree&&) -> SpanningTree& = delete; + SpanningTree(SpanningTree&) = delete; /** - * @brief Initializes a new instance of the MasterTree class + * @brief Initializes a new instance of the SpanningTree class * @param id Peer ID. * @param parent Parent server tree node. */ - MasterTree(uint32_t id, uint32_t masterId, MasterTree* parent); + SpanningTree(uint32_t id, uint32_t masterId, SpanningTree* parent); /** - * @brief Finalizes a instance of the MasterTree class + * @brief Finalizes a instance of the SpanningTree class */ - ~MasterTree(); + ~SpanningTree(); /** * @brief Flag indicating whether or not this server is a tree root. @@ -92,24 +91,24 @@ namespace network bool hasChildren() const { return !m_children.empty(); } /** - * @brief Find a peer master tree by peer ID. + * @brief Find a peer tree by peer ID. * @param peerId Peer ID. - * @return MasterTree* Pointer to the MasterTree instance, or nullptr if not found. + * @return SpanningTree* Pointer to the SpanningTree instance, or nullptr if not found. */ - static MasterTree* findByPeerID(const uint32_t peerId); + static SpanningTree* findByPeerID(const uint32_t peerId); /** - * @brief Find a peer master tree by master peer ID. + * @brief Find a peer tree by master peer ID. * @param masterId Master Peer ID. - * @return MasterTree* Pointer to the MasterTree instance, or nullptr if not found. + * @return SpanningTree* Pointer to the SpanningTree instance, or nullptr if not found. */ - static MasterTree* findByMasterID(const uint32_t masterId); + static SpanningTree* findByMasterID(const uint32_t masterId); /** - * @brief Count all children of a master tree node. - * @param node Pointer to MasterTree node. + * @brief Count all children of a tree node. + * @param node Pointer to SpanningTree node. * @return uint32_t Number of child nodes. */ - static uint32_t countChildren(MasterTree* node); + static uint32_t countChildren(SpanningTree* node); /** * @brief Erase a peer from the tree. @@ -118,39 +117,39 @@ namespace network static void erasePeer(const uint32_t peerId); /** - * @brief Helper to recursively serialize master tree node to JSON array. - * @param node Pointer to MasterTree node. + * @brief Helper to recursively serialize tree node to JSON array. + * @param node Pointer to SpanningTree node. * @param jsonArray JSON array to write node to. */ - static void serializeTree(MasterTree* node, json::array& jsonArray); + static void serializeTree(SpanningTree* node, json::array& jsonArray); /** - * @brief Helper to recursively deserialize master tree node from JSON array. + * @brief Helper to recursively deserialize tree node from JSON array. * @param jsonArray JSON array to read node from. - * @param parent Pointer to parent MasterTree node. + * @param parent Pointer to parent SpanningTree node. * @param duplicatePeers Pointer to vector to receive duplicate peer IDs found during deserialization. */ - static void deserializeTree(json::array& jsonArray, MasterTree* parent, std::vector* duplicatePeers); + static void deserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers); /** - * @brief Helper to move the master tree node to a different parent master tree node. - * @param node Pointer to a MasterTree node. - * @param parent Pointer to new parent MasterTree Node. + * @brief Helper to move the tree node to a different parent tree node. + * @param node Pointer to a SpanningTree node. + * @param parent Pointer to new parent SpanningTree Node. */ - static void moveParent(MasterTree* node, MasterTree* parent); + static void moveParent(SpanningTree* node, SpanningTree* parent); /** * @brief Helper to visualize the tree structure in the log. - * @param node Pointer to MasterTree node. + * @param node Pointer to SpanningTree node. * @param level Current tree level. */ - static void visualizeTreeToLog(MasterTree* node, uint32_t level = 0U); + static void visualizeTreeToLog(SpanningTree* node, uint32_t level = 0U); public: - MasterTree* m_parent; //!< Parent master tree node. (i.e. master FNE above this) - std::vector m_children; //!< Child master tree nodes. (i.e. peer FNEs below this) + SpanningTree* m_parent; //!< Parent tree node. (i.e. master FNE above this) + std::vector m_children; //!< Child tree nodes. (i.e. peer FNEs below this) - static std::unordered_map m_masterTrees; //!< Static map of all master trees by peer ID. + static std::unordered_map m_SpanningTrees; //!< Static map of all trees by peer ID. static uint8_t m_maxUpdatesBeforeReparent; //!< Maximum count of updates before allowing node reparenting. /** @@ -166,11 +165,11 @@ namespace network uint8_t m_updatesBeforeReparent; /** - * @brief Helper to erase all children of a master tree node. - * @param node Pointer to MasterTree node. + * @brief Helper to erase all children of a spanning tree node. + * @param node Pointer to SpanningTree node. */ - static void eraseChildren(MasterTree* node); + static void eraseChildren(SpanningTree* node); }; } // namespace network -#endif // __MASTER_TREE_H__ +#endif // __SPANNING_TREE_H__ diff --git a/src/remote/RESTClientMain.cpp b/src/remote/RESTClientMain.cpp index 312720cc..c22c6c38 100644 --- a/src/remote/RESTClientMain.cpp +++ b/src/remote/RESTClientMain.cpp @@ -54,7 +54,7 @@ #define RCD_FNE_SAVE_TGID_ACL "fne-tgid-commit" #define RCD_FNE_SAVE_PEER_ACL "fne-peer-commit" -#define RCD_FNE_GET_MASTERTREE "fne-master-tree" +#define RCD_FNE_GET_SPANNINGTREE "fne-spanning-tree" #define RCD_MODE "mdm-mode" #define RCD_MODE_OPT_IDLE "idle" @@ -224,7 +224,7 @@ void usage(const char* message, const char* arg) reply += " fne-tgid-commit Saves the current TGID ACL to permenant storage (Converged FNE only)\r\n"; reply += " fne-peer-commit Saves the current peer ACL to permenant storage (Converged FNE only)\r\n"; reply += "\r\n"; - reply += " fne-master-tree Retrieves the current master FNE tree (Converged FNE only)\r\n"; + reply += " fne-spanning-tree Retrieves the current FNE spanning tree (Converged FNE only)\r\n"; reply += "\r\n"; reply += " mdm-mode Set current mode of host (idle, lockout, dmr, p25, nxdn)\r\n"; reply += " mdm-kill Causes the host to quit\r\n"; @@ -929,8 +929,8 @@ int main(int argc, char** argv) else if (rcom == RCD_FNE_SAVE_PEER_ACL) { retCode = client->send(HTTP_GET, FNE_GET_PEER_COMMIT, json::object(), response); } - else if (rcom == RCD_FNE_GET_MASTERTREE) { - retCode = client->send(HTTP_GET, FNE_GET_MASTER_TREE, json::object(), response); + else if (rcom == RCD_FNE_GET_SPANNINGTREE) { + retCode = client->send(HTTP_GET, FNE_GET_SPANNING_TREE, json::object(), response); } else { args.clear(); From aefba6b11e7c7fd09d6db23385f88ddb4475b28c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 24 Oct 2025 12:38:08 -0400 Subject: [PATCH 099/200] whoops errant i++; --- src/common/network/Network.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index a094fbe2..7f4b41a7 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -846,7 +846,6 @@ void Network::clock(uint32_t ms) LogDebugEx(LOG_NET, "Network::clock()", "HA PARAMS, %s:%u", address.c_str(), port); m_haIPs.push_back(PeerHAIPEntry(address, port)); - i++; } if (m_haIPs.size() > 1U) { From 9a8957bb05612cdb92a5f105d65a6549785ac0c1 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 11:24:42 -0400 Subject: [PATCH 100/200] fix up some concurrency problems when dealing with parrot transmissions, due to migration of parrot playback into its own thread; --- src/common/network/Network.cpp | 4 +-- src/fne/network/FNENetwork.cpp | 34 ++++++++++++++++++ src/fne/network/callhandler/TagAnalogData.cpp | 19 ++++++++-- src/fne/network/callhandler/TagAnalogData.h | 36 +++++++++++++++++++ src/fne/network/callhandler/TagDMRData.cpp | 18 ++++++++-- src/fne/network/callhandler/TagDMRData.h | 36 +++++++++++++++++++ src/fne/network/callhandler/TagNXDNData.cpp | 18 ++++++++-- src/fne/network/callhandler/TagNXDNData.h | 36 +++++++++++++++++++ src/fne/network/callhandler/TagP25Data.cpp | 21 +++++++++-- src/fne/network/callhandler/TagP25Data.h | 36 +++++++++++++++++++ 10 files changed, 247 insertions(+), 11 deletions(-) diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index 7f4b41a7..eff17f96 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -67,6 +67,7 @@ Network::Network(const std::string& address, uint16_t port, uint16_t localPort, m_maxRetryCount(MAX_RETRY_BEFORE_RECONNECT), m_flaggedDuplicateConn(false), m_timeoutTimer(1000U, MAX_PEER_PING_TIME), + m_pingsReceived(0U), m_pktSeq(0U), m_loginStreamId(0U), m_metadata(nullptr), @@ -1021,8 +1022,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(); diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 70659456..041d7e86 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -752,6 +752,40 @@ void* FNENetwork::threadParrotHandler(void* arg) fne->m_parrotDelayTimer.stop(); } + if (!fne->m_parrotDelayTimer.isRunning()) { + // if the DMR handle is marked as playing back parrot frames, but has no more frames in the queue + // clear the playback flag + if (fne->m_tagDMR->isParrotPlayback() && !fne->m_tagDMR->hasParrotFrames()) { + LogMessage(LOG_MASTER, "DMR, Parrot Call End, peer = %u, srcId = %u, dstId = %u", + fne->m_tagDMR->lastParrotPeerId(), fne->m_tagDMR->lastParrotSrcId(), fne->m_tagDMR->lastParrotDstId()); + fne->m_tagDMR->clearParrotPlayback(); + } + + // if the P25 handle is marked as playing back parrot frames, but has no more frames in the queue + // clear the playback flag + if (fne->m_tagP25->isParrotPlayback() && !fne->m_tagDMR->hasParrotFrames()) { + LogMessage(LOG_MASTER, "P25, Parrot Call End, peer = %u, srcId = %u, dstId = %u", + fne->m_tagP25->lastParrotPeerId(), fne->m_tagP25->lastParrotSrcId(), fne->m_tagP25->lastParrotDstId()); + fne->m_tagP25->clearParrotPlayback(); + } + + // if the NXDN handle is marked as playing back parrot frames, but has no more frames in the queue + // clear the playback flag + if (fne->m_tagNXDN->isParrotPlayback() && !fne->m_tagDMR->hasParrotFrames()) { + LogMessage(LOG_MASTER, "NXDN, Parrot Call End, peer = %u, srcId = %u, dstId = %u", + fne->m_tagNXDN->lastParrotPeerId(), fne->m_tagNXDN->lastParrotSrcId(), fne->m_tagNXDN->lastParrotDstId()); + fne->m_tagNXDN->clearParrotPlayback(); + } + + // if the analog handle is marked as playing back parrot frames, but has no more frames in the queue + // clear the playback flag + if (fne->m_tagAnalog->isParrotPlayback() && !fne->m_tagDMR->hasParrotFrames()) { + LogMessage(LOG_MASTER, "Analog, Parrot Call End, peer = %u, srcId = %u, dstId = %u", + fne->m_tagAnalog->lastParrotPeerId(), fne->m_tagAnalog->lastParrotSrcId(), fne->m_tagAnalog->lastParrotDstId()); + fne->m_tagAnalog->clearParrotPlayback(); + } + } + Thread::sleep(1U); } } diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index a86d796c..671a76df 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -37,6 +37,10 @@ TagAnalogData::TagAnalogData(FNENetwork* network, bool debug) : m_network(network), m_parrotFrames(), m_parrotFramesReady(false), + m_parrotPlayback(false), + m_lastParrotPeerId(0U), + m_lastParrotSrcId(0U), + m_lastParrotDstId(0U), m_status(), m_debug(debug) { @@ -109,7 +113,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // is this a parrot talkgroup? if so, clear any remaining frames from the buffer lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId); - if (tg.config().parrot()) { + if (tg.config().parrot() && !m_parrotPlayback) { if (m_parrotFrames.size() > 0) { m_parrotFramesReady = true; LogMessage(LOG_NET, "Analog, Parrot Playback will Start, peer = %u, ssrc = %u, srcId = %u", peerId, ssrc, srcId); @@ -183,14 +187,16 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee else { // is this a parrot talkgroup? if so, clear any remaining frames from the buffer lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId); - if (tg.config().parrot()) { + if (tg.config().parrot() && !m_parrotPlayback) { m_parrotFramesReady = false; if (m_parrotFrames.size() > 0) { + m_parrotFrames.lock(false); for (auto& pkt : m_parrotFrames) { if (pkt.buffer != nullptr) { delete[] pkt.buffer; } } + m_parrotFrames.unlock(); m_parrotFrames.clear(); } } @@ -341,11 +347,19 @@ void TagAnalogData::playbackParrot() { if (m_parrotFrames.size() == 0) { m_parrotFramesReady = false; + m_parrotPlayback = false; return; } + m_parrotPlayback = true; + auto& pkt = m_parrotFrames[0]; + m_parrotFrames.lock(); if (pkt.buffer != nullptr) { + m_lastParrotPeerId = pkt.peerId; + m_lastParrotSrcId = pkt.srcId; + m_lastParrotDstId = pkt.dstId; + if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { @@ -367,6 +381,7 @@ void TagAnalogData::playbackParrot() delete[] pkt.buffer; } Thread::sleep(60); + m_parrotFrames.unlock(); m_parrotFrames.pop_front(); } diff --git a/src/fne/network/callhandler/TagAnalogData.h b/src/fne/network/callhandler/TagAnalogData.h index 6ddd3552..43dd059d 100644 --- a/src/fne/network/callhandler/TagAnalogData.h +++ b/src/fne/network/callhandler/TagAnalogData.h @@ -74,6 +74,38 @@ namespace network */ bool hasParrotFrames() const { return m_parrotFramesReady && !m_parrotFrames.empty(); } + /** + * @brief Helper to determine if the parrot is playing back frames. + * @returns True, if parrot playback was started, otherwise false. + */ + bool isParrotPlayback() const { return m_parrotPlayback; } + /** + * @brief Helper to clear the parrot playback flag. + */ + void clearParrotPlayback() + { + m_parrotPlayback = false; + m_lastParrotPeerId = 0U; + m_lastParrotSrcId = 0U; + m_lastParrotDstId = 0U; + } + + /** + * @brief Returns the last processed peer ID for a parrot frame. + * @return uint32_t Peer ID. + */ + uint32_t lastParrotPeerId() const { return m_lastParrotPeerId; } + /** + * @brief Returns the last processed source ID for a parrot frame. + * @return uint32_t Source ID. + */ + uint32_t lastParrotSrcId() const { return m_lastParrotSrcId; } + /** + * @brief Returns the last processed destination ID for a parrot frame. + * @return uint32_t Destination ID. + */ + uint32_t lastParrotDstId() const { return m_lastParrotDstId; } + private: FNENetwork* m_network; @@ -109,6 +141,10 @@ namespace network }; concurrent::deque m_parrotFrames; bool m_parrotFramesReady; + bool m_parrotPlayback; + uint32_t m_lastParrotPeerId; + uint32_t m_lastParrotSrcId; + uint32_t m_lastParrotDstId; /** * @brief Represents the receive status of a call. diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 18c1d289..69f7997d 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -41,6 +41,10 @@ TagDMRData::TagDMRData(FNENetwork* network, bool debug) : m_network(network), m_parrotFrames(), m_parrotFramesReady(false), + m_parrotPlayback(false), + m_lastParrotPeerId(0U), + m_lastParrotSrcId(0U), + m_lastParrotDstId(0U), m_status(), m_statusPVCall(), m_debug(debug) @@ -171,7 +175,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // is this a parrot talkgroup? if so, clear any remaining frames from the buffer lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId); - if (tg.config().parrot()) { + if (tg.config().parrot() && !m_parrotPlayback) { if (m_parrotFrames.size() > 0) { m_parrotFramesReady = true; LogMessage(LOG_NET, "DMR, Parrot Playback will Start, peer = %u, ssrc = %u, srcId = %u", peerId, ssrc, srcId); @@ -277,14 +281,16 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId else { // is this a parrot talkgroup? if so, clear any remaining frames from the buffer lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId); - if (tg.config().parrot()) { + if (tg.config().parrot() && !m_parrotPlayback) { m_parrotFramesReady = false; if (m_parrotFrames.size() > 0) { + m_parrotFrames.lock(false); for (auto& pkt : m_parrotFrames) { if (pkt.buffer != nullptr) { delete[] pkt.buffer; } } + m_parrotFrames.unlock(); m_parrotFrames.clear(); } } @@ -585,8 +591,15 @@ void TagDMRData::playbackParrot() return; } + m_parrotPlayback = true; + auto& pkt = m_parrotFrames[0]; + m_parrotFrames.lock(); if (pkt.buffer != nullptr) { + m_lastParrotPeerId = pkt.peerId; + m_lastParrotSrcId = pkt.srcId; + m_lastParrotDstId = pkt.dstId; + if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { @@ -608,6 +621,7 @@ void TagDMRData::playbackParrot() delete[] pkt.buffer; } Thread::sleep(60); + m_parrotFrames.unlock(); m_parrotFrames.pop_front(); } diff --git a/src/fne/network/callhandler/TagDMRData.h b/src/fne/network/callhandler/TagDMRData.h index 1290efdf..ae2ff484 100644 --- a/src/fne/network/callhandler/TagDMRData.h +++ b/src/fne/network/callhandler/TagDMRData.h @@ -86,6 +86,38 @@ namespace network */ bool hasParrotFrames() const { return m_parrotFramesReady && !m_parrotFrames.empty(); } + /** + * @brief Helper to determine if the parrot is playing back frames. + * @returns True, if parrot playback was started, otherwise false. + */ + bool isParrotPlayback() const { return m_parrotPlayback; } + /** + * @brief Helper to clear the parrot playback flag. + */ + void clearParrotPlayback() + { + m_parrotPlayback = false; + m_lastParrotPeerId = 0U; + m_lastParrotSrcId = 0U; + m_lastParrotDstId = 0U; + } + + /** + * @brief Returns the last processed peer ID for a parrot frame. + * @return uint32_t Peer ID. + */ + uint32_t lastParrotPeerId() const { return m_lastParrotPeerId; } + /** + * @brief Returns the last processed source ID for a parrot frame. + * @return uint32_t Source ID. + */ + uint32_t lastParrotSrcId() const { return m_lastParrotSrcId; } + /** + * @brief Returns the last processed destination ID for a parrot frame. + * @return uint32_t Destination ID. + */ + uint32_t lastParrotDstId() const { return m_lastParrotDstId; } + /** * @brief Helper to write a extended function packet on the RF interface. * @param peerId Peer ID. @@ -150,6 +182,10 @@ namespace network }; concurrent::deque m_parrotFrames; bool m_parrotFramesReady; + bool m_parrotPlayback; + uint32_t m_lastParrotPeerId; + uint32_t m_lastParrotSrcId; + uint32_t m_lastParrotDstId; /** * @brief Represents the receive status of a call. diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index ec59e575..a29b41fb 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -44,6 +44,10 @@ TagNXDNData::TagNXDNData(FNENetwork* network, bool debug) : m_network(network), m_parrotFrames(), m_parrotFramesReady(false), + m_parrotPlayback(false), + m_lastParrotPeerId(0U), + m_lastParrotSrcId(0U), + m_lastParrotDstId(0U), m_status(), m_statusPVCall(), m_debug(debug) @@ -210,7 +214,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // is this a parrot talkgroup? if so, clear any remaining frames from the buffer lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId); - if (tg.config().parrot()) { + if (tg.config().parrot() && !m_parrotPlayback) { if (m_parrotFrames.size() > 0) { m_parrotFramesReady = true; LogMessage(LOG_NET, "NXDN, Parrot Playback will Start, peer = %u, srcId = %u", peerId, srcId); @@ -315,14 +319,16 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI else { // is this a parrot talkgroup? if so, clear any remaining frames from the buffer lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId); - if (tg.config().parrot()) { + if (tg.config().parrot() && !m_parrotPlayback) { m_parrotFramesReady = false; if (m_parrotFrames.size() > 0) { + m_parrotFrames.lock(false); for (auto& pkt : m_parrotFrames) { if (pkt.buffer != nullptr) { delete[] pkt.buffer; } } + m_parrotFrames.unlock(); m_parrotFrames.clear(); } } @@ -615,8 +621,15 @@ void TagNXDNData::playbackParrot() return; } + m_parrotPlayback = true; + auto& pkt = m_parrotFrames[0]; + m_parrotFrames.lock(); if (pkt.buffer != nullptr) { + m_lastParrotPeerId = pkt.peerId; + m_lastParrotSrcId = pkt.srcId; + m_lastParrotDstId = pkt.dstId; + if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { @@ -638,6 +651,7 @@ void TagNXDNData::playbackParrot() delete[] pkt.buffer; } Thread::sleep(60); + m_parrotFrames.unlock(); m_parrotFrames.pop_front(); } diff --git a/src/fne/network/callhandler/TagNXDNData.h b/src/fne/network/callhandler/TagNXDNData.h index c5a3dec7..aa70f7b6 100644 --- a/src/fne/network/callhandler/TagNXDNData.h +++ b/src/fne/network/callhandler/TagNXDNData.h @@ -84,6 +84,38 @@ namespace network */ bool hasParrotFrames() const { return m_parrotFramesReady && !m_parrotFrames.empty(); } + /** + * @brief Helper to determine if the parrot is playing back frames. + * @returns True, if parrot playback was started, otherwise false. + */ + bool isParrotPlayback() const { return m_parrotPlayback; } + /** + * @brief Helper to clear the parrot playback flag. + */ + void clearParrotPlayback() + { + m_parrotPlayback = false; + m_lastParrotPeerId = 0U; + m_lastParrotSrcId = 0U; + m_lastParrotDstId = 0U; + } + + /** + * @brief Returns the last processed peer ID for a parrot frame. + * @return uint32_t Peer ID. + */ + uint32_t lastParrotPeerId() const { return m_lastParrotPeerId; } + /** + * @brief Returns the last processed source ID for a parrot frame. + * @return uint32_t Source ID. + */ + uint32_t lastParrotSrcId() const { return m_lastParrotSrcId; } + /** + * @brief Returns the last processed destination ID for a parrot frame. + * @return uint32_t Destination ID. + */ + uint32_t lastParrotDstId() const { return m_lastParrotDstId; } + private: FNENetwork* m_network; @@ -119,6 +151,10 @@ namespace network }; concurrent::deque m_parrotFrames; bool m_parrotFramesReady; + bool m_parrotPlayback; + uint32_t m_lastParrotPeerId; + uint32_t m_lastParrotSrcId; + uint32_t m_lastParrotDstId; /** * @brief Represents the receive status of a call. diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 6bb4bb27..916a95c5 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -47,6 +47,10 @@ TagP25Data::TagP25Data(FNENetwork* network, bool debug) : m_parrotFrames(), m_parrotFramesReady(false), m_parrotFirstFrame(true), + m_parrotPlayback(false), + m_lastParrotPeerId(0U), + m_lastParrotSrcId(0U), + m_lastParrotDstId(0U), m_status(), m_statusPVCall(), m_packetData(nullptr), @@ -216,9 +220,9 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId else { m_status[dstId].reset(); - // is this a parrot talkgroup? if so, clear any remaining frames from the buffer + // is this a parrot talkgroup? if so, reset parrot states lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId); - if (tg.config().parrot()) { + if (tg.config().parrot() && !m_parrotPlayback) { if (m_parrotFrames.size() > 0) { m_parrotFramesReady = true; m_parrotFirstFrame = true; @@ -325,14 +329,16 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId else { // is this a parrot talkgroup? if so, clear any remaining frames from the buffer lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId); - if (tg.config().parrot()) { + if (tg.config().parrot() && !m_parrotPlayback) { m_parrotFramesReady = false; if (m_parrotFrames.size() > 0) { + m_parrotFrames.lock(false); for (auto& pkt : m_parrotFrames) { if (pkt.buffer != nullptr) { delete[] pkt.buffer; } } + m_parrotFrames.unlock(); m_parrotFrames.clear(); } } @@ -650,10 +656,14 @@ void TagP25Data::playbackParrot() if (m_parrotFrames.size() == 0) { m_parrotFramesReady = false; m_parrotFirstFrame = true; + m_parrotPlayback = false; return; } + m_parrotPlayback = true; + auto& pkt = m_parrotFrames[0]; + m_parrotFrames.lock(); if (pkt.buffer != nullptr) { if (m_parrotFirstFrame) { if (m_network->m_parrotGrantDemand) { @@ -692,6 +702,10 @@ void TagP25Data::playbackParrot() m_parrotFirstFrame = false; } + m_lastParrotPeerId = pkt.peerId; + m_lastParrotSrcId = pkt.srcId; + m_lastParrotDstId = pkt.dstId; + if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); if (m_network->m_debug) { @@ -712,6 +726,7 @@ void TagP25Data::playbackParrot() delete[] pkt.buffer; } Thread::sleep(180); + m_parrotFrames.unlock(); m_parrotFrames.pop_front(); } diff --git a/src/fne/network/callhandler/TagP25Data.h b/src/fne/network/callhandler/TagP25Data.h index 63d4f661..e8e0910d 100644 --- a/src/fne/network/callhandler/TagP25Data.h +++ b/src/fne/network/callhandler/TagP25Data.h @@ -90,6 +90,38 @@ namespace network */ bool hasParrotFrames() const { return m_parrotFramesReady && !m_parrotFrames.empty(); } + /** + * @brief Helper to determine if the parrot is playing back frames. + * @returns True, if parrot playback was started, otherwise false. + */ + bool isParrotPlayback() const { return m_parrotPlayback; } + /** + * @brief Helper to clear the parrot playback flag. + */ + void clearParrotPlayback() + { + m_parrotPlayback = false; + m_lastParrotPeerId = 0U; + m_lastParrotSrcId = 0U; + m_lastParrotDstId = 0U; + } + + /** + * @brief Returns the last processed peer ID for a parrot frame. + * @return uint32_t Peer ID. + */ + uint32_t lastParrotPeerId() const { return m_lastParrotPeerId; } + /** + * @brief Returns the last processed source ID for a parrot frame. + * @return uint32_t Source ID. + */ + uint32_t lastParrotSrcId() const { return m_lastParrotSrcId; } + /** + * @brief Returns the last processed destination ID for a parrot frame. + * @return uint32_t Destination ID. + */ + uint32_t lastParrotDstId() const { return m_lastParrotDstId; } + /** * @brief Helper to write a call alert packet. * @param peerId Peer ID. @@ -168,6 +200,10 @@ namespace network concurrent::deque m_parrotFrames; bool m_parrotFramesReady; bool m_parrotFirstFrame; + bool m_parrotPlayback; + uint32_t m_lastParrotPeerId; + uint32_t m_lastParrotSrcId; + uint32_t m_lastParrotDstId; /** * @brief Represents the receive status of a call. From 022e974d1677971959228f6d9632d85c601e7291 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 13:09:08 -0400 Subject: [PATCH 101/200] make sure to share lock peers while processing in maintainence loop; --- src/fne/network/FNENetwork.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 041d7e86..6cd4cf52 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -459,6 +459,7 @@ void FNENetwork::clock(uint32_t ms) if (m_maintainenceTimer.isRunning() && m_maintainenceTimer.hasExpired()) { // check to see if any peers have been quiet (no ping) longer than allowed std::vector peersToRemove = std::vector(); + m_peers.shared_lock(); for (auto peer : m_peers) { uint32_t id = peer.first; FNEPeerConnection* connection = peer.second; @@ -477,18 +478,11 @@ void FNENetwork::clock(uint32_t ms) connection->connected(false); connection->connectionState(NET_STAT_INVALID); - // if the connection was an downstream FNE neighbor peer or a peer replica -- be noisy about a possible - // netsplit - if (connection->isNeighborFNEPeer() || connection->isReplica()) { - for (uint8_t i = 0U; i < 3U; i++) - LogWarning(LOG_MASTER, "PEER %u (%s) downstream netsplit, dt = %u, now = %u", id, connection->identWithQualifier().c_str(), - dt, now); - } - peersToRemove.push_back(id); } } } + m_peers.shared_unlock(); // remove any peers for (uint32_t peerId : peersToRemove) { @@ -518,16 +512,18 @@ void FNENetwork::clock(uint32_t ms) if (m_peers.size() > 0) { json::array peers = json::array(); + m_peers.shared_lock(); for (auto entry : m_peers) { uint32_t peerId = entry.first; - network::FNEPeerConnection* peerConn = entry.second; - if (peerConn != nullptr) { - json::object peerObj = fneConnObject(peerId, peerConn); + network::FNEPeerConnection* connection = entry.second; + if (connection != nullptr) { + json::object peerObj = fneConnObject(peerId, connection); uint32_t peerNetPeerId = peer.second->getPeerId(); peerObj["parentPeerId"].set(peerNetPeerId); peers.push_back(json::value(peerObj)); } } + m_peers.shared_unlock(); peer.second->writePeerLinkPeers(&peers); } From b7fa8614f573769dad1dd126d2e9626d46210cb7 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 13:24:47 -0400 Subject: [PATCH 102/200] add mutex locking around spanning tree updates; --- src/fne/network/DiagNetwork.cpp | 3 +++ src/fne/network/FNENetwork.cpp | 4 ++++ src/fne/network/FNENetwork.h | 1 + 3 files changed, 8 insertions(+) diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index eee94061..5e66cad3 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -649,6 +649,9 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) else { json::array arr = v.get(); LogInfoEx(LOG_STP, "PEER %u (%s) Network Tree, Tree List, updating %u peer entries", peerId, connection->identWithQualifier().c_str(), arr.size()); + + std::lock_guard guard(network->m_treeLock); + std::vector duplicatePeers; SpanningTree::deserializeTree(arr, network->m_treeRoot, &duplicatePeers); diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 6cd4cf52..d5da47ce 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -98,6 +98,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_ccPeerMap(), m_peerReplicaKeyQueue(), m_treeRoot(nullptr), + m_treeLock(), m_peerReplicaHAParams(), m_advertisedHAAddress(), m_advertisedHAPort(TRAFFIC_DEFAULT_PORT), @@ -497,6 +498,7 @@ void FNENetwork::clock(uint32_t ms) // perform master tree maintainence tasks if (peer.second->isEnabled() && peer.second->getRemotePeerId() > 0U && m_enableSpanningTree) { + std::lock_guard guard(m_treeLock); peer.second->writeSpanningTree(m_treeRoot); } @@ -1261,6 +1263,8 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } if (network->m_enableSpanningTree && !connection->isSysView()) { + std::lock_guard guard(network->m_treeLock); + // check if this peer is already connected via another peer SpanningTree* tree = SpanningTree::findByMasterID(masterPeerId); if (tree != nullptr) { diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 8c8d9301..016161e0 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -328,6 +328,7 @@ namespace network std::unordered_map m_peerReplicaKeyQueue; SpanningTree* m_treeRoot; + std::mutex m_treeLock; concurrent::vector m_peerReplicaHAParams; std::string m_advertisedHAAddress; From aab7368f170b205a63d3df401ca904f8fa7779b5 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 15:55:16 -0400 Subject: [PATCH 103/200] simplify log levels, deprecate LogMessage log level in favor of just using LogInfoEx (message and info logs are basically the same thing); --- src/bridge/HostBridge.cpp | 80 ++++---- src/common/Log.cpp | 9 +- src/common/Log.h | 36 ++-- src/common/lookups/AdjSiteMapLookup.cpp | 2 +- src/common/lookups/AffiliationLookup.cpp | 12 +- src/common/lookups/IdenTableLookup.cpp | 2 +- src/common/lookups/PeerListLookup.cpp | 4 +- src/common/lookups/RadioIdLookup.cpp | 2 +- src/common/lookups/TalkgroupRulesLookup.cpp | 2 +- src/common/network/NetRPC.cpp | 4 +- src/common/network/Network.cpp | 32 +-- src/common/network/udp/Socket.cpp | 2 +- src/fne/HostFNE.cpp | 12 +- src/fne/network/DiagNetwork.cpp | 4 +- src/fne/network/FNENetwork.cpp | 30 +-- src/fne/network/P25OTARService.cpp | 8 +- src/fne/network/callhandler/TagAnalogData.cpp | 6 +- src/fne/network/callhandler/TagDMRData.cpp | 24 +-- src/fne/network/callhandler/TagNXDNData.cpp | 16 +- src/fne/network/callhandler/TagP25Data.cpp | 40 ++-- .../callhandler/packetdata/DMRPacketData.cpp | 14 +- .../callhandler/packetdata/P25PacketData.cpp | 68 +++---- src/host/Host.DMR.cpp | 20 +- src/host/Host.NXDN.cpp | 10 +- src/host/Host.P25.cpp | 10 +- src/host/Host.cpp | 44 ++--- src/host/calibrate/HostCal.cpp | 184 +++++++++--------- src/host/dmr/Control.cpp | 8 +- src/host/dmr/Slot.cpp | 32 +-- src/host/dmr/lookups/DMRAffiliationLookup.cpp | 4 +- src/host/dmr/packet/ControlSignaling.cpp | 56 +++--- src/host/dmr/packet/Data.cpp | 54 ++--- src/host/dmr/packet/Voice.cpp | 36 ++-- src/host/modem/Modem.cpp | 20 +- src/host/modem/ModemV24.cpp | 8 +- src/host/modem/port/PseudoPTYPort.cpp | 2 +- .../modem/port/specialized/V24UDPPort.cpp | 12 +- src/host/nxdn/Control.cpp | 28 +-- src/host/nxdn/packet/ControlSignaling.cpp | 20 +- src/host/nxdn/packet/Data.cpp | 12 +- src/host/nxdn/packet/Voice.cpp | 32 +-- src/host/p25/Control.cpp | 40 ++-- src/host/p25/packet/ControlSignaling.cpp | 92 ++++----- src/host/p25/packet/Data.cpp | 124 ++++++------ src/host/p25/packet/Voice.cpp | 50 ++--- src/host/setup/HostSetup.cpp | 86 ++++---- src/host/setup/SetupMainWnd.h | 38 ++-- src/patch/HostPatch.cpp | 46 ++--- src/patch/mmdvm/P25Network.cpp | 6 +- src/peered/PeerEdMain.cpp | 2 +- src/peered/PeerEdMainWnd.h | 4 +- src/peered/PeerEditWnd.h | 6 +- src/peered/PeerListWnd.h | 2 +- src/sysview/HostWS.cpp | 4 +- src/sysview/NodeStatusWnd.h | 4 +- src/sysview/SysViewMain.cpp | 66 +++---- src/sysview/TransmitWndBase.h | 4 +- src/sysview/network/PeerNetwork.cpp | 2 +- src/tged/TGEdMain.cpp | 2 +- src/tged/TGEdMainWnd.h | 4 +- src/tged/TGEditPeerListWnd.h | 8 +- src/tged/TGEditRIDListWnd.h | 6 +- src/tged/TGEditWnd.h | 16 +- src/tged/TGListWnd.h | 12 +- 64 files changed, 807 insertions(+), 818 deletions(-) diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index b20125f5..8e233938 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -133,7 +133,7 @@ void mdcPacketDetected(int frameCount, mdc_u8_t op, mdc_u8_t arg, mdc_u16_t unit return; if (op == OP_PTT_ID && bridge->m_overrideSrcIdFromMDC) { - ::LogMessage(LOG_HOST, "Local Traffic, MDC Detect, unitId = $%04X", unitID); + ::LogInfoEx(LOG_HOST, "Local Traffic, MDC Detect, unitId = $%04X", unitID); // HACK: nasty bullshit to convert MDC unitID to decimal char* pCharRes = new char[16]; // enough space for "0xFFFFFFFF" @@ -150,7 +150,7 @@ void mdcPacketDetected(int frameCount, mdc_u8_t op, mdc_u8_t arg, mdc_u16_t unit delete[] pCharRes; bridge->m_srcIdOverride = res; - ::LogMessage(LOG_HOST, "Local Traffic, MDC Detect, converted srcId = %u", bridge->m_srcIdOverride); + ::LogInfoEx(LOG_HOST, "Local Traffic, MDC Detect, converted srcId = %u", bridge->m_srcIdOverride); } } @@ -1449,7 +1449,7 @@ void HostBridge::processDMRNetwork(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); m_rxStartTime = now; - LogMessage(LOG_HOST, "DMR, call start, srcId = %u, dstId = %u, slot = %u", srcId, dstId, slotNo); + LogInfoEx(LOG_HOST, "DMR, call start, srcId = %u, dstId = %u, slot = %u", srcId, dstId, slotNo); if (m_preambleLeaderTone) generatePreambleTone(); @@ -1490,7 +1490,7 @@ void HostBridge::processDMRNetwork(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); uint64_t diff = now - m_rxStartTime; - LogMessage(LOG_HOST, "DMR, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); + LogInfoEx(LOG_HOST, "DMR, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); } m_rxDMRLC = lc::LC(); @@ -1520,7 +1520,7 @@ void HostBridge::processDMRNetwork(uint8_t* buffer, uint32_t length) if (m_udpUsrp) sendUsrpEot(); - LogMessage(LOG_HOST, "P25, call end (T), srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); + LogInfoEx(LOG_HOST, "P25, call end (T), srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); } m_ignoreCall = true; @@ -1534,7 +1534,7 @@ void HostBridge::processDMRNetwork(uint8_t* buffer, uint32_t length) ambe[13] |= (uint8_t)(data[19] & 0x0F); ::memcpy(ambe + 14U, data.get() + 20U, 13U); - LogMessage(LOG_NET, DMR_DT_VOICE ", audio, slot = %u, srcId = %u, dstId = %u, seqNo = %u", slotNo, srcId, dstId, n); + LogInfoEx(LOG_NET, DMR_DT_VOICE ", audio, slot = %u, srcId = %u, dstId = %u, seqNo = %u", slotNo, srcId, dstId, n); decodeDMRAudioFrame(ambe, srcId, dstId, n); } @@ -1569,7 +1569,7 @@ void HostBridge::decodeDMRAudioFrame(uint8_t* ambe, uint32_t srcId, uint32_t dst #endif // defined(_WIN32) if (m_debug) - LogMessage(LOG_HOST, DMR_DT_VOICE ", Frame, VC%u.%u, srcId = %u, dstId = %u, errs = %u", dmrN, n, srcId, dstId, errs); + LogInfoEx(LOG_HOST, DMR_DT_VOICE ", Frame, VC%u.%u, srcId = %u, dstId = %u, errs = %u", dmrN, n, srcId, dstId, errs); // post-process: apply gain to decoded audio frames AnalogAudio::gain(samples, AUDIO_SAMPLES_LENGTH, m_rxAudioGain); @@ -1742,7 +1742,7 @@ void HostBridge::encodeDMRAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_ dmrData.setData(data); - LogMessage(LOG_HOST, DMR_DT_VOICE_LC_HEADER ", slot = %u, srcId = %u, dstId = %u, FLCO = $%02X", m_slot, + LogInfoEx(LOG_HOST, DMR_DT_VOICE_LC_HEADER ", slot = %u, srcId = %u, dstId = %u, FLCO = $%02X", m_slot, dmrLC.getSrcId(), dmrLC.getDstId(), dmrData.getFLCO()); m_network->writeDMR(dmrData, false); @@ -1775,7 +1775,7 @@ void HostBridge::encodeDMRAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_ emb.encode(data); } - LogMessage(LOG_HOST, DMR_DT_VOICE ", srcId = %u, dstId = %u, slot = %u, seqNo = %u", srcId, dstId, m_slot, m_dmrN); + LogInfoEx(LOG_HOST, DMR_DT_VOICE ", srcId = %u, dstId = %u, slot = %u, seqNo = %u", srcId, dstId, m_slot, m_dmrN); // generate DMR network frame NetData dmrData; @@ -1953,7 +1953,7 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); m_rxStartTime = now; - LogMessage(LOG_HOST, "P25, call start, srcId = %u, dstId = %u, callAlgoId = $%02X, callKID = $%04X", srcId, dstId, m_callAlgoId, callKID); + LogInfoEx(LOG_HOST, "P25, call start, srcId = %u, dstId = %u, callAlgoId = $%02X, callKID = $%04X", srcId, dstId, m_callAlgoId, callKID); if (m_preambleLeaderTone) generatePreambleTone(); } @@ -1972,7 +1972,7 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length) sendUsrpEot(); } - LogMessage(LOG_HOST, "P25, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); + LogInfoEx(LOG_HOST, "P25, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); } m_rxP25LC = lc::LC(); @@ -2015,7 +2015,7 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); uint64_t diff = now - m_rxStartTime; - LogMessage(LOG_HOST, "P25, call end (T), srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); + LogInfoEx(LOG_HOST, "P25, call end (T), srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); } m_ignoreCall = true; @@ -2070,7 +2070,7 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length) dfsiLC.decodeLDU1(data.get() + count, m_netLDU1 + 204U); count += DFSI_LDU1_VOICE9_FRAME_LENGTH_BYTES; - LogMessage(LOG_NET, P25_LDU1_STR " audio, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_NET, P25_LDU1_STR " audio, srcId = %u, dstId = %u", srcId, dstId); // decode 9 IMBE codewords into PCM samples decodeP25AudioFrame(m_netLDU1, srcId, dstId, 1U); @@ -2121,7 +2121,7 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length) dfsiLC.decodeLDU2(data.get() + count, m_netLDU2 + 204U); count += DFSI_LDU2_VOICE18_FRAME_LENGTH_BYTES; - LogMessage(LOG_NET, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", dfsiLC.control()->getAlgId(), dfsiLC.control()->getKId()); + LogInfoEx(LOG_NET, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", dfsiLC.control()->getAlgId(), dfsiLC.control()->getKId()); // decode 9 IMBE codewords into PCM samples decodeP25AudioFrame(m_netLDU2, srcId, dstId, 2U); @@ -2501,14 +2501,14 @@ void HostBridge::encodeP25AudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_ // send P25 LDU1 if (m_p25N == 8U) { - LogMessage(LOG_HOST, P25_LDU1_STR " audio, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_HOST, P25_LDU1_STR " audio, srcId = %u, dstId = %u", srcId, dstId); m_network->writeP25LDU1(lc, lsd, m_netLDU1, FrameType::HDU_VALID, controlByte); m_txStreamId = m_network->getP25StreamId(); } // send P25 LDU2 if (m_p25N == 17U) { - LogMessage(LOG_HOST, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", m_tekAlgoId, m_tekKeyId); + LogInfoEx(LOG_HOST, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", m_tekAlgoId, m_tekKeyId); m_network->writeP25LDU2(lc, lsd, m_netLDU2, controlByte); } @@ -2572,7 +2572,7 @@ void HostBridge::processAnalogNetwork(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); m_rxStartTime = now; - LogMessage(LOG_HOST, "Analog, call start, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_HOST, "Analog, call start, srcId = %u, dstId = %u", srcId, dstId); if (m_preambleLeaderTone) generatePreambleTone(); } @@ -2586,7 +2586,7 @@ void HostBridge::processAnalogNetwork(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); uint64_t diff = now - m_rxStartTime; - LogMessage(LOG_HOST, "Analog, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); + LogInfoEx(LOG_HOST, "Analog, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); } m_rxStartTime = 0U; @@ -2604,7 +2604,7 @@ void HostBridge::processAnalogNetwork(uint8_t* buffer, uint32_t length) return; if (frameType == AudioFrameType::VOICE_START || frameType == AudioFrameType::VOICE) { - LogMessage(LOG_NET, ANO_VOICE ", audio, srcId = %u, dstId = %u, seqNo = %u", srcId, dstId, analogData.getSeqNo()); + LogInfoEx(LOG_NET, ANO_VOICE ", audio, srcId = %u, dstId = %u, seqNo = %u", srcId, dstId, analogData.getSeqNo()); short samples[AUDIO_SAMPLES_LENGTH]; int smpIdx = 0; @@ -2913,7 +2913,7 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) data.setBER(0U); data.setRSSI(0U); - LogMessage(LOG_HOST, DMR_DT_TERMINATOR_WITH_LC ", slot = %u, dstId = %u", m_slot, dstId); + LogInfoEx(LOG_HOST, DMR_DT_TERMINATOR_WITH_LC ", slot = %u, dstId = %u", m_slot, dstId); m_network->writeDMRTerminator(data, &m_dmrSeqNo, &m_dmrN, m_dmrEmbeddedData); m_network->resetDMR(data.getSlotNo()); @@ -2930,7 +2930,7 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) p25::data::LowSpeedData lsd = p25::data::LowSpeedData(); - LogMessage(LOG_HOST, P25_TDU_STR); + LogInfoEx(LOG_HOST, P25_TDU_STR); uint8_t controlByte = 0x00U; m_network->writeP25TDU(lc, lsd, controlByte); @@ -2939,7 +2939,7 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) break; case TX_MODE_ANALOG: { - LogMessage(LOG_HOST, ANO_TERMINATOR); + LogInfoEx(LOG_HOST, ANO_TERMINATOR); uint8_t controlByte = 0x00U; @@ -2960,7 +2960,7 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) } } - LogMessage(LOG_HOST, "%s, call end, srcId = %u, dstId = %u", trafficType.c_str(), srcId, dstId); + LogInfoEx(LOG_HOST, "%s, call end, srcId = %u, dstId = %u", trafficType.c_str(), srcId, dstId); m_srcIdOverride = 0; m_txStreamId = 0; @@ -2991,7 +2991,7 @@ void HostBridge::processTEKResponse(p25::kmm::KeyItem* ki, uint8_t algId, uint8_ return; if (algId == m_tekAlgoId && ki->kId() == m_tekKeyId) { - LogMessage(LOG_HOST, "TEK loaded, algId = $%02X, kId = $%04X, sln = $%04X", algId, ki->kId(), ki->sln()); + LogInfoEx(LOG_HOST, "TEK loaded, algId = $%02X, kId = $%04X, sln = $%04X", algId, ki->kId(), ki->sln()); UInt8Array tek = std::make_unique(keyLength); ki->getKey(tek.get()); @@ -3030,7 +3030,7 @@ void* HostBridge::threadAudioProcess(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -3089,7 +3089,7 @@ void* HostBridge::threadAudioProcess(void* arg) bridge->m_audioDetect = true; if (bridge->m_txStreamId == 0U) { bridge->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", trafficType.c_str(), srcId, dstId); + LogInfoEx(LOG_HOST, "%s, call start, srcId = %u, dstId = %u", trafficType.c_str(), srcId, dstId); if (bridge->m_grantDemand) { switch (bridge->m_txMode) { @@ -3155,7 +3155,7 @@ void* HostBridge::threadAudioProcess(void* arg) Thread::sleep(1U); } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -3186,7 +3186,7 @@ void* HostBridge::threadUDPAudioProcess(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -3247,7 +3247,7 @@ void* HostBridge::threadUDPAudioProcess(void* arg) if (req->srcId != 0U && bridge->m_udpSrcId != 0U) { // if the UDP source ID now doesn't match the current call ID, reset call states if (bridge->m_resetCallForSourceIdChange && (req->srcId != bridge->m_udpSrcId)) { - LogMessage(LOG_HOST, "%s, call switch over, old srcId = %u, new srcId = %u", UDP_CALL, bridge->m_udpSrcId, req->srcId); + LogInfoEx(LOG_HOST, "%s, call switch over, old srcId = %u, new srcId = %u", UDP_CALL, bridge->m_udpSrcId, req->srcId); bridge->callEnd(bridge->m_udpSrcId, bridge->m_dstId); if (bridge->m_udpDropTime.isRunning()) @@ -3286,7 +3286,7 @@ void* HostBridge::threadUDPAudioProcess(void* arg) if (forceCallStart) bridge->m_txStreamId = txStreamId; - LogMessage(LOG_HOST, "%s, call start, srcId = %u, dstId = %u", UDP_CALL, bridge->m_udpSrcId, bridge->m_udpDstId); + LogInfoEx(LOG_HOST, "%s, call start, srcId = %u, dstId = %u", UDP_CALL, bridge->m_udpSrcId, bridge->m_udpDstId); if (bridge->m_grantDemand) { switch (bridge->m_txMode) { case TX_MODE_P25: @@ -3370,7 +3370,7 @@ void* HostBridge::threadUDPAudioProcess(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -3401,7 +3401,7 @@ void* HostBridge::threadNetworkProcess(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -3416,7 +3416,7 @@ void* HostBridge::threadNetworkProcess(void* arg) if (bridge->m_tekAlgoId != P25DEF::ALGO_UNENCRYPT && bridge->m_tekKeyId > 0U) { if (bridge->m_p25Crypto->getTEKLength() == 0U && !bridge->m_requestedTek) { bridge->m_requestedTek = true; - LogMessage(LOG_HOST, "Bridge encryption enabled, requesting TEK from network."); + LogInfoEx(LOG_HOST, "Bridge encryption enabled, requesting TEK from network."); bridge->m_network->writeKeyReq(bridge->m_tekKeyId, bridge->m_tekAlgoId); } } @@ -3451,7 +3451,7 @@ void* HostBridge::threadNetworkProcess(void* arg) Thread::sleep(1U); } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -3542,7 +3542,7 @@ void HostBridge::padSilenceAudio(uint32_t srcId, uint32_t dstId) emb.encode(data); } - LogMessage(LOG_HOST, DMR_DT_VOICE ", audio (silence), srcId = %u, dstId = %u, slot = %u, seqNo = %u", srcId, dstId, m_slot, m_dmrN); + LogInfoEx(LOG_HOST, DMR_DT_VOICE ", audio (silence), srcId = %u, dstId = %u, slot = %u, seqNo = %u", srcId, dstId, m_slot, m_dmrN); // generate DMR network frame NetData dmrData; @@ -3690,7 +3690,7 @@ void HostBridge::padSilenceAudio(uint32_t srcId, uint32_t dstId) // send P25 LDU1 if (m_p25N == 8U) { - LogMessage(LOG_HOST, P25_LDU1_STR " audio (silence padded), srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_HOST, P25_LDU1_STR " audio (silence padded), srcId = %u, dstId = %u", srcId, dstId); m_network->writeP25LDU1(lc, lsd, m_netLDU1, FrameType::DATA_UNIT); m_p25N = 9U; break; @@ -3698,7 +3698,7 @@ void HostBridge::padSilenceAudio(uint32_t srcId, uint32_t dstId) // send P25 LDU2 if (m_p25N == 17U) { - LogMessage(LOG_HOST, P25_LDU2_STR " audio (silence padded), algo = $%02X, kid = $%04X", ALGO_UNENCRYPT, 0U); + LogInfoEx(LOG_HOST, P25_LDU2_STR " audio (silence padded), algo = $%02X, kid = $%04X", ALGO_UNENCRYPT, 0U); m_network->writeP25LDU2(lc, lsd, m_netLDU2); m_p25N = 0U; break; @@ -3732,7 +3732,7 @@ void* HostBridge::threadCallWatchdog(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -3782,7 +3782,7 @@ void* HostBridge::threadCallWatchdog(void* arg) else { // if we've exceeded the drop timeout, then really drop the audio if (bridge->m_localDropTime.isRunning() && (bridge->m_localDropTime.getTimer() >= dropTimeout)) { - LogMessage(LOG_HOST, "%s, terminating stuck call", trafficType.c_str()); + LogInfoEx(LOG_HOST, "%s, terminating stuck call", trafficType.c_str()); bridge->callEnd(srcId, dstId); } } @@ -3790,7 +3790,7 @@ void* HostBridge::threadCallWatchdog(void* arg) Thread::sleep(5U); } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/common/Log.cpp b/src/common/Log.cpp index a80af6a3..f4cf12b5 100644 --- a/src/common/Log.cpp +++ b/src/common/Log.cpp @@ -237,16 +237,13 @@ void log_internal::LogInternal(uint32_t level, const std::string& log) syslogLevel = LOG_DEBUG; break; case 2U: - syslogLevel = LOG_NOTICE; - break; - case 3U: case 9999U: // in-band U: messages should also be info level syslogLevel = LOG_INFO; break; - case 4U: + case 3U: syslogLevel = LOG_WARNING; break; - case 5U: + case 4U: syslogLevel = LOG_ERR; break; default: @@ -265,7 +262,7 @@ void log_internal::LogInternal(uint32_t level, const std::string& log) } // fatal error (specially allow any log levels above 9999) - if (level >= 6U && level < 9999U) { + if (level >= 5U && level < 9999U) { if (m_fpLog != nullptr) ::fclose(m_fpLog); #if !defined(_WIN32) diff --git a/src/common/Log.h b/src/common/Log.h index 52735807..2d356539 100644 --- a/src/common/Log.h +++ b/src/common/Log.h @@ -83,14 +83,6 @@ * This is a variable argument function. */ #define LogDebugEx(_module, _func, fmt, ...) Log(1U, {_module, __FILE__, __LINE__, _func}, fmt, ##__VA_ARGS__) -/** - * @brief Macro helper to create a message log entry. - * @param _module Name of module generating log entry. - * @param fmt String format. - * - * This is a variable argument function. - */ -#define LogMessage(_module, fmt, ...) Log(2U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a informational log entry. * @param _module Name of module generating log entry. @@ -99,7 +91,7 @@ * This is a variable argument function. LogInfo() does not use a module * name when creating a log entry. */ -#define LogInfo(fmt, ...) Log(3U, {nullptr, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) +#define LogInfo(fmt, ...) Log(2U, {nullptr, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a informational log entry with module name. * @param _module Name of module generating log entry. @@ -107,7 +99,7 @@ * * This is a variable argument function. */ -#define LogInfoEx(_module, fmt, ...) Log(3U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) +#define LogInfoEx(_module, fmt, ...) Log(2U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a warning log entry. * @param _module Name of module generating log entry. @@ -115,7 +107,7 @@ * * This is a variable argument function. */ -#define LogWarning(_module, fmt, ...) Log(4U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) +#define LogWarning(_module, fmt, ...) Log(3U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a error log entry. * @param _module Name of module generating log entry. @@ -123,7 +115,7 @@ * * This is a variable argument function. */ -#define LogError(_module, fmt, ...) Log(5U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) +#define LogError(_module, fmt, ...) Log(4U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a fatal log entry. * @param _module Name of module generating log entry. @@ -131,7 +123,7 @@ * * This is a variable argument function. */ -#define LogFatal(_module, fmt, ...) Log(6U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) +#define LogFatal(_module, fmt, ...) Log(5U, {_module, nullptr, 0, nullptr}, fmt, ##__VA_ARGS__) // --------------------------------------------------------------------------- // Externs @@ -156,7 +148,7 @@ extern bool g_disableNetworkLog; namespace log_internal { - constexpr static char LOG_LEVELS[] = " DMIWEF"; + constexpr static char LOG_LEVELS[] = " DIWEF"; // --------------------------------------------------------------------------- // Structure Declaration @@ -375,7 +367,7 @@ namespace log_stacktrace p.snippet = false; p.color_mode = backward::ColorMode::never; - log_internal::LogInternal(3U, "UNRECOVERABLE FATAL ERROR!"); + log_internal::LogInternal(2U, "UNRECOVERABLE FATAL ERROR!"); if (m_foreground > 0) { p.print(st, stderr); } @@ -696,7 +688,7 @@ namespace log_stacktrace p.snippet = false; p.color_mode = backward::ColorMode::never; - log_internal::LogInternal(3U, "UNRECOVERABLE FATAL ERROR!"); + log_internal::LogInternal(2U, "UNRECOVERABLE FATAL ERROR!"); p.print(st, std::cerr); std::string filePath = log_internal::GetLogFilePath(); @@ -837,8 +829,8 @@ HOST_SW_API void Log(uint32_t level, log_internal::SourceLocation sourceLoc, con struct timeval nowMillis; ::gettimeofday(&nowMillis, NULL); - if (level > 7U) - level = 3U; // default this sort of log message to INFO + if (level > 6U) + level = 2U; // default this sort of log message to INFO if (sourceLoc.module != nullptr) { // level 1 is DEBUG @@ -895,8 +887,8 @@ HOST_SW_API void Log(uint32_t level, log_internal::SourceLocation sourceLoc, con } else { if (sourceLoc.module != nullptr) { - if (level > 7U) - level = 3U; // default this sort of log message to INFO + if (level > 6U) + level = 2U; // default this sort of log message to INFO // level 1 is DEBUG if (level == 1U) { @@ -926,8 +918,8 @@ HOST_SW_API void Log(uint32_t level, log_internal::SourceLocation sourceLoc, con prefixLen = ::sprintf(prefixBuf, "U: "); } else { - if (level > 7U) - level = 3U; // default this sort of log message to INFO + if (level > 6U) + level = 2U; // default this sort of log message to INFO // if we have a file and line number -- add that to the log entry if (sourceLoc.filename != nullptr && sourceLoc.line > 0) { diff --git a/src/common/lookups/AdjSiteMapLookup.cpp b/src/common/lookups/AdjSiteMapLookup.cpp index 383f7033..bcd0d784 100644 --- a/src/common/lookups/AdjSiteMapLookup.cpp +++ b/src/common/lookups/AdjSiteMapLookup.cpp @@ -276,7 +276,7 @@ bool AdjSiteMapLookup::save() } try { - LogMessage(LOG_HOST, "Saving adjacent site map file to %s", m_rulesFile.c_str()); + LogInfoEx(LOG_HOST, "Saving adjacent site map file to %s", m_rulesFile.c_str()); yaml::Serialize(newRules, m_rulesFile.c_str()); LogDebug(LOG_HOST, "Saved adj. site map file to %s", m_rulesFile.c_str()); } diff --git a/src/common/lookups/AffiliationLookup.cpp b/src/common/lookups/AffiliationLookup.cpp index 57fa9d93..92a18574 100644 --- a/src/common/lookups/AffiliationLookup.cpp +++ b/src/common/lookups/AffiliationLookup.cpp @@ -75,7 +75,7 @@ void AffiliationLookup::unitReg(uint32_t srcId) m_unitRegTimers[srcId].start(); if (m_verbose) { - LogMessage(LOG_HOST, "%s, unit registration, srcId = %u", + LogInfoEx(LOG_HOST, "%s, unit registration, srcId = %u", m_name.c_str(), srcId); } @@ -97,7 +97,7 @@ bool AffiliationLookup::unitDereg(uint32_t srcId, bool automatic) __lock(); if (m_verbose) { - LogMessage(LOG_HOST, "%s, unit deregistration, srcId = %u", + LogInfoEx(LOG_HOST, "%s, unit deregistration, srcId = %u", m_name.c_str(), srcId); } @@ -213,7 +213,7 @@ void AffiliationLookup::groupAff(uint32_t srcId, uint32_t dstId) m_grpAffTable[srcId] = dstId; if (m_verbose) { - LogMessage(LOG_HOST, "%s, group affiliation, srcId = %u, dstId = %u", + LogInfoEx(LOG_HOST, "%s, group affiliation, srcId = %u, dstId = %u", m_name.c_str(), srcId, dstId); } @@ -231,7 +231,7 @@ bool AffiliationLookup::groupUnaff(uint32_t srcId) auto it = m_grpAffTable.find(srcId); if (it != m_grpAffTable.end()) { if (m_verbose) { - LogMessage(LOG_HOST, "%s, group unaffiliation, srcId = %u, dstId = %u", + LogInfoEx(LOG_HOST, "%s, group unaffiliation, srcId = %u, dstId = %u", m_name.c_str(), srcId, it->second); } } else { @@ -360,7 +360,7 @@ bool AffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTi m_grantTimers[dstId].start(); if (m_verbose) { - LogMessage(LOG_HOST, "%s, granting channel, chNo = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_HOST, "%s, granting channel, chNo = %u, dstId = %u, srcId = %u, group = %u", m_name.c_str(), chNo, dstId, srcId, grp); } @@ -432,7 +432,7 @@ bool AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) uint32_t srcId = getGrantedSrcId(dstId); if (m_verbose) { - LogMessage(LOG_HOST, "%s, releasing channel grant, chNo = %u, dstId = %u", + LogInfoEx(LOG_HOST, "%s, releasing channel grant, chNo = %u, dstId = %u", m_name.c_str(), chNo, dstId); } diff --git a/src/common/lookups/IdenTableLookup.cpp b/src/common/lookups/IdenTableLookup.cpp index 40427a58..313fb59b 100644 --- a/src/common/lookups/IdenTableLookup.cpp +++ b/src/common/lookups/IdenTableLookup.cpp @@ -143,7 +143,7 @@ bool IdenTableLookup::load() IdenTable entry = IdenTable(channelId, baseFrequency, chSpaceKhz, txOffsetMhz, chBandwidthKhz); - LogMessage(LOG_HOST, "Channel Id %u: BaseFrequency = %uHz, TXOffsetMhz = %fMHz, BandwidthKhz = %fKHz, SpaceKhz = %fKHz", + LogInfoEx(LOG_HOST, "Channel Id %u: BaseFrequency = %uHz, TXOffsetMhz = %fMHz, BandwidthKhz = %fKHz, SpaceKhz = %fKHz", entry.channelId(), entry.baseFrequency(), entry.txOffsetMhz(), entry.chBandwidthKhz(), entry.chSpaceKhz()); m_table[channelId] = entry; diff --git a/src/common/lookups/PeerListLookup.cpp b/src/common/lookups/PeerListLookup.cpp index 4e7affce..849d2f49 100644 --- a/src/common/lookups/PeerListLookup.cpp +++ b/src/common/lookups/PeerListLookup.cpp @@ -251,7 +251,7 @@ bool PeerListLookup::load() m_table[id] = entry; // log depending on what was loaded - LogMessage(LOG_HOST, "Loaded peer ID %u%s into peer ID lookup table, %s%s%s", id, + LogInfoEx(LOG_HOST, "Loaded peer ID %u%s into peer ID lookup table, %s%s%s", id, (!alias.empty() ? (" (" + alias + ")").c_str() : ""), (!password.empty() ? "using unique peer password" : "using master password"), (peerReplica) ? ", Replication Enabled" : "", @@ -286,7 +286,7 @@ bool PeerListLookup::save(bool quiet) } if (!quiet) - LogMessage(LOG_HOST, "Saving peer lookup file to %s", m_filename.c_str()); + LogInfoEx(LOG_HOST, "Saving peer lookup file to %s", m_filename.c_str()); // Counter for lines written unsigned int lines = 0; diff --git a/src/common/lookups/RadioIdLookup.cpp b/src/common/lookups/RadioIdLookup.cpp index 06317b62..ecebab10 100644 --- a/src/common/lookups/RadioIdLookup.cpp +++ b/src/common/lookups/RadioIdLookup.cpp @@ -254,7 +254,7 @@ bool RadioIdLookup::save(bool quiet) } if (!quiet) - LogMessage(LOG_HOST, "Saving RID lookup file to %s", m_filename.c_str()); + LogInfoEx(LOG_HOST, "Saving RID lookup file to %s", m_filename.c_str()); // Counter for lines written unsigned int lines = 0; diff --git a/src/common/lookups/TalkgroupRulesLookup.cpp b/src/common/lookups/TalkgroupRulesLookup.cpp index 512bed31..187fed4f 100644 --- a/src/common/lookups/TalkgroupRulesLookup.cpp +++ b/src/common/lookups/TalkgroupRulesLookup.cpp @@ -415,7 +415,7 @@ bool TalkgroupRulesLookup::save(bool quiet) try { if (!quiet) - LogMessage(LOG_HOST, "Saving talkgroup rules file to %s", m_rulesFile.c_str()); + LogInfoEx(LOG_HOST, "Saving talkgroup rules file to %s", m_rulesFile.c_str()); yaml::Serialize(newRules, m_rulesFile.c_str()); } catch (yaml::OperationException const& e) { diff --git a/src/common/network/NetRPC.cpp b/src/common/network/NetRPC.cpp index f03824b2..d16d96da 100644 --- a/src/common/network/NetRPC.cpp +++ b/src/common/network/NetRPC.cpp @@ -286,7 +286,7 @@ void NetRPC::defaultResponse(json::object& reply, std::string message, StatusTyp bool NetRPC::open() { if (m_debug) - LogMessage(LOG_NET, "Opening RPC network"); + LogInfoEx(LOG_NET, "Opening RPC network"); // generate AES256 key size_t size = m_password.size(); @@ -313,7 +313,7 @@ bool NetRPC::open() void NetRPC::close() { if (m_debug) - LogMessage(LOG_NET, "Closing RPC network"); + LogInfoEx(LOG_NET, "Closing RPC network"); m_socket->close(); } diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index eff17f96..2b99e729 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -697,7 +697,7 @@ void Network::clock(uint32_t ms) offs += 4U; } - LogMessage(LOG_NET, "Network Announced %u whitelisted RIDs", len); + LogInfoEx(LOG_NET, "Network Announced %u whitelisted RIDs", len); // save to file if enabled and we got RIDs if (m_saveLookup && len > 0) { @@ -723,7 +723,7 @@ void Network::clock(uint32_t ms) offs += 4U; } - LogMessage(LOG_NET, "Network Announced %u blacklisted RIDs", len); + LogInfoEx(LOG_NET, "Network Announced %u blacklisted RIDs", len); // save to file if enabled and we got RIDs if (m_saveLookup && len > 0) { @@ -766,7 +766,7 @@ void Network::clock(uint32_t ms) m_tidLookup->eraseEntry(id, slot); } - LogMessage(LOG_NET, "Activated%s%s TG %u TS %u in TGID table", + LogInfoEx(LOG_NET, "Activated%s%s TG %u TS %u in TGID table", (nonPreferred) ? " non-preferred" : "", (affiliated) ? " affiliated" : "", id, slot); m_tidLookup->addEntry(id, slot, true, affiliated, nonPreferred); } @@ -774,7 +774,7 @@ void Network::clock(uint32_t ms) offs += 5U; } - LogMessage(LOG_NET, "Activated %u TGs; loaded %u entries into talkgroup rules table", len, m_tidLookup->groupVoice().size()); + LogInfoEx(LOG_NET, "Activated %u TGs; loaded %u entries into talkgroup rules table", len, m_tidLookup->groupVoice().size()); // save if saving from network is enabled if (m_saveLookup && len > 0) { @@ -800,14 +800,14 @@ void Network::clock(uint32_t ms) lookups::TalkgroupRuleGroupVoice tid = m_tidLookup->find(id, slot); if (!tid.isInvalid()) { - LogMessage(LOG_NET, "Deactivated TG %u TS %u in TGID table", id, slot); + LogInfoEx(LOG_NET, "Deactivated TG %u TS %u in TGID table", id, slot); m_tidLookup->eraseEntry(id, slot); } offs += 5U; } - LogMessage(LOG_NET, "Deactivated %u TGs; loaded %u entries into talkgroup rules table", len, m_tidLookup->groupVoice().size()); + LogInfoEx(LOG_NET, "Deactivated %u TGs; loaded %u entries into talkgroup rules table", len, m_tidLookup->groupVoice().size()); // save if saving from network is enabled if (m_saveLookup && len > 0) { @@ -852,7 +852,7 @@ void Network::clock(uint32_t ms) if (m_haIPs.size() > 1U) { m_currentHAIP = 1U; // because the first entry is our configured entry, set // the current HA IP to the next available - LogMessage(LOG_NET, "Loaded %u HA IPs from master", m_haIPs.size() - 1U); + LogInfoEx(LOG_NET, "Loaded %u HA IPs from master", m_haIPs.size() - 1U); } } } @@ -950,7 +950,7 @@ void Network::clock(uint32_t ms) if (ks.keys().size() > 0U) { // fetch first key (a master response should never really send back more then one key) KeyItem ki = ks.keys()[0]; - LogMessage(LOG_NET, "PEER %u, master reported enc. key, algId = $%02X, kID = $%04X", m_peerId, + LogInfoEx(LOG_NET, "PEER %u, master reported enc. key, algId = $%02X, kID = $%04X", m_peerId, ks.algId(), ki.kId()); // fire off key response callback if we have one @@ -1042,7 +1042,7 @@ void Network::clock(uint32_t ms) { switch (m_status) { case NET_STAT_WAITING_LOGIN: - LogMessage(LOG_NET, "PEER %u RPTL ACK, performing login exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); + LogInfoEx(LOG_NET, "PEER %u RPTL ACK, performing login exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); ::memcpy(m_salt, buffer.get() + 6U, sizeof(uint32_t)); writeAuthorisation(); @@ -1052,7 +1052,7 @@ void Network::clock(uint32_t ms) m_retryTimer.start(); break; case NET_STAT_WAITING_AUTHORISATION: - LogMessage(LOG_NET, "PEER %u RPTK ACK, performing configuration exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); + LogInfoEx(LOG_NET, "PEER %u RPTK ACK, performing configuration exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); writeConfig(); @@ -1061,7 +1061,7 @@ void Network::clock(uint32_t ms) m_retryTimer.start(); break; case NET_STAT_WAITING_CONFIG: - LogMessage(LOG_NET, "PEER %u RPTC ACK, logged into the master successfully, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); + LogInfoEx(LOG_NET, "PEER %u RPTC ACK, logged into the master successfully, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); m_loginStreamId = 0U; m_remotePeerId = rtpHeader.getSSRC(); @@ -1080,7 +1080,7 @@ void Network::clock(uint32_t ms) if (length > 6) { m_useAlternatePortForDiagnostics = (buffer[6U] & 0x80U) == 0x80U; if (m_useAlternatePortForDiagnostics) { - LogMessage(LOG_NET, "PEER %u RPTC ACK, master commanded alternate port for diagnostics and activity logging, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); + LogInfoEx(LOG_NET, "PEER %u RPTC ACK, master commanded alternate port for diagnostics and activity logging, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); } else { // disable diagnostic and activity logging automatically if the master doesn't utilize the alternate port m_allowDiagnosticTransfer = false; @@ -1190,7 +1190,7 @@ bool Network::open() if (!m_enabled) return false; if (m_debug) - LogMessage(LOG_NET, "PEER %u opening network", m_peerId); + LogInfoEx(LOG_NET, "PEER %u opening network", m_peerId); m_status = NET_STAT_WAITING_CONNECT; @@ -1205,7 +1205,7 @@ bool Network::open() m_currentHAIP = 0U; } - LogMessage(LOG_NET, "PEER %u connection to the master has timed out, %s:%u is non-responsive, trying next HA %s:%u", m_peerId, + LogInfoEx(LOG_NET, "PEER %u connection to the master has timed out, %s:%u is non-responsive, trying next HA %s:%u", m_peerId, m_address.c_str(), m_port, entry.masterAddress.c_str(), entry.masterPort); m_address = entry.masterAddress; m_port = entry.masterPort; @@ -1218,7 +1218,7 @@ bool Network::open() m_pingsReceived = 0U; if (udp::Socket::lookup(m_address, m_port, m_addr, m_addrLen) != 0) { - LogMessage(LOG_NET, "!!! Could not lookup the address of the master!"); + LogInfoEx(LOG_NET, "!!! Could not lookup the address of the master!"); return false; } @@ -1230,7 +1230,7 @@ bool Network::open() void Network::close() { if (m_debug) - LogMessage(LOG_NET, "PEER %u closing Network", m_peerId); + LogInfoEx(LOG_NET, "PEER %u closing Network", m_peerId); if (m_status == NET_STAT_RUNNING) { uint8_t buffer[1U]; diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index 9a120fce..ab9c119d 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -307,7 +307,7 @@ ssize_t Socket::read(uint8_t* buffer, uint32_t length, sockaddr_storage& address #endif // defined(_WIN32) if (len == -1 && errno == ENOTSOCK) { - LogMessage(LOG_NET, "Re-opening UDP port on %u", m_localPort); + LogInfoEx(LOG_NET, "Re-opening UDP port on %u", m_localPort); close(); open(); } diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index ef6ce929..bc8269c7 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -706,7 +706,7 @@ void* HostFNE::threadMasterNetwork(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -726,7 +726,7 @@ void* HostFNE::threadMasterNetwork(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -762,7 +762,7 @@ void* HostFNE::threadDiagNetwork(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -782,7 +782,7 @@ void* HostFNE::threadDiagNetwork(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -965,7 +965,7 @@ void* HostFNE::threadVirtualNetworking(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -999,7 +999,7 @@ void* HostFNE::threadVirtualNetworking(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 5e66cad3..74af0f3e 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -117,7 +117,7 @@ void DiagNetwork::clock(uint32_t ms) bool DiagNetwork::open() { if (m_debug) - LogMessage(LOG_DIAG, "Opening Network"); + LogInfoEx(LOG_DIAG, "Opening Network"); m_threadPool.start(); @@ -146,7 +146,7 @@ bool DiagNetwork::open() void DiagNetwork::close() { if (m_debug) - LogMessage(LOG_DIAG, "Closing Network"); + LogInfoEx(LOG_DIAG, "Closing Network"); m_threadPool.stop(); m_threadPool.wait(); diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index d5da47ce..fdc38a1d 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -609,7 +609,7 @@ void FNENetwork::clock(uint32_t ms) bool FNENetwork::open() { if (m_debug) - LogMessage(LOG_MASTER, "Opening Network"); + LogInfoEx(LOG_MASTER, "Opening Network"); // start thread pool m_threadPool.start(); @@ -650,7 +650,7 @@ bool FNENetwork::open() void FNENetwork::close() { if (m_debug) - LogMessage(LOG_MASTER, "Closing Network"); + LogInfoEx(LOG_MASTER, "Closing Network"); if (m_status == NET_STAT_MST_RUNNING) { uint8_t buffer[1U]; @@ -709,7 +709,7 @@ void* FNENetwork::threadParrotHandler(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -754,7 +754,7 @@ void* FNENetwork::threadParrotHandler(void* arg) // if the DMR handle is marked as playing back parrot frames, but has no more frames in the queue // clear the playback flag if (fne->m_tagDMR->isParrotPlayback() && !fne->m_tagDMR->hasParrotFrames()) { - LogMessage(LOG_MASTER, "DMR, Parrot Call End, peer = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_MASTER, "DMR, Parrot Call End, peer = %u, srcId = %u, dstId = %u", fne->m_tagDMR->lastParrotPeerId(), fne->m_tagDMR->lastParrotSrcId(), fne->m_tagDMR->lastParrotDstId()); fne->m_tagDMR->clearParrotPlayback(); } @@ -762,7 +762,7 @@ void* FNENetwork::threadParrotHandler(void* arg) // if the P25 handle is marked as playing back parrot frames, but has no more frames in the queue // clear the playback flag if (fne->m_tagP25->isParrotPlayback() && !fne->m_tagDMR->hasParrotFrames()) { - LogMessage(LOG_MASTER, "P25, Parrot Call End, peer = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_MASTER, "P25, Parrot Call End, peer = %u, srcId = %u, dstId = %u", fne->m_tagP25->lastParrotPeerId(), fne->m_tagP25->lastParrotSrcId(), fne->m_tagP25->lastParrotDstId()); fne->m_tagP25->clearParrotPlayback(); } @@ -770,7 +770,7 @@ void* FNENetwork::threadParrotHandler(void* arg) // if the NXDN handle is marked as playing back parrot frames, but has no more frames in the queue // clear the playback flag if (fne->m_tagNXDN->isParrotPlayback() && !fne->m_tagDMR->hasParrotFrames()) { - LogMessage(LOG_MASTER, "NXDN, Parrot Call End, peer = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_MASTER, "NXDN, Parrot Call End, peer = %u, srcId = %u, dstId = %u", fne->m_tagNXDN->lastParrotPeerId(), fne->m_tagNXDN->lastParrotSrcId(), fne->m_tagNXDN->lastParrotDstId()); fne->m_tagNXDN->clearParrotPlayback(); } @@ -778,7 +778,7 @@ void* FNENetwork::threadParrotHandler(void* arg) // if the analog handle is marked as playing back parrot frames, but has no more frames in the queue // clear the playback flag if (fne->m_tagAnalog->isParrotPlayback() && !fne->m_tagDMR->hasParrotFrames()) { - LogMessage(LOG_MASTER, "Analog, Parrot Call End, peer = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_MASTER, "Analog, Parrot Call End, peer = %u, srcId = %u, dstId = %u", fne->m_tagAnalog->lastParrotPeerId(), fne->m_tagAnalog->lastParrotSrcId(), fne->m_tagAnalog->lastParrotDstId()); fne->m_tagAnalog->clearParrotPlayback(); } @@ -788,7 +788,7 @@ void* FNENetwork::threadParrotHandler(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -1021,7 +1021,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) FNEPeerConnection* connection = network->m_peers[peerId]; if (connection != nullptr) { if (connection->connectionState() == NET_STAT_RUNNING) { - LogMessage(LOG_MASTER, "PEER %u (%s) resetting peer connection, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + LogInfoEx(LOG_MASTER, "PEER %u (%s) resetting peer connection, connectionState = %u", peerId, connection->identWithQualifier().c_str(), connection->connectionState()); delete connection; @@ -1492,7 +1492,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) { KMMModifyKey* modifyKey = static_cast(frame.get()); if (modifyKey->getAlgId() > 0U && modifyKey->getKId() > 0U) { - LogMessage(LOG_MASTER, "PEER %u (%s) requested enc. key, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), + LogInfoEx(LOG_MASTER, "PEER %u (%s) requested enc. key, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); ::KeyItem keyItem = network->m_cryptoLookup->find(modifyKey->getKId()); if (!keyItem.isInvalid()) { @@ -1505,7 +1505,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) Utils::dump(1U, "FNENetwork::taskNetworkRx(), Key", key, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); } - LogMessage(LOG_MASTER, "PEER %u (%s) local enc. key, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), + LogInfoEx(LOG_MASTER, "PEER %u (%s) local enc. key, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); // build response buffer @@ -1541,7 +1541,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) for (auto peer : network->m_host->m_peerNetworks) { if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isReplica()) { - LogMessage(LOG_PEER, "PEER %u (%s) no local key or container, requesting key from upstream master, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), + LogInfoEx(LOG_PEER, "PEER %u (%s) no local key or container, requesting key from upstream master, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); bool locked = network->m_keyQueueMutex.try_lock_for(std::chrono::milliseconds(60)); @@ -1758,7 +1758,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) aff->groupAff(srcId, dstId); offs += 8U; } - LogMessage(LOG_MASTER, "PEER %u (%s) announced %u affiliations", peerId, connection->identWithQualifier().c_str(), len); + LogInfoEx(LOG_MASTER, "PEER %u (%s) announced %u affiliations", peerId, connection->identWithQualifier().c_str(), len); // attempt to repeat traffic to replica masters if (network->m_host->m_peerNetworks.size() > 0) { @@ -1806,7 +1806,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } offs += 4U; } - LogMessage(LOG_MASTER, "PEER %u (%s) announced %u VCs", peerId, connection->identWithQualifier().c_str(), len); + LogInfoEx(LOG_MASTER, "PEER %u (%s) announced %u VCs", peerId, connection->identWithQualifier().c_str(), len); network->m_ccPeerMap[peerId] = vcPeers; // attempt to repeat traffic to replica masters @@ -2927,7 +2927,7 @@ void FNENetwork::processTEKResponse(p25::kmm::KeyItem* rspKi, uint8_t algId, uin if (rspKi == nullptr) return; - LogMessage(LOG_PEER, "upstream master enc. key, algId = $%02X, kID = $%04X", algId, rspKi->kId()); + LogInfoEx(LOG_PEER, "upstream master enc. key, algId = $%02X, kID = $%04X", algId, rspKi->kId()); m_keyQueueMutex.lock(); diff --git a/src/fne/network/P25OTARService.cpp b/src/fne/network/P25OTARService.cpp index cdf69c0c..89bfddad 100644 --- a/src/fne/network/P25OTARService.cpp +++ b/src/fne/network/P25OTARService.cpp @@ -36,7 +36,7 @@ using namespace p25::kmm; // Macro helper to verbose log a generic KMM. #define VERBOSE_LOG_KMM(_PCKT_STR, __LLID) \ if (m_verbose) { \ - LogMessage(LOG_P25, "KMM, %s, llId = %u", _PCKT_STR.c_str(), __LLID); \ + LogInfoEx(LOG_P25, "KMM, %s, llId = %u", _PCKT_STR.c_str(), __LLID); \ } // --------------------------------------------------------------------------- @@ -291,7 +291,7 @@ UInt8Array P25OTARService::cryptKMM(uint8_t algoId, uint16_t kid, uint8_t* mi, c Utils::dump(1U, "P25OTARService::cryptKMM(), Key", key, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); } - LogMessage(LOG_P25, P25_KMM_STR ", algId = $%02X, kID = $%04X", algoId, kid); + LogInfoEx(LOG_P25, P25_KMM_STR ", algId = $%02X, kID = $%04X", algoId, kid); crypto.setKey(key, keyLength); crypto.generateKeystream(); @@ -357,7 +357,7 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ { KMMHello* kmm = static_cast(frame.get()); if (m_verbose) { - LogMessage(LOG_P25, P25_KMM_STR ", %s, llId = %u, flag = $%02X", kmm->toString().c_str(), + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, flag = $%02X", kmm->toString().c_str(), llId, kmm->getFlag()); } @@ -389,7 +389,7 @@ UInt8Array P25OTARService::write_KMM_NoService(uint32_t llId, uint32_t kmmRSI, u outKmm.setDstLLId(kmmRSI); if (m_verbose) { - LogMessage(LOG_P25, P25_KMM_STR ", %s, llId = %u, RSI = %u", outKmm.toString().c_str(), + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, RSI = %u", outKmm.toString().c_str(), outKmm.getSrcLLId(), outKmm.getDstLLId()); } diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 671a76df..51c9e6b2 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -116,12 +116,12 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee if (tg.config().parrot() && !m_parrotPlayback) { if (m_parrotFrames.size() > 0) { m_parrotFramesReady = true; - LogMessage(LOG_NET, "Analog, Parrot Playback will Start, peer = %u, ssrc = %u, srcId = %u", peerId, ssrc, srcId); + LogInfoEx(LOG_NET, "Analog, Parrot Playback will Start, peer = %u, ssrc = %u, srcId = %u", peerId, ssrc, srcId); m_network->m_parrotDelayTimer.start(); } } - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB @@ -211,7 +211,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status[dstId].activeCall = true; m_status.unlock(); - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } } diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 69f7997d..f239dba8 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -178,7 +178,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (tg.config().parrot() && !m_parrotPlayback) { if (m_parrotFrames.size() > 0) { m_parrotFramesReady = true; - LogMessage(LOG_NET, "DMR, Parrot Playback will Start, peer = %u, ssrc = %u, srcId = %u", peerId, ssrc, srcId); + LogInfoEx(LOG_NET, "DMR, Parrot Playback will Start, peer = %u, ssrc = %u, srcId = %u", peerId, ssrc, srcId); m_network->m_parrotDelayTimer.start(); } } @@ -193,11 +193,11 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream); } else - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB @@ -246,7 +246,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, fromUpstream); m_status[dstId].srcId = srcId; } @@ -323,11 +323,11 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } else - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } } @@ -635,7 +635,7 @@ void TagDMRData::write_Ext_Func(uint32_t peerId, uint8_t slot, uint32_t func, ui csbk->setSrcId(arg); csbk->setDstId(dstId); - LogMessage(LOG_DMR, "DMR Slot %u, DT_CSBK, %s, op = $%02X, arg = %u, tgt = %u", + LogInfoEx(LOG_DMR, "DMR Slot %u, DT_CSBK, %s, op = $%02X, arg = %u, tgt = %u", slot, csbk->toString().c_str(), func, arg, dstId); write_CSBK(peerId, slot, csbk.get()); @@ -650,7 +650,7 @@ void TagDMRData::write_Call_Alrt(uint32_t peerId, uint8_t slot, uint32_t srcId, csbk->setSrcId(srcId); csbk->setDstId(dstId); - LogMessage(LOG_DMR, "DMR Slot %u, DT_CSBK, %s, srcId = %u, dstId = %u", + LogInfoEx(LOG_DMR, "DMR Slot %u, DT_CSBK, %s, srcId = %u, dstId = %u", slot, csbk->toString().c_str(), srcId, dstId); write_CSBK(peerId, slot, csbk.get()); @@ -793,7 +793,7 @@ bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::NetDat return false; } else { if (m_network->m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, DT_CSBK, %s, sysId = $%03X, chNo = %u, peerId = %u", dmrData.getSlotNo(), csbk->toString().c_str(), + LogInfoEx(LOG_DMR, "DMR Slot %u, DT_CSBK, %s, sysId = $%03X, chNo = %u, peerId = %u", dmrData.getSlotNo(), csbk->toString().c_str(), osp->getSystemId(), osp->getLogicalCh1(), peerId); } } @@ -1261,7 +1261,7 @@ bool TagDMRData::write_CSBK_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstI csbk->setSlotNo(slot); if (m_network->m_verbose) { - LogMessage(LOG_DMR, "DMR, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u, peerId = %u", + LogInfoEx(LOG_DMR, "DMR, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u, peerId = %u", csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId, peerId); } @@ -1277,7 +1277,7 @@ bool TagDMRData::write_CSBK_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstI csbk->setSlotNo(slot); if (m_network->m_verbose) { - LogMessage(LOG_DMR, "DMR, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u, peerId = %u", + LogInfoEx(LOG_DMR, "DMR, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u, peerId = %u", csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId, peerId); } @@ -1302,7 +1302,7 @@ void TagDMRData::write_CSBK_NACK_RSP(uint32_t peerId, uint32_t dstId, uint8_t sl csbk->setDstId(dstId); if (m_network->m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, CSBK, %s, reason = $%02X (%s), srcId = %u, dstId = %u", + LogInfoEx(LOG_DMR, "DMR Slot %u, CSBK, %s, reason = $%02X (%s), srcId = %u, dstId = %u", slot, csbk->toString().c_str(), reason, DMRUtils::rsnToString(reason).c_str(), csbk->getSrcId(), csbk->getDstId()); } diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index a29b41fb..abadc4e9 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -217,7 +217,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI if (tg.config().parrot() && !m_parrotPlayback) { if (m_parrotFrames.size() > 0) { m_parrotFramesReady = true; - LogMessage(LOG_NET, "NXDN, Parrot Playback will Start, peer = %u, srcId = %u", peerId, srcId); + LogInfoEx(LOG_NET, "NXDN, Parrot Playback will Start, peer = %u, srcId = %u", peerId, srcId); m_network->m_parrotDelayTimer.start(); } } @@ -232,11 +232,11 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); } else - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB @@ -284,7 +284,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Source Switched, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); m_status[dstId].srcId = srcId; } @@ -358,11 +358,11 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } else - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } } @@ -1063,7 +1063,7 @@ bool TagNXDNData::write_Message_Grant(uint32_t peerId, uint32_t srcId, uint32_t rcch->setPriority(priority); if (m_network->m_verbose) { - LogMessage(LOG_NXDN, "%s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u, peerId = %u", + LogInfoEx(LOG_NXDN, "%s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u, peerId = %u", rcch->toString().c_str(), rcch->getEmergency(), rcch->getEncrypted(), rcch->getPriority(), rcch->getGrpVchNo(), rcch->getSrcId(), rcch->getDstId(), peerId); } @@ -1090,7 +1090,7 @@ void TagNXDNData::write_Message_Deny(uint32_t peerId, uint32_t srcId, uint32_t d rcch->setDstId(dstId); if (m_network->m_verbose) { - LogMessage(LOG_NXDN, "MSG_DENIAL (Message Denial), reason = $%02X (%s), service = $%02X, srcId = %u, dstId = %u", + LogInfoEx(LOG_NXDN, "MSG_DENIAL (Message Denial), reason = $%02X (%s), service = $%02X, srcId = %u, dstId = %u", reason, NXDNUtils::causeToString(reason).c_str(), service, srcId, dstId); } diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 916a95c5..0279c44e 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -226,7 +226,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (m_parrotFrames.size() > 0) { m_parrotFramesReady = true; m_parrotFirstFrame = true; - LogMessage(LOG_NET, "P25, Parrot Playback will Start, peer = %u, srcId = %u", peerId, srcId); + LogInfoEx(LOG_NET, "P25, Parrot Playback will Start, peer = %u, srcId = %u", peerId, srcId); m_network->m_parrotDelayTimer.start(); } } @@ -241,11 +241,11 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Private Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Private Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream); } else - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB @@ -294,7 +294,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Source Switched, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Source Switched, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); m_status[dstId].srcId = srcId; } @@ -368,11 +368,11 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Private Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Private Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream); } else - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream); } } @@ -685,13 +685,13 @@ void TagP25Data::playbackParrot() UInt8Array message = m_network->createP25_TDUMessage(messageLength, control, lsd, controlByte); if (message != nullptr) { if (m_network->m_parrotOnlyOriginating) { - LogMessage(LOG_P25, "Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", pkt.peerId, srcId, dstId); + LogInfoEx(LOG_P25, "Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", pkt.peerId, srcId, dstId); m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, m_network->createStreamId(), false); } else { // repeat traffic to the connected peers for (auto peer : m_network->m_peers) { - LogMessage(LOG_P25, "Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", peer.first, srcId, dstId); + LogInfoEx(LOG_P25, "Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", peer.first, srcId, dstId); m_network->writePeer(peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, m_network->createStreamId(), false); } @@ -738,7 +738,7 @@ void TagP25Data::write_TSDU_Call_Alrt(uint32_t peerId, uint32_t srcId, uint32_t iosp->setSrcId(srcId); iosp->setDstId(dstId); - LogMessage(LOG_P25, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", iosp->toString().c_str(), srcId, dstId); + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", iosp->toString().c_str(), srcId, dstId); write_TSDU(peerId, iosp.get()); } @@ -752,7 +752,7 @@ void TagP25Data::write_TSDU_Radio_Mon(uint32_t peerId, uint32_t srcId, uint32_t iosp->setDstId(dstId); iosp->setTxMult(txMult); - LogMessage(LOG_P25, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", iosp->toString().c_str(), srcId, dstId, txMult); + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", iosp->toString().c_str(), srcId, dstId, txMult); write_TSDU(peerId, iosp.get()); } @@ -771,7 +771,7 @@ void TagP25Data::write_TSDU_Ext_Func(uint32_t peerId, uint32_t func, uint32_t ar iosp->setMFId(MFG_MOT); } - LogMessage(LOG_P25, P25_TSDU_STR ", %s, mfId = $%02X, op = $%02X, arg = %u, tgt = %u", + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, mfId = $%02X, op = $%02X, arg = %u, tgt = %u", iosp->toString().c_str(), iosp->getMFId(), iosp->getExtendedFunction(), iosp->getSrcId(), iosp->getDstId()); write_TSDU(peerId, iosp.get()); @@ -785,7 +785,7 @@ void TagP25Data::write_TSDU_Grp_Aff_Q(uint32_t peerId, uint32_t dstId) osp->setSrcId(WUID_FNE); osp->setDstId(dstId); - LogMessage(LOG_P25, P25_TSDU_STR ", %s, dstId = %u", osp->toString().c_str(), dstId); + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, dstId = %u", osp->toString().c_str(), dstId); write_TSDU(peerId, osp.get()); } @@ -798,7 +798,7 @@ void TagP25Data::write_TSDU_U_Reg_Cmd(uint32_t peerId, uint32_t dstId) osp->setSrcId(WUID_FNE); osp->setDstId(dstId); - LogMessage(LOG_P25, P25_TSDU_STR ", %s, dstId = %u", osp->toString().c_str(), dstId); + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, dstId = %u", osp->toString().c_str(), dstId); write_TSDU(peerId, osp.get()); } @@ -832,7 +832,7 @@ void TagP25Data::routeRewrite(uint8_t* buffer, uint32_t peerId, uint8_t duid, ui switch (tsbk->getLCO()) { case TSBKO::IOSP_GRP_VCH: { - LogMessage(LOG_P25, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", tsbk->toString(true).c_str(), tsbk->getEmergency(), tsbk->getEncrypted(), tsbk->getPriority(), tsbk->getGrpVchId(), tsbk->getGrpVchNo(), srcId, rewriteDstId); tsbk->setDstId(rewriteDstId); @@ -947,7 +947,7 @@ bool TagP25Data::processTSDUFrom(uint8_t* buffer, uint32_t peerId, uint8_t duid) lc::tsbk::OSP_ADJ_STS_BCAST* osp = static_cast(tsbk.get()); if (m_network->m_verbose) { - LogMessage(LOG_P25, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X, peerId = %u", tsbk->toString().c_str(), + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X, peerId = %u", tsbk->toString().c_str(), osp->getAdjSiteSysId(), osp->getAdjSiteRFSSId(), osp->getAdjSiteId(), osp->getAdjSiteChnId(), osp->getAdjSiteChnNo(), osp->getAdjSiteSvcClass(), peerId); } @@ -1100,7 +1100,7 @@ bool TagP25Data::processTSDUToNeighbor(uint8_t* buffer, uint32_t srcPeerId, uint lc::tsbk::OSP_ADJ_STS_BCAST* osp = static_cast(tsbk.get()); if (m_network->m_verbose) { - LogMessage(LOG_P25, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X, peerId = %u", tsbk->toString().c_str(), + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X, peerId = %u", tsbk->toString().c_str(), osp->getAdjSiteSysId(), osp->getAdjSiteRFSSId(), osp->getAdjSiteId(), osp->getAdjSiteChnId(), osp->getAdjSiteChnNo(), osp->getAdjSiteSvcClass(), srcPeerId); } } @@ -1660,7 +1660,7 @@ bool TagP25Data::write_TSDU_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstI iosp->setPriority(priority); if (m_network->m_verbose) { - LogMessage(LOG_P25, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u, peerId = %u", + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u, peerId = %u", iosp->toString().c_str(), iosp->getEmergency(), iosp->getEncrypted(), iosp->getPriority(), iosp->getGrpVchId(), iosp->getGrpVchNo(), iosp->getSrcId(), iosp->getDstId(), peerId); } @@ -1677,7 +1677,7 @@ bool TagP25Data::write_TSDU_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstI iosp->setPriority(priority); if (m_network->m_verbose) { - LogMessage(LOG_P25, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u, peerId = %u", + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u, peerId = %u", iosp->toString().c_str(), iosp->getEmergency(), iosp->getEncrypted(), iosp->getPriority(), iosp->getGrpVchId(), iosp->getGrpVchNo(), iosp->getSrcId(), iosp->getDstId(), peerId); } @@ -1700,7 +1700,7 @@ void TagP25Data::write_TSDU_Deny(uint32_t peerId, uint32_t srcId, uint32_t dstId osp->setGroup(grp); if (m_network->m_verbose) { - LogMessage(LOG_P25, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", osp->toString().c_str(), osp->getAIV(), reason, P25Utils::denyRsnToString(reason).c_str(), osp->getSrcId(), osp->getDstId()); } @@ -1721,7 +1721,7 @@ void TagP25Data::write_TSDU_Queue(uint32_t peerId, uint32_t srcId, uint32_t dstI osp->setGroup(grp); if (m_network->m_verbose) { - LogMessage(LOG_P25, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", + LogInfoEx(LOG_P25, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", osp->toString().c_str(), osp->getAIV(), reason, P25Utils::queueRsnToString(reason).c_str(), osp->getSrcId(), osp->getDstId()); } diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp index 23a5dad3..e9c07484 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp @@ -145,7 +145,7 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee uint32_t srcId = status->header.getSrcId(); uint32_t dstId = status->header.getDstId(); - LogMessage(LOG_DMR, DMR_DT_DATA_HEADER ", peerId = %u, slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_DMR, DMR_DT_DATA_HEADER ", peerId = %u, slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", peerId, status->slotNo, status->header.getDPF(), status->header.getA(), status->header.getSAP(), status->header.getFullMesage(), status->header.getBlocksToFollow(), status->header.getPadLength(), status->header.getPacketLength(), status->header.getFSN(), dstId, srcId, gi); @@ -157,7 +157,7 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status[peerId] = status; - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call Start, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, streamId = %u, fromUpstream = %u", peerId, status->slotNo, status->srcId, status->dstId, gi, streamId, fromUpstream); + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call Start, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, streamId = %u, fromUpstream = %u", peerId, status->slotNo, status->srcId, status->dstId, gi, streamId, fromUpstream); dispatchToFNE(peerId, dmrData, data, len, seqNo, pktSeq, streamId); return true; @@ -170,7 +170,7 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // a PDU header only with no blocks to follow is usually a response header if (status->header.getBlocksToFollow() == 0U) { - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, status->slotNo, status->srcId, status->dstId, streamId, fromUpstream); delete status; @@ -191,12 +191,12 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee dataBlock.setLastBlock(true); if (dataType == DataType::RATE_34_DATA) { - LogMessage(LOG_DMR, DMR_DT_RATE_34_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); + LogInfoEx(LOG_DMR, DMR_DT_RATE_34_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); } else if (dataType == DataType::RATE_12_DATA) { - LogMessage(LOG_DMR, DMR_DT_RATE_12_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); + LogInfoEx(LOG_DMR, DMR_DT_RATE_12_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); } else { - LogMessage(LOG_DMR, DMR_DT_RATE_1_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); + LogInfoEx(LOG_DMR, DMR_DT_RATE_1_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); } dispatchToFNE(peerId, dmrData, data, len, seqNo, pktSeq, streamId); @@ -211,7 +211,7 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee bool gi = status->header.getGI(); uint32_t srcId = status->header.getSrcId(); uint32_t dstId = status->header.getDstId(); - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, srcId, dstId, gi, status->header.getBlocksToFollow(), duration / 1000, streamId, fromUpstream); // report call event to InfluxDB diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index 0d226bad..33fd7c0b 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -126,7 +126,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return true; } - LogMessage(LOG_P25, P25_PDU_STR ", peerId = %u, ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, hdrOffset = %u, llId = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", peerId = %u, ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, hdrOffset = %u, llId = %u", peerId, status->header.getAckNeeded(), status->header.getOutbound(), status->header.getFormat(), status->header.getSAP(), status->header.getFullMessage(), status->header.getBlocksToFollow(), status->header.getPadLength(), status->header.getPacketLength(), status->header.getSynchronize(), status->header.getNs(), status->header.getFSN(), status->header.getHeaderOffset(), status->header.getLLId()); @@ -154,7 +154,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return true; } - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Data Call Start, peer = %u, llId = %u, streamId = %u, fromUpstream = %u", peerId, status->llId, streamId, fromUpstream); + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Data Call Start, peer = %u, llId = %u, streamId = %u, fromUpstream = %u", peerId, status->llId, streamId, fromUpstream); return true; } @@ -216,7 +216,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return false; } - LogMessage(LOG_P25, P25_PDU_STR ", ISP, extended address, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, extended address, sap = $%02X, srcLlId = %u", status->header.getEXSAP(), status->header.getSrcLLId()); status->extendedAddress = true; @@ -254,14 +254,14 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee status->blockData[i].getData(secondHeader); status->header.decodeExtAddr(secondHeader); - LogMessage(LOG_P25, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", status->blockData[i].getSerialNo(), status->blockData[i].getFormat(), status->blockData[i].getLastBlock(), status->header.getEXSAP(), status->header.getSrcLLId()); status->extendedAddress = true; } else { - LogMessage(LOG_P25, P25_PDU_STR ", peerId = %u, block %u, fmt = $%02X, lastBlock = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", peerId = %u, block %u, fmt = $%02X, lastBlock = %u", peerId, (status->header.getFormat() == PDUFormatType::CONFIRMED) ? status->blockData[i].getSerialNo() : status->dataBlockCnt, status->blockData[i].getFormat(), status->blockData[i].getLastBlock()); } @@ -298,7 +298,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee uint64_t duration = hrc::diff(pktTime, status->callStartTime); uint32_t srcId = (status->extendedAddress) ? status->header.getSrcLLId() : status->header.getLLId(); uint32_t dstId = status->header.getLLId(); - LogMessage((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Data Call End, peer = %u, srcId = %u, dstId = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Data Call End, peer = %u, srcId = %u, dstId = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, srcId, dstId, status->header.getBlocksToFollow(), duration / 1000, streamId, fromUpstream); // report call event to InfluxDB @@ -352,7 +352,7 @@ void P25PacketData::processPacketFrame(const uint8_t* data, uint32_t len, bool a std::string srcIpStr = __IP_FROM_UINT(srcProtoAddr); std::string tgtIpStr = __IP_FROM_UINT(tgtProtoAddr); - LogMessage(LOG_P25, "VTUN -> PDU IP Data, srcIp = %s (%u), dstIp = %s (%u), pktLen = %u, proto = %02X", + LogInfoEx(LOG_P25, "VTUN -> PDU IP Data, srcIp = %s (%u), dstIp = %s (%u), pktLen = %u, proto = %02X", srcIpStr.c_str(), WUID_FNE, tgtIpStr.c_str(), llId, pktLen, proto); // assemble a P25 PDU frame header for transport... @@ -478,7 +478,7 @@ void P25PacketData::clock(uint32_t ms) } std::string tgtIpStr = __IP_FROM_UINT(frame->tgtProtoAddr); - LogMessage(LOG_P25, "VTUN -> PDU IP Data, dstIp = %s (%u), userDataLen = %u, retries = %u", + LogInfoEx(LOG_P25, "VTUN -> PDU IP Data, dstIp = %s (%u), userDataLen = %u, retries = %u", tgtIpStr.c_str(), frame->llId, frame->userDataLen, frame->retryCnt); // do we have a valid target address? @@ -566,7 +566,7 @@ void P25PacketData::dispatch(uint32_t peerId) } if (status->header.getFormat() == PDUFormatType::RSP) { - LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, peer = %u, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, peer = %u, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", peerId, status->header.getFormat(), status->header.getResponseClass(), status->header.getResponseType(), status->header.getResponseStatus(), status->header.getLLId(), status->header.getSrcLLId()); @@ -574,26 +574,26 @@ void P25PacketData::dispatch(uint32_t peerId) m_readyForNextPkt[status->header.getSrcLLId()] = true; if (status->header.getResponseClass() == PDUAckClass::ACK && status->header.getResponseType() == PDUAckType::ACK) { - LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, OSP ACK, peer = %u, llId = %u, all blocks received OK, n = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, OSP ACK, peer = %u, llId = %u, all blocks received OK, n = %u", peerId, status->header.getLLId(), status->header.getResponseStatus()); } else { if (status->header.getResponseClass() == PDUAckClass::NACK) { switch (status->header.getResponseType()) { case PDUAckType::NACK_ILLEGAL: - LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, illegal format, peer = %u, llId = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, illegal format, peer = %u, llId = %u", peerId, status->header.getLLId()); break; case PDUAckType::NACK_PACKET_CRC: - LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet CRC error, peer = %u, llId = %u, n = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet CRC error, peer = %u, llId = %u, n = %u", peerId, status->header.getLLId(), status->header.getResponseStatus()); break; case PDUAckType::NACK_SEQ: case PDUAckType::NACK_OUT_OF_SEQ: - LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet out of sequence, peer = %u, llId = %u, seqNo = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet out of sequence, peer = %u, llId = %u, seqNo = %u", peerId, status->header.getLLId(), status->header.getResponseStatus()); break; case PDUAckType::NACK_UNDELIVERABLE: - LogMessage(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet undeliverable, peer = %u, llId = %u, n = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet undeliverable, peer = %u, llId = %u, n = %u", peerId, status->header.getLLId(), status->header.getResponseStatus()); break; @@ -634,14 +634,14 @@ void P25PacketData::dispatch(uint32_t peerId) uint32_t tgtProtoAddr = GET_UINT32(arpPacket, 18U); if (opcode == P25_PDU_ARP_REQUEST) { - LogMessage(LOG_P25, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); + LogInfoEx(LOG_P25, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); if (fneIPv4 == tgtProtoAddr) { write_PDU_ARP_Reply(fneIPv4, srcHWAddr, srcProtoAddr, WUID_FNE); } else { write_PDU_ARP_Reply(tgtProtoAddr, srcHWAddr, srcProtoAddr); } } else if (opcode == P25_PDU_ARP_REPLY) { - LogMessage(LOG_P25, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); + LogInfoEx(LOG_P25, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); if (fneIPv4 == srcProtoAddr) { LogWarning(LOG_P25, P25_PDU_STR ", ARP reply, %u is trying to masquerade as us...", srcHWAddr); } else { @@ -690,7 +690,7 @@ void P25PacketData::dispatch(uint32_t peerId) // reflect broadcast messages back to the CAI network bool handled = false; if (status->header.getLLId() == WUID_ALL) { - LogMessage(LOG_P25, "PDU -> VTUN, IP Data, repeated to CAI, broadcast packet, dstIp = %s (%u)", + LogInfoEx(LOG_P25, "PDU -> VTUN, IP Data, repeated to CAI, broadcast packet, dstIp = %s (%u)", dstIp, status->header.getLLId()); dispatchUserFrameToFNE(status->header, status->extendedAddress, status->pduUserData); @@ -700,7 +700,7 @@ void P25PacketData::dispatch(uint32_t peerId) 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_P25, P25_PDU_STR ", adding ARP entry, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), status->header.getSrcLLId()); + LogInfoEx(LOG_P25, 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); } } @@ -708,7 +708,7 @@ void P25PacketData::dispatch(uint32_t peerId) // 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_P25, "PDU -> VTUN, IP Data, repeated to CAI, destination IP has a CAI ARP table entry, dstIp = %s (%u)", + LogInfoEx(LOG_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); @@ -718,13 +718,13 @@ void P25PacketData::dispatch(uint32_t peerId) 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_P25, P25_PDU_STR ", adding ARP entry, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), status->header.getSrcLLId()); + LogInfoEx(LOG_P25, 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_P25, "PDU -> VTUN, IP Data, srcIp = %s (%u), dstIp = %s (%u), pktLen = %u, proto = %02X", + LogInfoEx(LOG_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); DECLARE_UINT8_ARRAY(ipFrame, pktLen); @@ -746,7 +746,7 @@ void P25PacketData::dispatch(uint32_t peerId) break; case PDUSAP::CONV_DATA_REG: { - LogMessage(LOG_P25, P25_PDU_STR ", CONV_DATA_REG (Conventional Data Registration), peer = %u, blocksToFollow = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", CONV_DATA_REG (Conventional Data Registration), peer = %u, blocksToFollow = %u", peerId, status->header.getBlocksToFollow()); processConvDataReg(status); @@ -754,7 +754,7 @@ void P25PacketData::dispatch(uint32_t peerId) break; case PDUSAP::SNDCP_CTRL_DATA: { - LogMessage(LOG_P25, P25_PDU_STR ", SNDCP_CTRL_DATA (SNDCP Control Data), peer = %u, blocksToFollow = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", SNDCP_CTRL_DATA (SNDCP Control Data), peer = %u, blocksToFollow = %u", peerId, status->header.getBlocksToFollow()); processSNDCPControl(status); @@ -763,7 +763,7 @@ void P25PacketData::dispatch(uint32_t peerId) case PDUSAP::UNENC_KMM: case PDUSAP::ENC_KMM: { - LogMessage(LOG_P25, P25_PDU_STR ", KMM (Key Management Message), peer = %u, blocksToFollow = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", KMM (Key Management Message), peer = %u, blocksToFollow = %u", peerId, status->header.getBlocksToFollow()); bool encrypted = (sap == PDUSAP::ENC_KMM); @@ -896,7 +896,7 @@ bool P25PacketData::processConvDataReg(RxStatus* status) ipAddr = getIPAddress(llId); } - LogMessage(LOG_P25, P25_PDU_STR ", CONNECT (Registration Request Connect), llId = %u, ipAddr = %s", llId, __IP_FROM_UINT(ipAddr).c_str()); + LogInfoEx(LOG_P25, P25_PDU_STR ", CONNECT (Registration Request Connect), llId = %u, ipAddr = %s", llId, __IP_FROM_UINT(ipAddr).c_str()); m_arpTable[llId] = ipAddr; // update ARP table } break; @@ -904,7 +904,7 @@ bool P25PacketData::processConvDataReg(RxStatus* status) { uint32_t llId = (status->pduUserData[1U] << 16) + (status->pduUserData[2U] << 8) + status->pduUserData[3U]; - LogMessage(LOG_P25, P25_PDU_STR ", DISCONNECT (Registration Request Disconnect), llId = %u", llId); + LogInfoEx(LOG_P25, P25_PDU_STR ", DISCONNECT (Registration Request Disconnect), llId = %u", llId); m_arpTable.erase(llId); } @@ -933,7 +933,7 @@ bool P25PacketData::processSNDCPControl(RxStatus* status) case SNDCP_PDUType::ACT_TDS_CTX: { SNDCPCtxActRequest* isp = static_cast(packet.get()); - LogMessage(LOG_P25, P25_PDU_STR ", SNDCP context activation request, llId = %u, nsapi = %u, ipAddr = %s, nat = $%02X, dsut = $%02X, mdpco = $%02X", llId, + LogInfoEx(LOG_P25, P25_PDU_STR ", SNDCP context activation request, llId = %u, nsapi = %u, ipAddr = %s, nat = $%02X, dsut = $%02X, mdpco = $%02X", llId, isp->getNSAPI(), __IP_FROM_UINT(isp->getIPAddress()).c_str(), isp->getNAT(), isp->getDSUT(), isp->getMDPCO()); m_arpTable[llId] = isp->getIPAddress(); @@ -943,7 +943,7 @@ bool P25PacketData::processSNDCPControl(RxStatus* status) case SNDCP_PDUType::DEACT_TDS_CTX_REQ: { SNDCPCtxDeactivation* isp = static_cast(packet.get()); - LogMessage(LOG_P25, P25_PDU_STR ", SNDCP context deactivation request, llId = %u, deactType = %02X", llId, + LogInfoEx(LOG_P25, P25_PDU_STR ", SNDCP context deactivation request, llId = %u, deactType = %02X", llId, isp->getDeactType()); m_arpTable.erase(llId); @@ -983,7 +983,7 @@ void P25PacketData::write_PDU_ARP(uint32_t addr) #if DEBUG_P25_PDU_DATA Utils::dump(1U, "P25, P25PacketData::write_PDU_ARP(), arpPacket", arpPacket, P25_PDU_ARP_PCKT_LENGTH); #endif - LogMessage(LOG_P25, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(addr).c_str(), fneIPv4.c_str(), WUID_FNE); + LogInfoEx(LOG_P25, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(addr).c_str(), fneIPv4.c_str(), WUID_FNE); // assemble a P25 PDU frame header for transport... data::DataHeader rspHeader = data::DataHeader(); @@ -1039,7 +1039,7 @@ void P25PacketData::write_PDU_ARP_Reply(uint32_t targetAddr, uint32_t requestorL #if DEBUG_P25_PDU_DATA Utils::dump(1U, "P25, P25PacketData::write_PDU_ARP_Reply(), arpPacket", arpPacket, P25_PDU_ARP_PCKT_LENGTH); #endif - LogMessage(LOG_P25, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(targetAddr).c_str(), tgtLlid); + LogInfoEx(LOG_P25, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(targetAddr).c_str(), tgtLlid); // assemble a P25 PDU frame header for transport... data::DataHeader rspHeader = data::DataHeader(); @@ -1077,7 +1077,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); if (m_network->m_verbosePacketData) - LogMessage(LOG_P25, P25_PDU_STR ", OSP, peerId = %u, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, peerId = %u, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", peerId, dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(), dataHeader.getSynchronize(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), dataHeader.getHeaderOffset(), dataHeader.getLLId()); @@ -1111,7 +1111,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: networkBlock++; if (m_network->m_verbosePacketData) - LogMessage(LOG_P25, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } @@ -1120,7 +1120,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: dataHeader.encodeExtAddr(pduUserData); if (m_network->m_verbosePacketData) - LogMessage(LOG_P25, P25_PDU_STR ", OSP, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } @@ -1140,7 +1140,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: dataBlock.setData(pduUserData + dataOffset); if (m_network->m_verbosePacketData) - LogMessage(LOG_P25, P25_PDU_STR ", OSP, peerId = %u, block %u, fmt = $%02X, lastBlock = %u", + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, peerId = %u, block %u, fmt = $%02X, lastBlock = %u", peerId, (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? dataBlock.getSerialNo() : i, dataBlock.getFormat(), dataBlock.getLastBlock()); diff --git a/src/host/Host.DMR.cpp b/src/host/Host.DMR.cpp index 945affe2..1e3d3a9a 100644 --- a/src/host/Host.DMR.cpp +++ b/src/host/Host.DMR.cpp @@ -57,7 +57,7 @@ void* Host::threadDMRReader1(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -165,7 +165,7 @@ void* Host::threadDMRReader1(void* arg) if (host->m_dmr->getRFState(1U) == RS_RF_REJECTED) { host->m_dmr1RejectTimer.clock(ms); if (host->m_dmr1RejectTimer.hasExpired()) { - LogMessage(LOG_HOST, "DMR, slot 1 reset from previous call reject, frames = %u", host->m_dmr1RejCnt); + LogInfoEx(LOG_HOST, "DMR, slot 1 reset from previous call reject, frames = %u", host->m_dmr1RejCnt); host->m_dmr1RejectTimer.stop(); host->m_dmr->clearRFReject(1U); host->m_dmr1RejCnt = 0U; @@ -182,7 +182,7 @@ void* Host::threadDMRReader1(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -213,7 +213,7 @@ void* Host::threadDMRWriter1(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -311,7 +311,7 @@ void* Host::threadDMRWriter1(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -342,7 +342,7 @@ void* Host::threadDMRReader2(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -449,7 +449,7 @@ void* Host::threadDMRReader2(void* arg) if (host->m_dmr->getRFState(2U) == RS_RF_REJECTED) { host->m_dmr2RejectTimer.clock(ms); if (host->m_dmr2RejectTimer.hasExpired()) { - LogMessage(LOG_HOST, "DMR, slot 2 reset from previous in-call reject, frames = %u", host->m_dmr2RejCnt); + LogInfoEx(LOG_HOST, "DMR, slot 2 reset from previous in-call reject, frames = %u", host->m_dmr2RejCnt); host->m_dmr2RejectTimer.stop(); host->m_dmr->clearRFReject(2U); host->m_dmr2RejCnt = 0U; @@ -466,7 +466,7 @@ void* Host::threadDMRReader2(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -497,7 +497,7 @@ void* Host::threadDMRWriter2(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -595,7 +595,7 @@ void* Host::threadDMRWriter2(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/host/Host.NXDN.cpp b/src/host/Host.NXDN.cpp index 29681fe2..a99153cd 100644 --- a/src/host/Host.NXDN.cpp +++ b/src/host/Host.NXDN.cpp @@ -44,7 +44,7 @@ void* Host::threadNXDNReader(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -114,7 +114,7 @@ void* Host::threadNXDNReader(void* arg) if (host->m_nxdn->getRFState() == RS_RF_REJECTED) { host->m_nxdnRejectTimer.clock(ms); if (host->m_nxdnRejectTimer.hasExpired()) { - LogMessage(LOG_HOST, "NXDN, reset from previous call reject, frames = %u", host->m_nxdnRejCnt); + LogInfoEx(LOG_HOST, "NXDN, reset from previous call reject, frames = %u", host->m_nxdnRejCnt); host->m_nxdnRejectTimer.stop(); host->m_nxdn->clearRFReject(); host->m_nxdnRejCnt = 0U; @@ -131,7 +131,7 @@ void* Host::threadNXDNReader(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -162,7 +162,7 @@ void* Host::threadNXDNWriter(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -247,7 +247,7 @@ void* Host::threadNXDNWriter(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/host/Host.P25.cpp b/src/host/Host.P25.cpp index 45f9980e..f8eb602f 100644 --- a/src/host/Host.P25.cpp +++ b/src/host/Host.P25.cpp @@ -45,7 +45,7 @@ void* Host::threadP25Reader(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -156,7 +156,7 @@ void* Host::threadP25Reader(void* arg) if (host->m_p25->getRFState() == RS_RF_REJECTED) { host->m_p25RejectTimer.clock(ms); if (host->m_p25RejectTimer.hasExpired()) { - LogMessage(LOG_HOST, "P25, reset from previous call reject, frames = %u", host->m_p25RejCnt); + LogInfoEx(LOG_HOST, "P25, reset from previous call reject, frames = %u", host->m_p25RejCnt); host->m_p25RejectTimer.stop(); host->m_p25->clearRFReject(); host->m_p25RejCnt = 0U; @@ -173,7 +173,7 @@ void* Host::threadP25Reader(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -204,7 +204,7 @@ void* Host::threadP25Writer(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -332,7 +332,7 @@ void* Host::threadP25Writer(void* arg) } } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/host/Host.cpp b/src/host/Host.cpp index 761493f1..1837b6ce 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -997,7 +997,7 @@ int Host::run() m_nxdnBcastDurationTimer.stop(); } - LogMessage(LOG_HOST, "CW, start transmitting"); + LogInfoEx(LOG_HOST, "CW, start transmitting"); m_isTxCW = true; std::lock_guard lock(m_clockingMutex); @@ -1021,7 +1021,7 @@ int Host::run() m_modem->clock(ms); if (!first && !m_modem->hasTX()) { - LogMessage(LOG_HOST, "CW, finished transmitting"); + LogInfoEx(LOG_HOST, "CW, finished transmitting"); break; } @@ -1073,10 +1073,10 @@ int Host::run() g_fireDMRBeacon = false; if (m_dmrTSCCData) { - LogMessage(LOG_HOST, "DMR, start CC broadcast"); + LogInfoEx(LOG_HOST, "DMR, start CC broadcast"); } else { - LogMessage(LOG_HOST, "DMR, roaming beacon burst"); + LogInfoEx(LOG_HOST, "DMR, roaming beacon burst"); } dmrBeaconIntervalTimer.start(); m_dmrBeaconDurationTimer.start(); @@ -1134,7 +1134,7 @@ int Host::run() // hide this message for continuous CC -- otherwise display every time we process if (!m_p25CtrlChannel) { - LogMessage(LOG_HOST, "P25, start CC broadcast"); + LogInfoEx(LOG_HOST, "P25, start CC broadcast"); } g_fireP25Control = false; @@ -1188,7 +1188,7 @@ int Host::run() // hide this message for continuous CC -- otherwise display every time we process if (!m_nxdnCtrlChannel) { - LogMessage(LOG_HOST, "NXDN, start CC broadcast"); + LogInfoEx(LOG_HOST, "NXDN, start CC broadcast"); } g_fireNXDNControl = false; @@ -1519,7 +1519,7 @@ bool Host::rmtPortModemOpen(Modem* modem) if (!ret) return false; - LogMessage(LOG_MODEM, "Modem Ready [Remote Mode]"); + LogInfoEx(LOG_MODEM, "Modem Ready [Remote Mode]"); // handled modem open return true; @@ -1756,7 +1756,7 @@ void* Host::threadRPC(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -1783,7 +1783,7 @@ void* Host::threadRPC(void* arg) Thread::sleep(m_idleTickDelay); } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -1814,7 +1814,7 @@ void* Host::threadModem(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -1843,7 +1843,7 @@ void* Host::threadModem(void* arg) Thread::sleep(m_idleTickDelay); } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -1874,7 +1874,7 @@ void* Host::threadWatchdog(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -2058,7 +2058,7 @@ void* Host::threadWatchdog(void* arg) Thread::sleep(m_idleTickDelay); } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -2089,7 +2089,7 @@ void* Host::threadSiteData(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -2127,7 +2127,7 @@ void* Host::threadSiteData(void* arg) Thread::sleep(m_idleTickDelay); } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -2158,7 +2158,7 @@ void* Host::threadPresence(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -2219,12 +2219,12 @@ void* Host::threadPresence(void* arg) host->rfCh()->setRFChData(channelNo, voiceCh); host->m_voiceChPeerId[channelNo] = peerId; - LogMessage(LOG_REST, "VC %s:%u, registration notice, peerId = %u, chNo = %u-%u", voiceCh.address().c_str(), voiceCh.port(), peerId, voiceCh.chId(), channelNo); + LogInfoEx(LOG_REST, "VC %s:%u, registration notice, peerId = %u, chNo = %u-%u", voiceCh.address().c_str(), voiceCh.port(), peerId, voiceCh.chId(), channelNo); LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X REST API Address %s:%u", voiceCh.chId(), channelNo, voiceCh.address().c_str(), voiceCh.port()); g_fireCCVCNotification = true; // announce this registration immediately to the FNE } else { - LogMessage(LOG_REST, "VC, registration rejected, peerId = %u, chNo = %u, VC wasn't a defined member of the CC voice channel list", peerId, channelNo); + LogInfoEx(LOG_REST, "VC, registration rejected, peerId = %u, chNo = %u, VC wasn't a defined member of the CC voice channel list", peerId, channelNo); g_RPC->defaultResponse(reply, "registration rejected", network::NetRPC::BAD_REQUEST); } }); @@ -2248,7 +2248,7 @@ void* Host::threadPresence(void* arg) if (!host->m_controlChData.address().empty() && host->m_controlChData.port() != 0 && host->m_network != nullptr && !host->m_dmrCtrlChannel && !host->m_p25CtrlChannel && !host->m_nxdnCtrlChannel) { if ((presenceNotifyTimer.isRunning() && presenceNotifyTimer.hasExpired()) || !hasInitialRegistered) { - LogMessage(LOG_HOST, "CC %s:%u, notifying CC of VC registration, peerId = %u", host->m_controlChData.address().c_str(), host->m_controlChData.port(), host->m_network->getPeerId()); + LogInfoEx(LOG_HOST, "CC %s:%u, notifying CC of VC registration, peerId = %u", host->m_controlChData.address().c_str(), host->m_controlChData.port(), host->m_network->getPeerId()); hasInitialRegistered = true; std::string localAddress = network::udp::Socket::getLocalAddress(); @@ -2279,7 +2279,7 @@ void* Host::threadPresence(void* arg) } } else - ::LogMessage(LOG_HOST, "CC %s:%u, VC registered, peerId = %u", host->m_controlChData.address().c_str(), host->m_controlChData.port(), host->m_network->getPeerId()); + ::LogInfoEx(LOG_HOST, "CC %s:%u, VC registered, peerId = %u", host->m_controlChData.address().c_str(), host->m_controlChData.port(), host->m_network->getPeerId()); }, host->m_controlChData.address(), host->m_controlChData.port()); presenceNotifyTimer.start(); @@ -2291,7 +2291,7 @@ void* Host::threadPresence(void* arg) if ((presenceNotifyTimer.isRunning() && presenceNotifyTimer.hasExpired()) || g_fireCCVCNotification) { g_fireCCVCNotification = false; if (host->m_network != nullptr && host->m_voiceChPeerId.size() > 0) { - LogMessage(LOG_HOST, "notifying FNE of VC registrations, peerId = %u", host->m_network->getPeerId()); + LogInfoEx(LOG_HOST, "notifying FNE of VC registrations, peerId = %u", host->m_network->getPeerId()); std::vector peers; for (auto it : host->m_voiceChPeerId) { @@ -2312,7 +2312,7 @@ void* Host::threadPresence(void* arg) Thread::sleep(m_idleTickDelay); } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/host/calibrate/HostCal.cpp b/src/host/calibrate/HostCal.cpp index 0aa658ca..1bbb3f90 100644 --- a/src/host/calibrate/HostCal.cpp +++ b/src/host/calibrate/HostCal.cpp @@ -175,7 +175,7 @@ int HostCal::run(int argc, char **argv) { if (!m_isHotspot) { m_modem->m_txInvert = !m_modem->m_txInvert; - LogMessage(LOG_CAL, " - TX Invert: %s", m_modem->m_txInvert ? "On" : "Off"); + LogInfoEx(LOG_CAL, " - TX Invert: %s", m_modem->m_txInvert ? "On" : "Off"); writeConfig(); } } @@ -184,7 +184,7 @@ int HostCal::run(int argc, char **argv) { if (!m_isHotspot) { m_modem->m_rxInvert = !m_modem->m_rxInvert; - LogMessage(LOG_CAL, " - RX Invert: %s", m_modem->m_rxInvert ? "On" : "Off"); + LogInfoEx(LOG_CAL, " - RX Invert: %s", m_modem->m_rxInvert ? "On" : "Off"); writeConfig(); } } @@ -193,7 +193,7 @@ int HostCal::run(int argc, char **argv) { if (!m_isHotspot) { m_modem->m_pttInvert = !m_modem->m_pttInvert; - LogMessage(LOG_CAL, " - PTT Invert: %s", m_modem->m_pttInvert ? "On" : "Off"); + LogInfoEx(LOG_CAL, " - PTT Invert: %s", m_modem->m_pttInvert ? "On" : "Off"); writeConfig(); } } @@ -202,7 +202,7 @@ int HostCal::run(int argc, char **argv) { if (!m_isHotspot) { m_modem->m_dcBlocker = !m_modem->m_dcBlocker; - LogMessage(LOG_CAL, " - DC Blocker: %s", m_modem->m_dcBlocker ? "On" : "Off"); + LogInfoEx(LOG_CAL, " - DC Blocker: %s", m_modem->m_dcBlocker ? "On" : "Off"); writeConfig(); } } @@ -210,7 +210,7 @@ int HostCal::run(int argc, char **argv) case 'D': { m_debug = !m_debug; - LogMessage(LOG_CAL, " - Modem Debug: %s", m_debug ? "On" : "Off"); + LogInfoEx(LOG_CAL, " - Modem Debug: %s", m_debug ? "On" : "Off"); writeConfig(); } break; @@ -486,7 +486,7 @@ int HostCal::run(int argc, char **argv) m_p25TduTest = false; m_nxdnEnabled = false; - LogMessage(LOG_CAL, " - %s", m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_modeStr.c_str()); writeConfig(); } break; @@ -502,7 +502,7 @@ int HostCal::run(int argc, char **argv) m_p25TduTest = false; m_nxdnEnabled = false; - LogMessage(LOG_CAL, " - %s", m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_modeStr.c_str()); writeConfig(); } break; @@ -518,7 +518,7 @@ int HostCal::run(int argc, char **argv) m_p25TduTest = false; m_nxdnEnabled = false; - LogMessage(LOG_CAL, " - %s", m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_modeStr.c_str()); writeConfig(); } break; @@ -534,7 +534,7 @@ int HostCal::run(int argc, char **argv) m_p25TduTest = false; m_nxdnEnabled = false; - LogMessage(LOG_CAL, " - %s", m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_modeStr.c_str()); writeConfig(); } break; @@ -550,7 +550,7 @@ int HostCal::run(int argc, char **argv) m_p25TduTest = false; m_nxdnEnabled = false; - LogMessage(LOG_CAL, " - %s", m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_modeStr.c_str()); writeConfig(); } break; @@ -566,7 +566,7 @@ int HostCal::run(int argc, char **argv) m_p25TduTest = false; m_nxdnEnabled = false; - LogMessage(LOG_CAL, " - %s", m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_modeStr.c_str()); writeConfig(); } break; @@ -584,7 +584,7 @@ int HostCal::run(int argc, char **argv) m_p25TduTest = false; m_nxdnEnabled = false; - LogMessage(LOG_CAL, " - %s", m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_modeStr.c_str()); writeConfig(); } else { @@ -610,7 +610,7 @@ int HostCal::run(int argc, char **argv) m_p25Enabled = false; m_nxdnEnabled = false; - LogMessage(LOG_CAL, " - %s", m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_modeStr.c_str()); writeConfig(); } break; @@ -633,7 +633,7 @@ int HostCal::run(int argc, char **argv) m_p25TduTest = false; m_nxdnEnabled = false; - LogMessage(LOG_CAL, " - %s", m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_modeStr.c_str()); writeConfig(); } break; @@ -651,7 +651,7 @@ int HostCal::run(int argc, char **argv) m_p25TduTest = false; m_nxdnEnabled = true; - LogMessage(LOG_CAL, " - %s", m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_modeStr.c_str()); writeConfig(); } else { @@ -671,7 +671,7 @@ int HostCal::run(int argc, char **argv) m_p25Rx1K = false; m_p25TduTest = false; - LogMessage(LOG_CAL, " - %s", m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_modeStr.c_str()); writeConfig(); } break; @@ -757,65 +757,65 @@ int HostCal::run(int argc, char **argv) void HostCal::displayHelp() { - LogMessage(LOG_CAL, "General Commands:"); - LogMessage(LOG_CAL, " Toggle transmit"); - LogMessage(LOG_CAL, " ` Display current settings and operation mode"); - LogMessage(LOG_CAL, " ! Restart into Bootloader Mode (wipes configuration area!)"); - LogMessage(LOG_CAL, " V Display version of host"); - LogMessage(LOG_CAL, " v Display version of firmware"); - LogMessage(LOG_CAL, " H/h Display help"); - LogMessage(LOG_CAL, " S/s Save calibration settings to configuration file"); + LogInfoEx(LOG_CAL, "General Commands:"); + LogInfoEx(LOG_CAL, " Toggle transmit"); + LogInfoEx(LOG_CAL, " ` Display current settings and operation mode"); + LogInfoEx(LOG_CAL, " ! Restart into Bootloader Mode (wipes configuration area!)"); + LogInfoEx(LOG_CAL, " V Display version of host"); + LogInfoEx(LOG_CAL, " v Display version of firmware"); + LogInfoEx(LOG_CAL, " H/h Display help"); + LogInfoEx(LOG_CAL, " S/s Save calibration settings to configuration file"); if (!m_modem->m_flashDisabled) { - LogMessage(LOG_CAL, " U Read modem configuration area and reset local configuration"); + LogInfoEx(LOG_CAL, " U Read modem configuration area and reset local configuration"); } - LogMessage(LOG_CAL, " ) Swap Duplex Flag (depending on mode this will enable/disable duplex)"); - LogMessage(LOG_CAL, " Q/q Quit"); - LogMessage(LOG_CAL, "Level Adjustment Commands:"); + LogInfoEx(LOG_CAL, " ) Swap Duplex Flag (depending on mode this will enable/disable duplex)"); + LogInfoEx(LOG_CAL, " Q/q Quit"); + LogInfoEx(LOG_CAL, "Level Adjustment Commands:"); if (!m_isHotspot) { - LogMessage(LOG_CAL, " I Toggle transmit inversion"); - LogMessage(LOG_CAL, " i Toggle receive inversion"); - LogMessage(LOG_CAL, " p Toggle PTT inversion"); - LogMessage(LOG_CAL, " d Toggle DC blocker"); + LogInfoEx(LOG_CAL, " I Toggle transmit inversion"); + LogInfoEx(LOG_CAL, " i Toggle receive inversion"); + LogInfoEx(LOG_CAL, " p Toggle PTT inversion"); + LogInfoEx(LOG_CAL, " d Toggle DC blocker"); } if (!m_isHotspot) { - LogMessage(LOG_CAL, " R/r Increase/Decrease receive level"); - LogMessage(LOG_CAL, " T/t Increase/Decrease transmit level"); + LogInfoEx(LOG_CAL, " R/r Increase/Decrease receive level"); + LogInfoEx(LOG_CAL, " T/t Increase/Decrease transmit level"); } else { - LogMessage(LOG_CAL, " T/t Increase/Decrease deviation level"); + LogInfoEx(LOG_CAL, " T/t Increase/Decrease deviation level"); } if (!m_isHotspot) { - LogMessage(LOG_CAL, " C/c Increase/Decrease RX DC offset level"); - LogMessage(LOG_CAL, " O/o Increase/Decrease TX DC offset level"); + LogInfoEx(LOG_CAL, " C/c Increase/Decrease RX DC offset level"); + LogInfoEx(LOG_CAL, " O/o Increase/Decrease TX DC offset level"); } - LogMessage(LOG_CAL, " X Set FDMA Preambles"); - LogMessage(LOG_CAL, " W Set DMR Rx Delay"); + LogInfoEx(LOG_CAL, " X Set FDMA Preambles"); + LogInfoEx(LOG_CAL, " W Set DMR Rx Delay"); if (!m_isHotspot) { - LogMessage(LOG_CAL, " w Set P25 Correlation Count"); + LogInfoEx(LOG_CAL, " w Set P25 Correlation Count"); } if (m_isHotspot) { - LogMessage(LOG_CAL, " F Set Rx Frequency Adjustment"); - LogMessage(LOG_CAL, " f Set Tx Frequency Adjustment"); + LogInfoEx(LOG_CAL, " F Set Rx Frequency Adjustment"); + LogInfoEx(LOG_CAL, " f Set Tx Frequency Adjustment"); } if (!m_isHotspot) { - LogMessage(LOG_CAL, " 1/2 Increase/Decrease receive coarse level"); - LogMessage(LOG_CAL, " 3/4 Increase/Decrease receive fine level"); - LogMessage(LOG_CAL, " 5/6 Increase/Decrease transmit coarse level"); - LogMessage(LOG_CAL, " 9/0 Increase/Decrease RSSI coarse level"); + LogInfoEx(LOG_CAL, " 1/2 Increase/Decrease receive coarse level"); + LogInfoEx(LOG_CAL, " 3/4 Increase/Decrease receive fine level"); + LogInfoEx(LOG_CAL, " 5/6 Increase/Decrease transmit coarse level"); + LogInfoEx(LOG_CAL, " 9/0 Increase/Decrease RSSI coarse level"); } - LogMessage(LOG_CAL, "Mode Commands:"); - LogMessage(LOG_CAL, " Z %s", DMR_CAL_STR); - LogMessage(LOG_CAL, " z %s", P25_CAL_STR); - LogMessage(LOG_CAL, " L %s", DMR_LF_CAL_STR); - LogMessage(LOG_CAL, " M %s", DMR_CAL_1K_STR); - LogMessage(LOG_CAL, " m %s", DMR_DMO_CAL_1K_STR); - LogMessage(LOG_CAL, " P %s", P25_CAL_1K_STR); - LogMessage(LOG_CAL, " N %s", NXDN_CAL_1K_STR); - LogMessage(LOG_CAL, " B %s", DMR_FEC_STR); - LogMessage(LOG_CAL, " J %s", DMR_FEC_1K_STR); - LogMessage(LOG_CAL, " b %s", P25_FEC_STR); - LogMessage(LOG_CAL, " j %s", P25_FEC_1K_STR); - LogMessage(LOG_CAL, " n %s", NXDN_FEC_STR); - LogMessage(LOG_CAL, " x %s", RSSI_CAL_STR); + LogInfoEx(LOG_CAL, "Mode Commands:"); + LogInfoEx(LOG_CAL, " Z %s", DMR_CAL_STR); + LogInfoEx(LOG_CAL, " z %s", P25_CAL_STR); + LogInfoEx(LOG_CAL, " L %s", DMR_LF_CAL_STR); + LogInfoEx(LOG_CAL, " M %s", DMR_CAL_1K_STR); + LogInfoEx(LOG_CAL, " m %s", DMR_DMO_CAL_1K_STR); + LogInfoEx(LOG_CAL, " P %s", P25_CAL_1K_STR); + LogInfoEx(LOG_CAL, " N %s", NXDN_CAL_1K_STR); + LogInfoEx(LOG_CAL, " B %s", DMR_FEC_STR); + LogInfoEx(LOG_CAL, " J %s", DMR_FEC_1K_STR); + LogInfoEx(LOG_CAL, " b %s", P25_FEC_STR); + LogInfoEx(LOG_CAL, " j %s", P25_FEC_1K_STR); + LogInfoEx(LOG_CAL, " n %s", NXDN_FEC_STR); + LogInfoEx(LOG_CAL, " x %s", RSSI_CAL_STR); } /* Helper to change the Rx level. */ @@ -829,7 +829,7 @@ bool HostCal::setTXLevel(int incr) if (m_modem->m_cwIdTXLevel > 100.0F) m_modem->m_cwIdTXLevel = 100.0F; - LogMessage(LOG_CAL, " - TX Level: %.1f%%", m_modem->m_cwIdTXLevel); + LogInfoEx(LOG_CAL, " - TX Level: %.1f%%", m_modem->m_cwIdTXLevel); return writeConfig(); } @@ -840,7 +840,7 @@ bool HostCal::setTXLevel(int incr) if (m_modem->m_cwIdTXLevel < 0.0F) m_modem->m_cwIdTXLevel = 0.0F; - LogMessage(LOG_CAL, " - TX Level: %.1f%%", m_modem->m_cwIdTXLevel); + LogInfoEx(LOG_CAL, " - TX Level: %.1f%%", m_modem->m_cwIdTXLevel); return writeConfig(); } @@ -858,7 +858,7 @@ bool HostCal::setRXLevel(int incr) if (m_modem->m_rxLevel > 100.0F) m_modem->m_rxLevel = 100.0F; - LogMessage(LOG_CAL, " - RX Level: %.1f%%", m_modem->m_rxLevel); + LogInfoEx(LOG_CAL, " - RX Level: %.1f%%", m_modem->m_rxLevel); return writeConfig(); } @@ -869,7 +869,7 @@ bool HostCal::setRXLevel(int incr) if (m_modem->m_rxLevel < 0.0F) m_modem->m_rxLevel = 0.0F; - LogMessage(LOG_CAL, " - RX Level: %.1f%%", m_modem->m_rxLevel); + LogInfoEx(LOG_CAL, " - RX Level: %.1f%%", m_modem->m_rxLevel); return writeConfig(); } @@ -882,13 +882,13 @@ bool HostCal::setTXDCOffset(int incr) { if (incr > 0 && m_modem->m_txDCOffset < 127) { m_modem->m_txDCOffset++; - LogMessage(LOG_CAL, " - TX DC Offset: %d", m_modem->m_txDCOffset); + LogInfoEx(LOG_CAL, " - TX DC Offset: %d", m_modem->m_txDCOffset); return writeConfig(); } if (incr < 0 && m_modem->m_txDCOffset > -127) { m_modem->m_txDCOffset--; - LogMessage(LOG_CAL, " - TX DC Offset: %d", m_modem->m_txDCOffset); + LogInfoEx(LOG_CAL, " - TX DC Offset: %d", m_modem->m_txDCOffset); return writeConfig(); } @@ -901,13 +901,13 @@ bool HostCal::setRXDCOffset(int incr) { if (incr > 0 && m_modem->m_rxDCOffset < 127) { m_modem->m_rxDCOffset++; - LogMessage(LOG_CAL, " - RX DC Offset: %d", m_modem->m_rxDCOffset); + LogInfoEx(LOG_CAL, " - RX DC Offset: %d", m_modem->m_rxDCOffset); return writeConfig(); } if (incr < 0 && m_modem->m_rxDCOffset > -127) { m_modem->m_rxDCOffset--; - LogMessage(LOG_CAL, " - RX DC Offset: %d", m_modem->m_rxDCOffset); + LogInfoEx(LOG_CAL, " - RX DC Offset: %d", m_modem->m_rxDCOffset); return writeConfig(); } @@ -923,7 +923,7 @@ bool HostCal::setRXCoarseLevel(int incr) m_modem->m_rxCoarsePot += 1U; } - LogMessage(LOG_CAL, " - RX Coarse Level: %u", m_modem->m_rxCoarsePot); + LogInfoEx(LOG_CAL, " - RX Coarse Level: %u", m_modem->m_rxCoarsePot); return writeConfig(); } @@ -932,7 +932,7 @@ bool HostCal::setRXCoarseLevel(int incr) m_modem->m_rxCoarsePot -= 1U; } - LogMessage(LOG_CAL, " - RX Coarse Level: %u", m_modem->m_rxCoarsePot); + LogInfoEx(LOG_CAL, " - RX Coarse Level: %u", m_modem->m_rxCoarsePot); return writeConfig(); } @@ -948,7 +948,7 @@ bool HostCal::setRXFineLevel(int incr) m_modem->m_rxFinePot += 1U; } - LogMessage(LOG_CAL, " - RX Fine Level: %u", m_modem->m_rxFinePot); + LogInfoEx(LOG_CAL, " - RX Fine Level: %u", m_modem->m_rxFinePot); return writeConfig(); } @@ -957,7 +957,7 @@ bool HostCal::setRXFineLevel(int incr) m_modem->m_rxFinePot -= 1U; } - LogMessage(LOG_CAL, " - RX Fine Level: %u", m_modem->m_rxFinePot); + LogInfoEx(LOG_CAL, " - RX Fine Level: %u", m_modem->m_rxFinePot); return writeConfig(); } @@ -973,7 +973,7 @@ bool HostCal::setTXCoarseLevel(int incr) m_modem->m_txCoarsePot += 1U; } - LogMessage(LOG_CAL, " - TX Coarse Level: %u", m_modem->m_txCoarsePot); + LogInfoEx(LOG_CAL, " - TX Coarse Level: %u", m_modem->m_txCoarsePot); return writeConfig(); } @@ -982,7 +982,7 @@ bool HostCal::setTXCoarseLevel(int incr) m_modem->m_txCoarsePot -= 1U; } - LogMessage(LOG_CAL, " - TX Coarse Level: %u", m_modem->m_txCoarsePot); + LogInfoEx(LOG_CAL, " - TX Coarse Level: %u", m_modem->m_txCoarsePot); return writeConfig(); } @@ -998,7 +998,7 @@ bool HostCal::setRSSICoarseLevel(int incr) m_modem->m_rssiCoarsePot += 1U; } - LogMessage(LOG_CAL, " - RSSI Coarse Level: %u", m_modem->m_rssiCoarsePot); + LogInfoEx(LOG_CAL, " - RSSI Coarse Level: %u", m_modem->m_rssiCoarsePot); return writeConfig(); } @@ -1007,7 +1007,7 @@ bool HostCal::setRSSICoarseLevel(int incr) m_modem->m_rssiCoarsePot -= 1U; } - LogMessage(LOG_CAL, " - RSSI Coarse Level: %u", m_modem->m_rssiCoarsePot); + LogInfoEx(LOG_CAL, " - RSSI Coarse Level: %u", m_modem->m_rssiCoarsePot); return writeConfig(); } @@ -1027,61 +1027,61 @@ void HostCal::printStatus() std::string modemPort = uartConfig["port"].as(); uint32_t portSpeed = uartConfig["speed"].as(115200U); - LogMessage(LOG_CAL, " - Operating Mode: %s, Port Type: %s, Modem Port: %s, Port Speed: %u, Proto Ver: %u", m_modeStr.c_str(), type.c_str(), modemPort.c_str(), portSpeed, m_modem->getVersion()); + LogInfoEx(LOG_CAL, " - Operating Mode: %s, Port Type: %s, Modem Port: %s, Port Speed: %u, Proto Ver: %u", m_modeStr.c_str(), type.c_str(), modemPort.c_str(), portSpeed, m_modem->getVersion()); } { if (!m_isHotspot) { - LogMessage(LOG_CAL, " - PTT Invert: %s, RX Invert: %s, TX Invert: %s, DC Blocker: %s", + LogInfoEx(LOG_CAL, " - PTT Invert: %s, RX Invert: %s, TX Invert: %s, DC Blocker: %s", m_modem->m_pttInvert ? "yes" : "no", m_modem->m_rxInvert ? "yes" : "no", m_modem->m_txInvert ? "yes" : "no", m_modem->m_dcBlocker ? "yes" : "no"); } - LogMessage(LOG_CAL, " - RX Level: %.1f%%, TX Level: %.1f%%, TX DC Offset: %d, RX DC Offset: %d", + LogInfoEx(LOG_CAL, " - RX Level: %.1f%%, TX Level: %.1f%%, TX DC Offset: %d, RX DC Offset: %d", m_modem->m_rxLevel, m_modem->m_cwIdTXLevel, m_modem->m_txDCOffset, m_modem->m_rxDCOffset); if (!m_isHotspot) { - LogMessage(LOG_CAL, " - RX Coarse Level: %u, RX Fine Level: %u, TX Coarse Level: %u, RSSI Coarse Level: %u", + LogInfoEx(LOG_CAL, " - RX Coarse Level: %u, RX Fine Level: %u, TX Coarse Level: %u, RSSI Coarse Level: %u", m_modem->m_rxCoarsePot, m_modem->m_rxFinePot, m_modem->m_txCoarsePot, m_modem->m_rssiCoarsePot); - LogMessage(LOG_CAL, " - DMR Symbol +/- 3 Level Adj.: %d, DMR Symbol +/- 1 Level Adj.: %d, P25 Symbol +/- 3 Level Adj.: %d, P25 Symbol +/- 1 Level Adj.: %d", + LogInfoEx(LOG_CAL, " - DMR Symbol +/- 3 Level Adj.: %d, DMR Symbol +/- 1 Level Adj.: %d, P25 Symbol +/- 3 Level Adj.: %d, P25 Symbol +/- 1 Level Adj.: %d", m_modem->m_dmrSymLevel3Adj, m_modem->m_dmrSymLevel1Adj, m_modem->m_p25SymLevel3Adj, m_modem->m_p25SymLevel1Adj); // are we on a protocol version 3 firmware? if (m_modem->getVersion() >= 3U) { - LogMessage(LOG_CAL, " - NXDN Symbol +/- 3 Level Adj.: %d, NXDN Symbol +/- 1 Level Adj.: %d", + LogInfoEx(LOG_CAL, " - NXDN Symbol +/- 3 Level Adj.: %d, NXDN Symbol +/- 1 Level Adj.: %d", m_modem->m_nxdnSymLevel3Adj, m_modem->m_nxdnSymLevel1Adj); } } if (m_isHotspot) { - LogMessage(LOG_CAL, " - DMR Disc. BW: %d, P25 Disc. BW: %d, DMR Post Demod BW: %d, P25 Post Demod BW: %d", + LogInfoEx(LOG_CAL, " - DMR Disc. BW: %d, P25 Disc. BW: %d, DMR Post Demod BW: %d, P25 Post Demod BW: %d", m_modem->m_dmrDiscBWAdj, m_modem->m_p25DiscBWAdj, m_modem->m_dmrPostBWAdj, m_modem->m_p25PostBWAdj); // are we on a protocol version 3 firmware? if (m_modem->getVersion() >= 3U) { - LogMessage(LOG_CAL, " - NXDN Disc. BW: %d, NXDN Post Demod BW: %d", + LogInfoEx(LOG_CAL, " - NXDN Disc. BW: %d, NXDN Post Demod BW: %d", m_modem->m_nxdnDiscBWAdj, m_modem->m_nxdnPostBWAdj); - LogMessage(LOG_CAL, " - AFC Enabled: %u, AFC KI: %u, AFC KP: %u, AFC Range: %u", + LogInfoEx(LOG_CAL, " - AFC Enabled: %u, AFC KI: %u, AFC KP: %u, AFC Range: %u", m_modem->m_afcEnable, m_modem->m_afcKI, m_modem->m_afcKP, m_modem->m_afcRange); } switch (m_modem->m_adfGainMode) { case ADF_GAIN_AUTO_LIN: - LogMessage(LOG_CAL, " - ADF7021 Gain Mode: Auto High Linearity"); + LogInfoEx(LOG_CAL, " - ADF7021 Gain Mode: Auto High Linearity"); break; case ADF_GAIN_LOW: - LogMessage(LOG_CAL, " - ADF7021 Gain Mode: Low"); + LogInfoEx(LOG_CAL, " - ADF7021 Gain Mode: Low"); break; case ADF_GAIN_HIGH: - LogMessage(LOG_CAL, " - ADF7021 Gain Mode: High"); + LogInfoEx(LOG_CAL, " - ADF7021 Gain Mode: High"); break; case ADF_GAIN_AUTO: default: - LogMessage(LOG_CAL, " - ADF7021 Gain Mode: Auto"); + LogInfoEx(LOG_CAL, " - ADF7021 Gain Mode: Auto"); break; } } - LogMessage(LOG_CAL, " - FDMA Preambles: %u (%.1fms), DMR Rx Delay: %u (%.1fms), P25 Corr. Count: %u (%.1fms)", m_modem->m_fdmaPreamble, float(m_modem->m_fdmaPreamble) * 0.2222F, m_modem->m_dmrRxDelay, float(m_modem->m_dmrRxDelay) * 0.0416666F, + LogInfoEx(LOG_CAL, " - FDMA Preambles: %u (%.1fms), DMR Rx Delay: %u (%.1fms), P25 Corr. Count: %u (%.1fms)", m_modem->m_fdmaPreamble, float(m_modem->m_fdmaPreamble) * 0.2222F, m_modem->m_dmrRxDelay, float(m_modem->m_dmrRxDelay) * 0.0416666F, m_modem->m_p25CorrCount, float(m_modem->m_p25CorrCount) * 0.667F); - LogMessage(LOG_CAL, " - Rx Freq: %uHz, Tx Freq: %uHz, Rx Offset: %dHz, Tx Offset: %dHz", m_modem->m_rxFrequency, m_modem->m_txFrequency, m_modem->m_rxTuning, m_modem->m_txTuning); - LogMessage(LOG_CAL, " - Rx Effective Freq: %uHz, Tx Effective Freq: %uHz", m_rxAdjustedFreq, m_txAdjustedFreq); + LogInfoEx(LOG_CAL, " - Rx Freq: %uHz, Tx Freq: %uHz, Rx Offset: %dHz, Tx Offset: %dHz", m_modem->m_rxFrequency, m_modem->m_txFrequency, m_modem->m_rxTuning, m_modem->m_txTuning); + LogInfoEx(LOG_CAL, " - Rx Effective Freq: %uHz, Tx Effective Freq: %uHz", m_rxAdjustedFreq, m_txAdjustedFreq); } getStatus(); diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index 81c60922..9e30eb1c 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -220,7 +220,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa LogInfo(" TSCC Disable Grant Source ID Check: yes"); } if (m_supervisor) - LogMessage(LOG_DMR, "Host is configured to operate as a DMR TSCC, site controller mode."); + LogInfoEx(LOG_DMR, "Host is configured to operate as a DMR TSCC, site controller mode."); } if (disableNetworkGrant) { LogInfo(" Disable Network Grants: yes"); @@ -316,7 +316,7 @@ bool Control::processWakeup(const uint8_t* data) } if (m_verbose) { - LogMessage(LOG_RF, "DMR, CSBKO, BSDWNACT, srcId = %u", srcId); + LogInfoEx(LOG_RF, "DMR, CSBKO, BSDWNACT, srcId = %u", srcId); } return true; @@ -531,7 +531,7 @@ Slot* Control::getTSCCSlot() const void Control::tsccActivateSlot(uint32_t slotNo, uint32_t dstId, uint32_t srcId, bool group, bool voice) { if (m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, payload activation, srcId = %u, group = %u, dstId = %u", + LogInfoEx(LOG_DMR, "DMR Slot %u, payload activation, srcId = %u, group = %u, dstId = %u", slotNo, srcId, group, dstId); } @@ -561,7 +561,7 @@ void Control::tsccActivateSlot(uint32_t slotNo, uint32_t dstId, uint32_t srcId, void Control::tsccClearActivatedSlot(uint32_t slotNo) { if (m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, payload activation clear", slotNo); + LogInfoEx(LOG_DMR, "DMR Slot %u, payload activation clear", slotNo); } switch (slotNo) { diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index af5b9c38..e81c2ec0 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -243,7 +243,7 @@ bool Slot::processFrame(uint8_t *data, uint32_t len) // Convert the raw RSSI to dBm int rssi = m_rssiMapper->interpolate(raw); if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, raw RSSI = %u, reported RSSI = %d dBm", m_slotNo, raw, rssi); + LogInfoEx(LOG_RF, "DMR Slot %u, raw RSSI = %u, reported RSSI = %d dBm", m_slotNo, raw, rssi); } // RSSI is always reported as positive @@ -471,7 +471,7 @@ void Slot::processNetwork(const data::NetData& dmrData) } if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u", + LogInfoEx(LOG_NET, "DMR Slot %u, remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u", m_slotNo, dmrData.getSrcId(), dmrData.getDstId(), unitToUnit); } @@ -660,7 +660,7 @@ void Slot::clock() if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) { if (!m_rfTimeout) { - LogMessage(LOG_RF, "DMR Slot %u, user has timed out", m_slotNo); + LogInfoEx(LOG_RF, "DMR Slot %u, user has timed out", m_slotNo); m_rfTimeout = true; } } @@ -683,7 +683,7 @@ void Slot::clock() if (m_rfTGHang.hasExpired()) { m_rfTGHang.stop(); if (m_verbose) { - LogMessage(LOG_RF, "Slot %u, talkgroup hang has expired, lastDstId = %u", m_slotNo, m_rfLastDstId); + LogInfoEx(LOG_RF, "Slot %u, talkgroup hang has expired, lastDstId = %u", m_slotNo, m_rfLastDstId); } m_rfLastDstId = 0U; m_rfLastSrcId = 0U; @@ -697,7 +697,7 @@ void Slot::clock() if (m_netTimeoutTimer.isRunning() && m_netTimeoutTimer.hasExpired()) { if (!m_netTimeout) { - LogMessage(LOG_NET, "DMR Slot %u, user has timed out", m_slotNo); + LogInfoEx(LOG_NET, "DMR Slot %u, user has timed out", m_slotNo); m_netTimeout = true; } } @@ -709,7 +709,7 @@ void Slot::clock() if (m_netTGHang.hasExpired()) { m_netTGHang.stop(); if (m_verbose) { - LogMessage(LOG_NET, "Slot %u, talkgroup hang has expired, lastDstId = %u", m_slotNo, m_netLastDstId); + LogInfoEx(LOG_NET, "Slot %u, talkgroup hang has expired, lastDstId = %u", m_slotNo, m_netLastDstId); } m_netLastDstId = 0U; m_netLastSrcId = 0U; @@ -829,9 +829,9 @@ void Slot::permittedTG(uint32_t dstId) if (m_verbose) { if (dstId == 0U) - LogMessage(LOG_DMR, "DMR Slot %u, non-authoritative TG unpermit", m_slotNo); + LogInfoEx(LOG_DMR, "DMR Slot %u, non-authoritative TG unpermit", m_slotNo); else - LogMessage(LOG_DMR, "DMR Slot %u, non-authoritative TG permit, dstId = %u", m_slotNo, dstId); + LogInfoEx(LOG_DMR, "DMR Slot %u, non-authoritative TG permit, dstId = %u", m_slotNo, dstId); } m_permittedDstId = dstId; @@ -846,7 +846,7 @@ void Slot::grantTG(uint32_t srcId, uint32_t dstId, bool grp) } if (m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, network TG grant demand, srcId = %u, dstId = %u", m_slotNo, srcId, dstId); + LogInfoEx(LOG_DMR, "DMR Slot %u, network TG grant demand, srcId = %u, dstId = %u", m_slotNo, srcId, dstId); } m_control->writeRF_CSBK_Grant(srcId, dstId, 4U, grp); @@ -861,7 +861,7 @@ void Slot::releaseGrantTG(uint32_t dstId) } if (m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, VC request, release TG grant, dstId = %u", m_slotNo, dstId); + LogInfoEx(LOG_DMR, "DMR Slot %u, VC request, release TG grant, dstId = %u", m_slotNo, dstId); } if (m_affiliations->isGranted(dstId)) { @@ -870,7 +870,7 @@ void Slot::releaseGrantTG(uint32_t dstId) ::lookups::VoiceChData voiceCh = m_affiliations->rfCh()->getRFChData(chNo); if (m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, VC %s:%u, TG grant released, srcId = %u, dstId = %u, chNo = %u-%u", m_slotNo, voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); + LogInfoEx(LOG_DMR, "DMR Slot %u, VC %s:%u, TG grant released, srcId = %u, dstId = %u, chNo = %u-%u", m_slotNo, voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); } m_affiliations->releaseGrant(dstId, false); @@ -891,7 +891,7 @@ void Slot::touchGrantTG(uint32_t dstId) ::lookups::VoiceChData voiceCh = m_affiliations->rfCh()->getRFChData(chNo); if (m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, VC %s:%u, call in progress, srcId = %u, dstId = %u, chNo = %u-%u", m_slotNo, voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); + LogInfoEx(LOG_DMR, "DMR Slot %u, VC %s:%u, call in progress, srcId = %u, dstId = %u, chNo = %u-%u", m_slotNo, voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); } m_affiliations->touchGrant(dstId); @@ -1202,7 +1202,7 @@ void Slot::processFrameLoss() m_slotNo, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_frameLossCnt); } - LogMessage(LOG_RF, "DMR Slot %u, total frames: %d, total bits: %d, errors: %d, BER: %.4f%%", + LogInfoEx(LOG_RF, "DMR Slot %u, total frames: %d, total bits: %d, errors: %d, BER: %.4f%%", m_slotNo, m_rfFrames, m_rfBits, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); // release trunked grant (if necessary) @@ -1255,7 +1255,7 @@ void Slot::notifyCC_ReleaseGrant(uint32_t dstId) } if (m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, CC %s:%u, notifying CC of call termination, dstId = %u", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); + LogInfoEx(LOG_DMR, "DMR Slot %u, CC %s:%u, notifying CC of call termination, dstId = %u", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); } // callback REST API to release the granted TG on the specified control channel @@ -1279,7 +1279,7 @@ void Slot::notifyCC_ReleaseGrant(uint32_t dstId) } } else - ::LogMessage(LOG_DMR, "DMR Slot %u, CC %s:%u, released grant, dstId = %u", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); + ::LogInfoEx(LOG_DMR, "DMR Slot %u, CC %s:%u, released grant, dstId = %u", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); }, m_controlChData.address(), m_controlChData.port()); m_rfLastDstId = 0U; @@ -1326,7 +1326,7 @@ void Slot::notifyCC_TouchGrant(uint32_t dstId) } } else - ::LogMessage(LOG_DMR, "DMR Slot %u, CC %s:%u, touched grant, dstId = %u", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); + ::LogInfoEx(LOG_DMR, "DMR Slot %u, CC %s:%u, touched grant, dstId = %u", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); }, m_controlChData.address(), m_controlChData.port()); } diff --git a/src/host/dmr/lookups/DMRAffiliationLookup.cpp b/src/host/dmr/lookups/DMRAffiliationLookup.cpp index 4204ccf6..f0010b20 100644 --- a/src/host/dmr/lookups/DMRAffiliationLookup.cpp +++ b/src/host/dmr/lookups/DMRAffiliationLookup.cpp @@ -79,7 +79,7 @@ bool DMRAffiliationLookup::grantChSlot(uint32_t dstId, uint32_t srcId, uint8_t s m_grantTimers[dstId].start(); if (m_verbose) { - LogMessage(LOG_HOST, "%s, granting channel, chNo = %u, slot = %u, dstId = %u, group = %u", + LogInfoEx(LOG_HOST, "%s, granting channel, chNo = %u, slot = %u, dstId = %u, group = %u", m_name.c_str(), chNo, slot, dstId, grp); } @@ -121,7 +121,7 @@ bool DMRAffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) uint8_t slot = std::get<1>(slotData); if (m_verbose) { - LogMessage(LOG_HOST, "%s, releasing channel grant, chNo = %u, slot = %u, dstId = %u", + LogInfoEx(LOG_HOST, "%s, releasing channel grant, chNo = %u, slot = %u, dstId = %u", m_name.c_str(), chNo, slot, dstId); } diff --git a/src/host/dmr/packet/ControlSignaling.cpp b/src/host/dmr/packet/ControlSignaling.cpp index e458c1ed..1d3cb5dc 100644 --- a/src/host/dmr/packet/ControlSignaling.cpp +++ b/src/host/dmr/packet/ControlSignaling.cpp @@ -76,25 +76,25 @@ using namespace dmr::packet; // Macro helper to verbose log a generic CSBK. #define VERBOSE_LOG_CSBK(_PCKT_STR, _SRCID, _DSTID) \ if (m_verbose) { \ - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, dstId = %u", m_slot->m_slotNo, _PCKT_STR.c_str(), _SRCID, _DSTID); \ + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, dstId = %u", m_slot->m_slotNo, _PCKT_STR.c_str(), _SRCID, _DSTID); \ } // Macro helper to verbose log a generic CSBK. #define VERBOSE_LOG_CSBK_DST(_PCKT_STR, _DSTID) \ if (m_verbose) { \ - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, dstId = %u", m_slot->m_slotNo, _PCKT_STR.c_str(), _DSTID); \ + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, dstId = %u", m_slot->m_slotNo, _PCKT_STR.c_str(), _DSTID); \ } // Macro helper to verbose log a generic network CSBK. #define VERBOSE_LOG_CSBK_NET(_PCKT_STR, _SRCID, _DSTID) \ if (m_verbose) { \ - LogMessage(LOG_NET, "DMR Slot %u, CSBK, %s, srcId = %u, dstId = %u", m_slot->m_slotNo, _PCKT_STR.c_str(), _SRCID, _DSTID); \ + LogInfoEx(LOG_NET, "DMR Slot %u, CSBK, %s, srcId = %u, dstId = %u", m_slot->m_slotNo, _PCKT_STR.c_str(), _SRCID, _DSTID); \ } // Macro helper to verbose log a generic network CSBK. #define DEBUG_LOG_CSBK(_PCKT_STR) \ if (m_debug) { \ - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s", m_slot->m_slotNo, _PCKT_STR.c_str()); \ + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s", m_slot->m_slotNo, _PCKT_STR.c_str()); \ } // --------------------------------------------------------------------------- @@ -180,7 +180,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) { if (csbk->getFID() == FID_MOT) { if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, dstId = %u", m_slot->m_slotNo, csbk->toString().c_str(), srcId, dstId); } @@ -190,7 +190,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) CSBK_RAND* isp = static_cast(csbk.get()); if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), serviceKind = $%02X, serviceOptions = $%02X, serviceExtra = $%02X, srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), serviceKind = $%02X, serviceOptions = $%02X, serviceExtra = $%02X, srcId = %u, dstId = %u", m_slot->m_slotNo, isp->getServiceKind(), isp->getServiceOptions(), isp->getServiceExtra(), isp->getSrcId(), isp->getDstId()); } @@ -293,7 +293,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) { CSBK_EXT_FNCT* isp = static_cast(csbk.get()); if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, op = $%02X, arg = %u, tgt = %u", + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, op = $%02X, arg = %u, tgt = %u", m_slot->m_slotNo, csbk->toString().c_str(), isp->getExtendedFunction(), dstId, srcId); } @@ -332,7 +332,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) { CSBK_MAINT* isp = static_cast(csbk.get()); if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, kind = $%02X, srcId = %u", + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, kind = $%02X, srcId = %u", m_slot->m_slotNo, csbk->toString().c_str(), isp->getMaintKind(), srcId); } } @@ -340,7 +340,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) case CSBKO::PRECCSBK: { if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, PRECCSBK (%s Preamble CSBK), toFollow = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, PRECCSBK (%s Preamble CSBK), toFollow = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, csbk->getDataContent() ? "Data" : "CSBK", csbk->getCBF(), srcId, dstId); } } @@ -416,7 +416,7 @@ void ControlSignaling::processNetwork(const data::NetData& dmrData) } if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, CSBK, %s, sysId = $%03X, chNo = %u", m_slot->m_slotNo, csbk->toString().c_str(), + LogInfoEx(LOG_NET, "DMR Slot %u, CSBK, %s, sysId = $%03X, chNo = %u", m_slot->m_slotNo, csbk->toString().c_str(), osp->getSystemId(), osp->getLogicalCh1()); } @@ -465,7 +465,7 @@ void ControlSignaling::processNetwork(const data::NetData& dmrData) { if (csbk->getFID() == FID_MOT) { if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, CSBK, %s, srcId = %u, dstId = %u", + LogInfoEx(LOG_NET, "DMR Slot %u, CSBK, %s, srcId = %u, dstId = %u", m_slot->m_slotNo, csbk->toString().c_str(), srcId, dstId); } @@ -473,7 +473,7 @@ void ControlSignaling::processNetwork(const data::NetData& dmrData) } else { CSBK_RAND* isp = static_cast(csbk.get()); if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, CSBK, RAND (Random Access), serviceKind = $%02X, serviceOptions = $%02X, serviceExtra = $%02X, srcId = %u, dstId = %u", + LogInfoEx(LOG_NET, "DMR Slot %u, CSBK, RAND (Random Access), serviceKind = $%02X, serviceOptions = $%02X, serviceExtra = $%02X, srcId = %u, dstId = %u", m_slot->m_slotNo, isp->getServiceKind(), isp->getServiceOptions(), isp->getServiceExtra(), isp->getSrcId(), isp->getDstId()); } @@ -526,7 +526,7 @@ void ControlSignaling::processNetwork(const data::NetData& dmrData) { CSBK_EXT_FNCT* isp = static_cast(csbk.get()); if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, CSBK, %s, op = $%02X, arg = %u, tgt = %u", + LogInfoEx(LOG_NET, "DMR Slot %u, CSBK, %s, op = $%02X, arg = %u, tgt = %u", m_slot->m_slotNo, csbk->toString().c_str(), isp->getExtendedFunction(), dstId, srcId); } @@ -564,7 +564,7 @@ void ControlSignaling::processNetwork(const data::NetData& dmrData) case CSBKO::PRECCSBK: { if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, CSBK, PRECCSBK (%s Preamble CSBK), toFollow = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_NET, "DMR Slot %u, CSBK, PRECCSBK (%s Preamble CSBK), toFollow = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, csbk->getDataContent() ? "Data" : "CSBK", csbk->getCBF(), srcId, dstId); } } @@ -620,7 +620,7 @@ void ControlSignaling::writeAdjSSNetwork() csbk->setRequireReg(m_slot->m_siteData.requireReg()); if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, CSBK, %s, network announce, sysId = $%03X, chNo = %u", m_slot->m_slotNo, csbk->toString().c_str(), + LogInfoEx(LOG_NET, "DMR Slot %u, CSBK, %s, network announce, sysId = $%03X, chNo = %u", m_slot->m_slotNo, csbk->toString().c_str(), m_slot->m_siteData.systemIdentity(), m_slot->m_channelNo); } @@ -639,7 +639,7 @@ void ControlSignaling::writeRF_Ext_Func(uint32_t func, uint32_t arg, uint32_t ds csbk->setDstId(dstId); if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, op = $%02X, arg = %u, tgt = %u", + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, op = $%02X, arg = %u, tgt = %u", m_slot->m_slotNo, csbk->toString().c_str(), func, arg, dstId); } @@ -777,7 +777,7 @@ void ControlSignaling::writeRF_CSBK_ACK_RSP(uint32_t dstId, uint8_t reason, uint csbk->setDstId(dstId); if (m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, CSBK, %s, reason = $%02X (%s), srcId = %u, dstId = %u", + LogInfoEx(LOG_DMR, "DMR Slot %u, CSBK, %s, reason = $%02X (%s), srcId = %u, dstId = %u", m_slot->m_slotNo, csbk->toString().c_str(), reason, DMRUtils::rsnToString(reason).c_str(), csbk->getSrcId(), csbk->getDstId()); } @@ -796,7 +796,7 @@ void ControlSignaling::writeRF_CSBK_NACK_RSP(uint32_t dstId, uint8_t reason, uin csbk->setDstId(dstId); if (m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, CSBK, %s, reason = $%02X (%s), srcId = %u, dstId = %u", + LogInfoEx(LOG_DMR, "DMR Slot %u, CSBK, %s, reason = $%02X (%s), srcId = %u, dstId = %u", m_slot->m_slotNo, csbk->toString().c_str(), reason, DMRUtils::rsnToString(reason).c_str(), csbk->getSrcId(), csbk->getDstId()); } @@ -1010,7 +1010,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ csbk->setSlotNo(slot); if (m_verbose) { - LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", + LogInfoEx((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } @@ -1103,7 +1103,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ csbk->setSlotNo(slot); if (m_verbose) { - LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", + LogInfoEx((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } @@ -1257,7 +1257,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u csbk->setSlotNo(slot); if (m_verbose) { - LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", + LogInfoEx((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } @@ -1305,7 +1305,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u csbk->setSlotNo(slot); if (m_verbose) { - LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", + LogInfoEx((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } @@ -1360,7 +1360,7 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt csbk->setReason(ReasonCode::TS_DENY_RSN_REG_DENIED); if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, SU power saving unsupported, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, SU power saving unsupported, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); } csbk->setSrcId(WUID_REGI); @@ -1376,7 +1376,7 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt if (!dereg) { if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); } // remove dynamic unit registration table entry @@ -1400,7 +1400,7 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt if (csbk->getReason() == ReasonCode::TS_ACK_RSN_REG) { if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); } ::ActivityLog("DMR", true, "unit registration request from %u", srcId); @@ -1487,7 +1487,7 @@ void ControlSignaling::writeRF_CSBK_Payload_Activate(uint32_t dstId, uint32_t sr csbk->setDstId(dstId); if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, csbko = $%02X, chNo = %u, slot = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, csbko = $%02X, chNo = %u, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, csbk->toString().c_str(), csbk->getCSBKO(), csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } @@ -1513,7 +1513,7 @@ void ControlSignaling::writeRF_CSBK_Payload_Clear(uint32_t dstId, uint32_t srcId csbk->setDstId(dstId); if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, group = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, group = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, csbk->toString().c_str(), csbk->getGI(), csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } @@ -1549,7 +1549,7 @@ void ControlSignaling::writeRF_TSCC_Bcast_Ann_Wd(uint32_t channelNo, bool annWd, csbk->setRequireReg(requireReg); if (m_debug) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, channelNo = %u, annWd = %u", + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, channelNo = %u, annWd = %u", m_slot->m_slotNo, csbk->toString().c_str(), channelNo, annWd); } diff --git a/src/host/dmr/packet/Data.cpp b/src/host/dmr/packet/Data.cpp index 870338f3..c9d3a204 100644 --- a/src/host/dmr/packet/Data.cpp +++ b/src/host/dmr/packet/Data.cpp @@ -75,7 +75,7 @@ bool Data::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, DMR_DT_TERMINATOR_WITH_LC ", slot = %u, dstId = %u", m_slot->m_slotNo, m_slot->m_rfLC->getDstId()); + LogInfoEx(LOG_RF, DMR_DT_TERMINATOR_WITH_LC ", slot = %u, dstId = %u", m_slot->m_slotNo, m_slot->m_rfLC->getDstId()); } // release trunked grant (if necessary) @@ -97,7 +97,7 @@ bool Data::process(uint8_t* data, uint32_t len) m_slot->m_slotNo, float(m_slot->m_rfFrames) / 16.667F, float(m_slot->m_rfErrs * 100U) / float(m_slot->m_rfBits)); } - LogMessage(LOG_RF, "DMR Slot %u, total frames: %d, total bits: %d, errors: %d, BER: %.4f%%", + LogInfoEx(LOG_RF, "DMR Slot %u, total frames: %d, total bits: %d, errors: %d, BER: %.4f%%", m_slot->m_slotNo, m_slot->m_rfFrames, m_slot->m_rfBits, m_slot->m_rfErrs, float(m_slot->m_rfErrs * 100U) / float(m_slot->m_rfBits)); m_slot->m_dmr->tsccClearActivatedSlot(m_slot->m_slotNo); @@ -173,7 +173,7 @@ bool Data::process(uint8_t* data, uint32_t len) m_slot->m_rfLC = std::make_unique(gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId); if (m_verbose) { - LogMessage(LOG_RF, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_RF, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, m_rfDataHeader.getDPF(), m_rfDataHeader.getA(), m_rfDataHeader.getSAP(), m_rfDataHeader.getFullMesage(), m_rfDataHeader.getBlocksToFollow(), m_rfDataHeader.getPadLength(), m_rfDataHeader.getPacketLength(), m_rfDataHeader.getFSN(), dstId, srcId, gi); } @@ -181,26 +181,26 @@ bool Data::process(uint8_t* data, uint32_t len) // did we receive a response header? if (m_rfDataHeader.getDPF() == DPF::RESPONSE) { if (m_verbose) { - LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, slot = %u, sap = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, slot = %u, sap = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, m_rfDataHeader.getSAP(), m_rfDataHeader.getResponseClass(), m_rfDataHeader.getResponseType(), m_rfDataHeader.getResponseStatus(), dstId, srcId, gi); if (m_rfDataHeader.getResponseClass() == PDUResponseClass::ACK && m_rfDataHeader.getResponseType() == PDUResponseType::ACK) { - LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP ACK, slot = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP ACK, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); } else { if (m_rfDataHeader.getResponseClass() == PDUResponseClass::NACK) { switch (m_rfDataHeader.getResponseType()) { case PDUResponseType::NACK_ILLEGAL: - LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, illegal format, slot = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, illegal format, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); break; case PDUResponseType::NACK_PACKET_CRC: - LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet CRC error, slot = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet CRC error, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); break; case PDUResponseType::NACK_UNDELIVERABLE: - LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet undeliverable, slot = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet undeliverable, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); break; @@ -208,7 +208,7 @@ bool Data::process(uint8_t* data, uint32_t len) break; } } else if (m_rfDataHeader.getResponseClass() == PDUResponseClass::ACK_RETRY) { - LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP ACK RETRY, slot = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP ACK RETRY, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); } } @@ -245,7 +245,7 @@ bool Data::process(uint8_t* data, uint32_t len) } ::ActivityLog("DMR", true, "Slot %u RF data header from %u to %s%u, %u blocks", m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId, m_slot->m_rfFrames); - LogMessage(LOG_RF, "DMR Data Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); + LogInfoEx(LOG_RF, "DMR Data Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); ::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U); m_pduDataOffset = 0U; @@ -275,12 +275,12 @@ bool Data::process(uint8_t* data, uint32_t len) if (m_verbose) { if (dataType == DataType::RATE_34_DATA) { - LogMessage(LOG_RF, DMR_DT_RATE_34_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + LogInfoEx(LOG_RF, DMR_DT_RATE_34_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); } else if (dataType == DataType::RATE_12_DATA) { - LogMessage(LOG_RF, DMR_DT_RATE_12_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + LogInfoEx(LOG_RF, DMR_DT_RATE_12_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); } else { - LogMessage(LOG_RF, DMR_DT_RATE_1_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + LogInfoEx(LOG_RF, DMR_DT_RATE_1_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); } } @@ -315,7 +315,7 @@ bool Data::process(uint8_t* data, uint32_t len) } if (m_slot->m_rfFrames == 0U) { - LogMessage(LOG_RF, "DMR Slot %u, RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo); + LogInfoEx(LOG_RF, "DMR Slot %u, RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo); m_slot->writeEndRF(); } @@ -366,7 +366,7 @@ void Data::processNetwork(const data::NetData& dmrData) } if (m_verbose) { - LogMessage(LOG_RF, DMR_DT_TERMINATOR_WITH_LC ", slot = %u, dstId = %u", m_slot->m_slotNo, m_slot->m_netLC->getDstId()); + LogInfoEx(LOG_RF, DMR_DT_TERMINATOR_WITH_LC ", slot = %u, dstId = %u", m_slot->m_slotNo, m_slot->m_netLC->getDstId()); } // release trunked grant (if necessary) @@ -424,26 +424,26 @@ void Data::processNetwork(const data::NetData& dmrData) // did we receive a response header? if (m_netDataHeader.getDPF() == DPF::RESPONSE) { if (m_verbose) { - LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, slot = %u, sap = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, slot = %u, sap = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, m_netDataHeader.getSAP(), m_netDataHeader.getResponseClass(), m_netDataHeader.getResponseType(), m_netDataHeader.getResponseStatus(), dstId, srcId, gi); if (m_netDataHeader.getResponseClass() == PDUResponseClass::ACK && m_netDataHeader.getResponseType() == PDUResponseType::ACK) { - LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP ACK, slot = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP ACK, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); } else { if (m_netDataHeader.getResponseClass() == PDUResponseClass::NACK) { switch (m_netDataHeader.getResponseType()) { case PDUResponseType::NACK_ILLEGAL: - LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, illegal format, slot = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, illegal format, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); break; case PDUResponseType::NACK_PACKET_CRC: - LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet CRC error, slot = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet CRC error, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); break; case PDUResponseType::NACK_UNDELIVERABLE: - LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet undeliverable, slot = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, packet undeliverable, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); break; @@ -451,7 +451,7 @@ void Data::processNetwork(const data::NetData& dmrData) break; } } else if (m_netDataHeader.getResponseClass() == PDUResponseClass::ACK_RETRY) { - LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP ACK RETRY, slot = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP ACK RETRY, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); } } @@ -486,14 +486,14 @@ void Data::processNetwork(const data::NetData& dmrData) m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, Slot::SLCO_ACT_TYPE::DATA); if (m_verbose) { - LogMessage(LOG_NET, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", + LogInfoEx(LOG_NET, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, m_netDataHeader.getDPF(), m_netDataHeader.getA(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMesage(), m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadLength(), m_netDataHeader.getPacketLength(), m_netDataHeader.getFSN(), dstId, srcId, gi); } ::ActivityLog("DMR", false, "Slot %u network data header from %u to %s%u, %u blocks", m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId, m_slot->m_netFrames); - LogMessage(LOG_NET, "DMR Data Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); + LogInfoEx(LOG_NET, "DMR Data Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); ::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U); m_pduDataOffset = 0U; @@ -523,12 +523,12 @@ void Data::processNetwork(const data::NetData& dmrData) if (m_verbose) { if (dataType == DataType::RATE_34_DATA) { - LogMessage(LOG_NET, DMR_DT_RATE_34_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + LogInfoEx(LOG_NET, DMR_DT_RATE_34_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); } else if (dataType == DataType::RATE_12_DATA) { - LogMessage(LOG_NET, DMR_DT_RATE_12_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + LogInfoEx(LOG_NET, DMR_DT_RATE_12_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); } else { - LogMessage(LOG_NET, DMR_DT_RATE_1_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + LogInfoEx(LOG_NET, DMR_DT_RATE_1_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); } } @@ -564,7 +564,7 @@ void Data::processNetwork(const data::NetData& dmrData) } if (m_slot->m_netFrames == 0U) { - LogMessage(LOG_NET, "DMR Slot %u, RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo); + LogInfoEx(LOG_NET, "DMR Slot %u, RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo); m_slot->writeEndNet(); } } diff --git a/src/host/dmr/packet/Voice.cpp b/src/host/dmr/packet/Voice.cpp index c59c0e02..1d5fb164 100644 --- a/src/host/dmr/packet/Voice.cpp +++ b/src/host/dmr/packet/Voice.cpp @@ -210,12 +210,12 @@ bool Voice::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, DMR_DT_VOICE_LC_HEADER ", slot = %u, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, + LogInfoEx(LOG_RF, DMR_DT_VOICE_LC_HEADER ", slot = %u, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, m_slot->m_rfLC->getSrcId(), m_slot->m_rfLC->getDstId(), m_slot->m_rfLC->getFLCO(), m_slot->m_rfLC->getFID(), m_slot->m_rfLC->getPF()); } ::ActivityLog("DMR", true, "Slot %u RF %svoice header from %u to %s%u", m_slot->m_slotNo, encrypted ? "encrypted " : "", srcId, flco == FLCO::GROUP ? "TG " : "", dstId); - LogMessage(LOG_RF, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); + LogInfoEx(LOG_RF, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); return true; } else if (dataType == DataType::VOICE_PI_HEADER) { @@ -250,13 +250,13 @@ bool Voice::process(uint8_t* data, uint32_t len) m_slot->writeNetwork(data, DataType::VOICE_PI_HEADER, 0U); if (m_verbose) { - LogMessage(LOG_RF, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_slot->m_slotNo, + LogInfoEx(LOG_RF, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_slot->m_slotNo, m_slot->m_rfPrivacyLC->getAlgId(), m_slot->m_rfPrivacyLC->getKId(), m_slot->m_rfPrivacyLC->getDstId()); uint8_t mi[MI_LENGTH_BYTES]; m_slot->m_rfPrivacyLC->getMI(mi); - LogMessage(LOG_RF, DMR_DT_VOICE_PI_HEADER ", slot = %u, Enc Sync, MI = %02X %02X %02X %02X", + LogInfoEx(LOG_RF, DMR_DT_VOICE_PI_HEADER ", slot = %u, Enc Sync, MI = %02X %02X %02X %02X", m_slot->m_slotNo, mi[0U], mi[1U], mi[2U], mi[3U]); } @@ -294,7 +294,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, DMR_DT_VOICE_SYNC ", audio, slot = %u, srcId = %u, dstId = %u, seqNo = 0, fid = $%02X, pf = %u, errs = %u/141 (%.1f%%)", m_slot->m_slotNo, m_slot->m_rfLC->getSrcId(), m_slot->m_rfLC->getDstId(), + LogInfoEx(LOG_RF, DMR_DT_VOICE_SYNC ", audio, slot = %u, srcId = %u, dstId = %u, seqNo = 0, fid = $%02X, pf = %u, errs = %u/141 (%.1f%%)", m_slot->m_slotNo, m_slot->m_rfLC->getSrcId(), m_slot->m_rfLC->getDstId(), fid, pf, errors, float(errors) / 1.41F); } @@ -369,7 +369,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, DMR_DT_VOICE ", audio, slot = %u, srcId = %u, dstId = %u, seqNo = %u, fid = $%02X, pf = %u, errs = %u/141 (%.1f%%)", m_slot->m_slotNo, m_slot->m_rfLC->getSrcId(), m_slot->m_rfLC->getDstId(), + LogInfoEx(LOG_RF, DMR_DT_VOICE ", audio, slot = %u, srcId = %u, dstId = %u, seqNo = %u, fid = $%02X, pf = %u, errs = %u/141 (%.1f%%)", m_slot->m_slotNo, m_slot->m_rfLC->getSrcId(), m_slot->m_rfLC->getDstId(), m_rfN, fid, pf, errors, float(errors) / 1.41F); } @@ -630,7 +630,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, DMR_DT_VOICE ", audio, slot = %u, sequence no = %u, fid = $%02X, pf = %u, errs = %u/141 (%.1f%%)", + LogInfoEx(LOG_RF, DMR_DT_VOICE ", audio, slot = %u, sequence no = %u, fid = $%02X, pf = %u, errs = %u/141 (%.1f%%)", m_slot->m_slotNo, m_rfN, fid, pf, errors, float(errors) / 1.41F); } @@ -657,7 +657,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } ::ActivityLog("DMR", true, "Slot %u RF late entry from %u to %s%u", m_slot->m_slotNo, srcId, flco == FLCO::GROUP ? "TG " : "", dstId); - LogMessage(LOG_RF, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); + LogInfoEx(LOG_RF, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); return true; } } @@ -705,7 +705,7 @@ void Voice::processNetwork(const data::NetData& dmrData) srcId, flco == FLCO::GROUP ? "TG" : "", dstId); if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, VOICE_LC_HEADER, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, lc->getSrcId(), lc->getDstId(), lc->getFLCO(), lc->getFID(), lc->getPF()); + LogInfoEx(LOG_NET, "DMR Slot %u, VOICE_LC_HEADER, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, lc->getSrcId(), lc->getDstId(), lc->getFLCO(), lc->getFID(), lc->getPF()); } m_slot->m_netLC = std::move(lc); @@ -769,12 +769,12 @@ void Voice::processNetwork(const data::NetData& dmrData) m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, Slot::SLCO_ACT_TYPE::VOICE); if (m_verbose) { - LogMessage(LOG_NET, DMR_DT_VOICE_LC_HEADER ", slot = %u, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, + LogInfoEx(LOG_NET, DMR_DT_VOICE_LC_HEADER ", slot = %u, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, m_slot->m_netLC->getSrcId(), m_slot->m_netLC->getDstId(), m_slot->m_netLC->getFLCO(), m_slot->m_netLC->getFID(), m_slot->m_netLC->getPF()); } ::ActivityLog("DMR", false, "Slot %u network voice header from %u to %s%u", m_slot->m_slotNo, srcId, flco == FLCO::GROUP ? "TG " : "", dstId); - LogMessage(LOG_NET, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); + LogInfoEx(LOG_NET, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); } else if (dataType == DataType::VOICE_PI_HEADER) { if (m_slot->m_netState != RS_NET_AUDIO) { @@ -842,7 +842,7 @@ void Voice::processNetwork(const data::NetData& dmrData) ::ActivityLog("DMR", false, "Slot %u network late entry from %u to %s%u", m_slot->m_slotNo, srcId, m_slot->m_netLC->getFLCO() == FLCO::GROUP ? "TG " : "", dstId); - LogMessage(LOG_NET, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); + LogInfoEx(LOG_NET, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); } lc::FullLC fullLC; @@ -873,13 +873,13 @@ void Voice::processNetwork(const data::NetData& dmrData) m_slot->addFrame(data, true); if (m_verbose) { - LogMessage(LOG_NET, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_slot->m_slotNo, + LogInfoEx(LOG_NET, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_slot->m_slotNo, m_slot->m_netPrivacyLC->getAlgId(), m_slot->m_netPrivacyLC->getKId(), m_slot->m_netPrivacyLC->getDstId()); uint8_t mi[MI_LENGTH_BYTES]; m_slot->m_netPrivacyLC->getMI(mi); - LogMessage(LOG_NET, DMR_DT_VOICE_PI_HEADER ", slot = %u, Enc Sync, MI = %02X %02X %02X %02X", + LogInfoEx(LOG_NET, DMR_DT_VOICE_PI_HEADER ", slot = %u, Enc Sync, MI = %02X %02X %02X %02X", m_slot->m_slotNo, mi[0U], mi[1U], mi[2U], mi[3U]); } } @@ -955,7 +955,7 @@ void Voice::processNetwork(const data::NetData& dmrData) ::ActivityLog("DMR", false, "Slot %u network late entry from %u to %s%u", m_slot->m_slotNo, srcId, m_slot->m_netLC->getFLCO() == FLCO::GROUP ? "TG " : "", dstId); - LogMessage(LOG_NET, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); + LogInfoEx(LOG_NET, "DMR Voice Call, slot = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); } if (m_slot->m_netState == RS_NET_AUDIO) { @@ -970,7 +970,7 @@ void Voice::processNetwork(const data::NetData& dmrData) } if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, VOICE_SYNC audio, sequence no = %u, fid = $%02X, pf = %u, errs = %u/141 (%.1f%%)", + LogInfoEx(LOG_NET, "DMR Slot %u, VOICE_SYNC audio, sequence no = %u, fid = $%02X, pf = %u, errs = %u/141 (%.1f%%)", m_slot->m_slotNo, m_netN, fid, pf, m_slot->m_netErrs, float(m_slot->m_netErrs) / 1.41F); } @@ -1026,7 +1026,7 @@ void Voice::processNetwork(const data::NetData& dmrData) } if (m_verbose) { - LogMessage(LOG_NET, DMR_DT_VOICE ", audio, slot = %u, srcId = %u, dstId = %u, seqNo = %u, fid = $%02X, pf = %u, errs = %u/141 (%.1f%%)", m_slot->m_slotNo, m_slot->m_netLC->getSrcId(), m_slot->m_netLC->getDstId(), + LogInfoEx(LOG_NET, DMR_DT_VOICE ", audio, slot = %u, srcId = %u, dstId = %u, seqNo = %u, fid = $%02X, pf = %u, errs = %u/141 (%.1f%%)", m_slot->m_slotNo, m_slot->m_netLC->getSrcId(), m_slot->m_netLC->getDstId(), m_netN, fid, pf, m_slot->m_netErrs, float(m_slot->m_netErrs) / 1.41F); } @@ -1300,7 +1300,7 @@ void Voice::logGPSPosition(const uint32_t srcId, const uint8_t* data) longitude *= float(longitudeVal); latitude *= float(latitudeVal); - LogMessage(LOG_DMR, "GPS position for %u [lat %f, long %f] (Position error %s)", srcId, latitude, longitude, error); + LogInfoEx(LOG_DMR, "GPS position for %u [lat %f, long %f] (Position error %s)", srcId, latitude, longitude, error); } /* Helper to insert AMBE null frames for missing audio. */ diff --git a/src/host/modem/Modem.cpp b/src/host/modem/Modem.cpp index 578b2481..b565ff14 100644 --- a/src/host/modem/Modem.cpp +++ b/src/host/modem/Modem.cpp @@ -449,7 +449,7 @@ void Modem::setCloseHandler(std::function handler) bool Modem::open() { - LogMessage(LOG_MODEM, "Initializing modem"); + LogInfoEx(LOG_MODEM, "Initializing modem"); m_gotModemStatus = false; bool ret = m_port->open(); @@ -512,7 +512,7 @@ bool Modem::open() m_error = false; - LogMessage(LOG_MODEM, "Modem Ready [Direct Mode]"); + LogInfoEx(LOG_MODEM, "Modem Ready [Direct Mode]"); return true; } @@ -907,7 +907,7 @@ void Modem::clock(uint32_t ms) void Modem::close() { - LogMessage(LOG_MODEM, "Closing the modem"); + LogInfoEx(LOG_MODEM, "Closing the modem"); m_port->close(); m_gotModemStatus = false; @@ -1821,7 +1821,7 @@ bool Modem::getFirmwareVersion() continue; if (resp == RTM_OK && m_buffer[2U] == CMD_GET_VERSION) { - LogMessage(LOG_MODEM, "Protocol: %02x, CPU: %02X", m_buffer[3U], m_buffer[4U]); + LogInfoEx(LOG_MODEM, "Protocol: %02x, CPU: %02X", m_buffer[3U], m_buffer[4U]); m_protoVer = m_buffer[3U]; if (m_protoVer >= 2U) { @@ -1832,19 +1832,19 @@ bool Modem::getFirmwareVersion() switch (m_buffer[4U]) { case 0U: - LogMessage(LOG_MODEM, "Atmel ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[5U], m_buffer[6U], m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U], m_buffer[17U], m_buffer[18U], m_buffer[19U], m_buffer[20U]); + LogInfoEx(LOG_MODEM, "Atmel ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[5U], m_buffer[6U], m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U], m_buffer[17U], m_buffer[18U], m_buffer[19U], m_buffer[20U]); break; case 1U: - LogMessage(LOG_MODEM, "NXP ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[5U], m_buffer[6U], m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U], m_buffer[17U], m_buffer[18U], m_buffer[19U], m_buffer[20U]); + LogInfoEx(LOG_MODEM, "NXP ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[5U], m_buffer[6U], m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U], m_buffer[17U], m_buffer[18U], m_buffer[19U], m_buffer[20U]); break; case 2U: - LogMessage(LOG_MODEM, "ST-Micro ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[5U], m_buffer[6U], m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U]); + LogInfoEx(LOG_MODEM, "ST-Micro ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[5U], m_buffer[6U], m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U]); break; case 15U: - LogMessage(LOG_MODEM, "Null Modem, UDID: N/A"); + LogInfoEx(LOG_MODEM, "Null Modem, UDID: N/A"); break; default: - LogMessage(LOG_MODEM, "Unknown CPU type: %u", m_buffer[4U]); + LogInfoEx(LOG_MODEM, "Unknown CPU type: %u", m_buffer[4U]); break; } @@ -2206,7 +2206,7 @@ bool Modem::readFlash() void Modem::processFlashConfig(const uint8_t *buffer) { if (m_ignoreModemConfigArea) { - LogMessage(LOG_MODEM, "Modem configuration area checking is disabled!"); + LogInfoEx(LOG_MODEM, "Modem configuration area checking is disabled!"); return; } diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index 0bd4a970..b00b60c1 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -102,7 +102,7 @@ void ModemV24::setTIAFormat(bool set) bool ModemV24::open() { - LogMessage(LOG_MODEM, "Initializing modem"); + LogInfoEx(LOG_MODEM, "Initializing modem"); m_gotModemStatus = false; bool ret = m_port->open(); @@ -137,9 +137,9 @@ bool ModemV24::open() m_error = false; if (m_useTIAFormat) - LogMessage(LOG_MODEM, "Modem Ready [Direct Mode / TIA-102]"); + LogInfoEx(LOG_MODEM, "Modem Ready [Direct Mode / TIA-102]"); else - LogMessage(LOG_MODEM, "Modem Ready [Direct Mode / V.24]"); + LogInfoEx(LOG_MODEM, "Modem Ready [Direct Mode / V.24]"); return true; } @@ -409,7 +409,7 @@ void ModemV24::clock(uint32_t ms) void ModemV24::close() { - LogMessage(LOG_MODEM, "Closing the modem"); + LogInfoEx(LOG_MODEM, "Closing the modem"); m_port->close(); m_gotModemStatus = false; diff --git a/src/host/modem/port/PseudoPTYPort.cpp b/src/host/modem/port/PseudoPTYPort.cpp index 0ba8e05b..bc576fc0 100644 --- a/src/host/modem/port/PseudoPTYPort.cpp +++ b/src/host/modem/port/PseudoPTYPort.cpp @@ -64,7 +64,7 @@ bool PseudoPTYPort::open() return false; } - ::LogMessage(LOG_HOST, "Made symbolic link from %s to %s", slave, m_symlink.c_str()); + ::LogInfoEx(LOG_HOST, "Made symbolic link from %s to %s", slave, m_symlink.c_str()); m_device = std::string(::ttyname(m_fd)); return setTermios(); } diff --git a/src/host/modem/port/specialized/V24UDPPort.cpp b/src/host/modem/port/specialized/V24UDPPort.cpp index 2005c9d8..6dbc890a 100644 --- a/src/host/modem/port/specialized/V24UDPPort.cpp +++ b/src/host/modem/port/specialized/V24UDPPort.cpp @@ -313,7 +313,7 @@ void V24UDPPort::closeFSC() { if (m_controlSocket != nullptr) { if (m_fscState == CS_CONNECTED) { - LogMessage(LOG_MODEM, "V.24 UDP, Closing DFSI FSC Connection, vcBasePort = %u", m_localPort); + LogInfoEx(LOG_MODEM, "V.24 UDP, Closing DFSI FSC Connection, vcBasePort = %u", m_localPort); FSCDisconnect discoMessage = FSCDisconnect(); @@ -450,7 +450,7 @@ void V24UDPPort::taskCtrlNetworkRx(V24PacketRequest* req) network->m_heartbeatTimer.start(); network->m_timeoutTimer.start(); - LogMessage(LOG_MODEM, "V.24 UDP, Established DFSI FSC Connection, ctrlRemotePort = %u, vcLocalPort = %u, vcRemotePort = %u", remoteCtrlPort, network->m_localPort, vcBasePort); + LogInfoEx(LOG_MODEM, "V.24 UDP, Established DFSI FSC Connection, ctrlRemotePort = %u, vcLocalPort = %u, vcRemotePort = %u", remoteCtrlPort, network->m_localPort, vcBasePort); } break; @@ -529,7 +529,7 @@ void V24UDPPort::taskCtrlNetworkRx(V24PacketRequest* req) network->m_remoteCtrlAddr = req->address; network->m_remoteCtrlAddrLen = req->addrLen; - LogMessage(LOG_MODEM, "V.24 UDP, Incoming DFSI FSC Connection, ctrlRemotePort = %u, vcLocalPort = %u, vcRemotePort = %u, hostHBInterval = %u", remoteCtrlPort, network->m_localPort, vcBasePort, connMessage->getHostHeartbeatPeriod()); + LogInfoEx(LOG_MODEM, "V.24 UDP, Incoming DFSI FSC Connection, ctrlRemotePort = %u, vcLocalPort = %u, vcRemotePort = %u, hostHBInterval = %u", remoteCtrlPort, network->m_localPort, vcBasePort, connMessage->getHostHeartbeatPeriod()); // setup local RTP VC port (where we receive traffic) network->createVCPort(network->m_localPort); @@ -565,7 +565,7 @@ void V24UDPPort::taskCtrlNetworkRx(V24PacketRequest* req) ackResp.encode(buffer); if (network->m_ctrlFrameQueue->write(buffer, FSCACK::LENGTH + 3U, req->address, req->addrLen)) - LogMessage(LOG_MODEM, "V.24 UDP, Established DFSI FSC Connection, ctrlRemotePort = %u, vcLocalPort = %u, vcRemotePort = %u", remoteCtrlPort, network->m_localPort, vcBasePort); + LogInfoEx(LOG_MODEM, "V.24 UDP, Established DFSI FSC Connection, ctrlRemotePort = %u, vcLocalPort = %u, vcRemotePort = %u", remoteCtrlPort, network->m_localPort, vcBasePort); } break; @@ -619,7 +619,7 @@ void V24UDPPort::taskCtrlNetworkRx(V24PacketRequest* req) case FSCMessageType::FSC_DISCONNECT: { - LogMessage(LOG_MODEM, "V.24 UDP, DFSI FSC Disconnect, vcBasePort = %u", network->m_localPort); + LogInfoEx(LOG_MODEM, "V.24 UDP, DFSI FSC Disconnect, vcBasePort = %u", network->m_localPort); if (network->m_socket != nullptr) { network->m_socket->close(); @@ -780,7 +780,7 @@ void V24UDPPort::createRemoteVCPort(std::string address, uint16_t port) void V24UDPPort::writeConnect() { - LogMessage(LOG_MODEM, "V.24 UDP, Attempting DFSI FSC Connection, peerId = %u, vcBasePort = %u", m_peerId, m_localPort); + LogInfoEx(LOG_MODEM, "V.24 UDP, Attempting DFSI FSC Connection, peerId = %u, vcBasePort = %u", m_peerId, m_localPort); FSCConnect connect = FSCConnect(); connect.setFSHeartbeatPeriod(m_heartbeatInterval); diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index fd257b0a..ec08879c 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -328,7 +328,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw LogInfo(" Disable Grant Source ID Check: yes"); } if (m_supervisor) - LogMessage(LOG_NXDN, "Host is configured to operate as a NXDN control channel, site controller mode."); + LogInfoEx(LOG_NXDN, "Host is configured to operate as a NXDN control channel, site controller mode."); } if (m_defaultNetIdleTalkgroup != 0U) { @@ -410,7 +410,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) // Convert the raw RSSI to dBm int rssi = m_rssiMapper->interpolate(raw); if (m_verbose) { - LogMessage(LOG_RF, "NXDN, raw RSSI = %u, reported RSSI = %d dBm", raw, rssi); + LogInfoEx(LOG_RF, "NXDN, raw RSSI = %u, reported RSSI = %d dBm", raw, rssi); } // RSSI is always reported as positive @@ -653,7 +653,7 @@ void Control::clock() if (m_rfTGHang.hasExpired()) { m_rfTGHang.stop(); if (m_verbose) { - LogMessage(LOG_RF, "talkgroup hang has expired, lastDstId = %u", m_rfLastDstId); + LogInfoEx(LOG_RF, "talkgroup hang has expired, lastDstId = %u", m_rfLastDstId); } m_rfLastDstId = 0U; m_rfLastSrcId = 0U; @@ -684,7 +684,7 @@ void Control::clock() if (m_netTGHang.hasExpired()) { m_netTGHang.stop(); if (m_verbose) { - LogMessage(LOG_NET, "talkgroup hang has expired, lastDstId = %u", m_netLastDstId); + LogInfoEx(LOG_NET, "talkgroup hang has expired, lastDstId = %u", m_netLastDstId); } m_netLastDstId = 0U; m_netLastSrcId = 0U; @@ -758,9 +758,9 @@ void Control::permittedTG(uint32_t dstId) if (m_verbose) { if (dstId == 0U) - LogMessage(LOG_NXDN, "non-authoritative TG unpermit"); + LogInfoEx(LOG_NXDN, "non-authoritative TG unpermit"); else - LogMessage(LOG_NXDN, "non-authoritative TG permit, dstId = %u", dstId); + LogInfoEx(LOG_NXDN, "non-authoritative TG permit, dstId = %u", dstId); } m_permittedDstId = dstId; @@ -775,7 +775,7 @@ void Control::grantTG(uint32_t srcId, uint32_t dstId, bool grp) } if (m_verbose) { - LogMessage(LOG_NXDN, "network TG grant demand, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_NXDN, "network TG grant demand, srcId = %u, dstId = %u", srcId, dstId); } m_control->writeRF_Message_Grant(srcId, dstId, 4U, grp); @@ -1024,7 +1024,7 @@ void Control::processFrameLoss() float(m_voice->m_rfFrames) / 12.5F, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits), m_frameLossCnt); } - LogMessage(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", + LogInfoEx(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_voice->m_rfFrames, m_voice->m_rfBits, m_voice->m_rfUndecodableLC, m_voice->m_rfErrs, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits)); m_affiliations->releaseGrant(m_rfLC.getDstId(), false); @@ -1093,7 +1093,7 @@ void Control::notifyCC_ReleaseGrant(uint32_t dstId) } if (m_verbose) { - LogMessage(LOG_NXDN, "CC %s:%u, notifying CC of call termination, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); + LogInfoEx(LOG_NXDN, "CC %s:%u, notifying CC of call termination, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); } // callback REST API to release the granted TG on the specified control channel @@ -1115,7 +1115,7 @@ void Control::notifyCC_ReleaseGrant(uint32_t dstId) } } else - ::LogMessage(LOG_NXDN, "CC %s:%u, released grant, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); + ::LogInfoEx(LOG_NXDN, "CC %s:%u, released grant, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); }, m_controlChData.address(), m_controlChData.port()); m_rfLastDstId = 0U; @@ -1159,7 +1159,7 @@ void Control::notifyCC_TouchGrant(uint32_t dstId) } } else - ::LogMessage(LOG_NXDN, "CC %s:%u, touched grant, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); + ::LogInfoEx(LOG_NXDN, "CC %s:%u, touched grant, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); }, m_controlChData.address(), m_controlChData.port()); } @@ -1209,7 +1209,7 @@ void Control::RPC_releaseGrantTG(json::object& req, json::object& reply) // LogDebugEx(LOG_NXDN, "Control::RPC_releaseGrantTG()", "callback, dstId = %u", dstId); if (m_verbose) { - LogMessage(LOG_P25, "VC request, release TG grant, dstId = %u", dstId); + LogInfoEx(LOG_P25, "VC request, release TG grant, dstId = %u", dstId); } if (m_affiliations->isGranted(dstId)) { @@ -1218,7 +1218,7 @@ void Control::RPC_releaseGrantTG(json::object& req, json::object& reply) ::lookups::VoiceChData voiceCh = m_affiliations->rfCh()->getRFChData(chNo); if (m_verbose) { - LogMessage(LOG_P25, "VC %s:%u, TG grant released, srcId = %u, dstId = %u, chNo = %u-%u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); + LogInfoEx(LOG_P25, "VC %s:%u, TG grant released, srcId = %u, dstId = %u, chNo = %u-%u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); } m_affiliations->releaseGrant(dstId, false); @@ -1257,7 +1257,7 @@ void Control::RPC_touchGrantTG(json::object& req, json::object& reply) ::lookups::VoiceChData voiceCh = m_affiliations->rfCh()->getRFChData(chNo); if (m_verbose) { - LogMessage(LOG_P25, "VC %s:%u, call in progress, srcId = %u, dstId = %u, chNo = %u-%u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); + LogInfoEx(LOG_P25, "VC %s:%u, call in progress, srcId = %u, dstId = %u, chNo = %u-%u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); } m_affiliations->touchGrant(dstId); diff --git a/src/host/nxdn/packet/ControlSignaling.cpp b/src/host/nxdn/packet/ControlSignaling.cpp index e4e1fb86..47519dd4 100644 --- a/src/host/nxdn/packet/ControlSignaling.cpp +++ b/src/host/nxdn/packet/ControlSignaling.cpp @@ -90,25 +90,25 @@ using namespace nxdn::packet; // Macro helper to verbose log a generic message. #define VERBOSE_LOG_MSG(_PCKT_STR, _SRCID, _DSTID) \ if (m_verbose) { \ - LogMessage(LOG_RF, "NXDN, %s, srcId = %u, dstId = %u", _PCKT_STR.c_str(), _SRCID, _DSTID); \ + LogInfoEx(LOG_RF, "NXDN, %s, srcId = %u, dstId = %u", _PCKT_STR.c_str(), _SRCID, _DSTID); \ } // Macro helper to verbose log a generic message. #define VERBOSE_LOG_MSG_DST(_PCKT_STR, _DSTID) \ if (m_verbose) { \ - LogMessage(LOG_RF, "NXDN, %s, dstId = %u", _PCKT_STR.c_str(), _DSTID); \ + LogInfoEx(LOG_RF, "NXDN, %s, dstId = %u", _PCKT_STR.c_str(), _DSTID); \ } // Macro helper to verbose log a generic network message. #define VERBOSE_LOG_MSG_NET(_PCKT_STR, _SRCID, _DSTID) \ if (m_verbose) { \ - LogMessage(LOG_NET, "NXDN, %s, srcId = %u, dstId = %u", _PCKT_STR.c_str(), _SRCID, _DSTID); \ + LogInfoEx(LOG_NET, "NXDN, %s, srcId = %u, dstId = %u", _PCKT_STR.c_str(), _SRCID, _DSTID); \ } // Macro helper to verbose log a generic network message. #define DEBUG_LOG_MSG(_PCKT_STR) \ if (m_debug) { \ - LogMessage(LOG_RF, "NXDN, %s", _PCKT_STR.c_str()); \ + LogInfoEx(LOG_RF, "NXDN, %s", _PCKT_STR.c_str()); \ } // --------------------------------------------------------------------------- @@ -192,7 +192,7 @@ bool ControlSignaling::process(FuncChannelType::E fct, ChOption::E option, uint8 IS_SUPPORT_CONTROL_CHECK(rcch->toString(true), MessageType::RCCH_REG, srcId); if (m_verbose) { - LogMessage(LOG_RF, "NXDN, %s, srcId = %u, locId = $%06X, regOption = $%02X", + LogInfoEx(LOG_RF, "NXDN, %s, srcId = %u, locId = $%06X, regOption = $%02X", rcch->toString(true).c_str(), srcId, rcch->getLocId(), rcch->getRegOption()); } @@ -205,7 +205,7 @@ bool ControlSignaling::process(FuncChannelType::E fct, ChOption::E option, uint8 IS_SUPPORT_CONTROL_CHECK(rcch->toString(true), MessageType::RCCH_GRP_REG, srcId); if (m_verbose) { - LogMessage(LOG_RF, "NXDN, %s, srcId = %u, dstId = %u, locId = $%06X", + LogInfoEx(LOG_RF, "NXDN, %s, srcId = %u, dstId = %u, locId = $%06X", rcch->toString(true).c_str(), srcId, dstId, rcch->getLocId()); } @@ -252,7 +252,7 @@ bool ControlSignaling::processNetwork(FuncChannelType::E fct, ChOption::E option if (m_nxdn->m_dedicatedControl) { if (!m_nxdn->m_affiliations->isGranted(dstId)) { if (m_verbose) { - LogMessage(LOG_NET, "NXDN, %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_NET, "NXDN, %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u", rcch->toString().c_str(), rcch->getEmergency(), rcch->getEncrypted(), rcch->getPriority(), rcch->getGrpVchNo(), srcId, dstId); } @@ -645,7 +645,7 @@ bool ControlSignaling::writeRF_Message_Grant(uint32_t srcId, uint32_t dstId, uin rcch->setPriority(priority); if (m_verbose) { - LogMessage((net) ? LOG_NET : LOG_RF, "NXDN, %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u", + LogInfoEx((net) ? LOG_NET : LOG_RF, "NXDN, %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u", rcch->toString().c_str(), rcch->getEmergency(), rcch->getEncrypted(), rcch->getPriority(), rcch->getGrpVchNo(), rcch->getSrcId(), rcch->getDstId()); } @@ -673,7 +673,7 @@ void ControlSignaling::writeRF_Message_Deny(uint32_t srcId, uint32_t dstId, uint rcch->setDstId(dstId); if (m_verbose) { - LogMessage(LOG_RF, "NXDN, MSG_DENIAL (Message Denial), reason = $%02X (%s), service = $%02X, srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, "NXDN, MSG_DENIAL (Message Denial), reason = $%02X (%s), service = $%02X, srcId = %u, dstId = %u", reason, NXDNUtils::causeToString(reason).c_str(), service, srcId, dstId); } @@ -774,7 +774,7 @@ void ControlSignaling::writeRF_Message_U_Reg_Rsp(uint32_t srcId, uint32_t dstId, if (rcch->getCauseResponse() == CauseResponse::MM_REG_ACCEPTED) { if (m_verbose) { - LogMessage(LOG_RF, "NXDN, %s, srcId = %u, locId = $%06X", + LogInfoEx(LOG_RF, "NXDN, %s, srcId = %u, locId = $%06X", rcch->toString().c_str(), srcId, locId); } diff --git a/src/host/nxdn/packet/Data.cpp b/src/host/nxdn/packet/Data.cpp index 9198b564..97b0ed98 100644 --- a/src/host/nxdn/packet/Data.cpp +++ b/src/host/nxdn/packet/Data.cpp @@ -147,12 +147,12 @@ bool Data::process(ChOption::E option, uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_DCALL_HDR ", srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u", + LogInfoEx(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_DCALL_HDR ", srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u", srcId, dstId, lc.getPacketInfo().getDelivery(), lc.getPacketInfo().getBlockCount(), lc.getPacketInfo().getPadCount(), lc.getPacketInfo().getStart(), lc.getPacketInfo().getFragmentCount()); } ::ActivityLog("NXDN", true, "RF data transmission from %u to %s%u", srcId, group ? "TG " : "", dstId); - LogMessage(LOG_RF, "NXDN Data Call, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_RF, "NXDN Data Call, srcId = %u, dstId = %u", srcId, dstId); m_nxdn->m_rfLC = lc; m_nxdn->m_voice->m_rfFrames = 0U; @@ -199,7 +199,7 @@ bool Data::process(ChOption::E option, uint8_t* data, uint32_t len) if (data[0U] == modem::TAG_EOT) { ::ActivityLog("NXDN", true, "RF ended RF data transmission"); - LogMessage(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", total frames: %d", + LogInfoEx(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", total frames: %d", m_nxdn->m_voice->m_rfFrames); m_nxdn->writeEndRF(); @@ -316,12 +316,12 @@ bool Data::processNetwork(ChOption::E option, lc::RTCH& netLC, uint8_t* data, ui } if (m_verbose) { - LogMessage(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_DCALL_HDR ", srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u", + LogInfoEx(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_DCALL_HDR ", srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u", srcId, dstId, lc.getPacketInfo().getDelivery(), lc.getPacketInfo().getBlockCount(), lc.getPacketInfo().getPadCount(), lc.getPacketInfo().getStart(), lc.getPacketInfo().getFragmentCount()); } ::ActivityLog("NXDN", false, "network data transmission from %u to %s%u", srcId, group ? "TG " : "", dstId); - LogMessage(LOG_NET, "NXDN Data Call, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_NET, "NXDN Data Call, srcId = %u, dstId = %u", srcId, dstId); m_nxdn->m_netLC = lc; m_nxdn->m_voice->m_netFrames = 0U; @@ -364,7 +364,7 @@ bool Data::processNetwork(ChOption::E option, lc::RTCH& netLC, uint8_t* data, ui if (data[0U] == modem::TAG_EOT) { ::ActivityLog("NXDN", true, "network ended RF data transmission"); - LogMessage(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", total frames: %d", + LogInfoEx(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", total frames: %d", m_nxdn->m_voice->m_netFrames); m_nxdn->writeEndNet(); diff --git a/src/host/nxdn/packet/Voice.cpp b/src/host/nxdn/packet/Voice.cpp index 79a88b77..5c1009ab 100644 --- a/src/host/nxdn/packet/Voice.cpp +++ b/src/host/nxdn/packet/Voice.cpp @@ -231,7 +231,7 @@ bool Voice::process(FuncChannelType::E fct, ChOption::E option, uint8_t* data, u float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits)); } - LogMessage(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", + LogInfoEx(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_rfFrames, m_rfBits, m_rfUndecodableLC, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); m_nxdn->writeEndRF(); @@ -248,12 +248,12 @@ bool Voice::process(FuncChannelType::E fct, ChOption::E option, uint8_t* data, u m_nxdn->m_rssiCount = 1U; if (m_verbose) { - LogMessage(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%02X", + LogInfoEx(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%02X", srcId, dstId, group, lc.getEmergency(), encrypted, lc.getPriority(), lc.getAlgId(), lc.getKId()); } ::ActivityLog("NXDN", true, "RF %svoice transmission from %u to %s%u", encrypted ? "encrypted " : "", srcId, group ? "TG " : "", dstId); - LogMessage(LOG_RF, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_RF, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); } return true; @@ -400,12 +400,12 @@ bool Voice::process(FuncChannelType::E fct, ChOption::E option, uint8_t* data, u m_nxdn->m_rssiCount = 1U; if (m_verbose) { - LogMessage(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%04X", + LogInfoEx(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%04X", srcId, dstId, group, m_nxdn->m_rfLC.getEmergency(), encrypted, m_nxdn->m_rfLC.getPriority(), m_nxdn->m_rfLC.getAlgId(), m_nxdn->m_rfLC.getKId()); } ::ActivityLog("NXDN", true, "RF %slate entry from %u to %s%u", encrypted ? "encrypted ": "", srcId, group ? "TG " : "", dstId); - LogMessage(LOG_RF, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_RF, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); // create a dummy start message uint8_t start[NXDN_FRAME_LENGTH_BYTES + 2U]; @@ -504,7 +504,7 @@ bool Voice::process(FuncChannelType::E fct, ChOption::E option, uint8_t* data, u m_rfBits += 188U; if (m_verbose) { - LogMessage(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", audio, srcId = %u, dstId = %u, errs = %u/188 (%.1f%%)", m_nxdn->m_rfLC.getSrcId(), m_nxdn->m_rfLC.getDstId(), errors, float(errors) / 1.88F); + LogInfoEx(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", audio, srcId = %u, dstId = %u, errs = %u/188 (%.1f%%)", m_nxdn->m_rfLC.getSrcId(), m_nxdn->m_rfLC.getDstId(), errors, float(errors) / 1.88F); } } else if (option == ChOption::STEAL_FACCH1_1) { channel::FACCH1 facch11; @@ -534,7 +534,7 @@ bool Voice::process(FuncChannelType::E fct, ChOption::E option, uint8_t* data, u m_rfBits += 94U; if (m_verbose) { - LogMessage(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", audio, srcId = %u, dstId = %u, errs = %u/94 (%.1f%%)", m_nxdn->m_rfLC.getSrcId(), m_nxdn->m_rfLC.getDstId(), errors, float(errors) / 0.94F); + LogInfoEx(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", audio, srcId = %u, dstId = %u, errs = %u/94 (%.1f%%)", m_nxdn->m_rfLC.getSrcId(), m_nxdn->m_rfLC.getDstId(), errors, float(errors) / 0.94F); } } else if (option == ChOption::STEAL_FACCH1_2) { edac::AMBEFEC ambe; @@ -559,7 +559,7 @@ bool Voice::process(FuncChannelType::E fct, ChOption::E option, uint8_t* data, u m_rfBits += 94U; if (m_verbose) { - LogMessage(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", srcId = %u, dstId = %u, audio, errs = %u/94 (%.1f%%)", m_nxdn->m_rfLC.getSrcId(), m_nxdn->m_rfLC.getDstId(), errors, float(errors) / 0.94F); + LogInfoEx(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", srcId = %u, dstId = %u, audio, errs = %u/94 (%.1f%%)", m_nxdn->m_rfLC.getSrcId(), m_nxdn->m_rfLC.getDstId(), errors, float(errors) / 0.94F); } channel::FACCH1 facch12; @@ -747,7 +747,7 @@ bool Voice::processNetwork(FuncChannelType::E fct, ChOption::E option, lc::RTCH& ::ActivityLog("NXDN", false, "network end of transmission, %.1f seconds", float(m_netFrames) / 12.5F); - LogMessage(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", total frames: %d", m_netFrames); + LogInfoEx(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", total frames: %d", m_netFrames); m_nxdn->writeEndNet(); } else { @@ -756,12 +756,12 @@ bool Voice::processNetwork(FuncChannelType::E fct, ChOption::E option, lc::RTCH& m_nxdn->m_netState = RS_NET_AUDIO; if (m_verbose) { - LogMessage(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%02X", + LogInfoEx(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%02X", srcId, dstId, group, lc.getEmergency(), encrypted, lc.getPriority(), lc.getAlgId(), lc.getKId()); } ::ActivityLog("NXDN", false, "network %svoice transmission from %u to %s%u", encrypted ? "encrypted " : "", srcId, group ? "TG " : "", dstId); - LogMessage(LOG_NET, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_NET, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); } return true; @@ -893,12 +893,12 @@ bool Voice::processNetwork(FuncChannelType::E fct, ChOption::E option, lc::RTCH& m_nxdn->m_netState = RS_NET_AUDIO; if (m_verbose) { - LogMessage(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%04X", + LogInfoEx(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%04X", srcId, dstId, group, m_nxdn->m_netLC.getEmergency(), encrypted, m_nxdn->m_netLC.getPriority(), m_nxdn->m_netLC.getAlgId(), m_nxdn->m_netLC.getKId()); } ::ActivityLog("NXDN", false, "network %slate entry from %u to %s%u", encrypted ? "encrypted ": "", srcId, group ? "TG " : "", dstId); - LogMessage(LOG_NET, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_NET, "NXDN Voice Call, srcId = %u, dstId = %u", srcId, dstId); // create a dummy start message uint8_t start[NXDN_FRAME_LENGTH_BYTES + 2U]; @@ -975,7 +975,7 @@ bool Voice::processNetwork(FuncChannelType::E fct, ChOption::E option, lc::RTCH& m_rfBits += 188U; if (m_verbose) { - LogMessage(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", audio, srcId = %u, dstId = %u, errs = %u/141 (%.1f%%)", m_nxdn->m_netLC.getSrcId(), m_nxdn->m_netLC.getDstId(), errors, float(errors) / 1.88F); + LogInfoEx(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", audio, srcId = %u, dstId = %u, errs = %u/141 (%.1f%%)", m_nxdn->m_netLC.getSrcId(), m_nxdn->m_netLC.getDstId(), errors, float(errors) / 1.88F); } } else if (option == ChOption::STEAL_FACCH1_1) { channel::FACCH1 facch1; @@ -994,7 +994,7 @@ bool Voice::processNetwork(FuncChannelType::E fct, ChOption::E option, lc::RTCH& m_rfBits += 94U; if (m_verbose) { - LogMessage(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", audio, srcId = %u, dstId = %u, errs = %u/94 (%.1f%%)", m_nxdn->m_netLC.getSrcId(), m_nxdn->m_netLC.getDstId(), errors, float(errors) / 0.94F); + LogInfoEx(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", audio, srcId = %u, dstId = %u, errs = %u/94 (%.1f%%)", m_nxdn->m_netLC.getSrcId(), m_nxdn->m_netLC.getDstId(), errors, float(errors) / 0.94F); } } else if (option == ChOption::STEAL_FACCH1_2) { edac::AMBEFEC ambe; @@ -1008,7 +1008,7 @@ bool Voice::processNetwork(FuncChannelType::E fct, ChOption::E option, lc::RTCH& m_rfBits += 94U; if (m_verbose) { - LogMessage(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", audio, srcId = %u, dstId = %u, errs = %u/94 (%.1f%%)", m_nxdn->m_netLC.getSrcId(), m_nxdn->m_netLC.getDstId(), errors, float(errors) / 0.94F); + LogInfoEx(LOG_NET, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL ", audio, srcId = %u, dstId = %u, errs = %u/94 (%.1f%%)", m_nxdn->m_netLC.getSrcId(), m_nxdn->m_netLC.getDstId(), errors, float(errors) / 0.94F); } channel::FACCH1 facch1; bool valid = facch1.decode(data + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_FEC_LENGTH_BITS + NXDN_FACCH1_FEC_LENGTH_BITS); diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index 86960eb5..35f3177f 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -502,7 +502,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw LogInfo(" Disable Grant Source ID Check: yes"); } if (m_supervisor) - LogMessage(LOG_P25, "Host is configured to operate as a P25 control channel, site controller mode."); + LogInfoEx(LOG_P25, "Host is configured to operate as a P25 control channel, site controller mode."); } if (m_controlOnly) { @@ -580,7 +580,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw // are we overriding the NAC for split NAC operations? uint32_t txNAC = (uint32_t)::strtoul(systemConf["config"]["txNAC"].as("F7E").c_str(), NULL, 16); if (txNAC != NAC_DIGITAL_SQ && txNAC != m_nac) { - LogMessage(LOG_P25, "Split NAC operations, setting Tx NAC to $%03X", txNAC); + LogInfoEx(LOG_P25, "Split NAC operations, setting Tx NAC to $%03X", txNAC); m_txNAC = txNAC; m_nid.setTxNAC(m_txNAC); } @@ -684,7 +684,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) // Convert the raw RSSI to dBm int rssi = m_rssiMapper->interpolate(raw); if (m_verbose) { - LogMessage(LOG_RF, "P25, raw RSSI = %u, reported RSSI = %d dBm", raw, rssi); + LogInfoEx(LOG_RF, "P25, raw RSSI = %u, reported RSSI = %d dBm", raw, rssi); } // RSSI is always reported as positive @@ -936,7 +936,7 @@ void Control::clock() if (m_rfTGHang.hasExpired()) { m_rfTGHang.stop(); if (m_verbose) { - LogMessage(LOG_RF, "talkgroup hang has expired, lastDstId = %u", m_rfLastDstId); + LogInfoEx(LOG_RF, "talkgroup hang has expired, lastDstId = %u", m_rfLastDstId); } m_rfLastDstId = 0U; m_rfLastSrcId = 0U; @@ -972,7 +972,7 @@ void Control::clock() if (m_netTGHang.hasExpired()) { m_netTGHang.stop(); if (m_verbose) { - LogMessage(LOG_NET, "talkgroup hang has expired, lastDstId = %u", m_netLastDstId); + LogInfoEx(LOG_NET, "talkgroup hang has expired, lastDstId = %u", m_netLastDstId); } // if the group is still granted at this point -- forcibly release it @@ -1162,7 +1162,7 @@ void Control::clockSiteData(uint32_t ms) } } else - ::LogMessage(LOG_P25, "VC %s:%u, active TG update, activeCnt = %u", voiceChData.address().c_str(), voiceChData.port(), activeCnt); + ::LogInfoEx(LOG_P25, "VC %s:%u, active TG update, activeCnt = %u", voiceChData.address().c_str(), voiceChData.port(), activeCnt); }, voiceChData.address(), voiceChData.port()); } } @@ -1192,7 +1192,7 @@ void Control::clockSiteData(uint32_t ms) } } else - ::LogMessage(LOG_P25, "VC %s:%u, clear active TG update", voiceChData.address().c_str(), voiceChData.port()); + ::LogInfoEx(LOG_P25, "VC %s:%u, clear active TG update", voiceChData.address().c_str(), voiceChData.port()); }, voiceChData.address(), voiceChData.port()); } } @@ -1214,9 +1214,9 @@ void Control::permittedTG(uint32_t dstId, bool dataPermit) if (m_verbose) { if (dstId == 0U) - LogMessage(LOG_P25, "non-authoritative TG unpermit"); + LogInfoEx(LOG_P25, "non-authoritative TG unpermit"); else - LogMessage(LOG_P25, "non-authoritative TG permit, dstId = %u", dstId); + LogInfoEx(LOG_P25, "non-authoritative TG permit, dstId = %u", dstId); } m_permittedDstId = dstId; @@ -1236,7 +1236,7 @@ void Control::grantTG(uint32_t srcId, uint32_t dstId, bool grp) } if (m_verbose) { - LogMessage(LOG_P25, "network TG grant demand, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_P25, "network TG grant demand, srcId = %u, dstId = %u", srcId, dstId); } m_control->writeRF_TSDU_Grant(srcId, dstId, 4U, grp); @@ -1598,7 +1598,7 @@ void Control::processNetwork() (control.getPriority() & 0x07U); // Priority if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR " remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u, encrypted = %u", srcId, dstId, unitToUnit, grantEncrypt); + LogInfoEx(LOG_NET, P25_TSDU_STR " remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u, encrypted = %u", srcId, dstId, unitToUnit, grantEncrypt); } // are we denying the grant? @@ -1645,7 +1645,7 @@ void Control::processFrameLoss() float(m_voice->m_rfFrames) / 5.56F, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits), m_frameLossCnt); } - LogMessage(LOG_RF, P25_TDU_STR ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", + LogInfoEx(LOG_RF, P25_TDU_STR ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_voice->m_rfFrames, m_voice->m_rfBits, m_voice->m_rfUndecodableLC, m_voice->m_rfErrs, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits)); m_affiliations->releaseGrant(m_voice->m_rfLC.getDstId(), false); @@ -1750,7 +1750,7 @@ void Control::notifyCC_ReleaseGrant(uint32_t dstId) } if (m_verbose) { - LogMessage(LOG_P25, "CC %s:%u, notifying CC of call termination, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); + LogInfoEx(LOG_P25, "CC %s:%u, notifying CC of call termination, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); } // callback REST API to release the granted TG on the specified control channel @@ -1772,7 +1772,7 @@ void Control::notifyCC_ReleaseGrant(uint32_t dstId) } } else - ::LogMessage(LOG_P25, "CC %s:%u, released grant, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); + ::LogInfoEx(LOG_P25, "CC %s:%u, released grant, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); }, m_controlChData.address(), m_controlChData.port()); m_rfLastDstId = 0U; @@ -1816,7 +1816,7 @@ void Control::notifyCC_TouchGrant(uint32_t dstId) } } else - ::LogMessage(LOG_P25, "CC %s:%u, touched grant, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); + ::LogInfoEx(LOG_P25, "CC %s:%u, touched grant, dstId = %u", m_controlChData.address().c_str(), m_controlChData.port(), dstId); }, m_controlChData.address(), m_controlChData.port()); } @@ -2011,7 +2011,7 @@ void Control::RPC_activeTG(json::object& req, json::object& reply) } } - ::LogMessage(LOG_P25, "active TG update, activeCnt = %u", m_activeTG.size()); + ::LogInfoEx(LOG_P25, "active TG update, activeCnt = %u", m_activeTG.size()); } /* (RPC Handler) Clear active TGID list from the authoritative CC host. */ @@ -2053,7 +2053,7 @@ void Control::RPC_releaseGrantTG(json::object& req, json::object& reply) // LogDebugEx(LOG_P25, "Control::RPC_releaseGrantTG()", "callback, dstId = %u", dstId); if (m_verbose) { - LogMessage(LOG_P25, "VC request, release TG grant, dstId = %u", dstId); + LogInfoEx(LOG_P25, "VC request, release TG grant, dstId = %u", dstId); } if (m_affiliations->isGranted(dstId)) { @@ -2062,7 +2062,7 @@ void Control::RPC_releaseGrantTG(json::object& req, json::object& reply) ::lookups::VoiceChData voiceCh = m_affiliations->rfCh()->getRFChData(chNo); if (m_verbose) { - LogMessage(LOG_P25, "VC %s:%u, TG grant released, srcId = %u, dstId = %u, chNo = %u-%u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); + LogInfoEx(LOG_P25, "VC %s:%u, TG grant released, srcId = %u, dstId = %u, chNo = %u-%u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); } m_affiliations->releaseGrant(dstId, false); @@ -2101,7 +2101,7 @@ void Control::RPC_touchGrantTG(json::object& req, json::object& reply) ::lookups::VoiceChData voiceCh = m_affiliations->rfCh()->getRFChData(chNo); if (m_verbose) { - LogMessage(LOG_P25, "VC %s:%u, call in progress, srcId = %u, dstId = %u, chNo = %u-%u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); + LogInfoEx(LOG_P25, "VC %s:%u, call in progress, srcId = %u, dstId = %u, chNo = %u-%u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); } m_affiliations->touchGrant(dstId); @@ -2147,7 +2147,7 @@ void Control::generateLLA_AM1_Parameters() ::memcpy(m_llaKS, KS, AUTH_KEY_LENGTH_BYTES); if (m_verbose) { - LogMessage(LOG_P25, "P25, generated LLA AM1 parameters"); + LogInfoEx(LOG_P25, "P25, generated LLA AM1 parameters"); } // cleanup diff --git a/src/host/p25/packet/ControlSignaling.cpp b/src/host/p25/packet/ControlSignaling.cpp index 7cd0971f..5a4a9a70 100644 --- a/src/host/p25/packet/ControlSignaling.cpp +++ b/src/host/p25/packet/ControlSignaling.cpp @@ -114,25 +114,25 @@ using namespace p25::packet; // Macro helper to verbose log a generic TSBK. #define VERBOSE_LOG_TSBK(_PCKT_STR, _SRCID, _DSTID) \ if (m_verbose) { \ - LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, dstId = %u", _PCKT_STR.c_str(), _SRCID, _DSTID); \ + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, dstId = %u", _PCKT_STR.c_str(), _SRCID, _DSTID); \ } // Macro helper to verbose log a generic TSBK. #define VERBOSE_LOG_TSBK_DST(_PCKT_STR, _DSTID) \ if (m_verbose) { \ - LogMessage(LOG_RF, P25_TSDU_STR ", %s, dstId = %u", _PCKT_STR.c_str(), _DSTID); \ + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, dstId = %u", _PCKT_STR.c_str(), _DSTID); \ } // Macro helper to verbose log a generic network TSBK. #define VERBOSE_LOG_TSBK_NET(_PCKT_STR, _SRCID, _DSTID) \ if (m_verbose) { \ - LogMessage(LOG_NET, P25_TSDU_STR ", %s, srcId = %u, dstId = %u", _PCKT_STR.c_str(), _SRCID, _DSTID); \ + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, srcId = %u, dstId = %u", _PCKT_STR.c_str(), _SRCID, _DSTID); \ } // Macro helper to verbose log a generic network TSBK. #define DEBUG_LOG_TSBK(_PCKT_STR) \ if (m_debug) { \ - LogMessage(LOG_RF, P25_TSDU_STR ", %s", _PCKT_STR.c_str()); \ + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s", _PCKT_STR.c_str()); \ } #define RF_TO_WRITE_NET(OSP) \ @@ -286,7 +286,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr(tsbk.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, response = $%02X, srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, response = $%02X, srcId = %u, dstId = %u", tsbk->toString(true).c_str(), iosp->getResponse(), srcId, dstId); } @@ -331,7 +331,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr(tsbk.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, dataServiceOptions = $%02X, dataAccessControl = $%04X, srcId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, dataServiceOptions = $%02X, dataAccessControl = $%04X, srcId = %u", tsbk->toString(true).c_str(), isp->getDataServiceOptions(), isp->getDataAccessControl(), srcId); } @@ -353,7 +353,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr(tsbk.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, dataServiceOptions = $%02X, dataAccessControl = %u, srcId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, dataServiceOptions = $%02X, dataAccessControl = %u, srcId = %u", tsbk->toString(true).c_str(), isp->getDataServiceOptions(), isp->getDataAccessControl(), srcId); } @@ -372,7 +372,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr(tsbk.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, status = $%02X, srcId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, status = $%02X, srcId = %u", tsbk->toString(true).c_str(), iosp->getStatus(), srcId); } @@ -392,7 +392,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr(tsbk.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, message = $%02X, srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, message = $%02X, srcId = %u, dstId = %u", tsbk->toString(true).c_str(), iosp->getMessage(), srcId, dstId); } @@ -415,7 +415,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr(tsbk.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", tsbk->toString(true).c_str(), srcId, dstId, iosp->getTxMult()); } @@ -448,7 +448,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr(tsbk.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, serviceType = $%02X, srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, serviceType = $%02X, srcId = %u, dstId = %u", tsbk->toString(true).c_str(), iosp->getAIV(), iosp->getService(), srcId, dstId); } @@ -469,7 +469,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr(tsbk.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, serviceType = $%02X, reason = $%02X, srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, serviceType = $%02X, reason = $%02X, srcId = %u, dstId = %u", tsbk->toString(true).c_str(), isp->getAIV(), isp->getService(), isp->getResponse(), srcId, dstId); } @@ -482,7 +482,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr(tsbk.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, op = $%02X, arg = %u, tgt = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, op = $%02X, arg = %u, tgt = %u", tsbk->toString(true).c_str(), iosp->getExtendedFunction(), srcId, dstId); } @@ -564,7 +564,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr(tsbk.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, anncId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, anncId = %u", tsbk->toString(true).c_str(), srcId, dstId, isp->getAnnounceGroup()); } @@ -588,7 +588,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptrtoString(true), TSBKO::ISP_U_DEREG_REQ, srcId); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, sysId = $%03X, netId = $%05X", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, sysId = $%03X, netId = $%05X", tsbk->toString(true).c_str(), srcId, tsbk->getSysId(), tsbk->getNetId()); } @@ -605,7 +605,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptrtoString(true), TSBKO::IOSP_U_REG, srcId); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, sysId = $%03X, netId = $%05X", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, sysId = $%03X, netId = $%05X", tsbk->toString(true).c_str(), srcId, tsbk->getSysId(), tsbk->getNetId()); } @@ -637,7 +637,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr(tsbk.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, srcId = %u", tsbk->toString(true).c_str(), srcId); } @@ -752,7 +752,7 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr } if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X", tsbk->toString().c_str(), + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X", tsbk->toString().c_str(), osp->getAdjSiteSysId(), osp->getAdjSiteRFSSId(), osp->getAdjSiteId(), osp->getAdjSiteChnId(), osp->getAdjSiteChnNo(), osp->getAdjSiteSvcClass()); } @@ -774,7 +774,7 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr } if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X", tsbk->toString().c_str(), + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X", tsbk->toString().c_str(), osp->getAdjSiteSysId(), osp->getAdjSiteRFSSId(), osp->getAdjSiteId(), osp->getAdjSiteChnId(), osp->getAdjSiteChnNo(), osp->getAdjSiteSvcClass()); } @@ -801,7 +801,7 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr uint32_t chNo = tsbk->getGrpVchNo(); if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, chNo = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, chNo = %u, srcId = %u, dstId = %u", tsbk->toString().c_str(), chNo, srcId, dstId); } @@ -831,7 +831,7 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr if (m_p25->m_enableControl && m_p25->m_dedicatedControl) { if (!m_p25->m_affiliations->isGranted(dstId)) { if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", tsbk->toString(true).c_str(), tsbk->getEmergency(), tsbk->getEncrypted(), tsbk->getPriority(), tsbk->getGrpVchId(), tsbk->getGrpVchNo(), srcId, dstId); } @@ -852,7 +852,7 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr IOSP_UU_ANS* iosp = static_cast(tsbk.get()); if (iosp->getResponse() > 0U) { if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, response = $%02X, srcId = %u, dstId = %u", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, response = $%02X, srcId = %u, dstId = %u", tsbk->toString(true).c_str(), iosp->getResponse(), srcId, dstId); } } @@ -868,7 +868,7 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr IOSP_STS_UPDT* iosp = static_cast(tsbk.get()); if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, status = $%02X, srcId = %u", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, status = $%02X, srcId = %u", tsbk->toString(true).c_str(), iosp->getStatus(), srcId); } @@ -882,7 +882,7 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr IOSP_MSG_UPDT* iosp = static_cast(tsbk.get()); if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, message = $%02X, srcId = %u, dstId = %u", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, message = $%02X, srcId = %u, dstId = %u", tsbk->toString(true).c_str(), iosp->getMessage(), srcId, dstId); } @@ -933,7 +933,7 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr IOSP_ACK_RSP* iosp = static_cast(tsbk.get()); if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, AIV = %u, serviceType = $%02X, srcId = %u, dstId = %u", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, AIV = %u, serviceType = $%02X, srcId = %u, dstId = %u", tsbk->toString(true).c_str(), iosp->getAIV(), iosp->getService(), dstId, srcId); } @@ -947,7 +947,7 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr IOSP_EXT_FNCT* iosp = static_cast(tsbk.get()); if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, serviceType = $%02X, arg = %u, tgt = %u", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, serviceType = $%02X, arg = %u, tgt = %u", tsbk->toString(true).c_str(), iosp->getService(), srcId, dstId); } @@ -1070,7 +1070,7 @@ void ControlSignaling::writeAdjSSNetwork() osp->setAdjSiteSvcClass(m_p25->m_siteData.serviceClass()); if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, network announce, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X", osp->toString().c_str(), + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, network announce, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X", osp->toString().c_str(), m_p25->m_siteData.sysId(), m_p25->m_siteData.rfssId(), m_p25->m_siteData.siteId(), m_p25->m_siteData.channelId(), m_p25->m_siteData.channelNo(), m_p25->m_siteData.serviceClass()); } @@ -1107,7 +1107,7 @@ void ControlSignaling::writeRF_TSDU_Radio_Mon(uint32_t srcId, uint32_t dstId, ui iosp->setTxMult(txMult); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", iosp->toString().c_str(), srcId, dstId, txMult); + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, dstId = %u, txMult = %u", iosp->toString().c_str(), srcId, dstId, txMult); } ::ActivityLog("P25", true, "Radio Unit Monitor request from %u to %u", srcId, dstId); @@ -1135,7 +1135,7 @@ void ControlSignaling::writeRF_TSDU_Ext_Func(uint32_t func, uint32_t arg, uint32 } if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, mfId = $%02X, op = $%02X, arg = %u, tgt = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, mfId = $%02X, op = $%02X, arg = %u, tgt = %u", iosp->toString().c_str(), iosp->getMFId(), iosp->getExtendedFunction(), iosp->getSrcId(), iosp->getDstId()); } @@ -1409,7 +1409,7 @@ void ControlSignaling::writeRF_TDULC(lc::TDULC* lc, bool noNetwork) } //if (m_verbose) { - // LogMessage(LOG_RF, P25_TDULC_STR ", lc = $%02X, srcId = %u", m_rfTDULC.getLCO(), m_rfTDULC.getSrcId()); + // LogInfoEx(LOG_RF, P25_TDULC_STR ", lc = $%02X, srcId = %u", m_rfTDULC.getLCO(), m_rfTDULC.getSrcId()); //} } @@ -1438,7 +1438,7 @@ void ControlSignaling::writeNet_TDULC(lc::TDULC* lc) m_p25->addFrame(buffer, P25_TDULC_FRAME_LENGTH_BYTES + 2U, true); if (m_verbose) { - LogMessage(LOG_NET, P25_TDULC_STR ", lc = $%02X, srcId = %u", lc->getLCO(), lc->getSrcId()); + LogInfoEx(LOG_NET, P25_TDULC_STR ", lc = $%02X, srcId = %u", lc->getLCO(), lc->getSrcId()); } if (m_p25->m_voice->m_netFrames > 0) { @@ -1744,7 +1744,7 @@ void ControlSignaling::writeRF_TDULC_ChanRelease(bool grp, uint32_t srcId, uint3 } if (m_verbose) { - LogMessage(LOG_RF, P25_TDULC_STR ", CALL_TERM (Call Termination), srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_RF, P25_TDULC_STR ", CALL_TERM (Call Termination), srcId = %u, dstId = %u", srcId, dstId); } lc = std::make_unique(); @@ -2371,7 +2371,7 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_ osp->setForceChannelId(true); if (m_verbose) { - LogMessage((net) ? LOG_NET : LOG_RF, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", + LogInfoEx((net) ? LOG_NET : LOG_RF, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", osp->toString().c_str(), osp->getEmergency(), osp->getEncrypted(), osp->getPriority(), osp->getGrpVchId(), osp->getGrpVchNo(), osp->getSrcId(), osp->getDstId()); } @@ -2395,7 +2395,7 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (!voiceChData.isExplicitCh()) { if (m_verbose) { - LogMessage((net) ? LOG_NET : LOG_RF, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", + LogInfoEx((net) ? LOG_NET : LOG_RF, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", iosp->toString().c_str(), iosp->getEmergency(), iosp->getEncrypted(), iosp->getPriority(), iosp->getGrpVchId(), iosp->getGrpVchNo(), iosp->getSrcId(), iosp->getDstId()); } @@ -2475,7 +2475,7 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_ osp->setForceChannelId(true); if (m_verbose) { - LogMessage((net) ? LOG_NET : LOG_RF, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", + LogInfoEx((net) ? LOG_NET : LOG_RF, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", osp->toString().c_str(), osp->getEmergency(), osp->getEncrypted(), osp->getPriority(), osp->getGrpVchId(), osp->getGrpVchNo(), osp->getSrcId(), osp->getDstId()); } @@ -2499,7 +2499,7 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (!voiceChData.isExplicitCh()) { if (m_verbose) { - LogMessage((net) ? LOG_NET : LOG_RF, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", + LogInfoEx((net) ? LOG_NET : LOG_RF, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u, dstId = %u", iosp->toString().c_str(), iosp->getEmergency(), iosp->getEncrypted(), iosp->getPriority(), iosp->getGrpVchId(), iosp->getGrpVchNo(), iosp->getSrcId(), iosp->getDstId()); } @@ -2708,7 +2708,7 @@ bool ControlSignaling::writeRF_TSDU_SNDCP_Grant(uint32_t srcId, bool skip, uint3 ::ActivityLog("P25", true, "SNDCP grant request from %u", srcId); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, chNo = %u-%u, srcId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, chNo = %u-%u, srcId = %u", osp->toString().c_str(), voiceChData.chId(), osp->getDataChnNo(), osp->getSrcId()); } @@ -2750,7 +2750,7 @@ void ControlSignaling::writeRF_TSDU_ACK_FNE(uint32_t srcId, uint32_t service, bo } if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, EX = %u, serviceType = $%02X, srcId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, EX = %u, serviceType = $%02X, srcId = %u", iosp->toString().c_str(), iosp->getAIV(), iosp->getEX(), iosp->getService(), srcId); } @@ -2770,7 +2770,7 @@ void ControlSignaling::writeRF_TSDU_Deny(uint32_t srcId, uint32_t dstId, uint8_t osp->setGroup(grp); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", osp->toString().c_str(), osp->getAIV(), reason, P25Utils::denyRsnToString(reason).c_str(), osp->getSrcId(), osp->getDstId()); } @@ -2850,7 +2850,7 @@ uint8_t ControlSignaling::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstI if (iosp->getResponse() == ResponseCode::ACCEPT) { if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, anncId = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, anncId = %u, srcId = %u, dstId = %u", iosp->toString().c_str(), m_announcementGroup, srcId, dstId); } @@ -2867,7 +2867,7 @@ uint8_t ControlSignaling::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstI // is the RF talkgroup hang timer running? if (m_p25->m_rfTGHang.isRunning() && !m_p25->m_rfTGHang.hasExpired()) { if (m_verbose) { - LogMessage(LOG_RF, "talkgroup hang has terminated, lastDstId = %u", m_p25->m_rfLastDstId); + LogInfoEx(LOG_RF, "talkgroup hang has terminated, lastDstId = %u", m_p25->m_rfLastDstId); } m_p25->m_rfTGHang.stop(); @@ -2907,7 +2907,7 @@ void ControlSignaling::writeRF_TSDU_U_Reg_Rsp(uint32_t srcId, uint32_t sysId) if (iosp->getResponse() == ResponseCode::ACCEPT) { if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, sysId = $%03X", iosp->toString().c_str(), srcId, sysId); + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, sysId = $%03X", iosp->toString().c_str(), srcId, sysId); } ::ActivityLog("P25", true, "unit registration request from %u", srcId); @@ -2945,7 +2945,7 @@ void ControlSignaling::writeRF_TSDU_U_Dereg_Ack(uint32_t srcId) osp->setDstId(srcId); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u", osp->toString().c_str(), srcId); + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, srcId = %u", osp->toString().c_str(), srcId); } ::ActivityLog("P25", true, "unit deregistration request from %u", srcId); @@ -2970,7 +2970,7 @@ void ControlSignaling::writeRF_TSDU_Queue(uint32_t srcId, uint32_t dstId, uint8_ osp->setGroup(grp); if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X (%s), srcId = %u, dstId = %u", osp->toString().c_str(), osp->getAIV(), reason, P25Utils::queueRsnToString(reason).c_str(), osp->getSrcId(), osp->getDstId()); } @@ -3033,7 +3033,7 @@ bool ControlSignaling::writeRF_TSDU_Loc_Reg_Rsp(uint32_t srcId, uint32_t dstId, if (osp->getResponse() == ResponseCode::ACCEPT) { if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, dstId = %u", osp->toString().c_str(), srcId, dstId); + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, dstId = %u", osp->toString().c_str(), srcId, dstId); } ::ActivityLog("P25", true, "location registration request from %u", srcId); @@ -3077,7 +3077,7 @@ void ControlSignaling::writeRF_TSDU_Auth_Dmd(uint32_t srcId) m_llaDemandTable[srcId] = challenge; if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, RC = %X", osp->toString().c_str(), srcId, challenge); + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, RC = %X", osp->toString().c_str(), srcId, challenge); } writeRF_TSDU_AMBT(osp.get(), true); diff --git a/src/host/p25/packet/Data.cpp b/src/host/p25/packet/Data.cpp index a11b2052..b56cd0cc 100644 --- a/src/host/p25/packet/Data.cpp +++ b/src/host/p25/packet/Data.cpp @@ -124,7 +124,7 @@ bool Data::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", ISP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", m_rfDataHeader.getAckNeeded(), m_rfDataHeader.getOutbound(), m_rfDataHeader.getFormat(), m_rfDataHeader.getMFId(), m_rfDataHeader.getSAP(), m_rfDataHeader.getFullMessage(), m_rfDataHeader.getBlocksToFollow(), m_rfDataHeader.getPadLength(), m_rfDataHeader.getPacketLength(), m_rfDataHeader.getSynchronize(), m_rfDataHeader.getNs(), m_rfDataHeader.getFSN(), m_rfDataHeader.getLastFragment(), m_rfDataHeader.getHeaderOffset(), m_rfDataHeader.getLLId()); @@ -181,7 +181,7 @@ bool Data::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", ISP, extended address, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, extended address, sap = $%02X, srcLlId = %u", m_rfDataHeader.getEXSAP(), m_rfDataHeader.getSrcLLId()); } @@ -236,7 +236,7 @@ bool Data::process(uint8_t* data, uint32_t len) m_rfDataHeader.decodeExtAddr(secondHeader); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", m_rfData[i].getSerialNo(), m_rfData[i].getFormat(), m_rfData[i].getLastBlock(), m_rfDataHeader.getEXSAP(), m_rfDataHeader.getSrcLLId()); } @@ -245,7 +245,7 @@ bool Data::process(uint8_t* data, uint32_t len) } else { if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u", (m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_rfData[i].getSerialNo() : m_rfDataBlockCnt, m_rfData[i].getFormat(), m_rfData[i].getLastBlock()); } @@ -309,12 +309,12 @@ bool Data::process(uint8_t* data, uint32_t len) // did we receive a response header? if (m_rfDataHeader.getFormat() == PDUFormatType::RSP) { - LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", m_rfDataHeader.getFormat(), m_rfDataHeader.getResponseClass(), m_rfDataHeader.getResponseType(), m_rfDataHeader.getResponseStatus(), m_rfDataHeader.getLLId(), m_rfDataHeader.getSrcLLId()); if (m_rfDataHeader.getResponseClass() == PDUAckClass::ACK && m_rfDataHeader.getResponseType() == PDUAckType::ACK) { - LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK, llId = %u, all blocks received OK, n = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK, llId = %u, all blocks received OK, n = %u", m_rfDataHeader.getLLId(), m_rfDataHeader.getResponseStatus()); if (m_retryPDUData != nullptr && m_retryPDUBitLength > 0U) { delete m_retryPDUData; @@ -327,20 +327,20 @@ bool Data::process(uint8_t* data, uint32_t len) if (m_rfDataHeader.getResponseClass() == PDUAckClass::NACK) { switch (m_rfDataHeader.getResponseType()) { case PDUAckType::NACK_ILLEGAL: - LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, illegal format, llId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, illegal format, llId = %u", m_rfDataHeader.getLLId()); break; case PDUAckType::NACK_PACKET_CRC: - LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet CRC error, llId = %u, n = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet CRC error, llId = %u, n = %u", m_rfDataHeader.getLLId(), m_rfDataHeader.getResponseStatus()); break; case PDUAckType::NACK_SEQ: case PDUAckType::NACK_OUT_OF_SEQ: - LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet out of sequence, llId = %u, seqNo = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet out of sequence, llId = %u, seqNo = %u", m_rfDataHeader.getLLId(), m_rfDataHeader.getResponseStatus()); break; case PDUAckType::NACK_UNDELIVERABLE: - LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet undeliverable, llId = %u, n = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet undeliverable, llId = %u, n = %u", m_rfDataHeader.getLLId(), m_rfDataHeader.getResponseStatus()); break; @@ -348,7 +348,7 @@ bool Data::process(uint8_t* data, uint32_t len) break; } } else if (m_rfDataHeader.getResponseClass() == PDUAckClass::ACK_RETRY) { - LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK RETRY, llId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK RETRY, llId = %u", m_rfDataHeader.getLLId()); // really this is supposed to check the bit field in the included response @@ -366,7 +366,7 @@ bool Data::process(uint8_t* data, uint32_t len) m_retryPDUBitLength = 0U; m_retryCount = 0U; - LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK RETRY, llId = %u, exceeded retries, undeliverable", + LogInfoEx(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(), m_rfDataHeader.getSrcLLId()); @@ -404,9 +404,9 @@ bool Data::process(uint8_t* data, uint32_t len) if (m_verbose) { if (opcode == P25_PDU_ARP_REQUEST) { - LogMessage(LOG_RF, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); + LogInfoEx(LOG_RF, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); } else if (opcode == P25_PDU_ARP_REPLY) { - LogMessage(LOG_RF, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); + LogInfoEx(LOG_RF, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); } } @@ -417,7 +417,7 @@ bool Data::process(uint8_t* data, uint32_t len) case PDUSAP::SNDCP_CTRL_DATA: { if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", SNDCP_CTRL_DATA (SNDCP Control Data), blocksToFollow = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", SNDCP_CTRL_DATA (SNDCP Control Data), blocksToFollow = %u", m_rfDataHeader.getBlocksToFollow()); } @@ -428,7 +428,7 @@ bool Data::process(uint8_t* data, uint32_t len) case PDUSAP::CONV_DATA_REG: { if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", CONV_DATA_REG (Conventional Data Registration), blocksToFollow = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", CONV_DATA_REG (Conventional Data Registration), blocksToFollow = %u", m_rfDataHeader.getBlocksToFollow()); } @@ -440,7 +440,7 @@ bool Data::process(uint8_t* data, uint32_t len) case PDUSAP::ENC_KMM: { if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", KMM (Key Management Message), blocksToFollow = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", KMM (Key Management Message), blocksToFollow = %u", m_rfDataHeader.getBlocksToFollow()); } @@ -450,7 +450,7 @@ bool Data::process(uint8_t* data, uint32_t len) case PDUSAP::TRUNK_CTRL: { if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", TRUNK_CTRL (Alternate MBT Packet), lco = $%02X, blocksToFollow = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", TRUNK_CTRL (Alternate MBT Packet), lco = $%02X, blocksToFollow = %u", m_rfDataHeader.getAMBTOpcode(), m_rfDataHeader.getBlocksToFollow()); } @@ -463,10 +463,10 @@ 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) { ::ActivityLog("P25", true, "RF data transmission from %u to %u, %u blocks", srcId, dstId, m_rfDataHeader.getBlocksToFollow()); - LogMessage(LOG_RF, "P25 Data Call, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_RF, "P25 Data Call, srcId = %u, dstId = %u", srcId, dstId); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", repeating PDU, llId = %u", (m_rfExtendedAddress) ? m_rfDataHeader.getSrcLLId() : m_rfDataHeader.getLLId()); + LogInfoEx(LOG_RF, P25_PDU_STR ", repeating PDU, llId = %u", (m_rfExtendedAddress) ? m_rfDataHeader.getSrcLLId() : m_rfDataHeader.getLLId()); } writeRF_PDU_Buffered(); // re-generate buffered PDU and send it on @@ -529,7 +529,7 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) } if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, hdrOffset = %u, llId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, hdrOffset = %u, llId = %u", m_netDataHeader.getAckNeeded(), m_netDataHeader.getOutbound(), m_netDataHeader.getFormat(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMessage(), m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadLength(), m_netDataHeader.getPacketLength(), m_netDataHeader.getSynchronize(), m_netDataHeader.getNs(), m_netDataHeader.getFSN(), m_netDataHeader.getHeaderOffset(), m_netDataHeader.getLLId()); @@ -568,31 +568,31 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) m_p25->m_netState = RS_NET_IDLE; if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", m_netDataHeader.getFormat(), m_netDataHeader.getResponseClass(), m_netDataHeader.getResponseType(), m_netDataHeader.getResponseStatus(), m_netDataHeader.getLLId(), m_netDataHeader.getSrcLLId()); if (m_netDataHeader.getResponseClass() == PDUAckClass::ACK && m_netDataHeader.getResponseType() == PDUAckType::ACK) { - LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP ACK, llId = %u, all blocks received OK, n = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP ACK, llId = %u, all blocks received OK, n = %u", m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); } else { if (m_netDataHeader.getResponseClass() == PDUAckClass::NACK) { switch (m_netDataHeader.getResponseType()) { case PDUAckType::NACK_ILLEGAL: - LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, illegal format, llId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, illegal format, llId = %u", m_netDataHeader.getLLId()); break; case PDUAckType::NACK_PACKET_CRC: - LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet CRC error, llId = %u, n = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet CRC error, llId = %u, n = %u", m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); break; case PDUAckType::NACK_SEQ: case PDUAckType::NACK_OUT_OF_SEQ: - LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet out of sequence, llId = %u, seqNo = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet out of sequence, llId = %u, seqNo = %u", m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); break; case PDUAckType::NACK_UNDELIVERABLE: - LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet undeliverable, llId = %u, n = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet undeliverable, llId = %u, n = %u", m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); break; @@ -650,7 +650,7 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) } if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, extended address, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, extended address, sap = $%02X, srcLlId = %u", m_netDataHeader.getEXSAP(), m_netDataHeader.getSrcLLId()); } @@ -691,14 +691,14 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) m_netDataHeader.decodeExtAddr(secondHeader); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", m_netData[i].getSerialNo(), m_netData[i].getFormat(), m_netData[i].getLastBlock(), m_netDataHeader.getEXSAP(), m_netDataHeader.getSrcLLId()); } m_netExtendedAddress = true; } else { - LogMessage(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u", (m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_netData[i].getSerialNo() : m_netDataBlockCnt, m_netData[i].getFormat(), m_netData[i].getLastBlock()); } @@ -760,9 +760,9 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) if (m_verbose) { if (opcode == P25_PDU_ARP_REQUEST) { - LogMessage(LOG_NET, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); + LogInfoEx(LOG_NET, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); } else if (opcode == P25_PDU_ARP_REPLY) { - LogMessage(LOG_NET, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); + LogInfoEx(LOG_NET, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); } } @@ -771,10 +771,10 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) break; default: ::ActivityLog("P25", false, "Net data transmission from %u to %u, %u blocks", srcId, dstId, m_netDataHeader.getBlocksToFollow()); - LogMessage(LOG_NET, "P25 Data Call, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_NET, "P25 Data Call, srcId = %u, dstId = %u", srcId, dstId); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", transmitting network PDU, llId = %u", (m_netExtendedAddress) ? m_netDataHeader.getSrcLLId() : m_netDataHeader.getLLId()); + LogInfoEx(LOG_NET, P25_PDU_STR ", transmitting network PDU, llId = %u", (m_netExtendedAddress) ? m_netDataHeader.getSrcLLId() : m_netDataHeader.getLLId()); } writeNet_PDU_Buffered(); // re-generate buffered PDU and send it on @@ -837,7 +837,7 @@ void Data::writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(), dataHeader.getSynchronize(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), dataHeader.getHeaderOffset(), bitLength, dataHeader.getLLId()); @@ -868,7 +868,7 @@ void Data::writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, blocksToFollow--; if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } } @@ -878,7 +878,7 @@ void Data::writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, dataHeader.encodeExtAddr(pduUserData); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", OSP, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } } @@ -896,7 +896,7 @@ void Data::writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, dataBlock.setLastBlock((i + 1U) == blocksToFollow); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u", (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? dataBlock.getSerialNo() : i, dataBlock.getFormat(), dataBlock.getLastBlock()); } @@ -929,7 +929,7 @@ void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", FNE OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(), dataHeader.getSynchronize(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), dataHeader.getHeaderOffset(), bitLength, dataHeader.getLLId()); @@ -960,7 +960,7 @@ void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, blocksToFollow--; if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", FNE OSP, extended address, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, extended address, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } } @@ -970,7 +970,7 @@ void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, dataHeader.encodeExtAddr(pduUserData); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", FNE OSP, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } } @@ -988,7 +988,7 @@ void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, dataBlock.setLastBlock((i + 1U) == blocksToFollow); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", FNE OSP, block %u, fmt = $%02X, lastBlock = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, block %u, fmt = $%02X, lastBlock = %u", (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? dataBlock.getSerialNo() : i, dataBlock.getFormat(), dataBlock.getLastBlock()); } @@ -1044,7 +1044,7 @@ void Data::clock(uint32_t ms) m_sndcpReadyTimers[llId].start(); m_sndcpStateTable[llId] = SNDCPState::READY_S; if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", SNDCP, llId = %u, state = %u", llId, (uint8_t)state); + LogInfoEx(LOG_RF, P25_PDU_STR ", SNDCP, llId = %u, state = %u", llId, (uint8_t)state); } } } @@ -1056,7 +1056,7 @@ void Data::clock(uint32_t ms) m_sndcpStateTable[llId] = SNDCPState::IDLE; if (m_verbose) { - LogMessage(LOG_RF, P25_TDULC_STR ", CALL_TERM (Call Termination), llId = %u", llId); + LogInfoEx(LOG_RF, P25_TDULC_STR ", CALL_TERM (Call Termination), llId = %u", llId); } std::unique_ptr lc = std::make_unique(); @@ -1099,7 +1099,7 @@ void Data::sndcpInitialize(uint32_t llId) m_sndcpStandbyTimers[llId] = Timer(1000U, SNDCP_STANDBY_TIMEOUT); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", SNDCP, first initialize, llId = %u, state = %u", llId, (uint8_t)SNDCPState::IDLE); + LogInfoEx(LOG_RF, P25_PDU_STR ", SNDCP, first initialize, llId = %u, state = %u", llId, (uint8_t)SNDCPState::IDLE); } } } @@ -1122,7 +1122,7 @@ void Data::sndcpReset(uint32_t llId, bool callTerm) { if (isSNDCPInitialized(llId)) { if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", SNDCP, reset, llId = %u, state = %u", llId, (uint8_t)m_sndcpStateTable[llId]); + LogInfoEx(LOG_RF, P25_PDU_STR ", SNDCP, reset, llId = %u, state = %u", llId, (uint8_t)m_sndcpStateTable[llId]); } m_sndcpStateTable[llId] = SNDCPState::CLOSED; @@ -1131,7 +1131,7 @@ void Data::sndcpReset(uint32_t llId, bool callTerm) if (callTerm) { if (m_verbose) { - LogMessage(LOG_RF, P25_TDULC_STR ", CALL_TERM (Call Termination), llId = %u", llId); + LogInfoEx(LOG_RF, P25_TDULC_STR ", CALL_TERM (Call Termination), llId = %u", llId); } std::unique_ptr lc = std::make_unique(); @@ -1236,7 +1236,7 @@ bool Data::processConvDataReg(const uint8_t* pduUserData) uint32_t ipAddr = (pduUserData[8U] << 24) + (pduUserData[9U] << 16) + (pduUserData[10U] << 8) + pduUserData[11U]; if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", CONNECT (Registration Request Connect), llId = %u, ipAddr = %s", llId, __IP_FROM_UINT(ipAddr).c_str()); + LogInfoEx(LOG_RF, P25_PDU_STR ", CONNECT (Registration Request Connect), llId = %u, ipAddr = %s", llId, __IP_FROM_UINT(ipAddr).c_str()); } if (!acl::AccessControl::validateSrcId(llId)) { @@ -1250,7 +1250,7 @@ bool Data::processConvDataReg(const uint8_t* pduUserData) } if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", ACCEPT (Registration Response Accept), llId = %u, ipAddr = %s", llId, __IP_FROM_UINT(ipAddr).c_str()); + LogInfoEx(LOG_RF, P25_PDU_STR ", ACCEPT (Registration Response Accept), llId = %u, ipAddr = %s", llId, __IP_FROM_UINT(ipAddr).c_str()); } writeRF_PDU_Reg_Response(PDURegType::ACCEPT, llId, ipAddr); @@ -1262,7 +1262,7 @@ bool Data::processConvDataReg(const uint8_t* pduUserData) uint32_t llId = (pduUserData[1U] << 16) + (pduUserData[2U] << 8) + pduUserData[3U]; if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", DISCONNECT (Registration Request Disconnect), llId = %u", llId); + LogInfoEx(LOG_RF, P25_PDU_STR ", DISCONNECT (Registration Request Disconnect), llId = %u", llId); } // acknowledge @@ -1312,7 +1312,7 @@ bool Data::processSNDCPControl(const uint8_t* pduUserData) { SNDCPCtxActRequest* isp = static_cast(packet.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", SNDCP context activation request, llId = %u, nsapi = %u, ipAddr = %s, nat = $%02X, dsut = $%02X, mdpco = $%02X", llId, + LogInfoEx(LOG_RF, P25_PDU_STR ", SNDCP context activation request, llId = %u, nsapi = %u, ipAddr = %s, nat = $%02X, dsut = $%02X, mdpco = $%02X", llId, isp->getNSAPI(), __IP_FROM_UINT(isp->getIPAddress()).c_str(), isp->getNAT(), isp->getDSUT(), isp->getMDPCO()); } @@ -1415,7 +1415,7 @@ bool Data::processSNDCPControl(const uint8_t* pduUserData) { SNDCPCtxDeactivation* isp = static_cast(packet.get()); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", SNDCP context deactivation request, llId = %u, deactType = %02X", llId, + LogInfoEx(LOG_RF, P25_PDU_STR ", SNDCP context deactivation request, llId = %u, deactType = %02X", llId, isp->getDeactType()); } @@ -1476,7 +1476,7 @@ void Data::writeRF_PDU(const uint8_t* pdu, uint32_t bitLength, bool imm, bool ac m_retryPDUData = new uint8_t[retryByteLength]; ::memcpy(m_retryPDUData, pdu, retryByteLength); } else { - LogMessage(LOG_RF, P25_PDU_STR ", OSP, ack retry, bitLength = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, ack retry, bitLength = %u", m_retryPDUBitLength); } @@ -1529,7 +1529,7 @@ void Data::writeNet_PDU_Buffered() uint32_t blocksToFollow = m_netDataHeader.getBlocksToFollow(); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", m_netDataHeader.getAckNeeded(), m_netDataHeader.getOutbound(), m_netDataHeader.getFormat(), m_netDataHeader.getMFId(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMessage(), m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadLength(), m_netDataHeader.getNs(), m_netDataHeader.getFSN(), m_netDataHeader.getLastFragment(), m_netDataHeader.getHeaderOffset(), bitLength, m_netDataHeader.getLLId()); @@ -1558,7 +1558,7 @@ void Data::writeNet_PDU_Buffered() blocksToFollow--; if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", m_netDataHeader.getEXSAP(), m_netDataHeader.getSrcLLId()); } } @@ -1568,7 +1568,7 @@ void Data::writeNet_PDU_Buffered() m_netDataHeader.encodeExtAddr(m_netPduUserData); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", m_netDataHeader.getEXSAP(), m_netDataHeader.getSrcLLId()); } } @@ -1586,7 +1586,7 @@ void Data::writeNet_PDU_Buffered() m_netData[i].setData(m_netPduUserData + dataOffset); if (m_verbose) { - LogMessage(LOG_NET, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u", (m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_netData[i].getSerialNo() : i, m_netData[i].getFormat(), m_netData[i].getLastBlock()); } @@ -1621,7 +1621,7 @@ void Data::writeRF_PDU_Buffered() uint32_t blocksToFollow = m_rfDataHeader.getBlocksToFollow(); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", m_rfDataHeader.getAckNeeded(), m_rfDataHeader.getOutbound(), m_rfDataHeader.getFormat(), m_rfDataHeader.getMFId(), m_rfDataHeader.getSAP(), m_rfDataHeader.getFullMessage(), m_rfDataHeader.getBlocksToFollow(), m_rfDataHeader.getPadLength(), m_rfDataHeader.getNs(), m_rfDataHeader.getFSN(), m_rfDataHeader.getLastFragment(), m_rfDataHeader.getHeaderOffset(), bitLength, m_rfDataHeader.getLLId()); @@ -1650,7 +1650,7 @@ void Data::writeRF_PDU_Buffered() blocksToFollow--; if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", m_rfDataHeader.getEXSAP(), m_rfDataHeader.getSrcLLId()); } } @@ -1660,7 +1660,7 @@ void Data::writeRF_PDU_Buffered() m_rfDataHeader.encodeExtAddr(m_rfPduUserData); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", m_rfDataHeader.getEXSAP(), m_rfDataHeader.getSrcLLId()); } } @@ -1679,7 +1679,7 @@ void Data::writeRF_PDU_Buffered() m_rfData[i].setLastBlock((i + 1U) == blocksToFollow); if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u", (m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_rfData[i].getSerialNo() : i, m_rfData[i].getFormat(), m_rfData[i].getLastBlock()); } @@ -1772,7 +1772,7 @@ 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, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLLId = %u", + LogInfoEx(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()); } diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index 6803faa0..989a65e3 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -129,7 +129,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, P25_HDU_STR ", HDU_BSDWNACT, dstId = %u, algo = $%02X, kid = $%04X", lc.getDstId(), lc.getAlgId(), lc.getKId()); + LogInfoEx(LOG_RF, P25_HDU_STR ", HDU_BSDWNACT, dstId = %u, algo = $%02X, kid = $%04X", lc.getDstId(), lc.getAlgId(), lc.getKId()); if (lc.getAlgId() != ALGO_UNENCRYPT) { uint8_t mi[MI_LENGTH_BYTES]; @@ -137,7 +137,7 @@ bool Voice::process(uint8_t* data, uint32_t len) lc.getMI(mi); - LogMessage(LOG_RF, P25_HDU_STR ", Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + LogInfoEx(LOG_RF, P25_HDU_STR ", Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); } } @@ -350,7 +350,7 @@ bool Voice::process(uint8_t* data, uint32_t len) // bryanb: due to moronic reasons -- if this case happens, default the RID to something sane if (srcId == 0U && !lc.isStandardMFId()) { - LogMessage(LOG_RF, P25_HDU_STR " ** source RID was 0 with non-standard MFId defaulting source RID, dstId = %u, mfId = $%02X", dstId, lc.getMFId()); + LogInfoEx(LOG_RF, P25_HDU_STR " ** source RID was 0 with non-standard MFId defaulting source RID, dstId = %u, mfId = $%02X", dstId, lc.getMFId()); srcId = WUID_FNE; } @@ -363,7 +363,7 @@ bool Voice::process(uint8_t* data, uint32_t len) if (!group) controlByte |= network::NET_CTRL_U2U; // Unit-to-unit Flag - LogMessage(LOG_RF, P25_HDU_STR " remote grant demand, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_RF, P25_HDU_STR " remote grant demand, srcId = %u, dstId = %u", srcId, dstId); m_p25->m_network->writeP25TDU(lc, m_rfLSD, controlByte); } } @@ -374,7 +374,7 @@ bool Voice::process(uint8_t* data, uint32_t len) m_lastRejectId = 0U; ::ActivityLog("P25", true, "RF %svoice transmission from %u to %s%u", encrypted ? "encrypted ": "", srcId, group ? "TG " : "", dstId); - LogMessage(LOG_RF, "P25 Voice Call, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_RF, "P25 Voice Call, srcId = %u, dstId = %u", srcId, dstId); uint8_t serviceOptions = (m_rfLC.getEmergency() ? 0x80U : 0x00U) + // Emergency Flag (m_rfLC.getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag @@ -512,7 +512,7 @@ bool Voice::process(uint8_t* data, uint32_t len) frameType = FrameType::HDU_VALID; if (m_verbose) { - LogMessage(LOG_RF, P25_HDU_STR ", dstId = %u, algo = $%02X, kid = $%04X", m_rfLC.getDstId(), m_rfLC.getAlgId(), m_rfLC.getKId()); + LogInfoEx(LOG_RF, P25_HDU_STR ", dstId = %u, algo = $%02X, kid = $%04X", m_rfLC.getDstId(), m_rfLC.getAlgId(), m_rfLC.getKId()); } } else { @@ -764,7 +764,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, P25_LDU1_STR ", audio, mfId = $%02X srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, errs = %u/1233 (%.1f%%)", + LogInfoEx(LOG_RF, P25_LDU1_STR ", audio, mfId = $%02X srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, errs = %u/1233 (%.1f%%)", m_rfLC.getMFId(), m_rfLC.getSrcId(), m_rfLC.getDstId(), m_rfLC.getGroup(), m_rfLC.getEmergency(), m_rfLC.getEncrypted(), m_rfLC.getPriority(), errors, float(errors) / 12.33F); } @@ -881,7 +881,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, P25_LDU2_STR ", audio, algo = $%02X, kid = $%04X, errs = %u/1233 (%.1f%%)", + LogInfoEx(LOG_RF, P25_LDU2_STR ", audio, algo = $%02X, kid = $%04X, errs = %u/1233 (%.1f%%)", m_rfLC.getAlgId(), m_rfLC.getKId(), errors, float(errors) / 12.33F); if (m_rfLC.getAlgId() != ALGO_UNENCRYPT) { @@ -890,7 +890,7 @@ bool Voice::process(uint8_t* data, uint32_t len) m_rfLC.getMI(mi); - LogMessage(LOG_RF, P25_LDU2_STR ", Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + LogInfoEx(LOG_RF, P25_LDU2_STR ", Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); } } @@ -978,7 +978,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, P25_HDU_STR ", dstId = %u, algo = $%02X, kid = $%04X", m_rfLC.getDstId(), m_rfLC.getAlgId(), m_rfLC.getKId()); + LogInfoEx(LOG_RF, P25_HDU_STR ", dstId = %u, algo = $%02X, kid = $%04X", m_rfLC.getDstId(), m_rfLC.getAlgId(), m_rfLC.getKId()); } } else { @@ -1022,7 +1022,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, P25_VSELP1_STR ", audio"); + LogInfoEx(LOG_RF, P25_VSELP1_STR ", audio"); } return true; @@ -1064,7 +1064,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } if (m_verbose) { - LogMessage(LOG_RF, P25_VSELP2_STR ", audio"); + LogInfoEx(LOG_RF, P25_VSELP2_STR ", audio"); } return true; @@ -1107,7 +1107,7 @@ bool Voice::process(uint8_t* data, uint32_t len) float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits)); } - LogMessage(LOG_RF, P25_TDU_STR ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", + LogInfoEx(LOG_RF, P25_TDU_STR ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_rfFrames, m_rfBits, m_rfUndecodableLC, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); if (m_p25->m_dedicatedControl) { @@ -1677,7 +1677,7 @@ void Voice::writeNet_TDU() m_p25->addFrame(buffer, P25_TDU_FRAME_LENGTH_BYTES + 2U, true); if (m_verbose) { - LogMessage(LOG_NET, P25_TDU_STR ", srcId = %u", m_netLC.getSrcId()); + LogInfoEx(LOG_NET, P25_TDU_STR ", srcId = %u", m_netLC.getSrcId()); } if (m_netFrames > 0) { @@ -1741,7 +1741,7 @@ void Voice::writeNet_LDU1() if (m_netLastLDU1.getDstId() != 0U) { if (dstId != m_netLastLDU1.getDstId() && control.isStandardMFId()) { if (m_verbose) { - LogMessage(LOG_NET, P25_LDU1_STR ", dstId = %u doesn't match last LDU1 dstId = %u, fixing", + LogInfoEx(LOG_NET, P25_LDU1_STR ", dstId = %u doesn't match last LDU1 dstId = %u, fixing", dstId, m_netLastLDU1.getDstId()); } dstId = m_netLastLDU1.getDstId(); @@ -1752,7 +1752,7 @@ void Voice::writeNet_LDU1() if (m_netLastLDU1.getSrcId() != 0U) { if (srcId != m_netLastLDU1.getSrcId() && control.isStandardMFId()) { if (m_verbose) { - LogMessage(LOG_NET, P25_LDU1_STR ", srcId = %u doesn't match last LDU1 srcId = %u, fixing", + LogInfoEx(LOG_NET, P25_LDU1_STR ", srcId = %u doesn't match last LDU1 srcId = %u, fixing", srcId, m_netLastLDU1.getSrcId()); } srcId = m_netLastLDU1.getSrcId(); @@ -1760,7 +1760,7 @@ void Voice::writeNet_LDU1() } if (m_debug) { - LogMessage(LOG_NET, P25_LDU1_STR " service flags, emerg = %u, encrypt = %u, prio = %u, DFSI emerg = %u, DFSI encrypt = %u, DFSI prio = %u", + LogInfoEx(LOG_NET, P25_LDU1_STR " service flags, emerg = %u, encrypt = %u, prio = %u, DFSI emerg = %u, DFSI encrypt = %u, DFSI prio = %u", control.getEmergency(), control.getEncrypted(), control.getPriority(), m_dfsiLC.control()->getEmergency(), m_dfsiLC.control()->getEncrypted(), m_dfsiLC.control()->getPriority()); } @@ -1836,7 +1836,7 @@ void Voice::writeNet_LDU1() m_p25->writeRF_Preamble(); ::ActivityLog("P25", false, "network %svoice transmission from %u to %s%u", m_netLC.getEncrypted() ? "encrypted " : "", srcId, group ? "TG " : "", dstId); - LogMessage(LOG_NET, "P25 Voice Call, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_NET, "P25 Voice Call, srcId = %u, dstId = %u", srcId, dstId); // conventional registration or DVRS support? if (((m_p25->m_enableControl && !m_p25->m_dedicatedControl) || m_p25->m_voiceOnControl) && !m_p25->m_disableNetworkGrant) { @@ -1953,23 +1953,23 @@ void Voice::writeNet_LDU1() m_p25->addFrame(buffer, P25_HDU_FRAME_LENGTH_BYTES + 2U, true); if (m_verbose) { - LogMessage(LOG_NET, P25_HDU_STR ", dstId = %u, algo = $%02X, kid = $%04X", m_netLC.getDstId(), m_netLC.getAlgId(), m_netLC.getKId()); + LogInfoEx(LOG_NET, P25_HDU_STR ", dstId = %u, algo = $%02X, kid = $%04X", m_netLC.getDstId(), m_netLC.getAlgId(), m_netLC.getKId()); if (control.getAlgId() != ALGO_UNENCRYPT) { - LogMessage(LOG_NET, P25_HDU_STR ", Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + LogInfoEx(LOG_NET, P25_HDU_STR ", Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); } } } else { if (m_verbose) { - LogMessage(LOG_NET, P25_HDU_STR ", not transmitted; network HDU late entry, dstId = %u, algo = $%02X, kid = $%04X", m_netLC.getDstId(), m_netLC.getAlgId(), m_netLC.getKId()); + LogInfoEx(LOG_NET, P25_HDU_STR ", not transmitted; network HDU late entry, dstId = %u, algo = $%02X, kid = $%04X", m_netLC.getDstId(), m_netLC.getAlgId(), m_netLC.getKId()); } } } else { if (m_verbose) { - LogMessage(LOG_NET, P25_HDU_STR ", not transmitted; network HDU disabled, dstId = %u, algo = $%02X, kid = $%04X", m_netLC.getDstId(), m_netLC.getAlgId(), m_netLC.getKId()); + LogInfoEx(LOG_NET, P25_HDU_STR ", not transmitted; network HDU disabled, dstId = %u, algo = $%02X, kid = $%04X", m_netLC.getDstId(), m_netLC.getAlgId(), m_netLC.getKId()); } } } @@ -2095,7 +2095,7 @@ void Voice::writeNet_LDU1() m_p25->addFrame(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U, true); if (m_verbose) { - LogMessage(LOG_NET, P25_LDU1_STR " audio, mfId = $%02X, srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, sysId = $%03X, netId = $%05X", + LogInfoEx(LOG_NET, P25_LDU1_STR " audio, mfId = $%02X, srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, sysId = $%03X, netId = $%05X", m_netLC.getMFId(), m_netLC.getSrcId(), m_netLC.getDstId(), m_netLC.getGroup(), m_netLC.getEmergency(), m_netLC.getEncrypted(), m_netLC.getPriority(), sysId, netId); } @@ -2183,10 +2183,10 @@ void Voice::writeNet_LDU2() m_p25->addFrame(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U, true); if (m_verbose) { - LogMessage(LOG_NET, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", m_netLC.getAlgId(), m_netLC.getKId()); + LogInfoEx(LOG_NET, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", m_netLC.getAlgId(), m_netLC.getKId()); if (control.getAlgId() != ALGO_UNENCRYPT) { - LogMessage(LOG_NET, P25_LDU2_STR ", Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + LogInfoEx(LOG_NET, P25_LDU2_STR ", Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); } } diff --git a/src/host/setup/HostSetup.cpp b/src/host/setup/HostSetup.cpp index d9742e49..fa773ae0 100644 --- a/src/host/setup/HostSetup.cpp +++ b/src/host/setup/HostSetup.cpp @@ -287,7 +287,7 @@ bool HostSetup::portModemOpen(Modem* modem) } } - LogMessage(LOG_MODEM, "Modem Ready [Calibration Mode]"); + LogInfoEx(LOG_MODEM, "Modem Ready [Calibration Mode]"); // handled modem open return true; @@ -363,7 +363,7 @@ bool HostSetup::portModemHandler(Modem* modem, uint32_t ms, RESP_TYPE_DVM rspTyp short low = buffer[6U] << 8 | buffer[7U]; short diff = high - low; short centre = (high + low) / 2; - LogMessage(LOG_CAL, "Levels: inverted: %s, max: %d, min: %d, diff: %d, centre: %d", inverted ? "yes" : "no", high, low, diff, centre); + LogInfoEx(LOG_CAL, "Levels: inverted: %s, max: %d, min: %d, diff: %d, centre: %d", inverted ? "yes" : "no", high, low, diff, centre); } break; case CMD_RSSI_DATA: @@ -376,7 +376,7 @@ bool HostSetup::portModemHandler(Modem* modem, uint32_t ms, RESP_TYPE_DVM rspTyp uint16_t max = buffer[3U] << 8 | buffer[4U]; uint16_t min = buffer[5U] << 8 | buffer[6U]; uint16_t ave = buffer[7U] << 8 | buffer[8U]; - LogMessage(LOG_CAL, "RSSI: max: %u, min: %u, ave: %u", max, min, ave); + LogInfoEx(LOG_CAL, "RSSI: max: %u, min: %u, ave: %u", max, min, ave); } break; @@ -401,7 +401,7 @@ bool HostSetup::portModemHandler(Modem* modem, uint32_t ms, RESP_TYPE_DVM rspTyp break; } - LogMessage(LOG_CAL, "DMR Transmission lost, total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); + LogInfoEx(LOG_CAL, "DMR Transmission lost, total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); if (m_dmrEnabled) { m_berBits = 0U; @@ -429,7 +429,7 @@ bool HostSetup::portModemHandler(Modem* modem, uint32_t ms, RESP_TYPE_DVM rspTyp break; } - LogMessage(LOG_CAL, "P25 Transmission lost, total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); + LogInfoEx(LOG_CAL, "P25 Transmission lost, total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); if (m_p25Enabled) { m_berBits = 0U; @@ -460,7 +460,7 @@ bool HostSetup::portModemHandler(Modem* modem, uint32_t ms, RESP_TYPE_DVM rspTyp break; } - LogMessage(LOG_CAL, "NXDN Transmission lost, total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); + LogInfoEx(LOG_CAL, "NXDN Transmission lost, total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); if (m_nxdnEnabled) { m_berBits = 0U; @@ -500,7 +500,7 @@ bool HostSetup::portModemHandler(Modem* modem, uint32_t ms, RESP_TYPE_DVM rspTyp m_modem->m_nxdnSpace = buffer[11U] * (nxdn::defines::NXDN_FRAME_LENGTH_BYTES); if (m_hasFetchedStatus && m_requestedStatus) { - LogMessage(LOG_CAL, "Diagnostic Values [Modem State: %u, Transmitting: %d, ADC Overflow: %d, Rx Overflow: %d, Tx Overflow: %d, DAC Overflow: %d, HS: %u]", + LogInfoEx(LOG_CAL, "Diagnostic Values [Modem State: %u, Transmitting: %d, ADC Overflow: %d, Rx Overflow: %d, Tx Overflow: %d, DAC Overflow: %d, HS: %u]", modemState, tx, adcOverflow, rxOverflow, txOverflow, dacOverflow, m_isHotspot); } @@ -593,10 +593,10 @@ void HostSetup::saveConfig() } yaml::Serialize(m_conf, m_confFile.c_str(), yaml::SerializeConfig(4, 64, false, false)); - LogMessage(LOG_CAL, " - Saved configuration to %s", m_confFile.c_str()); + LogInfoEx(LOG_CAL, " - Saved configuration to %s", m_confFile.c_str()); if (m_isConnected) { if (writeFlash()) { - LogMessage(LOG_CAL, " - Wrote configuration area on modem"); + LogInfoEx(LOG_CAL, " - Wrote configuration area on modem"); } } } @@ -907,9 +907,9 @@ bool HostSetup::setTransmit() if (m_p25Enabled && m_p25TduTest) { if (m_transmit) - LogMessage(LOG_CAL, " - Modem start transmitting"); + LogInfoEx(LOG_CAL, " - Modem start transmitting"); else - LogMessage(LOG_CAL, " - Modem stop transmitting"); + LogInfoEx(LOG_CAL, " - Modem stop transmitting"); m_modem->clock(0U); return true; @@ -929,9 +929,9 @@ bool HostSetup::setTransmit() sleep(25U); if (m_transmit) - LogMessage(LOG_CAL, " - Modem start transmitting"); + LogInfoEx(LOG_CAL, " - Modem start transmitting"); else - LogMessage(LOG_CAL, " - Modem stop transmitting"); + LogInfoEx(LOG_CAL, " - Modem stop transmitting"); m_modem->clock(0U); @@ -971,7 +971,7 @@ void HostSetup::processDMRBER(const uint8_t* buffer, uint8_t seq) if (seq == 65U) { timerStart(); - LogMessage(LOG_CAL, "DMR voice header received"); + LogInfoEx(LOG_CAL, "DMR voice header received"); m_berBits = 0U; m_berErrs = 0U; @@ -982,7 +982,7 @@ void HostSetup::processDMRBER(const uint8_t* buffer, uint8_t seq) } else if (seq == 66U) { if (m_berFrames != 0U) { - LogMessage(LOG_CAL, "DMR voice end received, total frames: %d, total bits: %d, uncorrectable frames: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); + LogInfoEx(LOG_CAL, "DMR voice end received, total frames: %d, total bits: %d, uncorrectable frames: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); } timerStop(); @@ -1005,7 +1005,7 @@ void HostSetup::processDMRBER(const uint8_t* buffer, uint8_t seq) float ber = float(errs) / 1.41F; if (ber < 10.0F) - LogMessage(LOG_CAL, "DMR audio seq. %d, FEC BER %% (errs): %.3f%% (%u/141)", seq & 0x0FU, ber, errs); + LogInfoEx(LOG_CAL, "DMR audio seq. %d, FEC BER %% (errs): %.3f%% (%u/141)", seq & 0x0FU, ber, errs); else { LogWarning(LOG_CAL, "uncorrectable DMR audio seq. %d", seq & 0x0FU); m_berUncorrectable++; @@ -1040,7 +1040,7 @@ void HostSetup::processDMR1KBER(const uint8_t* buffer, uint8_t seq) m_berErrs += errs; m_berBits += 264; m_berFrames++; - LogMessage(LOG_CAL, "DMR voice header received, 1031 Test Pattern BER %% (errs): %.3f%% (%u/264)", float(errs) / 2.64F, errs); + LogInfoEx(LOG_CAL, "DMR voice header received, 1031 Test Pattern BER %% (errs): %.3f%% (%u/264)", float(errs) / 2.64F, errs); } else if (seq == 66U) { for (uint32_t i = 0U; i < 33U; i++) @@ -1051,7 +1051,7 @@ void HostSetup::processDMR1KBER(const uint8_t* buffer, uint8_t seq) m_berFrames++; if (m_berFrames != 0U) { - LogMessage(LOG_CAL, "DMR voice end received, total frames: %d, total bits: %d, uncorrectable frames: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); + LogInfoEx(LOG_CAL, "DMR voice end received, total frames: %d, total bits: %d, uncorrectable frames: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); } timerStop(); @@ -1085,7 +1085,7 @@ void HostSetup::processDMR1KBER(const uint8_t* buffer, uint8_t seq) m_berFrames++; if (ber < 10.0F) - LogMessage(LOG_CAL, "DMR audio seq. %d, 1031 Test Pattern BER %% (errs): %.3f%% (%u/264)", seq & 0x0FU, ber, errs); + LogInfoEx(LOG_CAL, "DMR audio seq. %d, 1031 Test Pattern BER %% (errs): %.3f%% (%u/264)", seq & 0x0FU, ber, errs); else { LogWarning(LOG_CAL, "uncorrectable DMR audio seq. %d", seq & 0x0FU); m_berUncorrectable++; @@ -1109,7 +1109,7 @@ void HostSetup::processP25BER(const uint8_t* buffer) for (uint8_t i = 0U; i < P25_SYNC_LENGTH_BYTES; i++) syncErrs += Utils::countBits8(sync[i] ^ P25_SYNC_BYTES[i]); - LogMessage(LOG_CAL, "P25, sync word, errs = %u, sync word = %02X %02X %02X %02X %02X %02X", syncErrs, + LogInfoEx(LOG_CAL, "P25, sync word, errs = %u, sync word = %02X %02X %02X %02X %02X %02X", syncErrs, sync[0U], sync[1U], sync[2U], sync[3U], sync[4U], sync[5U]); uint8_t nid[P25_NID_LENGTH_BYTES]; @@ -1130,14 +1130,14 @@ void HostSetup::processP25BER(const uint8_t* buffer) m_berUndecodableLC++; } else { - LogMessage(LOG_CAL, P25_HDU_STR ", dstId = %u, algo = %X, kid = %X", lc.getDstId(), lc.getAlgId(), lc.getKId()); + LogInfoEx(LOG_CAL, P25_HDU_STR ", dstId = %u, algo = %X, kid = %X", lc.getDstId(), lc.getAlgId(), lc.getKId()); uint8_t mi[MI_LENGTH_BYTES]; ::memset(mi, 0x00U, MI_LENGTH_BYTES); lc.getMI(mi); - LogMessage(LOG_CAL, P25_HDU_STR ", MI %02X %02X %02X %02X %02X %02X %02X %02X %02X", + LogInfoEx(LOG_CAL, P25_HDU_STR ", MI %02X %02X %02X %02X %02X %02X %02X %02X %02X", mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); } @@ -1149,7 +1149,7 @@ void HostSetup::processP25BER(const uint8_t* buffer) } else if (duid == DUID::TDU) { if (m_berFrames != 0U) { - LogMessage(LOG_CAL, P25_TDU_STR ", total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); + LogInfoEx(LOG_CAL, P25_TDU_STR ", total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); } timerStop(); @@ -1174,7 +1174,7 @@ void HostSetup::processP25BER(const uint8_t* buffer) m_berUndecodableLC++; } else { - LogMessage(LOG_CAL, P25_LDU1_STR " LC, mfId = $%02X, lco = $%02X, emerg = %u, encrypt = %u, prio = %u, group = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_CAL, P25_LDU1_STR " LC, mfId = $%02X, lco = $%02X, emerg = %u, encrypt = %u, prio = %u, group = %u, srcId = %u, dstId = %u", lc.getMFId(), lc.getLCO(), lc.getEmergency(), lc.getEncrypted(), lc.getPriority(), lc.getGroup(), lc.getSrcId(), lc.getDstId()); } @@ -1207,7 +1207,7 @@ void HostSetup::processP25BER(const uint8_t* buffer) float ber = float(errs) / 12.33F; if (ber < 10.0F) - LogMessage(LOG_CAL, P25_LDU1_STR ", audio FEC BER (errs): %.3f%% (%u/1233)", ber, errs); + LogInfoEx(LOG_CAL, P25_LDU1_STR ", audio FEC BER (errs): %.3f%% (%u/1233)", ber, errs); else { LogWarning(LOG_CAL, P25_LDU1_STR ", uncorrectable audio"); m_berUncorrectable++; @@ -1229,7 +1229,7 @@ void HostSetup::processP25BER(const uint8_t* buffer) m_berUndecodableLC++; } else { - LogMessage(LOG_CAL, P25_LDU2_STR " LC, mfId = $%02X, algo = %X, kid = %X", + LogInfoEx(LOG_CAL, P25_LDU2_STR " LC, mfId = $%02X, algo = %X, kid = %X", lc.getMFId(), lc.getAlgId(), lc.getKId()); uint8_t mi[MI_LENGTH_BYTES]; @@ -1237,7 +1237,7 @@ void HostSetup::processP25BER(const uint8_t* buffer) lc.getMI(mi); - LogMessage(LOG_CAL, P25_LDU2_STR ", MI %02X %02X %02X %02X %02X %02X %02X %02X %02X", + LogInfoEx(LOG_CAL, P25_LDU2_STR ", MI %02X %02X %02X %02X %02X %02X %02X %02X %02X", mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); } @@ -1270,7 +1270,7 @@ void HostSetup::processP25BER(const uint8_t* buffer) float ber = float(errs) / 12.33F; if (ber < 10.0F) - LogMessage(LOG_CAL, P25_LDU2_STR ", audio FEC BER (errs): %.3f%% (%u/1233)", ber, errs); + LogInfoEx(LOG_CAL, P25_LDU2_STR ", audio FEC BER (errs): %.3f%% (%u/1233)", ber, errs); else { LogWarning(LOG_CAL, P25_LDU2_STR ", uncorrectable audio"); m_berUncorrectable++; @@ -1307,7 +1307,7 @@ void HostSetup::processP25BER(const uint8_t* buffer) Utils::dump(1U, "P25, Unfixable PDU Data", pduBuffer, P25_PDU_FEC_LENGTH_BYTES); } else { - LogMessage(LOG_CAL, P25_PDU_STR ", ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u", + LogInfoEx(LOG_CAL, P25_PDU_STR ", ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u", dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), dataHeader.getHeaderOffset()); @@ -1327,7 +1327,7 @@ void HostSetup::processP25BER(const uint8_t* buffer) m_berUndecodableLC++; } else { - LogMessage(LOG_CAL, P25_TSDU_STR ", mfId = $%02X, lco = $%02X, srcId = %u, dstId = %u, service = %u, netId = %u, sysId = %u", + LogInfoEx(LOG_CAL, P25_TSDU_STR ", mfId = $%02X, lco = $%02X, srcId = %u, dstId = %u, service = %u, netId = %u, sysId = %u", tsbk->getMFId(), tsbk->getLCO(), tsbk->getSrcId(), tsbk->getDstId(), tsbk->getService(), tsbk->getNetId(), tsbk->getSysId()); } } @@ -1356,7 +1356,7 @@ void HostSetup::processP251KBER(const uint8_t* buffer) m_berUndecodableLC++; } else { - LogMessage(LOG_RF, P25_HDU_STR ", dstId = %u, algo = %X, kid = %X", lc.getDstId(), lc.getAlgId(), lc.getKId()); + LogInfoEx(LOG_RF, P25_HDU_STR ", dstId = %u, algo = %X, kid = %X", lc.getDstId(), lc.getAlgId(), lc.getKId()); } m_berBits = 0U; @@ -1367,7 +1367,7 @@ void HostSetup::processP251KBER(const uint8_t* buffer) } else if (duid == DUID::TDU) { if (m_berFrames != 0U) { - LogMessage(LOG_CAL, P25_TDU_STR ", total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); + LogInfoEx(LOG_CAL, P25_TDU_STR ", total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); } timerStop(); @@ -1392,7 +1392,7 @@ void HostSetup::processP251KBER(const uint8_t* buffer) m_berUndecodableLC++; } else { - LogMessage(LOG_CAL, P25_LDU1_STR " LC, mfId = $%02X, lco = $%02X, emerg = %u, encrypt = %u, prio = %u, group = %u, srcId = %u, dstId = %u", + LogInfoEx(LOG_CAL, P25_LDU1_STR " LC, mfId = $%02X, lco = $%02X, emerg = %u, encrypt = %u, prio = %u, group = %u, srcId = %u, dstId = %u", lc.getMFId(), lc.getLCO(), lc.getEmergency(), lc.getEncrypted(), lc.getPriority(), lc.getGroup(), lc.getSrcId(), lc.getDstId()); } @@ -1401,7 +1401,7 @@ void HostSetup::processP251KBER(const uint8_t* buffer) float ber = float(errs) / 12.33F; if (ber < 10.0F) - LogMessage(LOG_CAL, P25_LDU1_STR ", 1011 Test Pattern BER (errs): %.3f%% (%u/1233)", ber, errs); + LogInfoEx(LOG_CAL, P25_LDU1_STR ", 1011 Test Pattern BER (errs): %.3f%% (%u/1233)", ber, errs); else { LogWarning(LOG_CAL, P25_LDU1_STR ", uncorrectable audio"); m_berUncorrectable++; @@ -1423,7 +1423,7 @@ void HostSetup::processP251KBER(const uint8_t* buffer) m_berUndecodableLC++; } else { - LogMessage(LOG_CAL, P25_LDU2_STR " LC, mfId = $%02X, algo = %X, kid = %X", + LogInfoEx(LOG_CAL, P25_LDU2_STR " LC, mfId = $%02X, algo = %X, kid = %X", lc.getMFId(), lc.getAlgId(), lc.getKId()); } @@ -1432,7 +1432,7 @@ void HostSetup::processP251KBER(const uint8_t* buffer) float ber = float(errs) / 12.33F; if (ber < 10.0F) - LogMessage(LOG_CAL, P25_LDU2_STR ", 1011 Test Pattern BER (errs): %.3f%% (%u/1233)", ber, errs); + LogInfoEx(LOG_CAL, P25_LDU2_STR ", 1011 Test Pattern BER (errs): %.3f%% (%u/1233)", ber, errs); else { LogWarning(LOG_CAL, P25_LDU2_STR ", uncorrectable audio"); m_berUncorrectable++; @@ -1467,7 +1467,7 @@ void HostSetup::processNXDNBER(const uint8_t* buffer) if (usc == FuncChannelType::USC_SACCH_NS) { if (m_berFrames == 0U) { - LogMessage(LOG_CAL, "NXDN VCALL (Voice Call), BER Start"); + LogInfoEx(LOG_CAL, "NXDN VCALL (Voice Call), BER Start"); timerStart(); m_berErrs = 0U; @@ -1476,7 +1476,7 @@ void HostSetup::processNXDNBER(const uint8_t* buffer) return; } else { float ber = float(m_berErrs * 100U) / float(m_berBits); - LogMessage(LOG_CAL, "NXDN TX_REL (Transmission Release), BER Test, frames: %u, errs: %.3f%% (%u/%u)", m_berFrames, ber, m_berErrs, m_berBits); + LogInfoEx(LOG_CAL, "NXDN TX_REL (Transmission Release), BER Test, frames: %u, errs: %.3f%% (%u/%u)", m_berFrames, ber, m_berErrs, m_berBits); // handle displaying TUI updateTUIBER(ber); @@ -1502,7 +1502,7 @@ void HostSetup::processNXDNBER(const uint8_t* buffer) m_berFrames++; float ber = float(errors) / 1.88F; - LogMessage(LOG_CAL, "NXDN VCALL (Voice Call), BER Test, (errs): %.3f%% (%u/188)", ber, errors); + LogInfoEx(LOG_CAL, "NXDN VCALL (Voice Call), BER Test, (errs): %.3f%% (%u/188)", ber, errors); // handle displaying TUI updateTUIBER(ber); @@ -1813,7 +1813,7 @@ bool HostSetup::readFlash() void HostSetup::processFlashConfig(const uint8_t *buffer) { if (m_updateConfigFromModem) { - LogMessage(LOG_CAL, " - Restoring local configuration from configuration area on modem"); + LogInfoEx(LOG_CAL, " - Restoring local configuration from configuration area on modem"); // general config m_modem->m_rxInvert = (buffer[3U] & 0x01U) == 0x01U; @@ -1949,7 +1949,7 @@ bool HostSetup::eraseFlash() sleep(1000U); m_updateConfigFromModem = false; - LogMessage(LOG_CAL, " - Erased configuration area on modem"); + LogInfoEx(LOG_CAL, " - Erased configuration area on modem"); m_modem->clock(0U); return true; @@ -2074,7 +2074,7 @@ bool HostSetup::writeFlash() void HostSetup::writeBootload() { m_reqBootload = true; - LogMessage(LOG_CAL, "Rebooting modem into ST bootloader mode..."); + LogInfoEx(LOG_CAL, "Rebooting modem into ST bootloader mode..."); if (writeFlash()) { uint8_t buffer[4U]; @@ -2097,7 +2097,7 @@ void HostSetup::timerClock() m_timer += 1U; if (m_timer >= m_timeout) { - LogMessage(LOG_CAL, "Transmission lost, total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); + LogInfoEx(LOG_CAL, "Transmission lost, total frames: %d, bits: %d, uncorrectable frames: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_berFrames, m_berBits, m_berUncorrectable, m_berUndecodableLC, m_berErrs, float(m_berErrs * 100U) / float(m_berBits)); m_berBits = 0U; m_berErrs = 0U; diff --git a/src/host/setup/SetupMainWnd.h b/src/host/setup/SetupMainWnd.h index ddfe5be5..401b90d6 100644 --- a/src/host/setup/SetupMainWnd.h +++ b/src/host/setup/SetupMainWnd.h @@ -127,7 +127,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_p25Cal.addCallback("toggled", [&]() { @@ -144,7 +144,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_dmrLFCal.addCallback("toggled", [&]() { @@ -161,7 +161,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_dmrCal1K.addCallback("toggled", [&]() { @@ -178,7 +178,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_dmrDMOCal1K.addCallback("toggled", [&]() { @@ -195,7 +195,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_p25Cal1K.addCallback("toggled", [&]() { @@ -212,7 +212,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_p25TDUTest.addCallback("toggled", [&]() { @@ -229,7 +229,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { m_setup->m_queue.clear(); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_nxdnCal1K.addCallback("toggled", [&]() { @@ -248,7 +248,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); } else { @@ -267,7 +267,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(true); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_dmrFEC1K.addCallback("toggled", [&]() { @@ -282,7 +282,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(true); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_p25FEC.addCallback("toggled", [&]() { @@ -298,7 +298,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(true); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_p25FEC1K.addCallback("toggled", [&]() { @@ -314,7 +314,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(true); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_nxdnFEC.addCallback("toggled", [&]() { @@ -332,7 +332,7 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { updateDuplexState(); resetBERWnd(true); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); } else { @@ -351,42 +351,42 @@ class HOST_SW_API SetupMainWnd final : public finalcut::FWidget { resetBERWnd(); - LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); + LogInfoEx(LOG_CAL, " - %s", m_setup->m_modeStr.c_str()); m_setup->writeConfig(); }); m_toggleTxInvert.addCallback("toggled", this, [&]() { if (!m_setup->m_isHotspot) { m_setup->m_modem->m_txInvert = !m_setup->m_modem->m_txInvert; - LogMessage(LOG_CAL, "Tx Invert: %s", m_setup->m_modem->m_txInvert ? "on" : "off"); + LogInfoEx(LOG_CAL, "Tx Invert: %s", m_setup->m_modem->m_txInvert ? "on" : "off"); m_setup->writeConfig(); } }); m_toggleRxInvert.addCallback("toggled", this, [&]() { if (!m_setup->m_isHotspot) { m_setup->m_modem->m_rxInvert = !m_setup->m_modem->m_rxInvert; - LogMessage(LOG_CAL, "Rx Invert: %s", m_setup->m_modem->m_rxInvert ? "on" : "off"); + LogInfoEx(LOG_CAL, "Rx Invert: %s", m_setup->m_modem->m_rxInvert ? "on" : "off"); m_setup->writeConfig(); } }); m_togglePTTInvert.addCallback("toggled", this, [&]() { if (!m_setup->m_isHotspot) { m_setup->m_modem->m_pttInvert = !m_setup->m_modem->m_pttInvert; - LogMessage(LOG_CAL, "PTT Invert: %s", m_setup->m_modem->m_pttInvert ? "on" : "off"); + LogInfoEx(LOG_CAL, "PTT Invert: %s", m_setup->m_modem->m_pttInvert ? "on" : "off"); m_setup->writeConfig(); } }); m_toggleDCBlocker.addCallback("toggled", this, [&]() { if (!m_setup->m_isHotspot) { m_setup->m_modem->m_dcBlocker = !m_setup->m_modem->m_dcBlocker; - LogMessage(LOG_CAL, "DC Blocker: %s", m_setup->m_modem->m_dcBlocker ? "on" : "off"); + LogInfoEx(LOG_CAL, "DC Blocker: %s", m_setup->m_modem->m_dcBlocker ? "on" : "off"); m_setup->writeConfig(); } }); m_toggleDuplex.addCallback("toggled", this, [&]() { if (m_setup->m_isHotspot && m_setup->m_isConnected) { m_setup->m_duplex = !m_setup->m_duplex; - LogMessage(LOG_CAL, "Hotspot Rx: %s", m_setup->m_duplex ? "Rx Antenna" : "Tx Antenna"); + LogInfoEx(LOG_CAL, "Hotspot Rx: %s", m_setup->m_duplex ? "Rx Antenna" : "Tx Antenna"); m_setup->writeConfig(); } }); diff --git a/src/patch/HostPatch.cpp b/src/patch/HostPatch.cpp index f9d12bfc..b5b57d5e 100644 --- a/src/patch/HostPatch.cpp +++ b/src/patch/HostPatch.cpp @@ -681,7 +681,7 @@ void HostPatch::processDMRNetwork(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); m_rxStartTime = now; - LogMessage(LOG_HOST, "DMR, call start, srcId = %u, dstId = %u, slot = %u", srcId, dstId, slotNo); + LogInfoEx(LOG_HOST, "DMR, call start, srcId = %u, dstId = %u, slot = %u", srcId, dstId, slotNo); } if (dataSync && (dataType == DataType::TERMINATOR_WITH_LC)) { @@ -710,7 +710,7 @@ void HostPatch::processDMRNetwork(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); uint64_t diff = now - m_rxStartTime; - LogMessage(LOG_HOST, "DMR, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); + LogInfoEx(LOG_HOST, "DMR, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); } m_callInProgress = false; @@ -729,7 +729,7 @@ void HostPatch::processDMRNetwork(uint8_t* buffer, uint32_t length) lc::FullLC fullLC = lc::FullLC(); lc = *fullLC.decode(data.get(), DataType::VOICE_LC_HEADER); - LogMessage(LOG_HOST, DMR_DT_VOICE_LC_HEADER ", slot = %u, srcId = %u, dstId = %u, FLCO = $%02X", m_srcSlot, + LogInfoEx(LOG_HOST, DMR_DT_VOICE_LC_HEADER ", slot = %u, srcId = %u, dstId = %u, FLCO = $%02X", m_srcSlot, lc.getSrcId(), lc.getDstId(), flco); // send DMR voice header @@ -778,7 +778,7 @@ void HostPatch::processDMRNetwork(uint8_t* buffer, uint32_t length) lc::FullLC fullLC = lc::FullLC(); lc = *fullLC.decodePI(data.get()); - LogMessage(LOG_HOST, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_srcSlot, + LogInfoEx(LOG_HOST, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_srcSlot, lc.getAlgId(), lc.getKId(), lc.getDstId()); // send DMR voice header @@ -842,7 +842,7 @@ void HostPatch::processDMRNetwork(uint8_t* buffer, uint32_t length) emb.encode(buffer); } - LogMessage(LOG_HOST, DMR_DT_VOICE ", srcId = %u, dstId = %u, slot = %u, seqNo = %u", srcId, dstId, m_srcSlot, seqNo); + LogInfoEx(LOG_HOST, DMR_DT_VOICE ", srcId = %u, dstId = %u, slot = %u, seqNo = %u", srcId, dstId, m_srcSlot, seqNo); // generate DMR network frame data::NetData dmrData; @@ -1005,7 +1005,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); m_rxStartTime = now; - LogMessage(LOG_HOST, "P25, call start, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_HOST, "P25, call start, srcId = %u, dstId = %u", srcId, dstId); if (m_grantDemand) { p25::lc::LC lc = p25::lc::LC(); @@ -1034,7 +1034,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) p25::data::LowSpeedData lsd = p25::data::LowSpeedData(); - LogMessage(LOG_HOST, P25_TDU_STR); + LogInfoEx(LOG_HOST, P25_TDU_STR); if (m_mmdvmP25Reflector) { m_mmdvmP25Net->writeTDU(); @@ -1048,7 +1048,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); uint64_t diff = now - m_rxStartTime; - LogMessage(LOG_HOST, "P25, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); + LogInfoEx(LOG_HOST, "P25, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); } m_rxStartTime = 0U; @@ -1119,7 +1119,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) dfsiLC.decodeLDU1(data.get() + count, netLDU + 204U); count += DFSI_LDU1_VOICE9_FRAME_LENGTH_BYTES; - LogMessage(LOG_NET, P25_LDU1_STR " audio, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_NET, P25_LDU1_STR " audio, srcId = %u, dstId = %u", srcId, dstId); if (tekAlgoId != ALGO_UNENCRYPT && tekKeyId != 0U) { cryptP25AudioFrame(netLDU, reverseEncrypt, 1U); @@ -1219,7 +1219,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) dfsiLC.decodeLDU2(data.get() + count, netLDU + 204U); count += DFSI_LDU2_VOICE18_FRAME_LENGTH_BYTES; - LogMessage(LOG_NET, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", dfsiLC.control()->getAlgId(), dfsiLC.control()->getKId()); + LogInfoEx(LOG_NET, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", dfsiLC.control()->getAlgId(), dfsiLC.control()->getKId()); if (tekAlgoId != ALGO_UNENCRYPT && tekKeyId != 0U) { cryptP25AudioFrame(netLDU, reverseEncrypt, 2U); @@ -1398,7 +1398,7 @@ void HostPatch::processTEKResponse(p25::kmm::KeyItem* ki, uint8_t algId, uint8_t return; if (algId == m_tekSrcAlgoId && ki->kId() == m_tekSrcKeyId) { - LogMessage(LOG_HOST, "Source TEK loaded, algId = $%02X, kId = $%04X, sln = $%04X", algId, ki->kId(), ki->sln()); + LogInfoEx(LOG_HOST, "Source TEK loaded, algId = $%02X, kId = $%04X, sln = $%04X", algId, ki->kId(), ki->sln()); UInt8Array tek = std::make_unique(keyLength); ki->getKey(tek.get()); @@ -1408,7 +1408,7 @@ void HostPatch::processTEKResponse(p25::kmm::KeyItem* ki, uint8_t algId, uint8_t } if (algId == m_tekDstAlgoId && ki->kId() == m_tekDstKeyId) { - LogMessage(LOG_HOST, "Destination TEK loaded, algId = $%02X, kId = $%04X, sln = $%04X", algId, ki->kId(), ki->sln()); + LogInfoEx(LOG_HOST, "Destination TEK loaded, algId = $%02X, kId = $%04X, sln = $%04X", algId, ki->kId(), ki->sln()); UInt8Array tek = std::make_unique(keyLength); ki->getKey(tek.get()); @@ -1453,7 +1453,7 @@ void HostPatch::writeNet_LDU1(bool toFNE) uint32_t dstId = GET_UINT24(m_netLDU1, 76U); uint32_t srcId = GET_UINT24(m_netLDU1, 101U); - LogMessage(LOG_HOST, "MMDVM P25, call start, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_HOST, "MMDVM P25, call start, srcId = %u, dstId = %u", srcId, dstId); lc::LC lc = lc::LC(); m_netLC = lc; @@ -1479,7 +1479,7 @@ void HostPatch::writeNet_LDU1(bool toFNE) lsd.setLSD1(m_netLDU1[201U]); lsd.setLSD2(m_netLDU1[202U]); - LogMessage(LOG_NET, "MMDVM " P25_LDU1_STR " audio, srcId = %u, dstId = %u", m_netLC.getSrcId(), m_netLC.getDstId()); + LogInfoEx(LOG_NET, "MMDVM " P25_LDU1_STR " audio, srcId = %u, dstId = %u", m_netLC.getSrcId(), m_netLC.getDstId()); if (m_debug) Utils::dump(1U, "P25, HostPatch::writeNet_LDU1(), MMDVM -> DVM LDU1", m_netLDU1, 9U * 25U); @@ -1535,7 +1535,7 @@ void HostPatch::writeNet_LDU2(bool toFNE) lsd.setLSD1(m_netLDU2[201U]); lsd.setLSD2(m_netLDU2[202U]); - LogMessage(LOG_NET, "MMDVM " P25_LDU2_STR " audio"); + LogInfoEx(LOG_NET, "MMDVM " P25_LDU2_STR " audio"); if (m_debug) Utils::dump(1U, "P25, HostPatch::writeNet_LDU2(), MMDVM -> DVM LDU2", m_netLDU2, 9U * 25U); @@ -1585,7 +1585,7 @@ void* HostPatch::threadNetworkProcess(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -1601,7 +1601,7 @@ void* HostPatch::threadNetworkProcess(void* arg) if (patch->m_tekSrcAlgoId != P25DEF::ALGO_UNENCRYPT && patch->m_tekSrcKeyId > 0U) { if (patch->m_p25SrcCrypto->getTEKLength() == 0U && !patch->m_requestedSrcTek) { patch->m_requestedSrcTek = true; - LogMessage(LOG_HOST, "Patch source TGID encryption enabled, requesting TEK from network."); + LogInfoEx(LOG_HOST, "Patch source TGID encryption enabled, requesting TEK from network."); patch->m_network->writeKeyReq(patch->m_tekSrcKeyId, patch->m_tekSrcAlgoId); } } @@ -1610,7 +1610,7 @@ void* HostPatch::threadNetworkProcess(void* arg) if (patch->m_tekDstAlgoId != P25DEF::ALGO_UNENCRYPT && patch->m_tekDstKeyId > 0U) { if (patch->m_p25DstCrypto->getTEKLength() == 0U && !patch->m_requestedDstTek) { patch->m_requestedDstTek = true; - LogMessage(LOG_HOST, "Patch destination TGID encryption enabled, requesting TEK from network."); + LogInfoEx(LOG_HOST, "Patch destination TGID encryption enabled, requesting TEK from network."); patch->m_network->writeKeyReq(patch->m_tekDstKeyId, patch->m_tekDstAlgoId); } } @@ -1637,7 +1637,7 @@ void* HostPatch::threadNetworkProcess(void* arg) Thread::sleep(1U); } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -1672,7 +1672,7 @@ void* HostPatch::threadMMDVMProcess(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -1775,7 +1775,7 @@ void* HostPatch::threadMMDVMProcess(void* arg) p25::data::LowSpeedData lsd = p25::data::LowSpeedData(); - LogMessage(LOG_HOST, "MMDVM " P25_TDU_STR); + LogInfoEx(LOG_HOST, "MMDVM " P25_TDU_STR); uint8_t controlByte = 0x00U; patch->m_network->writeP25TDU(patch->m_netLC, lsd, controlByte); @@ -1784,7 +1784,7 @@ void* HostPatch::threadMMDVMProcess(void* arg) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); uint64_t diff = now - patch->m_rxStartTime; - LogMessage(LOG_HOST, "MMDVM P25, call end, srcId = %u, dstId = %u, dur = %us", patch->m_netLC.getSrcId(), patch->m_netLC.getDstId(), diff / 1000U); + LogInfoEx(LOG_HOST, "MMDVM P25, call end, srcId = %u, dstId = %u, dur = %us", patch->m_netLC.getSrcId(), patch->m_netLC.getDstId(), diff / 1000U); } patch->m_rxStartTime = 0U; @@ -1812,7 +1812,7 @@ void* HostPatch::threadMMDVMProcess(void* arg) Thread::sleep(5U); } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/patch/mmdvm/P25Network.cpp b/src/patch/mmdvm/P25Network.cpp index 892cf6f1..131c4f4f 100644 --- a/src/patch/mmdvm/P25Network.cpp +++ b/src/patch/mmdvm/P25Network.cpp @@ -451,7 +451,7 @@ void P25Network::clock(uint32_t ms) return; if (!Socket::match(m_addr, address)) { - LogMessage(LOG_NET, "MMDVM, packet received from an invalid source"); + LogInfoEx(LOG_NET, "MMDVM, packet received from an invalid source"); return; } @@ -480,7 +480,7 @@ bool P25Network::open() return false; } - LogMessage(LOG_NET, "MMDVM, Opening P25 network connection"); + LogInfoEx(LOG_NET, "MMDVM, Opening P25 network connection"); return m_socket.open(m_addr); } @@ -491,5 +491,5 @@ void P25Network::close() { m_socket.close(); - LogMessage(LOG_NET, "MMDVM, Closing P25 network connection"); + LogInfoEx(LOG_NET, "MMDVM, Closing P25 network connection"); } diff --git a/src/peered/PeerEdMain.cpp b/src/peered/PeerEdMain.cpp index a2ea5d60..116ad27c 100644 --- a/src/peered/PeerEdMain.cpp +++ b/src/peered/PeerEdMain.cpp @@ -210,7 +210,7 @@ int main(int argc, char** argv) g_pidLookups = new PeerListLookup(g_iniFile, 0U, false); g_pidLookups->read(); - LogMessage(LOG_HOST, "Loaded peer ID file: %s", g_iniFile.c_str()); + LogInfoEx(LOG_HOST, "Loaded peer ID file: %s", g_iniFile.c_str()); // show and start the application wnd.show(); diff --git a/src/peered/PeerEdMainWnd.h b/src/peered/PeerEdMainWnd.h index 7aa11d94..f0ebff91 100644 --- a/src/peered/PeerEdMainWnd.h +++ b/src/peered/PeerEdMainWnd.h @@ -73,7 +73,7 @@ class HOST_SW_API PeerEdMainWnd final : public finalcut::FWidget { 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_pidLookups->reload(); m_wnd->loadListView(); LogMessage(LOG_HOST, "Loaded peer ID file: %s", g_iniFile.c_str()); }); + m_keyF5.addCallback("activate", this, [&]() { g_pidLookups->reload(); m_wnd->loadListView(); LogInfoEx(LOG_HOST, "Loaded peer ID file: %s", g_iniFile.c_str()); }); m_backupOnSave.setChecked(); @@ -123,7 +123,7 @@ class HOST_SW_API PeerEdMainWnd final : public finalcut::FWidget { { if (m_backupOnSave.isChecked()) { std::string bakFile = g_iniFile + ".bak"; - LogMessage(LOG_HOST, "Backing up existing file %s to %s", g_iniFile.c_str(), bakFile.c_str()); + LogInfoEx(LOG_HOST, "Backing up existing file %s to %s", g_iniFile.c_str(), bakFile.c_str()); copyFile(g_iniFile.c_str(), bakFile.c_str()); } diff --git a/src/peered/PeerEditWnd.h b/src/peered/PeerEditWnd.h index f42fd99f..4b7de34c 100644 --- a/src/peered/PeerEditWnd.h +++ b/src/peered/PeerEditWnd.h @@ -361,7 +361,7 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { return x.peerId() == m_origPeerId; }); if (it != peers.end()) { - LogMessage(LOG_HOST, "Updating peer %s (%u) to %s (%u)", it->peerAlias().c_str(), it->peerId(), m_rule.peerAlias().c_str(), m_rule.peerId()); + LogInfoEx(LOG_HOST, "Updating peer %s (%u) to %s (%u)", it->peerAlias().c_str(), it->peerId(), m_rule.peerAlias().c_str(), m_rule.peerId()); g_pidLookups->eraseEntry(m_origPeerId); lookups::PeerId entry = lookups::PeerId(m_rule.peerId(), m_rule.peerAlias(), m_rule.peerPassword(), false); @@ -395,9 +395,9 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { // add new peer if (m_saveCopy.isChecked()) { - LogMessage(LOG_HOST, "Copying Peer. Adding Peer %s (%u)", m_rule.peerAlias().c_str(), m_rule.peerId()); + LogInfoEx(LOG_HOST, "Copying Peer. Adding Peer %s (%u)", m_rule.peerAlias().c_str(), m_rule.peerId()); } else { - LogMessage(LOG_HOST, "Adding Peer %s (%u)", m_rule.peerAlias().c_str(), m_rule.peerId()); + LogInfoEx(LOG_HOST, "Adding Peer %s (%u)", m_rule.peerAlias().c_str(), m_rule.peerId()); } lookups::PeerId entry = lookups::PeerId(m_rule.peerId(), m_rule.peerAlias(), m_rule.peerPassword(), false); diff --git a/src/peered/PeerListWnd.h b/src/peered/PeerListWnd.h index f4a82d4e..e96cd2d1 100644 --- a/src/peered/PeerListWnd.h +++ b/src/peered/PeerListWnd.h @@ -306,7 +306,7 @@ class HOST_SW_API PeerListWnd final : public FDblDialog { if (m_selected.peerDefault()) return; - LogMessage(LOG_HOST, "Deleting peer ID %s (%u)", m_selected.peerAlias().c_str(), m_selected.peerId()); + LogInfoEx(LOG_HOST, "Deleting peer ID %s (%u)", m_selected.peerAlias().c_str(), m_selected.peerId()); g_pidLookups->eraseEntry(m_selected.peerId()); // bryanb: HACK -- use HackTheGibson to access the private current listview iterator to get the scroll position diff --git a/src/sysview/HostWS.cpp b/src/sysview/HostWS.cpp index d5bb75e6..cdabfbdb 100644 --- a/src/sysview/HostWS.cpp +++ b/src/sysview/HostWS.cpp @@ -533,7 +533,7 @@ void* HostWS::threadWebSocket(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -542,7 +542,7 @@ void* HostWS::threadWebSocket(void* arg) ws->m_wsServer.start_accept(); ws->m_wsServer.run(); - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/sysview/NodeStatusWnd.h b/src/sysview/NodeStatusWnd.h index ec94486b..9ac1a9ae 100644 --- a/src/sysview/NodeStatusWnd.h +++ b/src/sysview/NodeStatusWnd.h @@ -887,7 +887,7 @@ class HOST_SW_API NodeStatusWnd final : public FDblDialog { return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -909,7 +909,7 @@ class HOST_SW_API NodeStatusWnd final : public FDblDialog { } wnd->m_threadStopped = true; - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/sysview/SysViewMain.cpp b/src/sysview/SysViewMain.cpp index c828c5cd..1572e21b 100644 --- a/src/sysview/SysViewMain.cpp +++ b/src/sysview/SysViewMain.cpp @@ -288,7 +288,7 @@ void* threadNetworkPump(void* arg) return nullptr; } - LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -378,7 +378,7 @@ void* threadNetworkPump(void* arg) if (std::find_if(g_dmrStatus.begin(), g_dmrStatus.end(), [&](StatusMapPair& x) { return (x.second.dstId == dstId && x.second.slotNo == slotNo); }) != g_dmrStatus.end()) { g_dmrStatus.erase(dstId); - LogMessage(LOG_NET, "DMR, Call End, srcId = %u (%s), dstId = %u (%s), duration = %u", + LogInfoEx(LOG_NET, "DMR, Call End, srcId = %u (%s), dstId = %u (%s), duration = %u", srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str(), duration / 1000); } } @@ -400,7 +400,7 @@ void* threadNetworkPump(void* arg) status.slotNo = slotNo; g_dmrStatus[dstId] = status; // this *could* be an issue if a dstId appears on both slots somehow... - LogMessage(LOG_NET, "DMR, Call Start, srcId = %u (%s), dstId = %u (%s)", + LogInfoEx(LOG_NET, "DMR, Call Start, srcId = %u (%s), dstId = %u (%s)", srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); } } @@ -428,7 +428,7 @@ void* threadNetworkPump(void* arg) { lc::csbk::CSBK_BROADCAST* osp = static_cast(csbk.get()); if (osp->getAnncType() == DMRDEF::BroadcastAnncType::ANN_WD_TSCC) { - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, %s, sysId = $%03X, chNo = %u", dmrData.getSlotNo(), csbk->toString().c_str(), + LogInfoEx(LOG_NET, "DMR Slot %u, DT_CSBK, %s, sysId = $%03X, chNo = %u", dmrData.getSlotNo(), csbk->toString().c_str(), osp->getSystemId(), osp->getLogicalCh1()); // generate a net event for this @@ -445,7 +445,7 @@ void* threadNetworkPump(void* arg) break; default: { - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, %s, srcId = %u (%s), dstId = %u (%s)", dmrData.getSlotNo(), csbk->toString().c_str(), + LogInfoEx(LOG_NET, "DMR Slot %u, DT_CSBK, %s, srcId = %u (%s), dstId = %u (%s)", dmrData.getSlotNo(), csbk->toString().c_str(), srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); // generate a net event for this @@ -466,7 +466,7 @@ void* threadNetworkPump(void* arg) } if (g_debug) - LogMessage(LOG_NET, "DMR, slotNo = %u, seqNo = %u, flco = $%02X, srcId = %u, dstId = %u, len = %u", slotNo, seqNo, flco, srcId, dstId, length); + LogInfoEx(LOG_NET, "DMR, slotNo = %u, seqNo = %u, flco = $%02X, srcId = %u, dstId = %u, len = %u", slotNo, seqNo, flco, srcId, dstId, length); } UInt8Array p25Buffer = g_network->readP25(netReadRet, length); @@ -520,7 +520,7 @@ void* threadNetworkPump(void* arg) if (std::find_if(g_p25Status.begin(), g_p25Status.end(), [&](StatusMapPair& x) { return x.second.dstId == dstId; }) != g_p25Status.end()) { g_p25Status.erase(dstId); - LogMessage(LOG_NET, "P25, Call End, srcId = %u (%s), dstId = %u (%s), sysId = $%03X, netId = $%05X, duration = %u", + LogInfoEx(LOG_NET, "P25, Call End, srcId = %u (%s), dstId = %u (%s), sysId = $%03X, netId = $%05X, duration = %u", srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str(), sysId, netId, duration / 1000); } } @@ -541,7 +541,7 @@ void* threadNetworkPump(void* arg) status.dstId = dstId; g_p25Status[dstId] = status; - LogMessage(LOG_NET, "P25, Call Start, srcId = %u (%s), dstId = %u (%s), sysId = $%03X, netId = $%05X", + LogInfoEx(LOG_NET, "P25, Call Start, srcId = %u (%s), dstId = %u (%s), sysId = $%03X, netId = $%05X", srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str(), sysId, netId); } } @@ -551,7 +551,7 @@ void* threadNetworkPump(void* arg) case P25DEF::DUID::TDU: case P25DEF::DUID::TDULC: if (duid == P25DEF::DUID::TDU) { - LogMessage(LOG_NET, P25_TDU_STR ", srcId = %u (%s), dstId = %u (%s)", + LogInfoEx(LOG_NET, P25_TDU_STR ", srcId = %u (%s), dstId = %u (%s)", srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); } else { @@ -560,7 +560,7 @@ void* threadNetworkPump(void* arg) LogWarning(LOG_NET, P25_TDULC_STR ", undecodable TDULC"); } else { - LogMessage(LOG_NET, P25_TDULC_STR ", srcId = %u (%s), dstId = %u (%s)", + LogInfoEx(LOG_NET, P25_TDULC_STR ", srcId = %u (%s), dstId = %u (%s)", srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); } } @@ -585,7 +585,7 @@ void* threadNetworkPump(void* arg) case P25DEF::TSBKO::IOSP_GRP_VCH: case P25DEF::TSBKO::IOSP_UU_VCH: { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u (%s), dstId = %u (%s), sysId = $%03X, netId = $%05X", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u-%u, srcId = %u (%s), dstId = %u (%s), sysId = $%03X, netId = $%05X", tsbk->toString(true).c_str(), tsbk->getEmergency(), tsbk->getEncrypted(), tsbk->getPriority(), tsbk->getGrpVchId(), tsbk->getGrpVchNo(), srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str(), sysId, netId); @@ -617,7 +617,7 @@ void* threadNetworkPump(void* arg) { lc::tsbk::IOSP_UU_ANS* iosp = static_cast(tsbk.get()); if (iosp->getResponse() > 0U) { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, response = $%02X, srcId = %u (%s), dstId = %u (%s)", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, response = $%02X, srcId = %u (%s), dstId = %u (%s)", tsbk->toString(true).c_str(), iosp->getResponse(), srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); @@ -641,7 +641,7 @@ void* threadNetworkPump(void* arg) case P25DEF::TSBKO::IOSP_STS_UPDT: { lc::tsbk::IOSP_STS_UPDT* iosp = static_cast(tsbk.get()); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, status = $%02X, srcId = %u (%s)", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, status = $%02X, srcId = %u (%s)", tsbk->toString(true).c_str(), iosp->getStatus(), srcId, resolveRID(srcId).c_str()); // generate a net event for this @@ -663,7 +663,7 @@ void* threadNetworkPump(void* arg) case P25DEF::TSBKO::IOSP_MSG_UPDT: { lc::tsbk::IOSP_MSG_UPDT* iosp = static_cast(tsbk.get()); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, message = $%02X, srcId = %u (%s), dstId = %u (%s)", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, message = $%02X, srcId = %u (%s), dstId = %u (%s)", tsbk->toString(true).c_str(), iosp->getMessage(), srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); @@ -686,7 +686,7 @@ void* threadNetworkPump(void* arg) case P25DEF::TSBKO::IOSP_RAD_MON: { //lc::tsbk::IOSP_RAD_MON* iosp = static_cast(tsbk.get()); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s), dstId = %u (%s)", tsbk->toString(true).c_str(), + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s), dstId = %u (%s)", tsbk->toString(true).c_str(), srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); // generate a net event for this @@ -704,7 +704,7 @@ void* threadNetworkPump(void* arg) break; case P25DEF::TSBKO::IOSP_CALL_ALRT: { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s), dstId = %u (%s)", tsbk->toString(true).c_str(), + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s), dstId = %u (%s)", tsbk->toString(true).c_str(), srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); // generate a net event for this @@ -723,7 +723,7 @@ void* threadNetworkPump(void* arg) case P25DEF::TSBKO::IOSP_ACK_RSP: { lc::tsbk::IOSP_ACK_RSP* iosp = static_cast(tsbk.get()); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, AIV = %u, serviceType = $%02X, srcId = %u (%s), dstId = %u (%s)", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, AIV = %u, serviceType = $%02X, srcId = %u (%s), dstId = %u (%s)", tsbk->toString(true).c_str(), iosp->getAIV(), iosp->getService(), srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); @@ -746,7 +746,7 @@ void* threadNetworkPump(void* arg) case P25DEF::TSBKO::IOSP_EXT_FNCT: { lc::tsbk::IOSP_EXT_FNCT* iosp = static_cast(tsbk.get()); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, serviceType = $%02X, arg = %u, tgt = %u", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, serviceType = $%02X, arg = %u, tgt = %u", tsbk->toString(true).c_str(), iosp->getService(), srcId, dstId); // generate a net event for this @@ -769,7 +769,7 @@ void* threadNetworkPump(void* arg) // non-emergency mode is a TSBKO::OSP_DENY_RSP if (!tsbk->getEmergency()) { lc::tsbk::OSP_DENY_RSP* osp = static_cast(tsbk.get()); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X, srcId = %u (%s), dstId = %u (%s)", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, AIV = %u, reason = $%02X, srcId = %u (%s), dstId = %u (%s)", osp->toString().c_str(), osp->getAIV(), osp->getResponse(), osp->getSrcId(), resolveRID(osp->getSrcId()).c_str(), osp->getDstId(), resolveTGID(osp->getDstId()).c_str()); @@ -788,7 +788,7 @@ void* threadNetworkPump(void* arg) g_netDataEvent(netEvent); } } else { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s), dstId = %u (%s)", tsbk->toString().c_str(), + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s), dstId = %u (%s)", tsbk->toString().c_str(), srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); // generate a net event for this @@ -808,7 +808,7 @@ void* threadNetworkPump(void* arg) case P25DEF::TSBKO::IOSP_GRP_AFF: { lc::tsbk::OSP_GRP_AFF* iosp = static_cast(tsbk.get()); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, anncId = %u (%s), srcId = %u (%s), dstId = %u (%s), response = $%02X", tsbk->toString().c_str(), + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, anncId = %u (%s), srcId = %u (%s), dstId = %u (%s), response = $%02X", tsbk->toString().c_str(), iosp->getAnnounceGroup(), resolveTGID(iosp->getAnnounceGroup()).c_str(), srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str(), iosp->getResponse()); @@ -834,7 +834,7 @@ void* threadNetworkPump(void* arg) case P25DEF::TSBKO::OSP_U_DEREG_ACK: { //lc::tsbk::OSP_U_DEREG_ACK* iosp = static_cast(tsbk.get()); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s)", + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s)", tsbk->toString(true).c_str(), srcId, resolveRID(srcId).c_str()); // generate a net event for this @@ -850,7 +850,7 @@ void* threadNetworkPump(void* arg) case P25DEF::TSBKO::OSP_LOC_REG_RSP: { lc::tsbk::OSP_LOC_REG_RSP* osp = static_cast(tsbk.get()); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s), dstId = %u (%s)", osp->toString().c_str(), + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s), dstId = %u (%s)", osp->toString().c_str(), srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); // generate a net event for this @@ -869,7 +869,7 @@ void* threadNetworkPump(void* arg) case P25DEF::TSBKO::OSP_ADJ_STS_BCAST: { lc::tsbk::OSP_ADJ_STS_BCAST* osp = static_cast(tsbk.get()); - LogMessage(LOG_NET, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X", tsbk->toString().c_str(), + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, sysId = $%03X, rfss = $%02X, site = $%02X, chNo = %u-%u, svcClass = $%02X", tsbk->toString().c_str(), osp->getAdjSiteSysId(), osp->getAdjSiteRFSSId(), osp->getAdjSiteId(), osp->getAdjSiteChnId(), osp->getAdjSiteChnNo(), osp->getAdjSiteSvcClass()); // generate a net event for this @@ -893,7 +893,7 @@ void* threadNetworkPump(void* arg) break; default: { - LogMessage(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s), dstId = %u (%s)", tsbk->toString().c_str(), + LogInfoEx(LOG_NET, P25_TSDU_STR ", %s, srcId = %u (%s), dstId = %u (%s)", tsbk->toString().c_str(), srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); // generate a net event for this @@ -915,7 +915,7 @@ void* threadNetworkPump(void* arg) } if (g_debug) - LogMessage(LOG_NET, "P25, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u", duid, lco, MFId, srcId, dstId, length); + LogInfoEx(LOG_NET, "P25, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u", duid, lco, MFId, srcId, dstId, length); } UInt8Array nxdnBuffer = g_network->readNXDN(netReadRet, length); @@ -953,7 +953,7 @@ void* threadNetworkPump(void* arg) if (std::find_if(g_nxdnStatus.begin(), g_nxdnStatus.end(), [&](StatusMapPair& x) { return x.second.dstId == dstId; }) != g_nxdnStatus.end()) { g_nxdnStatus.erase(dstId); - LogMessage(LOG_NET, "NXDN, Call End, srcId = %u (%s), dstId = %u (%s), duration = %u", + LogInfoEx(LOG_NET, "NXDN, Call End, srcId = %u (%s), dstId = %u (%s), duration = %u", srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str(), duration / 1000); } } @@ -974,14 +974,14 @@ void* threadNetworkPump(void* arg) status.dstId = dstId; g_nxdnStatus[dstId] = status; - LogMessage(LOG_NET, "NXDN, Call Start, srcId = %u (%s), dstId = %u (%s)", + LogInfoEx(LOG_NET, "NXDN, Call Start, srcId = %u (%s), dstId = %u (%s)", srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); } } } if (g_debug) - LogMessage(LOG_NET, "NXDN, messageType = $%02X, srcId = %u, dstId = %u, len = %u", messageType, srcId, dstId, length); + LogInfoEx(LOG_NET, "NXDN, messageType = $%02X, srcId = %u, dstId = %u, len = %u", messageType, srcId, dstId, length); } UInt8Array analogBuffer = g_network->readAnalog(netReadRet, length); @@ -1006,7 +1006,7 @@ void* threadNetworkPump(void* arg) if (std::find_if(g_analogStatus.begin(), g_analogStatus.end(), [&](StatusMapPair& x) { return x.second.dstId == dstId; }) != g_analogStatus.end()) { g_analogStatus.erase(dstId); - LogMessage(LOG_NET, "Analog, Call End, srcId = %u (%s), dstId = %u (%s), duration = %u", + LogInfoEx(LOG_NET, "Analog, Call End, srcId = %u (%s), dstId = %u (%s), duration = %u", srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str(), duration / 1000); } } @@ -1027,13 +1027,13 @@ void* threadNetworkPump(void* arg) status.dstId = dstId; g_analogStatus[dstId] = status; - LogMessage(LOG_NET, "Analog, Call Start, srcId = %u (%s), dstId = %u (%s)", + LogInfoEx(LOG_NET, "Analog, Call Start, srcId = %u (%s), dstId = %u (%s)", srcId, resolveRID(srcId).c_str(), dstId, resolveTGID(dstId).c_str()); } } if (g_debug) - LogMessage(LOG_NET, "Analog, frameType = $%02X, srcId = %u, dstId = %u, len = %u", frameType, srcId, dstId, length); + LogInfoEx(LOG_NET, "Analog, frameType = $%02X, srcId = %u, dstId = %u, len = %u", frameType, srcId, dstId, length); } } @@ -1041,7 +1041,7 @@ void* threadNetworkPump(void* arg) Thread::sleep(1U); } - LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/sysview/TransmitWndBase.h b/src/sysview/TransmitWndBase.h index 5c5394d4..98460be7 100644 --- a/src/sysview/TransmitWndBase.h +++ b/src/sysview/TransmitWndBase.h @@ -250,7 +250,7 @@ class HOST_SW_API TransmitWndBase : public FDblDialog { csbk->setSrcId(arg); csbk->setDstId(dstId); - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, op = $%02X, arg = %u, tgt = %u", + LogInfoEx(LOG_RF, "DMR Slot %u, CSBK, %s, op = $%02X, arg = %u, tgt = %u", slot, csbk->toString().c_str(), func, arg, dstId); write_CSBK(slot, csbk.get()); @@ -320,7 +320,7 @@ class HOST_SW_API TransmitWndBase : public FDblDialog { iosp->setMFId(MFG_MOT); } - LogMessage(LOG_RF, P25_TSDU_STR ", %s, mfId = $%02X, op = $%02X, arg = %u, tgt = %u", + LogInfoEx(LOG_RF, P25_TSDU_STR ", %s, mfId = $%02X, op = $%02X, arg = %u, tgt = %u", iosp->toString().c_str(), iosp->getMFId(), iosp->getExtendedFunction(), iosp->getSrcId(), iosp->getDstId()); write_TSDU(iosp.get()); diff --git a/src/sysview/network/PeerNetwork.cpp b/src/sysview/network/PeerNetwork.cpp index 2f281a4d..eeaa9456 100644 --- a/src/sysview/network/PeerNetwork.cpp +++ b/src/sysview/network/PeerNetwork.cpp @@ -89,7 +89,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco std::string payload(rawPayload, rawPayload + (length - 11U)); if (g_debug) - LogMessage(LOG_NET, "Peer Status, peerId = %u", peerId); + LogInfoEx(LOG_NET, "Peer Status, peerId = %u", peerId); // parse JSON body json::value v; diff --git a/src/tged/TGEdMain.cpp b/src/tged/TGEdMain.cpp index da216e83..d8fc5a08 100644 --- a/src/tged/TGEdMain.cpp +++ b/src/tged/TGEdMain.cpp @@ -210,7 +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()); + LogInfoEx(LOG_HOST, "Loaded talkgroup rules file: %s", g_iniFile.c_str()); // show and start the application wnd.show(); diff --git a/src/tged/TGEdMainWnd.h b/src/tged/TGEdMainWnd.h index 6ed6f293..43388f4a 100644 --- a/src/tged/TGEdMainWnd.h +++ b/src/tged/TGEdMainWnd.h @@ -74,7 +74,7 @@ class HOST_SW_API TGEdMainWnd final : public finalcut::FWidget { 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(); LogMessage(LOG_HOST, "Loaded talkgroup rules file: %s", g_iniFile.c_str()); }); + m_keyF5.addCallback("activate", this, [&]() { g_tidLookups->reload(); m_wnd->loadListView(); LogInfoEx(LOG_HOST, "Loaded talkgroup rules file: %s", g_iniFile.c_str()); }); m_backupOnSave.setChecked(); @@ -138,7 +138,7 @@ class HOST_SW_API TGEdMainWnd final : public finalcut::FWidget { { if (m_backupOnSave.isChecked()) { std::string bakFile = g_iniFile + ".bak"; - LogMessage(LOG_HOST, "Backing up existing file %s to %s", g_iniFile.c_str(), bakFile.c_str()); + LogInfoEx(LOG_HOST, "Backing up existing file %s to %s", g_iniFile.c_str(), bakFile.c_str()); copyFile(g_iniFile.c_str(), bakFile.c_str()); } diff --git a/src/tged/TGEditPeerListWnd.h b/src/tged/TGEditPeerListWnd.h index 6b8314b4..a4c1ad46 100644 --- a/src/tged/TGEditPeerListWnd.h +++ b/src/tged/TGEditPeerListWnd.h @@ -173,7 +173,7 @@ class HOST_SW_API TGEditPeerListWnd final : public CloseWndBase { m_entry.addCallback("return-pressed", [&]() { size_t curItem = m_listBox.currentItem(); auto item = m_listBox.getItem(curItem); - LogMessage(LOG_HOST, "Updating %s peer ID %s to %s for TG %s (%u)", m_title.c_str(), item.getText().c_str(), m_entry.getText().c_str(), + LogInfoEx(LOG_HOST, "Updating %s peer ID %s to %s for TG %s (%u)", m_title.c_str(), item.getText().c_str(), m_entry.getText().c_str(), m_rule.name().c_str(), m_rule.source().tgId()); item.setText(m_entry.getText()); @@ -219,7 +219,7 @@ class HOST_SW_API TGEditPeerListWnd final : public CloseWndBase { */ void addEntry() { - LogMessage(LOG_HOST, "Adding %s peer ID %s from TG %s (%u)", m_title.c_str(), m_entry.getText().c_str(), + LogInfoEx(LOG_HOST, "Adding %s peer ID %s from TG %s (%u)", m_title.c_str(), m_entry.getText().c_str(), m_rule.name().c_str(), m_rule.source().tgId()); if (m_entry.getText() == "") { @@ -249,7 +249,7 @@ class HOST_SW_API TGEditPeerListWnd final : public CloseWndBase { for (std::vector::iterator it = peerList.begin(); it != peerList.end(); it++) { auto entry = *it; if (entry == peerId) { - LogMessage(LOG_HOST, "Removing %s peer ID %s from TG %s (%u)", m_title.c_str(), item.getText().c_str(), + LogInfoEx(LOG_HOST, "Removing %s peer ID %s from TG %s (%u)", m_title.c_str(), item.getText().c_str(), m_rule.name().c_str(), m_rule.source().tgId()); peerList.erase(it); break; @@ -301,7 +301,7 @@ class HOST_SW_API TGEditPeerListWnd final : public CloseWndBase { } for (auto entry : peerList) { - LogMessage(LOG_HOST, "%s peer ID %u for TG %s (%u)", m_title.c_str(), entry, + LogInfoEx(LOG_HOST, "%s peer ID %u for TG %s (%u)", m_title.c_str(), entry, m_rule.name().c_str(), m_rule.source().tgId()); } diff --git a/src/tged/TGEditRIDListWnd.h b/src/tged/TGEditRIDListWnd.h index 1bbc9410..87185422 100644 --- a/src/tged/TGEditRIDListWnd.h +++ b/src/tged/TGEditRIDListWnd.h @@ -173,7 +173,7 @@ class HOST_SW_API TGEditRIDListWnd final : public CloseWndBase { m_entry.addCallback("return-pressed", [&]() { size_t curItem = m_listBox.currentItem(); auto item = m_listBox.getItem(curItem); - LogMessage(LOG_HOST, "Updating %s radio ID %s to %s for TG %s (%u)", m_title.c_str(), item.getText().c_str(), m_entry.getText().c_str(), + LogInfoEx(LOG_HOST, "Updating %s radio ID %s to %s for TG %s (%u)", m_title.c_str(), item.getText().c_str(), m_entry.getText().c_str(), m_rule.name().c_str(), m_rule.source().tgId()); item.setText(m_entry.getText()); @@ -238,7 +238,7 @@ class HOST_SW_API TGEditRIDListWnd final : public CloseWndBase { size_t curItem = m_listBox.currentItem(); auto item = m_listBox.getItem(curItem); - LogMessage(LOG_HOST, "Removing %s radio ID %s from TG %s (%u)", m_title.c_str(), item.getText().c_str(), + LogInfoEx(LOG_HOST, "Removing %s radio ID %s from TG %s (%u)", m_title.c_str(), item.getText().c_str(), m_rule.name().c_str(), m_rule.source().tgId()); m_listBox.remove(curItem); @@ -288,7 +288,7 @@ class HOST_SW_API TGEditRIDListWnd final : public CloseWndBase { auto item = m_listBox.getItem(i + 1U); if (item.getText() != "") { uint32_t peerId = ::atoi(item.getText().c_str()); - LogMessage(LOG_HOST, "%s radio ID %s for TG %s (%u)", m_title.c_str(), item.getText().c_str(), + LogInfoEx(LOG_HOST, "%s radio ID %s for TG %s (%u)", m_title.c_str(), item.getText().c_str(), m_rule.name().c_str(), m_rule.source().tgId()); ridList.push_back(peerId); } diff --git a/src/tged/TGEditWnd.h b/src/tged/TGEditWnd.h index cbdbb918..d8e13358 100644 --- a/src/tged/TGEditWnd.h +++ b/src/tged/TGEditWnd.h @@ -301,7 +301,7 @@ class HOST_SW_API TGEditWnd final : public CloseWndBase { auto config = m_rule.config(); config.inclusion(wnd.peerList); m_rule.config(config); - LogMessage(LOG_HOST, "Updated %s (%u) peer inclusion list, %u inclusions.", m_rule.name().c_str(), m_rule.source().tgId(), m_rule.config().inclusionSize()); + LogInfoEx(LOG_HOST, "Updated %s (%u) peer inclusion list, %u inclusions.", m_rule.name().c_str(), m_rule.source().tgId(), m_rule.config().inclusionSize()); }); m_exclusionList.setGeometry(FPoint(20, 10), FSize(16, 1)); @@ -312,7 +312,7 @@ class HOST_SW_API TGEditWnd final : public CloseWndBase { auto config = m_rule.config(); config.exclusion(wnd.peerList); m_rule.config(config); - LogMessage(LOG_HOST, "Updated %s (%u) peer exclusion list, %u exclusions.", m_rule.name().c_str(), m_rule.source().tgId(), m_rule.config().exclusionSize()); + LogInfoEx(LOG_HOST, "Updated %s (%u) peer exclusion list, %u exclusions.", m_rule.name().c_str(), m_rule.source().tgId(), m_rule.config().exclusionSize()); }); m_alwaysList.setGeometry(FPoint(2, 12), FSize(16, 1)); @@ -323,7 +323,7 @@ class HOST_SW_API TGEditWnd final : public CloseWndBase { auto config = m_rule.config(); config.alwaysSend(wnd.peerList); m_rule.config(config); - LogMessage(LOG_HOST, "Updated %s (%u) peer always receiving list, %u always.", m_rule.name().c_str(), m_rule.source().tgId(), m_rule.config().alwaysSendSize()); + LogInfoEx(LOG_HOST, "Updated %s (%u) peer always receiving list, %u always.", m_rule.name().c_str(), m_rule.source().tgId(), m_rule.config().alwaysSendSize()); }); m_preferredList.setGeometry(FPoint(20, 12), FSize(16, 1)); @@ -334,7 +334,7 @@ class HOST_SW_API TGEditWnd final : public CloseWndBase { auto config = m_rule.config(); config.preferred(wnd.peerList); m_rule.config(config); - LogMessage(LOG_HOST, "Updated %s (%u) peer preference list, %u preferred.", m_rule.name().c_str(), m_rule.source().tgId(), m_rule.config().preferredSize()); + LogInfoEx(LOG_HOST, "Updated %s (%u) peer preference list, %u preferred.", m_rule.name().c_str(), m_rule.source().tgId(), m_rule.config().preferredSize()); }); m_rewriteList.setGeometry(FPoint(2, 14), FSize(16, 1)); @@ -351,7 +351,7 @@ class HOST_SW_API TGEditWnd final : public CloseWndBase { auto config = m_rule.config(); config.permittedRIDs(wnd.ridList); m_rule.config(config); - LogMessage(LOG_HOST, "Updated %s (%u) permitted radio list, %u permitted.", m_rule.name().c_str(), m_rule.source().tgId(), m_rule.config().permittedRIDsSize()); + LogInfoEx(LOG_HOST, "Updated %s (%u) permitted radio list, %u permitted.", m_rule.name().c_str(), m_rule.source().tgId(), m_rule.config().permittedRIDsSize()); }); CloseWndBase::initControls(); @@ -448,7 +448,7 @@ class HOST_SW_API TGEditWnd final : public CloseWndBase { return x.source().tgId() == m_origTgId && x.source().tgSlot() == m_origTgSlot; }); if (it != groupVoice.end()) { - LogMessage(LOG_HOST, "Updating TG %s (%u) to %s (%u)", it->name().c_str(), it->source().tgId(), m_rule.name().c_str(), m_rule.source().tgId()); + LogInfoEx(LOG_HOST, "Updating TG %s (%u) to %s (%u)", it->name().c_str(), it->source().tgId(), m_rule.name().c_str(), m_rule.source().tgId()); g_tidLookups->eraseEntry(m_origTgId, m_origTgSlot); g_tidLookups->addEntry(m_rule); @@ -476,9 +476,9 @@ class HOST_SW_API TGEditWnd final : public CloseWndBase { // add new TG if (m_saveCopy.isChecked()) { - LogMessage(LOG_HOST, "Copying TG. Adding TG %s (%u)", m_rule.name().c_str(), m_rule.source().tgId()); + LogInfoEx(LOG_HOST, "Copying TG. Adding TG %s (%u)", m_rule.name().c_str(), m_rule.source().tgId()); } else { - LogMessage(LOG_HOST, "Adding TG %s (%u)", m_rule.name().c_str(), m_rule.source().tgId()); + LogInfoEx(LOG_HOST, "Adding TG %s (%u)", m_rule.name().c_str(), m_rule.source().tgId()); } g_tidLookups->addEntry(m_rule); diff --git a/src/tged/TGListWnd.h b/src/tged/TGListWnd.h index 42ec8184..eaf957e3 100644 --- a/src/tged/TGListWnd.h +++ b/src/tged/TGListWnd.h @@ -177,7 +177,7 @@ class HOST_SW_API TGListWnd final : public FDblDialog { std::vector inclusions = config.inclusion(); auto it = std::find_if(inclusions.begin(), inclusions.end(), [&](uint32_t x) { return x == wnd.peerId; }); if (it == inclusions.end()) { - LogMessage(LOG_HOST, "Updating TG %s (%u) adding inclusion peer %u", rule.name().c_str(), rule.source().tgId(), peerId); + LogInfoEx(LOG_HOST, "Updating TG %s (%u) adding inclusion peer %u", rule.name().c_str(), rule.source().tgId(), peerId); inclusions.push_back(peerId); } @@ -216,7 +216,7 @@ class HOST_SW_API TGListWnd final : public FDblDialog { std::vector inclusions = config.inclusion(); auto it = std::find_if(inclusions.begin(), inclusions.end(), [&](uint32_t x) { return x == wnd.peerId; }); if (it != inclusions.end()) { - LogMessage(LOG_HOST, "Updating TG %s (%u) removing inclusion peer %u", rule.name().c_str(), rule.source().tgId(), peerId); + LogInfoEx(LOG_HOST, "Updating TG %s (%u) removing inclusion peer %u", rule.name().c_str(), rule.source().tgId(), peerId); inclusions.erase(it); } @@ -259,7 +259,7 @@ class HOST_SW_API TGListWnd final : public FDblDialog { std::vector alwaysSend = config.alwaysSend(); auto it = std::find_if(alwaysSend.begin(), alwaysSend.end(), [&](uint32_t x) { return x == wnd.peerId; }); if (it == alwaysSend.end()) { - LogMessage(LOG_HOST, "Updating TG %s (%u) adding always peer %u", rule.name().c_str(), rule.source().tgId(), peerId); + LogInfoEx(LOG_HOST, "Updating TG %s (%u) adding always peer %u", rule.name().c_str(), rule.source().tgId(), peerId); alwaysSend.push_back(peerId); } @@ -302,7 +302,7 @@ class HOST_SW_API TGListWnd final : public FDblDialog { std::vector alwaysSend = config.alwaysSend(); auto it = std::find_if(alwaysSend.begin(), alwaysSend.end(), [&](uint32_t x) { return x == wnd.peerId; }); if (it != alwaysSend.end()) { - LogMessage(LOG_HOST, "Updating TG %s (%u) removing always peer %u", rule.name().c_str(), rule.source().tgId(), peerId); + LogInfoEx(LOG_HOST, "Updating TG %s (%u) removing always peer %u", rule.name().c_str(), rule.source().tgId(), peerId); alwaysSend.erase(it); } @@ -414,7 +414,7 @@ class HOST_SW_API TGListWnd final : public FDblDialog { m_selected = entry; /* if (m_selectedTgId != tgid) - LogMessage(LOG_HOST, "Selected TG %s (%u) for editing", m_selected.name().c_str(), m_selected.source().tgId()); + LogInfoEx(LOG_HOST, "Selected TG %s (%u) for editing", m_selected.name().c_str(), m_selected.source().tgId()); */ m_selectedTgId = tgid; @@ -482,7 +482,7 @@ class HOST_SW_API TGListWnd final : public FDblDialog { if (m_selected.isInvalid()) return; - LogMessage(LOG_HOST, "Deleting TG %s (%u)", m_selected.name().c_str(), m_selected.source().tgId()); + LogInfoEx(LOG_HOST, "Deleting TG %s (%u)", m_selected.name().c_str(), m_selected.source().tgId()); g_tidLookups->eraseEntry(m_selected.source().tgId(), m_selected.source().tgSlot()); // bryanb: HACK -- use HackTheGibson to access the private current listview iterator to get the scroll position From f5fea08af1041189d43bba35c34d482c08080990 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 17:14:29 -0400 Subject: [PATCH 104/200] better classify log messages; --- src/fne/network/PeerNetwork.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 61746cc4..0c930f4f 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -129,7 +129,7 @@ bool PeerNetwork::writePeerLinkPeers(json::array* peerList) pkt.encode((uint8_t*)buffer, len); uint32_t streamId = createStreamId(); - LogInfoEx(LOG_PEER, "PEER %u Peer Replication, Active Peer List, blocks %u, streamId = %u", m_peerId, pkt.fragments.size(), streamId); + LogInfoEx(LOG_REPL, "PEER %u Peer Replication, Active Peer List, blocks %u, streamId = %u", m_peerId, pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writeMaster({ NET_FUNC::REPL, NET_SUBFUNC::REPL_ACT_PEER_LIST }, @@ -171,7 +171,7 @@ bool PeerNetwork::writeSpanningTree(SpanningTree* treeRoot) pkt.encode((uint8_t*)buffer, len); uint32_t streamId = createStreamId(); - LogInfoEx(LOG_PEER, "PEER %u Network Tree, Tree List, blocks %u, streamId = %u", m_peerId, pkt.fragments.size(), streamId); + LogInfoEx(LOG_STP, "PEER %u Network Tree, Tree List, blocks %u, streamId = %u", m_peerId, pkt.fragments.size(), streamId); if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writeMaster({ NET_FUNC::NET_TREE, NET_SUBFUNC::NET_TREE_LIST }, From 5d1a057df0632d2c3f7b0a77b6e0953ed99e6a93 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 20:33:16 -0400 Subject: [PATCH 105/200] simplify FNE configuration for peers, make identity a global applying to all peer connections, remove bogus frequency data; --- configs/fne-config.example.yml | 12 ++++-------- src/fne/HostFNE.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 05cf2a0f..fde12082 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -212,8 +212,7 @@ master: # (This is the list of connections to upstream FNEs this FNE instance should be connected to.) # peers: - - name: EXAMPLEPEER - # Flag indicating whether or not the peer is enabled. + - # Flag indicating whether or not the peer is enabled. enable: true # Hostname/IP address of the FNE master to connect to. masterAddress: 127.0.0.1 @@ -221,8 +220,6 @@ peers: masterPort: 32090 # FNE access password. password: RPT1234 - # Textual identity of this peer. - identity: EXPEER # Network Peer ID of this peer on the upstream FNE master. peerId: 9000990 @@ -233,10 +230,6 @@ peers: # 0 - 9, A - F.) presharedKey: "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F" - # - rxFrequency: 0 - # - txFrequency: 0 # Latitude. latitude: 0.0 # Longitude. @@ -251,6 +244,9 @@ peers: # System Configuration # system: + # Textual identity of this FNE (this is used when peering with upstream FNEs). + identity: MASTERFNE + # Time in seconds between pings to peers. pingTime: 5 # Maximum number of missable pings before a peer is considered disconnected. diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index bc8269c7..1ff9c155 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -793,6 +793,9 @@ void* HostFNE::threadDiagNetwork(void* arg) bool HostFNE::createPeerNetworks() { + yaml::Node systemConf = m_conf["system"]; + std::string identity = systemConf["identity"].as(); + yaml::Node masterConf = m_conf["master"]; uint32_t masterPeerId = masterConf["peerId"].as(1001U); @@ -844,9 +847,6 @@ bool HostFNE::createPeerNetworks() } } - std::string identity = peerConf["identity"].as(); - uint32_t rxFrequency = peerConf["rxFrequency"].as(0U); - uint32_t txFrequency = peerConf["txFrequency"].as(0U); float latitude = peerConf["latitude"].as(0.0F); float longitude = peerConf["longitude"].as(0.0F); std::string location = peerConf["location"].as(); @@ -861,7 +861,7 @@ bool HostFNE::createPeerNetworks() // initialize networking network::PeerNetwork* network = new PeerNetwork(masterAddress, masterPort, 0U, id, password, true, debug, m_dmrEnabled, m_p25Enabled, m_nxdnEnabled, m_analogEnabled, true, true, m_allowActivityTransfer, m_allowDiagnosticTransfer, false, false); - network->setMetadata(identity, rxFrequency, txFrequency, 0.0F, 0.0F, 0, 0, 0, latitude, longitude, 0, location); + network->setMetadata(identity, 0U, 0U, 0.0F, 0.0F, 0, 0, 0, latitude, longitude, 0, location); network->setLookups(m_ridLookup, m_tidLookup); network->setMasterPeerId(masterPeerId); network->setPeerLookups(m_peerListLookup); From 5e164a87ea9e90aa2c8736aba4b6d44fba947942 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 20:35:43 -0400 Subject: [PATCH 106/200] better report global identity; --- src/fne/HostFNE.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 1ff9c155..49dc9505 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -540,6 +540,9 @@ bool HostFNE::initializeRESTAPI() bool HostFNE::createMasterNetwork() { + yaml::Node systemConf = m_conf["system"]; + std::string identity = systemConf["identity"].as(); + yaml::Node masterConf = m_conf["master"]; std::string address = masterConf["address"].as(); uint16_t port = (uint16_t)masterConf["port"].as(TRAFFIC_DEFAULT_PORT); @@ -610,6 +613,7 @@ bool HostFNE::createMasterNetwork() bool parrotGrantDemand = masterConf["parrotGrantDemand"].as(true); LogInfo("Network Parameters"); + LogInfo(" Identity: %s", identity.c_str()); LogInfo(" Peer ID: %u", id); LogInfo(" Address: %s", address.c_str()); LogInfo(" Port: %u", port); @@ -851,7 +855,7 @@ bool HostFNE::createPeerNetworks() float longitude = peerConf["longitude"].as(0.0F); std::string location = peerConf["location"].as(); - ::LogInfoEx(LOG_HOST, "Peer ID %u Master Address %s Master Port %u Identity %s Enabled %u Encrypted %u", id, masterAddress.c_str(), masterPort, identity.c_str(), enabled, encrypted); + ::LogInfoEx(LOG_HOST, "Peer ID %u Master Address %s Master Port %u Enabled %u Encrypted %u", id, masterAddress.c_str(), masterPort, enabled, encrypted); if (id > 999999999U) { ::LogError(LOG_HOST, "Network Peer ID cannot be greater then 999999999."); From 3d9f6f7412ae5ebc0ec5ee67ebc1fb85e7c30284 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 21:10:58 -0400 Subject: [PATCH 107/200] allow overriding the global identity for upstream connections; implement use of identity for spanning tree; --- src/fne/HostFNE.cpp | 16 ++++++++++++---- src/fne/network/FNENetwork.cpp | 13 ++++++++++--- src/fne/network/FNENetwork.h | 4 +++- src/fne/network/SpanningTree.cpp | 28 ++++++++++++++++++---------- src/fne/network/SpanningTree.h | 4 ++++ 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 49dc9505..af17a68f 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -541,7 +541,7 @@ bool HostFNE::initializeRESTAPI() bool HostFNE::createMasterNetwork() { yaml::Node systemConf = m_conf["system"]; - std::string identity = systemConf["identity"].as(); + std::string identity = systemConf["identity"].as("CHANGEME"); yaml::Node masterConf = m_conf["master"]; std::string address = masterConf["address"].as(); @@ -643,8 +643,10 @@ bool HostFNE::createMasterNetwork() } // initialize networking - m_network = new FNENetwork(this, address, port, id, password, debug, kmfDebug, verbose, reportPeerPing, m_dmrEnabled, m_p25Enabled, m_nxdnEnabled, m_analogEnabled, - parrotDelay, parrotGrantDemand, m_allowActivityTransfer, m_allowDiagnosticTransfer, m_pingTime, m_updateLookupTime, workerCnt); + m_network = new FNENetwork(this, address, port, id, password, identity, debug, kmfDebug, verbose, reportPeerPing, + m_dmrEnabled, m_p25Enabled, m_nxdnEnabled, m_analogEnabled, + parrotDelay, parrotGrantDemand, m_allowActivityTransfer, m_allowDiagnosticTransfer, + m_pingTime, m_updateLookupTime, workerCnt); m_network->setOptions(masterConf, true); m_network->setLookups(m_ridLookup, m_tidLookup, m_peerListLookup, m_cryptoLookup, m_adjSiteMapLookup); @@ -798,7 +800,7 @@ void* HostFNE::threadDiagNetwork(void* arg) bool HostFNE::createPeerNetworks() { yaml::Node systemConf = m_conf["system"]; - std::string identity = systemConf["identity"].as(); + std::string identity = systemConf["identity"].as("CHANGEME"); yaml::Node masterConf = m_conf["master"]; uint32_t masterPeerId = masterConf["peerId"].as(1001U); @@ -857,6 +859,12 @@ bool HostFNE::createPeerNetworks() ::LogInfoEx(LOG_HOST, "Peer ID %u Master Address %s Master Port %u Enabled %u Encrypted %u", id, masterAddress.c_str(), masterPort, enabled, encrypted); + std::string identOverride = peerConf["identity"].as(); + if (identOverride != "") { + identity = identOverride; + ::LogInfoEx(LOG_HOST, "Peer ID %u overrides global identity, Identity %s", id, identity.c_str()); + } + if (id > 999999999U) { ::LogError(LOG_HOST, "Network Peer ID cannot be greater then 999999999."); continue; diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index fdc38a1d..1a5e8958 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -63,7 +63,8 @@ std::timed_mutex FNENetwork::m_keyQueueMutex; /* Initializes a new instance of the FNENetwork class. */ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, uint32_t peerId, const std::string& password, - bool debug, bool kmfDebug, bool verbose, bool reportPeerPing, bool dmr, bool p25, bool nxdn, bool analog, + std::string identity, bool debug, bool kmfDebug, bool verbose, bool reportPeerPing, + bool dmr, bool p25, bool nxdn, bool analog, uint32_t parrotDelay, bool parrotGrantDemand, bool allowActivityTransfer, bool allowDiagnosticTransfer, uint32_t pingTime, uint32_t updateLookupTime, uint16_t workerCnt) : BaseNetwork(peerId, true, debug, true, true, allowActivityTransfer, allowDiagnosticTransfer), @@ -154,6 +155,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, SpanningTree::m_maxUpdatesBeforeReparent = (uint8_t)host->m_maxMissedPings; m_treeRoot = new SpanningTree(peerId, peerId, nullptr); + m_treeRoot->identity(identity); /* ** Initialize Threads @@ -1204,8 +1206,9 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) json::object peerConfig = connection->config(); + std::string identity = "* UNK *"; if (peerConfig["identity"].is()) { - std::string identity = peerConfig["identity"].get(); + identity = peerConfig["identity"].getDefault("* UNK *"); connection->identity(identity); LogInfoEx(LOG_MASTER, "PEER %u >> Identity [%8s]", peerId, identity.c_str()); } @@ -1275,6 +1278,9 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) network->m_spanningTreeFastReconnect) { LogWarning(LOG_STP, "PEER %u (%s) server already announced in server tree, fast peer reconnect, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), peerId, masterPeerId, tree->id(), tree->masterId(), connection->connectionState()); + if (identity != tree->identity()) { + LogWarning(LOG_STP, "PEER %u (%s) why has this server's announced identity changed? *big hmmmm*", peerId, connection->identWithQualifier().c_str()); + } SpanningTree::moveParent(tree, network->m_treeRoot); network->logSpanningTree(connection); } else { @@ -1285,7 +1291,8 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) break; } } else { - new SpanningTree(peerId, masterPeerId, network->m_treeRoot); + SpanningTree* node = new SpanningTree(peerId, masterPeerId, network->m_treeRoot); + node->identity(identity); network->logSpanningTree(connection); } } diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 016161e0..31880aa6 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -147,6 +147,7 @@ namespace network * @param port Network port number. * @param peerId Unique ID on the network. * @param password Network authentication password. + * @param identity Textual identity of this FNE (this is used when peering with upstream FNEs). * @param debug Flag indicating whether network debug is enabled. * @param kmfDebug Flag indicating whether P25 OTAR KMF services debug is enabled. * @param verbose Flag indicating whether network verbose logging is enabled. @@ -164,7 +165,8 @@ namespace network * @param workerCnt Number of worker threads. */ FNENetwork(HostFNE* host, const std::string& address, uint16_t port, uint32_t peerId, const std::string& password, - bool debug, bool kmfDebug, bool verbose, bool reportPeerPing, bool dmr, bool p25, bool nxdn, bool analog, + std::string identity, bool debug, bool kmfDebug, bool verbose, bool reportPeerPing, + bool dmr, bool p25, bool nxdn, bool analog, uint32_t parrotDelay, bool parrotGrantDemand, bool allowActivityTransfer, bool allowDiagnosticTransfer, uint32_t pingTime, uint32_t updateLookupTime, uint16_t workerCnt); /** diff --git a/src/fne/network/SpanningTree.cpp b/src/fne/network/SpanningTree.cpp index 315880e5..d513f4fc 100644 --- a/src/fne/network/SpanningTree.cpp +++ b/src/fne/network/SpanningTree.cpp @@ -29,6 +29,7 @@ uint8_t SpanningTree::m_maxUpdatesBeforeReparent = 5U; SpanningTree::SpanningTree(uint32_t id, uint32_t masterId, SpanningTree* parent) : m_parent(parent), m_children(), + m_identity("UNK"), m_id(id), m_masterId(masterId), m_updatesBeforeReparent(0U) @@ -133,6 +134,8 @@ void SpanningTree::serializeTree(SpanningTree* node, json::array& jsonArray) obj["id"].set(id); uint32_t masterId = node->masterId(); obj["masterId"].set(masterId); + std::string identity = node->identity(); + obj["identity"].set(identity); json::array childArray; for (auto child : node->m_children) { @@ -161,6 +164,9 @@ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, uint32_t id = obj["id"].get(); uint32_t masterId = obj["masterId"].get(); + std::string identity = "* UNK *"; + if (obj["identity"].is()) + identity = obj["identity"].getDefault("* UNK *"); // check if this peer is already connected via another peer SpanningTree* tree = SpanningTree::findByMasterID(masterId); @@ -176,8 +182,10 @@ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, } SpanningTree* existingNode = findByPeerID(id); - if (existingNode == nullptr) + if (existingNode == nullptr) { existingNode = new SpanningTree(id, masterId, parent); + existingNode->identity(identity); + } else { // are the parents different? if so, start counting down to reparenting if (existingNode->m_parent != parent) { @@ -269,7 +277,7 @@ void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) // the root node cannot be moved if (node->m_parent == nullptr) { - LogError(LOG_STP, "PEER %u is a root tree node, can't be moved. BUGBUG.", node->id()); + LogError(LOG_STP, "PEER %u (%s) is a root tree node, can't be moved. BUGBUG.", node->id(), node->identity().c_str()); return; } @@ -290,8 +298,8 @@ void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) nodeParent->m_children.erase(it); hasReleasedFromParent = true; } else { - LogError(LOG_STP, "PEER %u failed to release ownership from PEER %u, tree is potentially inconsistent", - node->id(), nodeParent->id()); + LogError(LOG_STP, "PEER %u (%s) failed to release ownership from PEER %u, tree is potentially inconsistent", + node->id(), node->identity().c_str(), nodeParent->id()); } } @@ -305,8 +313,8 @@ void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) if (node->m_updatesBeforeReparent > 0U) node->m_updatesBeforeReparent = 0U; - LogWarning(LOG_STP, "PEER %u ownership has changed from PEER %u to PEER %u; this normally shouldn't happen", - node->id(), nodeParentId, parent->id()); + LogWarning(LOG_STP, "PEER %u (%s) ownership has changed from PEER %u to PEER %u; this normally shouldn't happen", + node->id(), node->identity().c_str(), nodeParentId, parent->id()); } } } @@ -322,8 +330,8 @@ void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) } if (level == 0U) - LogInfoEx(LOG_STP, "Peer ID: %u, Master Peer ID: %u, Children: %u, IsRoot: %u", - node->id(), node->masterId(), node->m_children.size(), node->isRoot()); + LogInfoEx(LOG_STP, "Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", + node->id(), node->masterId(), node->identity().c_str(), node->m_children.size(), node->isRoot()); std::string indent; for (uint32_t i = 0U; i < level; i++) { @@ -331,8 +339,8 @@ void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) } for (auto child : node->m_children) { - LogInfoEx(LOG_STP, "%s- Peer ID: %u, Master Peer ID: %u, Children: %u, IsRoot: %u", - indent.c_str(), child->id(), child->masterId(), child->m_children.size(), child->isRoot()); + LogInfoEx(LOG_STP, "%s- Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", + indent.c_str(), child->id(), child->masterId(), child->identity().c_str(), child->m_children.size(), child->isRoot()); visualizeTreeToLog(child, level + 1U); } } diff --git a/src/fne/network/SpanningTree.h b/src/fne/network/SpanningTree.h index 884ca122..4c488b7d 100644 --- a/src/fne/network/SpanningTree.h +++ b/src/fne/network/SpanningTree.h @@ -152,6 +152,10 @@ namespace network static std::unordered_map m_SpanningTrees; //!< Static map of all trees by peer ID. static uint8_t m_maxUpdatesBeforeReparent; //!< Maximum count of updates before allowing node reparenting. + /** + * @brief Peer Identity. + */ + DECLARE_PROPERTY_PLAIN(std::string, identity); /** * @brief Peer ID. */ From 63f8e714e423052e81c624413686117eefb157db Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 21:19:27 -0400 Subject: [PATCH 108/200] update config examples to reflect new log levels; --- configs/bridge-config.example.yml | 9 ++++----- configs/config.example.yml | 9 ++++----- configs/fne-config.example.yml | 9 ++++----- configs/fne-sysview.example.yml | 9 ++++----- configs/patch-config.example.yml | 9 ++++----- 5 files changed, 20 insertions(+), 25 deletions(-) diff --git a/configs/bridge-config.example.yml b/configs/bridge-config.example.yml index a48d0085..8a9cb0bc 100644 --- a/configs/bridge-config.example.yml +++ b/configs/bridge-config.example.yml @@ -10,11 +10,10 @@ daemon: true # # Logging Levels: # 1 - Debug -# 2 - Message -# 3 - Informational -# 4 - Warning -# 5 - Error -# 6 - Fatal +# 2 - Informational +# 3 - Warning +# 4 - Error +# 5 - Fatal # log: # Console display logging level (used when in foreground). diff --git a/configs/config.example.yml b/configs/config.example.yml index 13f8d29d..233511bc 100644 --- a/configs/config.example.yml +++ b/configs/config.example.yml @@ -10,11 +10,10 @@ daemon: true # # Logging Levels: # 1 - Debug -# 2 - Message -# 3 - Informational -# 4 - Warning -# 5 - Error -# 6 - Fatal +# 2 - Informational +# 3 - Warning +# 4 - Error +# 5 - Fatal # log: # Flag indicating whether or not the NON-AUTHORITATIVE errors should be logged. diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index fde12082..d1a19890 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -9,11 +9,10 @@ daemon: true # Logging Configuration # Logging Levels: # 1 - Debug -# 2 - Message -# 3 - Informational -# 4 - Warning -# 5 - Error -# 6 - Fatal +# 2 - Informational +# 3 - Warning +# 4 - Error +# 5 - Fatal # log: # Console display logging level (used when in foreground). diff --git a/configs/fne-sysview.example.yml b/configs/fne-sysview.example.yml index 8ccd14d5..37605921 100644 --- a/configs/fne-sysview.example.yml +++ b/configs/fne-sysview.example.yml @@ -7,11 +7,10 @@ # # Logging Levels: # 1 - Debug -# 2 - Message -# 3 - Informational -# 4 - Warning -# 5 - Error -# 6 - Fatal +# 2 - Informational +# 3 - Warning +# 4 - Error +# 5 - Fatal # log: # Console display logging level (used when in foreground). diff --git a/configs/patch-config.example.yml b/configs/patch-config.example.yml index 39954a1f..63c1952b 100644 --- a/configs/patch-config.example.yml +++ b/configs/patch-config.example.yml @@ -10,11 +10,10 @@ daemon: true # # Logging Levels: # 1 - Debug -# 2 - Message -# 3 - Informational -# 4 - Warning -# 5 - Error -# 6 - Fatal +# 2 - Informational +# 3 - Warning +# 4 - Error +# 5 - Fatal # log: # Console display logging level (used when in foreground). From dbe827c715c173f222e937e531c214ac95e5b515 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 22:12:21 -0400 Subject: [PATCH 109/200] add back peer "name" field, this is strictly informational to make the config file easier on the brain; --- configs/fne-config.example.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index d1a19890..49be4b99 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -211,7 +211,8 @@ master: # (This is the list of connections to upstream FNEs this FNE instance should be connected to.) # peers: - - # Flag indicating whether or not the peer is enabled. + - name: MASTERFNE + # Flag indicating whether or not the peer is enabled. enable: true # Hostname/IP address of the FNE master to connect to. masterAddress: 127.0.0.1 From 231f78f9af34147eb174f4bcb24fccf6340da8cf Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 22:29:23 -0400 Subject: [PATCH 110/200] Win32: bump version number for file resource metadata; --- src/bridge/win32/resource.rc | Bin 5140 -> 5140 bytes src/fne/win32/resource.rc | Bin 5194 -> 5194 bytes src/host/win32/resource.rc | Bin 5140 -> 5140 bytes src/patch/win32/resource.rc | Bin 5142 -> 5142 bytes src/remote/win32/resource.rc | Bin 5200 -> 5200 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/bridge/win32/resource.rc b/src/bridge/win32/resource.rc index 964be84fbe4cb6acaaa7c8b99f5d9c13a998968f..86e3e5186daa596195624ad7f8a2d3dde627ea9d 100644 GIT binary patch delta 38 scmbQDF-2oT3m2p5oLF}aVQZ?gj50w$o$L4i(Y0B^Vs A-2eap delta 46 zcmX@5aY|#u94oLF}aVQZ?gj50w$o$L4i(Y0B=(c A*#H0l diff --git a/src/host/win32/resource.rc b/src/host/win32/resource.rc index d1eb8f1b813539f0e4bdad32c9e165b6bfd72f43..4df7014b0444419db471c1e4cc797579ab6c2a59 100644 GIT binary patch delta 38 scmbQDF-2p;3@%2~$uqeO8BI5z=3-?AQWtqoGXcde@+xmWAP~X~0Q9E}W&i*H delta 38 scmbQDF-2p;3@%2K$uqeO8BI2y=3-?AQWtqoGXcde@+xmWAP~X~0Q6Q2VgLXD diff --git a/src/patch/win32/resource.rc b/src/patch/win32/resource.rc index bb9ede682f1e48698bdbee3fcd3db8cc7fa8c043..f310e91ca416f84ef161bf1266666661d68fcf89 100644 GIT binary patch delta 38 scmbQHF->Db2N$F1Db2N$Es Date: Mon, 27 Oct 2025 22:57:27 -0400 Subject: [PATCH 111/200] add documentation for packet payloads for: writeLogin, writeAuthorisation, writeConfig and writePing; --- src/common/network/Network.h | 83 ++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/src/common/network/Network.h b/src/common/network/Network.h index 042423f7..d9b61794 100644 --- a/src/common/network/Network.h +++ b/src/common/network/Network.h @@ -415,21 +415,104 @@ namespace network /** * @brief Writes login request to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the repeater/end point login message. + * The message is 8 bytes in length. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Protocol Tag (RPTL) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Peer ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @returns bool True, if login request was sent, otherwise false. */ bool writeLogin(); /** * @brief Writes network authentication challenge. + * \code{.unparsed} + * Below is the representation of the data layout for the repeater/end point login message. + * The message is 40 bytes in length. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Protocol Tag (RPTK) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Peer ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 30 bytes of SHA-256 ......................................... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @returns bool True, if authorization response was sent, otherwise false. */ bool writeAuthorisation(); /** * @brief Writes modem configuration to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the repeater/end point login message. + * The message is 40 bytes in length. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Protocol Tag (RPTC) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Variable Length JSON Payload ................................ | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The JSON payload is variable length and looks like this: + * { + * "identity": "", + * "rxFrequency": , + * "txFrequency": , + * "info": + * { + * "latitude": , + * "longitude": , + * "height": , + * "location": "" + * }, + * "channel": + * { + * "txPower": , + * "txOffsetMhz": , + * "chBandwidthKhz": , + * "channelId": , + * "channelNo": , + * }, + * "software": "", + * } + * + * These are extra parameters used in the root of the above JSON. + * { + * "externalPeer": , + * "masterPeerId": , + * + * "conventionalPeer": , + * + * "sysView": , + * } + * \endcode * @returns bool True, if configuration response was sent, otherwise false. */ virtual bool writeConfig(); /** * @brief Writes a network stay-alive ping. + * \code{.unparsed} + * Below is the representation of the data layout for the repeater/end point login message. + * The message is 1 bytes in length. + * + * Byte 0 + * Bit 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+ + * \endcode * @returns bool True, if stay-alive ping was sent, otherwise false. */ bool writePing(); From 9ce2e4e2ad3b774101998b43a7552d392a2acd4c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 27 Oct 2025 23:28:55 -0400 Subject: [PATCH 112/200] add some more packet payload documentation; --- src/common/network/BaseNetwork.h | 171 +++++++++++++++++++++++++++++- src/common/network/PacketBuffer.h | 14 +++ src/fne/network/PeerNetwork.h | 75 +++++++++++++ 3 files changed, 259 insertions(+), 1 deletion(-) diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index 4b4efe17..2b9f1d62 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -394,6 +394,26 @@ namespace network /** * @brief Writes a grant request to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the group affiliation + * announcement message. The message is 24 bytes in length. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | Source ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source ID | Destination ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Destination ID | Reserved |S| Reserverd | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Mode | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param mode Digital Mode. * @param srcId Source Radio ID. * @param dstId Destination Radio ID. @@ -413,6 +433,22 @@ namespace network /** * @brief Writes the local activity log to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the activity log message. + * The message is variable length bytes. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | Variable | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Length Activity Log Message ................................. | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param message Textual string to send as activity log information. * @returns bool True, if message was sent, otherwise false. */ @@ -420,13 +456,100 @@ namespace network /** * @brief Writes the local diagnostic logs to the network. - * @param message Textual string to send as diagnostic log information. + * \code{.unparsed} + * Below is the representation of the data layout for the diagnostic log message. + * The message is variable length bytes. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | Variable | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Length Diagnostics Message .................................. | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @returns bool True, if message was sent, otherwise false. */ virtual bool writeDiagLog(const char* message); /** * @brief Writes the local status to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the peer status message. + * The message is variable length bytes. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | Variable | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Length JSON Payload ......................................... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The JSON payload is variable length and looks like this: + * { + * "state": , + * "isTxCW": , + * "fixedMode": , + * "dmrTSCCEnable": , + * "dmrCC": , + * "p25CtrlEnable": , + * "p25CC": , + * "nxdnCtrlEnable": , + * "nxdnCC": , + * "tx": , + * "channelId": , + * "channelNo": , + * "lastDstId": , + * "lastSrcId": , + * "peerId": , + * "sysId": , + * "siteId": , + * "p25RfssId": , + * "p25NetId": , + * "p25NAC": , + * "vcChannels": [ + * { + * "channelId": , + * "channelNo": , + * "tx": , + * "lastDstId": , + * "lastSrcId": , + * } + * ], + * "modem": { + * "portType": "", + * "modemPort": "", + * "portSpeed": , + * "rxLevel": , + * "cwTxLevel": , + * "dmrTxLevel": , + * "p25TxLevel": , + * "nxdnTxLevel": , + * "rxDCOffset": , + * "txDCOffset": , + * "fdmaPremables": , + * "dmrRxDelay": , + * "p25CorrCount": , + * "rxFrequency": , + * "txFrequency": , + * "rxTuning": , + * "txTuning": , + * "rxFrequencyEffective": , + * "txFrequencyEffective": , + * "v24Connected": , + * "protoVer": + * } + * } + * \endcode * @param obj JSON object representing the local peer status. * @returns bool True, if peer status was sent, otherwise false. */ @@ -453,6 +576,16 @@ namespace network virtual bool announceGroupAffiliation(uint32_t srcId, uint32_t dstId); /** * @brief Writes a group affiliation removal to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the unit registration + * announcement message. The message is 3 bytes in length. + * + * Byte 0 1 2 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param srcId Source Radio ID. * @returns bool True, if group affiliation announcement was sent, otherwise false. */ @@ -476,6 +609,16 @@ namespace network virtual bool announceUnitRegistration(uint32_t srcId); /** * @brief Writes a unit deregistration to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the unit deregistration + * announcement message. The message is 3 bytes in length. + * + * Byte 0 1 2 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param srcId Source Radio ID. * @returns bool True, if unit deregistration announcement was sent, otherwise false. */ @@ -483,6 +626,20 @@ namespace network /** * @brief Writes a complete update of the peer affiliation list to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the repeater/end point login message. + * The message is variable bytes in length. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of entries | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: Source ID | E: Dst Id | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: Destination ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param affs Complete map of peer unit affiliations. * @returns bool True, if affiliation update announcement was sent, otherwise false. */ @@ -490,6 +647,18 @@ namespace network /** * @brief Writes a complete update of the peer's voice channel list to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the repeater/end point login message. + * The message is variable bytes in length. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of entries | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: Peer ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param peers List of voice channel peers. * @returns bool True, if peer update announcement was sent, otherwise false. */ diff --git a/src/common/network/PacketBuffer.h b/src/common/network/PacketBuffer.h index 37d03667..4aa4e13d 100644 --- a/src/common/network/PacketBuffer.h +++ b/src/common/network/PacketBuffer.h @@ -39,6 +39,20 @@ namespace network /** * @brief Represents a fragmented packet buffer. * @ingroup network_core + * \code{.unparsed} + * Below is the representation of the fragment layout for a packet buffer message. The ultimate + * buffer is variable length up to a maximum of 534 bytes for a single fragment. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Uncompressed Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Compressed Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Block Number | Total Blocks | Payload ..................... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode */ class HOST_SW_API PacketBuffer { public: diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index d7be69ab..c974017a 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -144,18 +144,93 @@ namespace network /** * @brief Writes a complete update of this CFNE's active peer list to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the active peer listmessage. + * The active peer list message is a JSON body, and is a packet buffer compressed message. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Protocol Tag (REPL) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Variable Length JSON Payload ................................ | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The JSON payload is variable length and looks like this: + * { + * "peerId": , + * "address": "", + * "port": , + * "connected": , + * "connectionState": , + * "pingsReceived": , + * "lastPing": , + * "controlChannel": , + * "config": { + * + * }, + * "voiceChannels": [ + * , + * ] + * } + * \endcode * @param peerList List of active peers. * @returns bool True, if list was sent, otherwise false. */ bool writePeerLinkPeers(json::array* peerList); /** * @brief Writes a complete update of this CFNE's known spanning tree upstream to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the spanning tree message. + * The spanning tree message is a JSON body, and is a packet buffer compressed message. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Protocol Tag (REPL) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Variable Length JSON Payload ................................ | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The JSON payload is variable length and looks like this: + * { + * "id": , + * "masterId": , + * "identity": "", + * "children": [ + * "id": , + * "masterId": , + * "identity": "", + * "children": [], + * ] + * } + * \endcode * @param treeRoot Root of the master tree. * @returns bool True, if list was sent, otherwise false. */ bool writeSpanningTree(SpanningTree* treeRoot); /** * @brief Writes a complete update of this CFNE's HA parameters to the network. + * \code{.unparsed} + * Below is the representation of the data layout for the repeater/end point login message. + * The message is variable bytes in length. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Total length of all included entries | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: Peer ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: IP Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: Port | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param haParams List of HA parameters. * @returns bool True, if list was sent, otherwise false. */ From a162ec3945d2714f76bb6688e50fc2e683f29b24 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 28 Oct 2025 10:22:43 -0400 Subject: [PATCH 113/200] continue packet payload documenting; --- src/common/network/BaseNetwork.h | 19 ++++-- src/fne/network/FNENetwork.h | 109 +++++++++++++++++++++++++++++++ src/fne/network/SpanningTree.cpp | 2 +- 3 files changed, 125 insertions(+), 5 deletions(-) diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index 2b9f1d62..bad056e8 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -444,9 +444,9 @@ namespace network * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | | Variable | + * | | Log Message . | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Length Activity Log Message ................................. | + * | Log Message ................................................. | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @param message Textual string to send as activity log information. @@ -467,9 +467,9 @@ namespace network * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | | Variable | + * | | Log Message . | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Length Diagnostics Message .................................. | + * | Log Message ................................................. | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \endcode * @returns bool True, if message was sent, otherwise false. @@ -630,6 +630,8 @@ namespace network * Below is the representation of the data layout for the repeater/end point login message. * The message is variable bytes in length. * + * Each affiliation update entry is 7 bytes. + * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -651,6 +653,8 @@ namespace network * Below is the representation of the data layout for the repeater/end point login message. * The message is variable bytes in length. * + * Each peer ID entry is 4 bytes. + * * Byte 0 1 2 3 * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -972,6 +976,9 @@ namespace network * | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * + * S = Slot Number (clear Slot 1, set Slot 2) + * G = Group Flag + * * The data starting at offset 20 for 33 bytes of the raw DMR frame. * * DMR frame message has 2 trailing bytes: @@ -1134,6 +1141,8 @@ namespace network * | Blk to Flw | Current Block | DUID | Frame Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * + * C = Confirmed PDU Flag + * * The data starting at offset 24 for variable number of bytes (DUID dependant) * is the P25 frame. * \endcode @@ -1169,6 +1178,8 @@ namespace network * | | Frame Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * + * G = Group Flag + * * The data starting at offset 24 for 48 bytes if the raw NXDN frame. * \endcode * @param[out] length Length of network message buffer. diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 31880aa6..da07cca6 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -480,6 +480,23 @@ namespace network /** * @brief Helper to send the list of whitelisted RIDs to the specified peer. + * \code{.unparsed} + * Below is the representation of the data layout for the active/whitelisted RIDs message. + * The message is variable bytes in length. This layout does not apply for peer replication + * messages, as those messages are a packet buffered message of the entire RID ACL file. + * + * The RID ACL is chunked and sent in blocks of a maximum of 50 RIDs per message. + * + * Each radio ID ACL entry is 4 bytes. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of entries | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: Radio ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param peerId Peer ID. * @param streamId Stream ID for this message. * @param sendReplica Flag indicating the RID transfer is to an neighbor replica peer. @@ -487,12 +504,47 @@ namespace network void writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sendReplica); /** * @brief Helper to send the list of blacklisted RIDs to the specified peer. + * \code{.unparsed} + * Below is the representation of the data layout for the deactivated/blacklisted RIDs message. + * The message is variable bytes in length. + * + * The RID ACL is chunked and sent in blocks of a maximum of 50 RIDs per message. + * + * Each radio ID ACL entry is 4 bytes. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of entries | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: Radio ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param peerId Peer ID. * @param streamId Stream ID for this message. */ void writeBlacklistRIDs(uint32_t peerId, uint32_t streamId); /** * @brief Helper to send the list of active TGIDs to the specified peer. + * \code{.unparsed} + * Below is the representation of the data layout for the active TGs message. + * The message is variable bytes in length. This layout does not apply for peer replication + * messages, as those messages are a packet buffered message of the entire talkgroup ACL file. + * + * Each talkgroup ACL entry is 5 bytes. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of entries | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: Talkgroup ID |N|A| Slot | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * N = Non-Preferred Flag + * A = Affiliated Flag + * + * \endcode * @param peerId Peer ID. * @param streamId Stream ID for this message. * @param sendReplica Flag indicating the TGID transfer is to an neighbor replica peer. @@ -500,18 +552,49 @@ namespace network void writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendReplica); /** * @brief Helper to send the list of deactivated TGIDs to the specified peer. + * \code{.unparsed} + * Below is the representation of the data layout for the deactivated TGs message. + * The message is variable bytes in length. + * + * Each talkgroup ACL entry is 5 bytes. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of entries | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: Talkgroup ID | R | Slot | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param peerId Peer ID. * @param streamId Stream ID for this message. */ void writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId); /** * @brief Helper to send the list of peers to the specified peer. + * @note This doesn't have a data layout document because it is *only* sent as a packet buffered message. * @param peerId Peer ID. * @param streamId Stream ID for this message. */ void writePeerList(uint32_t peerId, uint32_t streamId); /** * @brief Helper to send the HA parameters to the specified peer. + * \code{.unparsed} + * Below is the representation of the data layout for the HA parameters message. + * The message is variable bytes in length. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Total length of all included entries | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: Peer ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: IP Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Entry: Port | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param peerId Peer ID. * @param streamId Stream ID for this message. * @param sendReplica Flag indicating the HA transfer is to an neighbor replica peer. @@ -521,6 +604,16 @@ namespace network /** * @brief Helper to send a network tree disconnect to the specified peer. * This will cause the peer to issue a link disconnect to the offending peer to prevent network loops. + * \code{.unparsed} + * Below is the representation of the data layout for the tree disconnect message. + * The message is 4 bytes in length. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Offending Peer ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param peerId Peer ID. * @param offendingPeerId Offending Peer ID. */ @@ -528,6 +621,22 @@ namespace network /** * @brief Helper to send a In-Call Control command to the specified peer. + * \code{.unparsed} + * Below is the representation of the data layout for the In-Call control message. + * The message is 15 bytes in length. + * + * Byte 0 1 2 3 + * Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | Peer ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Peer ID | ICC Command | Destination | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Destination ID | Slot | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endcode * @param peerId Peer ID. * @param streamId Stream ID for this message. * @param subFunc Network Sub-Function. diff --git a/src/fne/network/SpanningTree.cpp b/src/fne/network/SpanningTree.cpp index d513f4fc..1ef692fd 100644 --- a/src/fne/network/SpanningTree.cpp +++ b/src/fne/network/SpanningTree.cpp @@ -29,7 +29,7 @@ uint8_t SpanningTree::m_maxUpdatesBeforeReparent = 5U; SpanningTree::SpanningTree(uint32_t id, uint32_t masterId, SpanningTree* parent) : m_parent(parent), m_children(), - m_identity("UNK"), + m_identity("CHANGEME"), m_id(id), m_masterId(masterId), m_updatesBeforeReparent(0U) From 17895d2c9416fe52d4484106388083dadc12d222 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 28 Oct 2025 10:30:51 -0400 Subject: [PATCH 114/200] minor comment alteration; --- src/fne/network/FNENetwork.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 1a5e8958..379bda27 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -982,7 +982,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } break; - case NET_FUNC::RPTL: // Repeater Login + case NET_FUNC::RPTL: // Repeater/Peer Login { if (peerId > 0 && (network->m_peers.find(peerId) == network->m_peers.end())) { if (network->m_peers.size() >= MAX_HARD_CONN_CAP) { @@ -1062,7 +1062,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } } break; - case NET_FUNC::RPTK: // Repeater Authentication + case NET_FUNC::RPTK: // Repeater/Peer Authentication { if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) { FNEPeerConnection* connection = network->m_peers[peerId]; @@ -1160,7 +1160,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } } break; - case NET_FUNC::RPTC: // Repeater Configuration + case NET_FUNC::RPTC: // Repeater/Peer Configuration { if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) { FNEPeerConnection* connection = network->m_peers[peerId]; @@ -1336,7 +1336,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } break; - case NET_FUNC::RPT_DISC: // Repeater Disconnect + case NET_FUNC::RPT_DISC: // Repeater/Peer Disconnect { if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) { FNEPeerConnection* connection = network->m_peers[peerId]; @@ -1352,7 +1352,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } } break; - case NET_FUNC::PING: // Repeater Ping + case NET_FUNC::PING: // Ping { if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) { FNEPeerConnection* connection = network->m_peers[peerId]; @@ -1397,7 +1397,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } break; - case NET_FUNC::GRANT_REQ: // Repeater Grant Request + case NET_FUNC::GRANT_REQ: // Grant Request { if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) { FNEPeerConnection* connection = network->m_peers[peerId]; From e8a264ae2996b989eb546eb9c23b16e389628173 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 28 Oct 2025 18:23:19 -0400 Subject: [PATCH 115/200] correct DMR data handling; refactor FNE DMR data calls to be structured more akin to P25 data calls; --- src/common/dmr/DMRDefines.h | 13 +- src/common/dmr/data/DataBlock.cpp | 213 ++++++++++++------ src/common/dmr/data/DataHeader.cpp | 92 +++++++- src/common/dmr/data/DataHeader.h | 12 +- src/common/edac/CRC.cpp | 62 +++++ src/common/edac/CRC.h | 13 ++ src/common/p25/data/DataBlock.cpp | 4 + .../callhandler/packetdata/DMRPacketData.cpp | 186 +++++++-------- .../callhandler/packetdata/DMRPacketData.h | 4 +- src/host/dmr/packet/Data.cpp | 38 +++- 10 files changed, 452 insertions(+), 185 deletions(-) diff --git a/src/common/dmr/DMRDefines.h b/src/common/dmr/DMRDefines.h index 0fc996b2..bf0a621f 100644 --- a/src/common/dmr/DMRDefines.h +++ b/src/common/dmr/DMRDefines.h @@ -112,12 +112,14 @@ namespace dmr const uint32_t MAX_PDU_COUNT = 32U; - const uint32_t DMR_PDU_UNCONFIRMED_LENGTH_BYTES = 12U; - const uint32_t DMR_PDU_CONFIRMED_LENGTH_BYTES = 18U; - const uint32_t DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES = 16U; - const uint32_t DMR_PDU_CONFIRMED_HALFRATE_DATA_LENGTH_BYTES = 10U; + const uint32_t DMR_PDU_HALFRATE_LENGTH_BYTES = 12U; + const uint32_t DMR_PDU_THREEQUARTER_LENGTH_BYTES = 18U; const uint32_t DMR_PDU_UNCODED_LENGTH_BYTES = 24U; + const uint32_t DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES = 16U; + const uint32_t DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES = 10U; + const uint32_t DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES = 22U; + const uint32_t MI_LENGTH_BYTES = 4U; // This was guessed based on OTA data captures -- the message indicator seems to be the same length as a source/destination address const uint32_t RAW_AMBE_LENGTH_BYTES = 9U; /** @} */ @@ -302,7 +304,8 @@ namespace dmr */ VOICE_SYNC = 0xF0U, //!< Internal - Voice Sync - VOICE = 0xF1U //!< Internal - Voice + VOICE = 0xF1U, //!< Internal - Voice + GENERIC_DATA = 0xF2U //!< Internal - Data }; } diff --git a/src/common/dmr/data/DataBlock.cpp b/src/common/dmr/data/DataBlock.cpp index c13b131a..4d7c6292 100644 --- a/src/common/dmr/data/DataBlock.cpp +++ b/src/common/dmr/data/DataBlock.cpp @@ -88,9 +88,11 @@ bool DataBlock::decode(const uint8_t* data, const DataHeader& header) ::memset(m_data, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); if (m_dataType == DataType::RATE_34_DATA) - ::memcpy(m_data, buffer + 2U, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data + ::memcpy(m_data, buffer + 2U, DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES); // Payload Data else if (m_dataType == DataType::RATE_12_DATA) - ::memcpy(m_data, buffer + 2U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); // Payload Data + ::memcpy(m_data, buffer + 2U, DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES); // Payload Data + else if (m_dataType == DataType::RATE_1_DATA) + ::memcpy(m_data, buffer + 2U, DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES); // Payload Data else { LogError(LOG_DMR, "DataBlock::decode(), failed to decode block, invalid dataType = $%02X", m_dataType); return false; @@ -100,22 +102,40 @@ bool DataBlock::decode(const uint8_t* data, const DataHeader& header) ::memset(crcBuffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); // generate CRC buffer - uint32_t crcBitLength = 144U; - if (m_dataType == DataType::RATE_12_DATA) - crcBitLength = 96U; - - for (uint32_t i = 16U; i < crcBitLength; i++) { - bool b = READ_BIT(buffer, i); - WRITE_BIT(crcBuffer, i - 16U, b); + if (m_dataType == DataType::RATE_34_DATA) { + ::memcpy(crcBuffer, buffer + 2U, DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES); + for (uint32_t i = 0U; i < 7U; i++) { + bool b = READ_BIT(buffer, i); + WRITE_BIT(crcBuffer, i + 128U, b); + } } - - for (uint32_t i = 0U; i < 6U; i++) { - bool b = READ_BIT(buffer, i); - WRITE_BIT(crcBuffer, i + (crcBitLength - 16U), b); + else if (m_dataType == DataType::RATE_12_DATA) { + ::memcpy(crcBuffer, buffer + 2U, DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES); + for (uint32_t i = 0U; i < 7U; i++) { + bool b = READ_BIT(buffer, i); + WRITE_BIT(crcBuffer, i + 80U, b); + } + } + else if (m_dataType == DataType::RATE_1_DATA) { + ::memcpy(crcBuffer, buffer + 2U, DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES); + for (uint32_t i = 0U; i < 7U; i++) { + bool b = READ_BIT(buffer, i); + WRITE_BIT(crcBuffer, i + 176U, b); + } } +#if DEBUG_DMR_PDU_DATA + Utils::dump(2U, "DMR, CRC Bit Buffer", crcBuffer, DMR_PDU_UNCODED_LENGTH_BYTES); +#endif + + uint32_t crcBitLength = 135U; // 3/4 Rate + if (m_dataType == DataType::RATE_12_DATA) + crcBitLength = 87U; + if (m_dataType == DataType::RATE_1_DATA) + crcBitLength = 183U; + // compute CRC-9 for the packet - uint16_t calculated = edac::CRC::createCRC9(crcBuffer, crcBitLength - 9U); + uint16_t calculated = edac::CRC::createCRC9(crcBuffer, crcBitLength); calculated = ~calculated & 0x1FFU; if ((crc ^ calculated) != 0) { @@ -151,9 +171,9 @@ bool DataBlock::decode(const uint8_t* data, const DataHeader& header) ::memset(m_data, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); if (m_dataType == DataType::RATE_34_DATA) - ::memcpy(m_data, buffer, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data + ::memcpy(m_data, buffer, DMR_PDU_THREEQUARTER_LENGTH_BYTES); // Payload Data else if (m_dataType == DataType::RATE_12_DATA) - ::memcpy(m_data, buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); // Payload Data + ::memcpy(m_data, buffer, DMR_PDU_HALFRATE_LENGTH_BYTES); // Payload Data else { LogError(LOG_DMR, "DataBlock::decode(), failed to decode block, invalid dataType = $%02X", m_dataType); return false; @@ -180,98 +200,122 @@ void DataBlock::encode(uint8_t* data) if (m_DPF == DPF::CONFIRMED_DATA) { if (m_dataType == DataType::RATE_34_DATA) { - uint8_t buffer[DMR_PDU_CONFIRMED_LENGTH_BYTES]; - ::memset(buffer, 0x00U, DMR_PDU_CONFIRMED_LENGTH_BYTES); + uint8_t buffer[DMR_PDU_THREEQUARTER_LENGTH_BYTES]; + ::memset(buffer, 0x00U, DMR_PDU_THREEQUARTER_LENGTH_BYTES); buffer[0U] = ((m_serialNo << 1) & 0xFEU); // Confirmed Data Serial No. - ::memcpy(buffer + 2U, m_data, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data + ::memcpy(buffer + 2U, m_data, DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES); // Payload Data uint8_t crcBuffer[DMR_PDU_UNCODED_LENGTH_BYTES]; ::memset(crcBuffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); // generate CRC buffer - uint32_t bufferLen = DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES; - uint32_t crcBitLength = 144U; - - for (uint32_t i = 2U; i < bufferLen; i++) - crcBuffer[i - 2U] = buffer[2U]; - for (uint32_t i = 0; i < 6U; i++) { + ::memcpy(crcBuffer, buffer + 2U, DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES); + for (uint32_t i = 0U; i < 7U; i++) { bool b = READ_BIT(buffer, i); - WRITE_BIT(crcBuffer, i + (crcBitLength - 15U), b); + WRITE_BIT(crcBuffer, i + 128U, b); } uint16_t crc = edac::CRC::createCRC9(crcBuffer, 135U); + crc = ~crc & 0x1FFU; + buffer[0U] = buffer[0U] + ((crc >> 8) & 0x01U); // CRC-9 Check Sum (b8) buffer[1U] = (crc & 0xFFU); // CRC-9 Check Sum (b0 - b7) #if DEBUG_DMR_PDU_DATA - Utils::dump(1U, "DMR, DataBlock::encode(), Confirmed PDU Data Block", buffer, DMR_PDU_CONFIRMED_LENGTH_BYTES); + Utils::dump(1U, "DMR, DataBlock::encode(), Confirmed 3/4 Rate PDU Data Block", buffer, DMR_PDU_THREEQUARTER_LENGTH_BYTES); #endif m_trellis.encode34(buffer, data, true); } else if (m_dataType == DataType::RATE_12_DATA) { - uint8_t buffer[DMR_PDU_UNCONFIRMED_LENGTH_BYTES]; - ::memset(buffer, 0x00U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + uint8_t buffer[DMR_PDU_HALFRATE_LENGTH_BYTES]; + ::memset(buffer, 0x00U, DMR_PDU_HALFRATE_LENGTH_BYTES); buffer[0U] = ((m_serialNo << 1) & 0xFEU); // Confirmed Data Serial No. - ::memcpy(buffer + 2U, m_data, DMR_PDU_CONFIRMED_HALFRATE_DATA_LENGTH_BYTES); // Payload Data + ::memcpy(buffer + 2U, m_data, DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES); // Payload Data uint8_t crcBuffer[DMR_PDU_UNCODED_LENGTH_BYTES]; ::memset(crcBuffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); // generate CRC buffer - uint32_t bufferLen = DMR_PDU_CONFIRMED_HALFRATE_DATA_LENGTH_BYTES; - uint32_t crcBitLength = 96U; - - for (uint32_t i = 2U; i < bufferLen; i++) - crcBuffer[i - 2U] = buffer[2U]; - for (uint32_t i = 0; i < 6U; i++) { + ::memcpy(crcBuffer, buffer + 2U, DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES); + for (uint32_t i = 0U; i < 7U; i++) { bool b = READ_BIT(buffer, i); - WRITE_BIT(crcBuffer, i + (crcBitLength - 15U), b); + WRITE_BIT(crcBuffer, i + 80U, b); } uint16_t crc = edac::CRC::createCRC9(crcBuffer, 87U); + crc = ~crc & 0x1FFU; + buffer[0U] = buffer[0U] + ((crc >> 8) & 0x01U); // CRC-9 Check Sum (b8) buffer[1U] = (crc & 0xFFU); // CRC-9 Check Sum (b0 - b7) - ::memcpy(buffer, m_data, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + ::memcpy(buffer, m_data, DMR_PDU_HALFRATE_LENGTH_BYTES); #if DEBUG_DMR_PDU_DATA - Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed PDU Data Block", buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + Utils::dump(1U, "DMR, DataBlock::encode(), Confirmed 1/2 Rate PDU Data Block", buffer, DMR_PDU_HALFRATE_LENGTH_BYTES); #endif m_bptc.encode(buffer, data); } - else { - LogError(LOG_DMR, "DataBlock::encode(), cowardly refusing to encode confirmed full-rate (rate 1) data"); - return; + else if (m_dataType == DataType::RATE_1_DATA) { + uint8_t buffer[DMR_PDU_UNCODED_LENGTH_BYTES]; + ::memset(buffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); + + buffer[0U] = ((m_serialNo << 1) & 0xFEU); // Confirmed Data Serial No. + + ::memcpy(buffer + 2U, m_data, DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES); // Payload Data + + uint8_t crcBuffer[DMR_PDU_UNCODED_LENGTH_BYTES]; + ::memset(crcBuffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); + + // generate CRC buffer + ::memcpy(crcBuffer, buffer + 2U, DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES); + for (uint32_t i = 0U; i < 7U; i++) { + bool b = READ_BIT(buffer, i); + WRITE_BIT(crcBuffer, i + 176U, b); + } + + uint16_t crc = edac::CRC::createCRC9(crcBuffer, 183U); + crc = ~crc & 0x1FFU; + + buffer[0U] = buffer[0U] + ((crc >> 8) & 0x01U); // CRC-9 Check Sum (b8) + buffer[1U] = (crc & 0xFFU); // CRC-9 Check Sum (b0 - b7) + + ::memcpy(buffer, m_data, DMR_PDU_UNCODED_LENGTH_BYTES); + +#if DEBUG_DMR_PDU_DATA + Utils::dump(1U, "DMR, DataBlock::encode(), Confirmed 1 Rate PDU Data Block", buffer, DMR_PDU_UNCODED_LENGTH_BYTES); +#endif + + ::memcpy(data, buffer, DMR_PDU_UNCODED_LENGTH_BYTES); } } else if (m_DPF == DPF::UNCONFIRMED_DATA || m_DPF == DPF::RESPONSE || m_DPF == DPF::DEFINED_RAW || m_DPF == DPF::DEFINED_SHORT || m_DPF == DPF::UDT) { if (m_dataType == DataType::RATE_34_DATA) { - uint8_t buffer[DMR_PDU_CONFIRMED_LENGTH_BYTES]; - ::memset(buffer, 0x00U, DMR_PDU_CONFIRMED_LENGTH_BYTES); + uint8_t buffer[DMR_PDU_THREEQUARTER_LENGTH_BYTES]; + ::memset(buffer, 0x00U, DMR_PDU_THREEQUARTER_LENGTH_BYTES); - ::memcpy(buffer, m_data, DMR_PDU_CONFIRMED_LENGTH_BYTES); + ::memcpy(buffer, m_data, DMR_PDU_THREEQUARTER_LENGTH_BYTES); #if DEBUG_DMR_PDU_DATA - Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed PDU Data Block", buffer, DMR_PDU_CONFIRMED_LENGTH_BYTES); + Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed 3/4 Rate PDU Data Block", buffer, DMR_PDU_THREEQUARTER_LENGTH_BYTES); #endif m_trellis.encode34(buffer, data, true); } else if (m_dataType == DataType::RATE_12_DATA) { - uint8_t buffer[DMR_PDU_UNCONFIRMED_LENGTH_BYTES]; - ::memset(buffer, 0x00U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + uint8_t buffer[DMR_PDU_HALFRATE_LENGTH_BYTES]; + ::memset(buffer, 0x00U, DMR_PDU_HALFRATE_LENGTH_BYTES); - ::memcpy(buffer, m_data, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + ::memcpy(buffer, m_data, DMR_PDU_HALFRATE_LENGTH_BYTES); #if DEBUG_DMR_PDU_DATA - Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed PDU Data Block", buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed 1/2 Rate PDU Data Block", buffer, DMR_PDU_HALFRATE_LENGTH_BYTES); #endif m_bptc.encode(buffer, data); @@ -283,6 +327,10 @@ void DataBlock::encode(uint8_t* data) ::memcpy(buffer, m_data, DMR_PDU_UNCODED_LENGTH_BYTES); ::memcpy(data, buffer, DMR_PDU_UNCODED_LENGTH_BYTES); + +#if DEBUG_DMR_PDU_DATA + Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed 1 Rate PDU Data Block", buffer, DMR_PDU_UNCODED_LENGTH_BYTES); +#endif } } else { @@ -333,14 +381,25 @@ void DataBlock::setData(const uint8_t* buffer) assert(buffer != nullptr); assert(m_data != nullptr); - if (m_dataType == DataType::RATE_34_DATA) - ::memcpy(m_data, buffer, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); - else if (m_dataType == DataType::RATE_12_DATA) - ::memcpy(m_data, buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); - else if (m_dataType == DataType::RATE_1_DATA) - ::memcpy(m_data, buffer, DMR_PDU_UNCODED_LENGTH_BYTES); - else - LogError(LOG_DMR, "unknown dataType value in PDU, dataType = $%02X", m_dataType); + if (m_DPF == DPF::CONFIRMED_DATA) { + if (m_dataType == DataType::RATE_34_DATA) + ::memcpy(m_data, buffer, DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES); + else if (m_dataType == DataType::RATE_12_DATA) + ::memcpy(m_data, buffer, DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES); + else if (m_dataType == DataType::RATE_1_DATA) + ::memcpy(m_data, buffer, DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES); + else + LogError(LOG_DMR, "unknown dataType value in PDU, dataType = $%02X", m_dataType); + } else { + if (m_dataType == DataType::RATE_34_DATA) + ::memcpy(m_data, buffer, DMR_PDU_THREEQUARTER_LENGTH_BYTES); + else if (m_dataType == DataType::RATE_12_DATA) + ::memcpy(m_data, buffer, DMR_PDU_HALFRATE_LENGTH_BYTES); + else if (m_dataType == DataType::RATE_1_DATA) + ::memcpy(m_data, buffer, DMR_PDU_UNCODED_LENGTH_BYTES); + else + LogError(LOG_DMR, "unknown dataType value in PDU, dataType = $%02X", m_dataType); + } } /* Gets the raw data stored in the data block. */ @@ -350,17 +409,33 @@ uint32_t DataBlock::getData(uint8_t* buffer) const assert(buffer != nullptr); assert(m_data != nullptr); - if (m_dataType == DataType::RATE_34_DATA) { - ::memcpy(buffer, m_data, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); - return DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES; - } else if (m_dataType == DataType::RATE_12_DATA) { - ::memcpy(buffer, m_data, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); - return DMR_PDU_UNCONFIRMED_LENGTH_BYTES; - } else if (m_dataType == DataType::RATE_1_DATA) { - ::memcpy(buffer, m_data, DMR_PDU_UNCODED_LENGTH_BYTES); - return DMR_PDU_UNCODED_LENGTH_BYTES; + if (m_DPF == DPF::CONFIRMED_DATA) { + if (m_dataType == DataType::RATE_34_DATA) { + ::memcpy(buffer, m_data, DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES); + return DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES; + } else if (m_dataType == DataType::RATE_12_DATA) { + ::memcpy(buffer, m_data, DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES); + return DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES; + } else if (m_dataType == DataType::RATE_1_DATA) { + ::memcpy(buffer, m_data, DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES); + return DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES; + } else { + LogError(LOG_DMR, "unknown dataType value in PDU, dataType = $%02X", m_dataType); + return 0U; + } } else { - LogError(LOG_DMR, "unknown dataType value in PDU, dataType = $%02X", m_dataType); - return 0U; + if (m_dataType == DataType::RATE_34_DATA) { + ::memcpy(buffer, m_data, DMR_PDU_THREEQUARTER_LENGTH_BYTES); + return DMR_PDU_THREEQUARTER_LENGTH_BYTES; + } else if (m_dataType == DataType::RATE_12_DATA) { + ::memcpy(buffer, m_data, DMR_PDU_HALFRATE_LENGTH_BYTES); + return DMR_PDU_HALFRATE_LENGTH_BYTES; + } else if (m_dataType == DataType::RATE_1_DATA) { + ::memcpy(buffer, m_data, DMR_PDU_UNCODED_LENGTH_BYTES); + return DMR_PDU_UNCODED_LENGTH_BYTES; + } else { + LogError(LOG_DMR, "unknown dataType value in PDU, dataType = $%02X", m_dataType); + return 0U; + } } } diff --git a/src/common/dmr/data/DataHeader.cpp b/src/common/dmr/data/DataHeader.cpp index a7a6be9d..e7e2028d 100644 --- a/src/common/dmr/data/DataHeader.cpp +++ b/src/common/dmr/data/DataHeader.cpp @@ -404,33 +404,65 @@ void DataHeader::reset() /* Gets the total length in bytes of enclosed packet data. */ -uint32_t DataHeader::getPacketLength() const +uint32_t DataHeader::getPacketLength(DataType::E dataType) const { if (m_DPF == DPF::RESPONSE) { return 0U; // responses have no packet length as they are header only } if (m_DPF == DPF::CONFIRMED_DATA) { - return DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength; + if (dataType == DataType::RATE_34_DATA) { + return DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength; + } + else if (dataType == DataType::RATE_12_DATA) { + return DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength; + } + else { + return DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength; + } } else { - return DMR_PDU_UNCONFIRMED_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength; + if (dataType == DataType::RATE_34_DATA) { + return DMR_PDU_THREEQUARTER_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength; + } + else if (dataType == DataType::RATE_12_DATA) { + return DMR_PDU_HALFRATE_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength; + } + else { + return DMR_PDU_UNCODED_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength; + } } } /* Gets the total length in bytes of entire PDU. */ -uint32_t DataHeader::getPDULength() const +uint32_t DataHeader::getPDULength(DataType::E dataType) const { if (m_DPF == DPF::RESPONSE) { return 0U; // responses have no packet length as they are header only } if (m_DPF == DPF::CONFIRMED_DATA) { - return DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES * m_blocksToFollow; + if (dataType == DataType::RATE_34_DATA) { + return DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES * m_blocksToFollow; + } + else if (dataType == DataType::RATE_12_DATA) { + return DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES * m_blocksToFollow; + } + else { + return DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES * m_blocksToFollow; + } } else { - return DMR_PDU_UNCONFIRMED_LENGTH_BYTES * m_blocksToFollow; + if (dataType == DataType::RATE_34_DATA) { + return DMR_PDU_THREEQUARTER_LENGTH_BYTES * m_blocksToFollow; + } + else if (dataType == DataType::RATE_12_DATA) { + return DMR_PDU_HALFRATE_LENGTH_BYTES * m_blocksToFollow; + } + else { + return DMR_PDU_UNCODED_LENGTH_BYTES * m_blocksToFollow; + } } } @@ -447,10 +479,32 @@ uint32_t DataHeader::getData(uint8_t* buffer) const /* Helper to calculate the number of blocks to follow and padding length for a PDU. */ -void DataHeader::calculateLength(uint32_t packetLength) +void DataHeader::calculateLength(DataType::E dataType, uint32_t packetLength) { uint32_t len = packetLength + 4U; // packet length + CRC32 - uint32_t blockLen = (m_DPF == DPF::CONFIRMED_DATA) ? DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES : DMR_PDU_UNCONFIRMED_LENGTH_BYTES; + uint32_t blockLen = DMR_PDU_UNCODED_LENGTH_BYTES; + if (m_DPF == DPF::CONFIRMED_DATA) { + if (dataType == DataType::RATE_34_DATA) { + blockLen = DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES; + } + else if (dataType == DataType::RATE_12_DATA) { + blockLen = DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES; + } + else { + blockLen = DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES; + } + } + else { + if (dataType == DataType::RATE_34_DATA) { + blockLen = DMR_PDU_THREEQUARTER_LENGTH_BYTES; + } + else if (dataType == DataType::RATE_12_DATA) { + blockLen = DMR_PDU_HALFRATE_LENGTH_BYTES; + } + else { + blockLen = DMR_PDU_UNCODED_LENGTH_BYTES; + } + } if (len > blockLen) { m_padLength = blockLen - (len % blockLen); @@ -463,13 +517,29 @@ void DataHeader::calculateLength(uint32_t packetLength) /* Helper to determine the pad length for a given packet length. */ -uint32_t DataHeader::calculatePadLength(DPF::E dpf, uint32_t packetLength) +uint32_t DataHeader::calculatePadLength(DPF::E dpf, DataType::E dataType, uint32_t packetLength) { uint32_t len = packetLength + 4U; // packet length + CRC32 if (dpf == DPF::CONFIRMED_DATA) { - return DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES - (len % DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); + if (dataType == DataType::RATE_34_DATA) { + return DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES - (len % DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES); + } + else if (dataType == DataType::RATE_12_DATA) { + return DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES - (len % DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES); + } + else { + return DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES - (len % DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES); + } } else { - return DMR_PDU_UNCONFIRMED_LENGTH_BYTES - (len % DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + if (dataType == DataType::RATE_34_DATA) { + return DMR_PDU_THREEQUARTER_LENGTH_BYTES - (len % DMR_PDU_THREEQUARTER_LENGTH_BYTES); + } + else if (dataType == DataType::RATE_12_DATA) { + return DMR_PDU_HALFRATE_LENGTH_BYTES - (len % DMR_PDU_HALFRATE_LENGTH_BYTES); + } + else { + return DMR_PDU_UNCODED_LENGTH_BYTES - (len % DMR_PDU_UNCODED_LENGTH_BYTES); + } } } diff --git a/src/common/dmr/data/DataHeader.h b/src/common/dmr/data/DataHeader.h index e308c491..52d10861 100644 --- a/src/common/dmr/data/DataHeader.h +++ b/src/common/dmr/data/DataHeader.h @@ -72,14 +72,16 @@ namespace dmr /** * @brief Gets the total length in bytes of enclosed packet data. + * @param dataType DMR Data Type. * @returns uint32_t Total length of packet in bytes. */ - uint32_t getPacketLength() const; + uint32_t getPacketLength(defines::DataType::E dataType) const; /** * @brief Gets the total length in bytes of entire PDU. + * @param dataType DMR Data Type. * @returns uint32_t Total length of PDU in bytes. */ - uint32_t getPDULength() const; + uint32_t getPDULength(defines::DataType::E dataType) const; /** * @brief Gets the raw header data. @@ -90,17 +92,19 @@ namespace dmr /** * @brief Helper to calculate the number of blocks to follow and padding length for a PDU. + * @param dataType DMR Data Type. * @param packetLength Length of PDU. */ - void calculateLength(uint32_t packetLength); + void calculateLength(defines::DataType::E dataType, uint32_t packetLength); /** * @brief Helper to determine the pad length for a given packet length. * @param dpf PDU format type. + * @param dataType DMR Data Type. * @param packetLength Length of PDU. * @returns uint32_t Number of pad bytes. */ - static uint32_t calculatePadLength(defines::DPF::E dpf, uint32_t packetLength); + static uint32_t calculatePadLength(defines::DPF::E dpf, defines::DataType::E dataType, uint32_t packetLength); public: /** diff --git a/src/common/edac/CRC.cpp b/src/common/edac/CRC.cpp index b4f6a852..9b483ee2 100644 --- a/src/common/edac/CRC.cpp +++ b/src/common/edac/CRC.cpp @@ -320,6 +320,36 @@ bool CRC::checkCRC32(const uint8_t *in, uint32_t length) return crc8[0U] == in[length - 1U] && crc8[1U] == in[length - 2U] && crc8[2U] == in[length - 3U] && crc8[3U] == in[length - 4U]; } +/* Check 32-bit CRC (inverted). */ + +bool CRC::checkInvertedCRC32(const uint8_t *in, uint32_t length) +{ + assert(in != nullptr); + assert(length > 4U); + + union { + uint32_t crc32; + uint8_t crc8[4U]; + }; + + uint32_t i = 0; + crc32 = 0x00000000U; + + for (uint32_t j = (length - 4U); j-- > 0; i++) { + uint32_t idx = ((crc32 >> 24) ^ in[i]) & 0xFFU; + crc32 = (CRC32_TABLE[idx] ^ (crc32 << 8)) & 0xFFFFFFFFU; + } + + crc32 &= 0xFFFFFFFFU; + +#if DEBUG_CRC_CHECK + uint32_t inCrc = (in[length - 4U] << 24) | (in[length - 3U] << 16) | (in[length - 2U] << 8) | (in[length - 1U] << 0); + LogDebugEx(LOG_HOST, "CRC::checkInvertedCRC32()", "crc = $%08X, in = $%08X, len = %u", crc32, inCrc, length); +#endif + + return crc8[0U] == in[length - 1U] && crc8[1U] == in[length - 2U] && crc8[2U] == in[length - 3U] && crc8[3U] == in[length - 4U]; +} + /* Encode 32-bit CRC. */ void CRC::addCRC32(uint8_t* in, uint32_t length) @@ -353,6 +383,38 @@ void CRC::addCRC32(uint8_t* in, uint32_t length) in[length - 4U] = crc8[3U]; } +/* Encode 32-bit CRC (inverted). */ + +void CRC::addInvertedCRC32(uint8_t* in, uint32_t length) +{ + assert(in != nullptr); + assert(length > 4U); + + union { + uint32_t crc32; + uint8_t crc8[4U]; + }; + + uint32_t i = 0; + crc32 = 0x00000000U; + + for (uint32_t j = (length - 4U); j-- > 0; i++) { + uint32_t idx = ((crc32 >> 24) ^ in[i]) & 0xFFU; + crc32 = (CRC32_TABLE[idx] ^ (crc32 << 8)) & 0xFFFFFFFFU; + } + + crc32 &= 0xFFFFFFFFU; + +#if DEBUG_CRC_ADD + LogDebugEx(LOG_HOST, "CRC::addInvertedCRC32()", "crc = $%08X, len = %u", crc32, length); +#endif + + in[length - 1U] = crc8[0U]; + in[length - 2U] = crc8[1U]; + in[length - 3U] = crc8[2U]; + in[length - 4U] = crc8[3U]; +} + /* Generate 8-bit CRC. */ uint8_t CRC::crc8(const uint8_t *in, uint32_t length) diff --git a/src/common/edac/CRC.h b/src/common/edac/CRC.h index 4d6cd89c..8007dd29 100644 --- a/src/common/edac/CRC.h +++ b/src/common/edac/CRC.h @@ -92,12 +92,25 @@ namespace edac * @returns bool True, if CRC is valid, otherwise false. */ static bool checkCRC32(const uint8_t* in, uint32_t length); + /** + * @brief Check 32-bit CRC (inverted). + * @param[in] in Input byte array. + * @param length Length of byte array. + * @returns bool True, if CRC is valid, otherwise false. + */ + static bool checkInvertedCRC32(const uint8_t* in, uint32_t length); /** * @brief Encode 32-bit CRC. * @param[out] in Input byte array. * @param length Length of byte array. */ static void addCRC32(uint8_t* in, uint32_t length); + /** + * @brief Encode 32-bit CRC (inverted). + * @param[out] in Input byte array. + * @param length Length of byte array. + */ + static void addInvertedCRC32(uint8_t* in, uint32_t length); /** * @brief Generate 8-bit CRC. diff --git a/src/common/p25/data/DataBlock.cpp b/src/common/p25/data/DataBlock.cpp index 0ba28984..ae518184 100644 --- a/src/common/p25/data/DataBlock.cpp +++ b/src/common/p25/data/DataBlock.cpp @@ -99,6 +99,10 @@ bool DataBlock::decode(const uint8_t* data, const DataHeader& header, bool noTre } } +#if DEBUG_P25_PDU_DATA + Utils::dump(2U, "P25, CRC Bit Buffer", crcBuffer, P25_PDU_CONFIRMED_LENGTH_BYTES); +#endif + // compute CRC-9 for the packet uint16_t calculated = edac::CRC::createCRC9(crcBuffer, 135U); if ((crc ^ calculated) != 0) { diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp index e9c07484..0cdcf92d 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp @@ -91,120 +91,115 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee uint8_t frame[DMR_FRAME_LENGTH_BYTES]; dmrData.getData(frame); - // is the stream valid? - if (m_tag->validate(peerId, dmrData, nullptr, streamId)) { - // is this peer ignored? - if (!m_tag->isPeerPermitted(peerId, dmrData, streamId)) { - return false; - } - + if (dataSync) { auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { return x.second->peerId == peerId; }); - if (it != m_status.end()) { - RxStatus* status = m_status[peerId]; - if (streamId != status->streamId) { - LogWarning(LOG_NET, "DMR, Data Call Collision, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", - peerId, streamId, status->peerId, status->srcId, status->slotNo, status->streamId, fromUpstream); + if (it == m_status.end()) { + // this is a new call stream + m_status.lock(); + RxStatus* status = new RxStatus(); + status->callStartTime = pktTime; + status->srcId = srcId; + status->dstId = dstId; + status->slotNo = slotNo; + status->streamId = streamId; + status->peerId = peerId; + m_status.unlock(); + + m_status.insert(peerId, status); + } - uint64_t duration = hrc::diff(pktTime, status->callStartTime); + RxStatus* status = m_status[peerId]; + status->streamId = streamId; - if ((duration / 1000) > DATA_CALL_COLL_TIMEOUT) { - LogWarning(LOG_NET, "DMR, force clearing stuck data call, timeout, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxStreamId = %u, fromUpstream = %u", - peerId, streamId, status->peerId, status->srcId, status->slotNo, status->streamId, fromUpstream); - - delete status; - m_status.erase(peerId); - } + if (dataType == DataType::DATA_HEADER) { + bool ret = status->header.decode(frame); + if (!ret) { + LogError(LOG_DMR, "DMR Slot %u, DataType::DATA_HEADER, unable to decode the network data header", status->slotNo); + Utils::dump(1U, "DMR, Unfixable PDU Data", frame, DMR_FRAME_LENGTH_BYTES); + delete status; + m_status.erase(peerId); return false; } - } else { - if (dataSync && (dataType == DataType::DATA_HEADER)) { - // this is a new call stream - RxStatus* status = new RxStatus(); - status->callStartTime = pktTime; - status->srcId = srcId; - status->dstId = dstId; - status->slotNo = slotNo; - status->streamId = streamId; - status->peerId = peerId; - - bool ret = status->header.decode(frame); - if (!ret) { - LogError(LOG_DMR, "DMR Slot %u, DataType::DATA_HEADER, unable to decode the network data header", status->slotNo); - Utils::dump(1U, "DMR, Unfixable PDU Data", frame, DMR_FRAME_LENGTH_BYTES); - delete status; - m_status.erase(peerId); - return false; - } + status->frames = status->header.getBlocksToFollow(); + status->dataBlockCnt = 0U; + status->hasRxHeader = true; - status->frames = status->header.getBlocksToFollow(); - status->dataBlockCnt = 0U; + bool gi = status->header.getGI(); + uint32_t srcId = status->header.getSrcId(); + uint32_t dstId = status->header.getDstId(); - bool gi = status->header.getGI(); - uint32_t srcId = status->header.getSrcId(); - uint32_t dstId = status->header.getDstId(); + LogInfoEx(LOG_DMR, DMR_DT_DATA_HEADER ", peerId = %u, slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", + peerId, status->slotNo, status->header.getDPF(), status->header.getA(), status->header.getSAP(), status->header.getFullMesage(), status->header.getBlocksToFollow(), status->header.getPadLength(), status->header.getPacketLength(dataType), + status->header.getFSN(), dstId, srcId, gi); - LogInfoEx(LOG_DMR, DMR_DT_DATA_HEADER ", peerId = %u, slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", - peerId, status->slotNo, status->header.getDPF(), status->header.getA(), status->header.getSAP(), status->header.getFullMesage(), status->header.getBlocksToFollow(), status->header.getPadLength(), status->header.getPacketLength(), - status->header.getFSN(), dstId, srcId, gi); + // make sure we don't get a PDU with more blocks then we support + if (status->header.getBlocksToFollow() >= MAX_PDU_COUNT) { + LogError(LOG_DMR, DMR_DT_DATA_HEADER ", too many PDU blocks to process, %u > %u", status->header.getBlocksToFollow(), MAX_PDU_COUNT); + return false; + } - // make sure we don't get a PDU with more blocks then we support - if (status->header.getBlocksToFollow() >= MAX_PDU_COUNT) { - LogError(LOG_DMR, DMR_DT_DATA_HEADER ", too many PDU blocks to process, %u > %u", status->header.getBlocksToFollow(), MAX_PDU_COUNT); - return false; - } + m_status[peerId] = status; - m_status[peerId] = status; - - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call Start, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, streamId = %u, fromUpstream = %u", peerId, status->slotNo, status->srcId, status->dstId, gi, streamId, fromUpstream); - dispatchToFNE(peerId, dmrData, data, len, seqNo, pktSeq, streamId); + dispatchToFNE(peerId, dmrData, data, len, seqNo, pktSeq, streamId); + // a PDU header only with no blocks to follow is usually a response header + if (status->header.getBlocksToFollow() == 0U) { + delete status; + m_status.erase(peerId); return true; - } else { - return false; } - } - - RxStatus* status = m_status[peerId]; - - // a PDU header only with no blocks to follow is usually a response header - if (status->header.getBlocksToFollow() == 0U) { - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", - peerId, status->slotNo, status->srcId, status->dstId, streamId, fromUpstream); - delete status; - m_status.erase(peerId); + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call Start, peer = %u, slot = %u, srcId = %u, dstId = %u, group = %u, streamId = %u, fromUpstream = %u", peerId, status->slotNo, status->srcId, status->dstId, gi, streamId, fromUpstream); return true; } - data::DataBlock dataBlock; - dataBlock.setDataType(dataType); + if ((dataType == DataType::RATE_34_DATA) || + (dataType == DataType::RATE_12_DATA) || + (dataType == DataType::RATE_1_DATA)) { + dispatchToFNE(peerId, dmrData, data, len, seqNo, pktSeq, streamId); - bool ret = dataBlock.decode(frame, status->header); - if (ret) { - uint32_t blockLen = dataBlock.getData(status->pduUserData + status->pduDataOffset); - status->pduDataOffset += blockLen; + data::DataBlock dataBlock; + dataBlock.setDataType(dataType); - status->frames--; - if (status->frames == 0U) - dataBlock.setLastBlock(true); + bool ret = dataBlock.decode(frame, status->header); + if (ret) { + uint32_t blockLen = dataBlock.getData(status->pduUserData + status->pduDataOffset); + status->pduDataOffset += blockLen; - if (dataType == DataType::RATE_34_DATA) { - LogInfoEx(LOG_DMR, DMR_DT_RATE_34_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); - } else if (dataType == DataType::RATE_12_DATA) { - LogInfoEx(LOG_DMR, DMR_DT_RATE_12_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); - } - else { - LogInfoEx(LOG_DMR, DMR_DT_RATE_1_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); + status->frames--; + if (status->frames == 0U) + dataBlock.setLastBlock(true); + status->dataBlockCnt++; } - - dispatchToFNE(peerId, dmrData, data, len, seqNo, pktSeq, streamId); - status->dataBlockCnt++; } // dispatch the PDU data - if (status->dataBlockCnt > 0U && status->frames == 0U) { + if (status->hasRxHeader && status->dataBlockCnt > 0U && status->frames == 0U) { + // is the source ID a blacklisted ID? + lookups::RadioId rid = m_network->m_ridLookup->find(status->header.getSrcId()); + if (!rid.radioDefault()) { + if (!rid.radioEnabled()) { + // report error event to InfluxDB + if (m_network->m_enableInfluxDB) { + influxdb::QueryBuilder() + .meas("call_error_event") + .tag("peerId", std::to_string(peerId)) + .tag("streamId", std::to_string(streamId)) + .tag("srcId", std::to_string(status->header.getSrcId())) + .tag("dstId", std::to_string(status->header.getDstId())) + .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .requestAsync(m_network->m_influxServer); + } + + delete status; + m_status.erase(peerId); + return false; + } + } + dispatch(peerId, dmrData, data, len); uint64_t duration = hrc::diff(pktTime, status->callStartTime); @@ -248,7 +243,20 @@ void DMRPacketData::dispatch(uint32_t peerId, dmr::data::NetData& dmrData, const RxStatus *status = m_status[peerId]; if (status->header.getBlocksToFollow() > 0U && status->frames == 0U) { - bool crcRet = edac::CRC::checkCRC32(status->pduUserData, status->pduDataOffset); + // ooookay -- lets do the insane, and ridiculously stupid, ETSI Big-Endian reversed byte ordering bullshit for the CRC-32 + uint8_t crcBytes[MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U]; + ::memset(crcBytes, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U); + for (uint8_t i = 0U; i < status->pduDataOffset - 4U; i += 2U) { + crcBytes[i + 1U] = status->pduUserData[i]; + crcBytes[i] = status->pduUserData[i + 1U]; + } + + crcBytes[status->pduDataOffset - 1U] = status->pduUserData[status->pduDataOffset - 4U]; + crcBytes[status->pduDataOffset - 2U] = status->pduUserData[status->pduDataOffset - 3U]; + crcBytes[status->pduDataOffset - 3U] = status->pduUserData[status->pduDataOffset - 2U]; + crcBytes[status->pduDataOffset - 4U] = status->pduUserData[status->pduDataOffset - 1U]; + + bool crcRet = edac::CRC::checkInvertedCRC32(crcBytes, status->pduDataOffset); if (!crcRet) { LogWarning(LOG_DMR, "DMR Data, failed CRC-32 check, blocks %u, len %u", status->header.getBlocksToFollow(), status->pduDataOffset); } diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.h b/src/fne/network/callhandler/packetdata/DMRPacketData.h index e8cd2266..1100a88a 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.h +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.h @@ -4,7 +4,7 @@ * 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 + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -85,6 +85,7 @@ namespace network uint32_t peerId; dmr::data::DataHeader header; + bool hasRxHeader; uint8_t dataBlockCnt; uint8_t frames; @@ -101,6 +102,7 @@ namespace network streamId(0U), peerId(0U), header(), + hasRxHeader(false), dataBlockCnt(0U), pduUserData(nullptr), pduDataOffset(0U) diff --git a/src/host/dmr/packet/Data.cpp b/src/host/dmr/packet/Data.cpp index c9d3a204..7ee6f386 100644 --- a/src/host/dmr/packet/Data.cpp +++ b/src/host/dmr/packet/Data.cpp @@ -174,7 +174,7 @@ bool Data::process(uint8_t* data, uint32_t len) if (m_verbose) { LogInfoEx(LOG_RF, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", - m_slot->m_slotNo, m_rfDataHeader.getDPF(), m_rfDataHeader.getA(), m_rfDataHeader.getSAP(), m_rfDataHeader.getFullMesage(), m_rfDataHeader.getBlocksToFollow(), m_rfDataHeader.getPadLength(), m_rfDataHeader.getPacketLength(), + m_slot->m_slotNo, m_rfDataHeader.getDPF(), m_rfDataHeader.getA(), m_rfDataHeader.getSAP(), m_rfDataHeader.getFullMesage(), m_rfDataHeader.getBlocksToFollow(), m_rfDataHeader.getPadLength(), m_rfDataHeader.getPacketLength(dataType), m_rfDataHeader.getFSN(), dstId, srcId, gi); } @@ -289,7 +289,20 @@ bool Data::process(uint8_t* data, uint32_t len) } if (m_rfDataHeader.getBlocksToFollow() > 0U && m_slot->m_rfFrames == 0U) { - bool crcRet = edac::CRC::checkCRC32(m_pduUserData, m_pduDataOffset); + // ooookay -- lets do the insane, and ridiculously stupid, ETSI Big-Endian reversed byte ordering bullshit for the CRC-32 + uint8_t crcBytes[MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U]; + ::memset(crcBytes, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U); + for (uint8_t i = 0U; i < m_pduDataOffset - 4U; i += 2U) { + crcBytes[i + 1U] = m_pduUserData[i]; + crcBytes[i] = m_pduUserData[i + 1U]; + } + + crcBytes[m_pduDataOffset - 1U] = m_pduUserData[m_pduDataOffset - 4U]; + crcBytes[m_pduDataOffset - 2U] = m_pduUserData[m_pduDataOffset - 3U]; + crcBytes[m_pduDataOffset - 3U] = m_pduUserData[m_pduDataOffset - 2U]; + crcBytes[m_pduDataOffset - 4U] = m_pduUserData[m_pduDataOffset - 1U]; + + bool crcRet = edac::CRC::checkInvertedCRC32(crcBytes, m_pduDataOffset); if (!crcRet) { LogWarning(LOG_RF, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", m_rfDataHeader.getBlocksToFollow(), m_pduDataOffset); } @@ -487,7 +500,7 @@ void Data::processNetwork(const data::NetData& dmrData) if (m_verbose) { LogInfoEx(LOG_NET, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", - m_slot->m_slotNo, m_netDataHeader.getDPF(), m_netDataHeader.getA(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMesage(), m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadLength(), m_netDataHeader.getPacketLength(), + m_slot->m_slotNo, m_netDataHeader.getDPF(), m_netDataHeader.getA(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMesage(), m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadLength(), m_netDataHeader.getPacketLength(dataType), m_netDataHeader.getFSN(), dstId, srcId, gi); } @@ -537,7 +550,20 @@ void Data::processNetwork(const data::NetData& dmrData) } if (m_netDataHeader.getBlocksToFollow() > 0U && m_slot->m_netFrames == 0U) { - bool crcRet = edac::CRC::checkCRC32(m_pduUserData, m_pduDataOffset); + // ooookay -- lets do the insane, and ridiculously stupid, ETSI Big-Endian reversed byte ordering bullshit for the CRC-32 + uint8_t crcBytes[MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U]; + ::memset(crcBytes, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U); + for (uint8_t i = 0U; i < m_pduDataOffset - 4U; i += 2U) { + crcBytes[i + 1U] = m_pduUserData[i]; + crcBytes[i] = m_pduUserData[i + 1U]; + } + + crcBytes[m_pduDataOffset - 1U] = m_pduUserData[m_pduDataOffset - 4U]; + crcBytes[m_pduDataOffset - 2U] = m_pduUserData[m_pduDataOffset - 3U]; + crcBytes[m_pduDataOffset - 3U] = m_pduUserData[m_pduDataOffset - 2U]; + crcBytes[m_pduDataOffset - 4U] = m_pduUserData[m_pduDataOffset - 1U]; + + bool crcRet = edac::CRC::checkInvertedCRC32(crcBytes, m_pduDataOffset); if (!crcRet) { LogWarning(LOG_NET, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", m_netDataHeader.getBlocksToFollow(), m_pduDataOffset); } @@ -676,8 +702,8 @@ void Data::writeRF_PDU_Ack_Response(uint8_t rspClass, uint8_t rspType, uint8_t r ::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES); // decode the BPTC (196,96) FEC - uint8_t payload[DMR_PDU_UNCONFIRMED_LENGTH_BYTES]; - ::memset(payload, 0x00U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + uint8_t payload[DMR_PDU_UNCODED_LENGTH_BYTES]; + ::memset(payload, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); // encode the BPTC (196,96) FEC bptc.encode(payload, data + 2U); From 600d5741771f0514195e76079840f533bfb9172f Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 28 Oct 2025 20:35:59 -0400 Subject: [PATCH 116/200] use WASAPI by default on Windows; --- src/bridge/BridgeMain.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bridge/BridgeMain.cpp b/src/bridge/BridgeMain.cpp index e9f97461..125357b7 100644 --- a/src/bridge/BridgeMain.cpp +++ b/src/bridge/BridgeMain.cpp @@ -118,7 +118,7 @@ void usage(const char* message, const char* arg) " -o output audio device\n" #ifdef _WIN32 "\n" - " -wasapi use WASAPI on Windows\n" + " -winmm use WinMM audio on Windows\n" #endif "\n" " -c specifies the configuration file to use\n" @@ -204,10 +204,10 @@ int checkArgs(int argc, char* argv[]) p += 2; } #ifdef _WIN32 - else if (IS("-wasapi")) { + else if (IS("-winmm")) { // Windows - g_backends[0] = ma_backend_wasapi; - g_backends[1] = ma_backend_winmm; + g_backends[0] = ma_backend_winmm; + g_backends[1] = ma_backend_wasapi; g_backends[2] = ma_backend_null; } #endif @@ -246,8 +246,8 @@ int main(int argc, char** argv) #ifdef _WIN32 // Windows - g_backends[0] = ma_backend_winmm; - g_backends[1] = ma_backend_wasapi; + g_backends[0] = ma_backend_wasapi; + g_backends[1] = ma_backend_winmm; g_backends[2] = ma_backend_null; #else // Linux From 86d97bb9377b700e5d5c082515ac4705dbc4d77c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 28 Oct 2025 23:42:24 -0400 Subject: [PATCH 117/200] better handle out of order blocks for PDUs; reduce packet retry to 2; correct handling ack response packets; --- src/common/dmr/DMRDefines.h | 5 ++++ .../callhandler/packetdata/P25PacketData.cpp | 30 ++++++++++++++----- src/host/p25/Control.cpp | 3 +- src/host/p25/packet/Data.cpp | 16 ++++++---- src/host/p25/packet/Data.h | 3 +- 5 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/common/dmr/DMRDefines.h b/src/common/dmr/DMRDefines.h index bf0a621f..187912dd 100644 --- a/src/common/dmr/DMRDefines.h +++ b/src/common/dmr/DMRDefines.h @@ -202,6 +202,11 @@ namespace dmr }; }; + /** @brief ARP Request */ + const uint8_t DMR_PDU_ARP_REQUEST = 0x01U; + /** @brief ARP Reply */ + const uint8_t DMR_PDU_ARP_REPLY = 0x02U; + /** @name Feature Set IDs */ /** @brief ETSI Standard Feature Set */ const uint8_t FID_ETSI = 0x00U; diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index 33fd7c0b..324c62ef 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -42,7 +42,7 @@ using namespace p25::sndcp; // --------------------------------------------------------------------------- const uint8_t DATA_CALL_COLL_TIMEOUT = 60U; -const uint8_t MAX_PKT_RETRY_CNT = 5U; +const uint8_t MAX_PKT_RETRY_CNT = 2U; const uint32_t INTERPACKET_DELAY = 100U; // milliseconds const uint32_t ARP_RETRY_MS = 5000U; // milliseconds @@ -158,7 +158,7 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return true; } - ::memcpy(status->netPDU + status->dataOffset, data + 24U, blockLength); + ::memcpy(status->netPDU + ((currentBlock - 1U) * blockLength), data + 24U, blockLength); status->dataOffset += blockLength; status->netPDUCount++; status->dataBlockCnt++; @@ -374,9 +374,9 @@ void P25PacketData::processPacketFrame(const uint8_t* data, uint32_t len, bool a DECLARE_UINT8_ARRAY(pduUserData, pduLength); ::memcpy(pduUserData, data, pktLen); -#if DEBUG_P25_PDU_DATA +//#if DEBUG_P25_PDU_DATA Utils::dump(1U, "P25, P25PacketData::processPacketFrame(), pduUserData", pduUserData, pduLength); -#endif +//#endif // queue frame for dispatch QueuedDataFrame* qf = new QueuedDataFrame(); @@ -676,6 +676,13 @@ void P25PacketData::dispatch(uint32_t peerId) if (status->header.getFormat() == PDUFormatType::UNCONFIRMED && status->extendedAddress) dataPktOffset = 12U; + uint32_t srcLlId = status->header.getSrcLLId(); + if (!status->extendedAddress) + srcLlId = status->header.getLLId(); + uint32_t dstLlId = status->header.getLLId(); + if (!status->extendedAddress) + dstLlId = WUID_FNE; + struct ip* ipHeader = (struct ip*)(status->pduUserData + dataPktOffset); char srcIp[INET_ADDRSTRLEN]; @@ -725,7 +732,7 @@ void P25PacketData::dispatch(uint32_t peerId) // transmit packet to IP network LogInfoEx(LOG_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); + srcIp, srcLlId, dstIp, dstLlId, pktLen, proto); DECLARE_UINT8_ARRAY(ipFrame, pktLen); ::memcpy(ipFrame, status->pduUserData + dataPktOffset, pktLen); @@ -738,8 +745,14 @@ void P25PacketData::dispatch(uint32_t peerId) // 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(), - true, status->header.getLLId()); + if (status->extendedAddress) { + m_readyForNextPkt[srcLlId] = true; + write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->header.getNs(), srcLlId, + true, dstLlId); + } else { + m_readyForNextPkt[srcLlId] = true; + write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->header.getNs(), srcLlId, false); + } } #endif // !defined(_WIN32) } @@ -1071,6 +1084,9 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: uint32_t streamId = m_network->createStreamId(); uint16_t pktSeq = 0U; + if (pduUserData == nullptr) + pktSeq = RTP_END_OF_CALL_SEQ; + uint8_t buffer[P25_PDU_FEC_LENGTH_BYTES]; ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index 35f3177f..0edb3c08 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -1460,13 +1460,14 @@ void Control::processNetwork() } uint32_t blockLength = GET_UINT24(buffer, 8U); + uint8_t currentBlock = buffer[21U]; if (m_debug) { LogDebug(LOG_NET, "P25, duid = $%02X, MFId = $%02X, blockLength = %u, len = %u", duid, MFId, blockLength, length); } if (!m_dedicatedControl) - m_data->processNetwork(data.get(), frameLength, blockLength); + m_data->processNetwork(data.get(), frameLength, currentBlock, blockLength); return; } diff --git a/src/host/p25/packet/Data.cpp b/src/host/p25/packet/Data.cpp index b56cd0cc..c8c60fa7 100644 --- a/src/host/p25/packet/Data.cpp +++ b/src/host/p25/packet/Data.cpp @@ -463,7 +463,7 @@ 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) { ::ActivityLog("P25", true, "RF data transmission from %u to %u, %u blocks", srcId, dstId, m_rfDataHeader.getBlocksToFollow()); - LogInfoEx(LOG_RF, "P25 Data Call, srcId = %u, dstId = %u", srcId, dstId); + LogInfoEx(LOG_RF, "P25 Data Call (Local Repeat), srcId = %u, dstId = %u", srcId, dstId); if (m_verbose) { LogInfoEx(LOG_RF, P25_PDU_STR ", repeating PDU, llId = %u", (m_rfExtendedAddress) ? m_rfDataHeader.getSrcLLId() : m_rfDataHeader.getLLId()); @@ -483,7 +483,7 @@ bool Data::process(uint8_t* data, uint32_t len) m_rfPDUBits = 0U; m_rfPduUserDataLength = 0U; - m_p25->m_rfState = m_prevRfState; + m_p25->m_rfState = RS_RF_LISTENING; } // switch (m_rfDataHeader.getSAP()) } @@ -499,9 +499,9 @@ bool Data::process(uint8_t* data, uint32_t len) /* Process a data frame from the network. */ -bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) +bool Data::processNetwork(uint8_t* data, uint32_t len, uint8_t currentBlock, uint32_t blockLength) { - if (m_p25->m_netState != RS_NET_DATA) { + if ((m_p25->m_netState != RS_NET_DATA) || (currentBlock == 0U)) { m_netDataHeader.reset(); m_netDataOffset = 0U; m_netDataBlockCnt = 0U; @@ -618,8 +618,13 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) } if (m_p25->m_netState == RS_NET_DATA) { + // block 0 is always the PDU header block -- if we got here with that bail bail bail + if (currentBlock == 0U) { + return false; // bail + } + m_inbound = false; // forcibly set inbound to false - ::memcpy(m_netPDU + m_netDataOffset, data + 24U, blockLength); + ::memcpy(m_netPDU + ((currentBlock - 1U) * blockLength), data + 24U, blockLength); m_netDataOffset += blockLength; m_netPDUCount++; m_netDataBlockCnt++; @@ -791,6 +796,7 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength) m_netPduUserDataLength = 0U; m_p25->m_netState = RS_NET_IDLE; + m_p25->m_network->resetP25(); } } diff --git a/src/host/p25/packet/Data.h b/src/host/p25/packet/Data.h index 21b94ef8..884e2a35 100644 --- a/src/host/p25/packet/Data.h +++ b/src/host/p25/packet/Data.h @@ -67,10 +67,11 @@ namespace p25 * @brief Process a data frame from the network. * @param data Buffer containing data frame. * @param len Length of data frame. + * @param currentBlock * @param blockLength * @returns bool True, if data frame is processed, otherwise false. */ - bool processNetwork(uint8_t* data, uint32_t len, uint32_t blockLength); + bool processNetwork(uint8_t* data, uint32_t len, uint8_t currentBlock, uint32_t blockLength); /** @} */ /** From 70a89472097e899b7b966b7ccb74899f3e56de26 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 31 Oct 2025 11:28:03 -0400 Subject: [PATCH 118/200] refactor how buffered UDP datagram queuing is performed; --- src/common/network/BaseNetwork.cpp | 18 +- src/common/network/BaseNetwork.h | 4 +- src/common/network/FrameQueue.cpp | 16 +- src/common/network/FrameQueue.h | 10 +- src/common/network/RawFrameQueue.cpp | 94 +++----- src/common/network/RawFrameQueue.h | 20 +- src/common/network/udp/Socket.cpp | 208 +++++++++--------- src/common/network/udp/Socket.h | 10 +- src/fne/network/DiagNetwork.cpp | 4 +- src/fne/network/FNENetwork.cpp | 78 ++++--- src/fne/network/FNENetwork.h | 34 ++- src/fne/network/PeerNetwork.cpp | 6 +- src/fne/network/callhandler/TagAnalogData.cpp | 31 ++- src/fne/network/callhandler/TagDMRData.cpp | 47 ++-- src/fne/network/callhandler/TagNXDNData.cpp | 33 ++- src/fne/network/callhandler/TagP25Data.cpp | 54 +++-- .../callhandler/packetdata/DMRPacketData.cpp | 11 +- .../callhandler/packetdata/P25PacketData.cpp | 32 +-- .../callhandler/packetdata/P25PacketData.h | 4 +- 19 files changed, 383 insertions(+), 331 deletions(-) diff --git a/src/common/network/BaseNetwork.cpp b/src/common/network/BaseNetwork.cpp index d112cd2c..0a50e58e 100644 --- a/src/common/network/BaseNetwork.cpp +++ b/src/common/network/BaseNetwork.cpp @@ -173,7 +173,7 @@ bool BaseNetwork::writeActLog(const char* message) #endif return writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_ACTIVITY }, (uint8_t*)buffer, (uint32_t)len + 11U, - RTP_END_OF_CALL_SEQ, 0U, false, m_useAlternatePortForDiagnostics); + RTP_END_OF_CALL_SEQ, 0U, m_useAlternatePortForDiagnostics); } /* Writes the local diagnostics log to the network. */ @@ -201,7 +201,7 @@ bool BaseNetwork::writeDiagLog(const char* message) #endif return writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_DIAG }, (uint8_t*)buffer, (uint32_t)len + 11U, - RTP_END_OF_CALL_SEQ, 0U, false, m_useAlternatePortForDiagnostics); + RTP_END_OF_CALL_SEQ, 0U, m_useAlternatePortForDiagnostics); } /* Writes the local status to the network. */ @@ -233,7 +233,7 @@ bool BaseNetwork::writePeerStatus(json::object obj) #endif return writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_STATUS }, (uint8_t*)buffer, (uint32_t)len + 11U, - RTP_END_OF_CALL_SEQ, 0U, false, m_useAlternatePortForDiagnostics); + RTP_END_OF_CALL_SEQ, 0U, m_useAlternatePortForDiagnostics); } /* Writes a group affiliation to the network. */ @@ -397,7 +397,7 @@ uint32_t BaseNetwork::getDMRStreamId(uint32_t slotNo) const /* Helper to send a data message to the master. */ bool BaseNetwork::writeMaster(FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, uint16_t pktSeq, uint32_t streamId, - bool queueOnly, bool useAlternatePort, uint32_t peerId, uint32_t ssrc) + bool useAlternatePort, uint32_t peerId, uint32_t ssrc) { if (peerId == 0U) peerId = m_peerId; @@ -412,17 +412,11 @@ bool BaseNetwork::writeMaster(FrameQueue::OpcodePair opcode, const uint8_t* data uint16_t port = udp::Socket::port(m_addr) + 1U; if (udp::Socket::lookup(address, port, addr, addrLen) == 0) { - if (!queueOnly) - return m_frameQueue->write(data, length, streamId, peerId, ssrc, opcode, pktSeq, addr, addrLen); - else - m_frameQueue->enqueueMessage(data, length, streamId, m_peerId, opcode, pktSeq, addr, addrLen); + return m_frameQueue->write(data, length, streamId, peerId, ssrc, opcode, pktSeq, addr, addrLen); } } else { - if (!queueOnly) - return m_frameQueue->write(data, length, streamId, peerId, ssrc, opcode, pktSeq, m_addr, m_addrLen); - else - m_frameQueue->enqueueMessage(data, length, streamId, m_peerId, opcode, pktSeq, m_addr, m_addrLen); + return m_frameQueue->write(data, length, streamId, peerId, ssrc, opcode, pktSeq, m_addr, m_addrLen); } return true; diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index bad056e8..bd4fbec7 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -732,15 +732,13 @@ namespace network * @param length Length of buffer to write. * @param pktSeq RTP packet sequence. * @param streamId Stream ID. - * @param queueOnly Flag indicating this message should be queued instead of send immediately. * @param useAlternatePort Flag indicating the message shuold be sent using the alternate port (mainly for activity and diagnostics). * @param peerId If non-zero, overrides the peer ID sent in the packet to the master. * @param ssrc If non-zero, overrides the RTP synchronization source ID sent in the packet to the master. * @returns bool True, if message was sent, otherwise false. */ bool writeMaster(FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, - uint16_t pktSeq, uint32_t streamId, bool queueOnly = false, bool useAlternatePort = false, uint32_t peerId = 0U, - uint32_t ssrc = 0U); + uint16_t pktSeq, uint32_t streamId, bool useAlternatePort = false, uint32_t peerId = 0U, uint32_t ssrc = 0U); // Digital Mobile Radio /** diff --git a/src/common/network/FrameQueue.cpp b/src/common/network/FrameQueue.cpp index b5442fe1..d2587138 100644 --- a/src/common/network/FrameQueue.cpp +++ b/src/common/network/FrameQueue.cpp @@ -172,17 +172,21 @@ bool FrameQueue::write(const uint8_t* message, uint32_t length, uint32_t streamI /* Cache message to frame queue. */ -void FrameQueue::enqueueMessage(const uint8_t* message, uint32_t length, uint32_t streamId, uint32_t peerId, - OpcodePair opcode, uint16_t rtpSeq, sockaddr_storage& addr, uint32_t addrLen) +void FrameQueue::enqueueMessage(udp::BufferQueue* queue, const uint8_t* message, uint32_t length, uint32_t streamId, + uint32_t peerId, OpcodePair opcode, uint16_t rtpSeq, sockaddr_storage& addr, uint32_t addrLen) { - enqueueMessage(message, length, streamId, peerId, peerId, opcode, rtpSeq, addr, addrLen); + enqueueMessage(queue, message, length, streamId, peerId, peerId, opcode, rtpSeq, addr, addrLen); } /* Cache message to frame queue. */ -void FrameQueue::enqueueMessage(const uint8_t* message, uint32_t length, uint32_t streamId, uint32_t peerId, - uint32_t ssrc, OpcodePair opcode, uint16_t rtpSeq, sockaddr_storage& addr, uint32_t addrLen) +void FrameQueue::enqueueMessage(udp::BufferQueue* queue, const uint8_t* message, uint32_t length, uint32_t streamId, + uint32_t peerId, uint32_t ssrc, OpcodePair opcode, uint16_t rtpSeq, sockaddr_storage& addr, uint32_t addrLen) { + if (queue == nullptr) { + LogError(LOG_NET, "FrameQueue::enqueueMessage(), queue is null"); + return; + } if (message == nullptr) { LogError(LOG_NET, "FrameQueue::enqueueMessage(), message is null"); return; @@ -207,7 +211,7 @@ void FrameQueue::enqueueMessage(const uint8_t* message, uint32_t length, uint32_ dgram->address = addr; dgram->addrLen = addrLen; - m_buffers.push_back(dgram); + queue->push(dgram); } /* Helper method to clear any tracked stream timestamps. */ diff --git a/src/common/network/FrameQueue.h b/src/common/network/FrameQueue.h index b4411ee0..4db7a933 100644 --- a/src/common/network/FrameQueue.h +++ b/src/common/network/FrameQueue.h @@ -90,6 +90,7 @@ namespace network /** * @brief Cache message to frame queue. + * @param[in] queue Queue of messages. * @param[in] message Message buffer to frame and queue. * @param length Length of message. * @param streamId Message stream ID. @@ -99,10 +100,11 @@ namespace network * @param addr IP address to write data to. * @param addrLen */ - void enqueueMessage(const uint8_t* message, uint32_t length, uint32_t streamId, uint32_t peerId, - OpcodePair opcode, uint16_t rtpSeq, sockaddr_storage& addr, uint32_t addrLen); + void enqueueMessage(udp::BufferQueue* queue, const uint8_t* message, uint32_t length, uint32_t streamId, + uint32_t peerId, OpcodePair opcode, uint16_t rtpSeq, sockaddr_storage& addr, uint32_t addrLen); /** * @brief Cache message to frame queue. + * @param[in] queue Queue of messages. * @param[in] message Message buffer to frame and queue. * @param length Length of message. * @param streamId Message stream ID. @@ -113,8 +115,8 @@ namespace network * @param addr IP address to write data to. * @param addrLen */ - void enqueueMessage(const uint8_t* message, uint32_t length, uint32_t streamId, uint32_t peerId, - uint32_t ssrc, OpcodePair opcode, uint16_t rtpSeq, sockaddr_storage& addr, uint32_t addrLen); + void enqueueMessage(udp::BufferQueue* queue, const uint8_t* message, uint32_t length, uint32_t streamId, + uint32_t peerId, uint32_t ssrc, OpcodePair opcode, uint16_t rtpSeq, sockaddr_storage& addr, uint32_t addrLen); /** * @brief Helper method to clear any tracked stream timestamps. diff --git a/src/common/network/RawFrameQueue.cpp b/src/common/network/RawFrameQueue.cpp index efbd3ad5..24488b48 100644 --- a/src/common/network/RawFrameQueue.cpp +++ b/src/common/network/RawFrameQueue.cpp @@ -23,8 +23,7 @@ using namespace network; // Static Class Members // --------------------------------------------------------------------------- -std::mutex RawFrameQueue::m_queueMutex; -bool RawFrameQueue::m_queueFlushing = false; +std::mutex RawFrameQueue::s_flushMtx; // --------------------------------------------------------------------------- // Public Class Members @@ -34,7 +33,6 @@ bool RawFrameQueue::m_queueFlushing = false; RawFrameQueue::RawFrameQueue(udp::Socket* socket, bool debug) : m_socket(socket), - m_buffers(), m_failedReadCnt(0U), m_debug(debug) { @@ -43,10 +41,7 @@ RawFrameQueue::RawFrameQueue(udp::Socket* socket, bool debug) : /* Finalizes a instance of the RawFrameQueue class. */ -RawFrameQueue::~RawFrameQueue() -{ - deleteBuffers(); -} +RawFrameQueue::~RawFrameQueue() = default; /* Read message from the received UDP packet. */ @@ -124,8 +119,12 @@ bool RawFrameQueue::write(const uint8_t* message, uint32_t length, sockaddr_stor /* Cache message to frame queue. */ -void RawFrameQueue::enqueueMessage(const uint8_t* message, uint32_t length, sockaddr_storage& addr, uint32_t addrLen) +void RawFrameQueue::enqueueMessage(udp::BufferQueue* queue, const uint8_t* message, uint32_t length, sockaddr_storage& addr, uint32_t addrLen) { + if (queue == nullptr) { + LogError(LOG_NET, "RawFrameQueue::enqueueMessage(), queue is null"); + return; + } if (message == nullptr) { LogError(LOG_NET, "RawFrameQueue::enqueueMessage(), message is null"); return; @@ -135,13 +134,6 @@ void RawFrameQueue::enqueueMessage(const uint8_t* message, uint32_t length, sock return; } - // if the queue is flushing -- don't attempt to enqueue any messages - if (m_queueFlushing) { - LogWarning(LOG_NET, "RawFrameQueue::enqueueMessage() -- queue is flushing, waiting to enqueue message"); - while (m_queueFlushing) - Thread::sleep(2U); - } - // bryanb: this is really a developer warning not a end-user warning, there's nothing the end-users can do about // this message if (length > (DATA_PACKET_LENGTH - OVERSIZED_PACKET_WARN)) { @@ -161,69 +153,37 @@ void RawFrameQueue::enqueueMessage(const uint8_t* message, uint32_t length, sock dgram->address = addr; dgram->addrLen = addrLen; - m_buffers.push_back(dgram); + queue->push(dgram); } /* Flush the message queue. */ -bool RawFrameQueue::flushQueue() +bool RawFrameQueue::flushQueue(udp::BufferQueue* queue) { - bool ret = true; - - // scope is intentional - { - std::lock_guard lock(m_queueMutex); - m_queueFlushing = true; + if (queue == nullptr) { + LogError(LOG_NET, "RawFrameQueue::flushQueue(), queue is null"); + return false; + } - if (m_buffers.empty()) { - return false; - } + std::lock_guard lock(s_flushMtx); - // bryanb: this is the same as above -- but for some assinine reason prevents - // weirdness - if (m_buffers.size() == 0U) { - return false; - } + if (queue->empty()) { + return false; + } - // LogDebug(LOG_NET, "m_buffers len = %u", m_buffers.size()); + // bryanb: this is the same as above -- but for some assinine reason prevents + // weirdness + if (queue->size() == 0U) { + return false; + } - ret = true; - if (!m_socket->write(m_buffers)) { - // LogError(LOG_NET, "Failed writing data to the network"); - ret = false; - } + // LogDebug(LOG_NET, "queue len = %u", queue.size()); - m_queueFlushing = false; + bool ret = true; + if (!m_socket->write(queue)) { + // LogError(LOG_NET, "Failed writing data to the network"); + ret = false; } - deleteBuffers(); return ret; } - -// --------------------------------------------------------------------------- -// Private Class Members -// --------------------------------------------------------------------------- - -/* Helper to ensure buffers are deleted. */ - -void RawFrameQueue::deleteBuffers() -{ - std::lock_guard lock(m_queueMutex); - m_queueFlushing = true; - - for (auto& buffer : m_buffers) { - if (buffer != nullptr) { - // LogDebug(LOG_NET, "deleting buffer, addr %p len %u", buffer->buffer, buffer->length); - if (buffer->buffer != nullptr) { - delete[] buffer->buffer; - buffer->length = 0; - buffer->buffer = nullptr; - } - - delete buffer; - buffer = nullptr; - } - } - m_buffers.clear(); - m_queueFlushing = false; -} diff --git a/src/common/network/RawFrameQueue.h b/src/common/network/RawFrameQueue.h index 74e566bf..4cc5468b 100644 --- a/src/common/network/RawFrameQueue.h +++ b/src/common/network/RawFrameQueue.h @@ -4,7 +4,7 @@ * 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 + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -78,37 +78,31 @@ namespace network /** * @brief Cache message to frame queue. + * @param[in] queue Queue of messages. * @param[in] message Message buffer to frame and queue. * @param length Length of message. * @param addr IP address to write data to. * @param addrLen */ - void enqueueMessage(const uint8_t* message, uint32_t length, sockaddr_storage& addr, uint32_t addrLen); + void enqueueMessage(udp::BufferQueue* queue, const uint8_t* message, uint32_t length, sockaddr_storage& addr, uint32_t addrLen); /** * @brief Flush the message queue. + * @param[in] queue Queue of messages. */ - bool flushQueue(); + bool flushQueue(udp::BufferQueue* queue); protected: sockaddr_storage m_addr; uint32_t m_addrLen; udp::Socket* m_socket; - static std::mutex m_queueMutex; - static bool m_queueFlushing; - udp::BufferVector m_buffers; + static std::mutex s_flushMtx; uint32_t m_failedReadCnt; bool m_debug; - - private: - /** - * @brief Helper to ensure buffers are deleted. - */ - void deleteBuffers(); }; } // namespace network -#endif // __RAW_FRAME_QUEUE_H__ \ No newline at end of file +#endif // __RAW_FRAME_QUEUE_H__ diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index ab9c119d..9c1fdeac 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -483,29 +483,30 @@ bool Socket::write(const uint8_t* buffer, uint32_t length, const sockaddr_storag /* Write data to the UDP socket. */ -bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept +bool Socket::write(BufferQueue* buffers, ssize_t* lenWritten) noexcept { - bool result = false; - if (m_fd < 0) { + if (buffers == nullptr) { if (lenWritten != nullptr) { *lenWritten = -1; } - LogError(LOG_NET, "tried to write datagram with no file descriptor? this shouldn't happen BUGBUG"); + LogError(LOG_NET, "tried to write datagram with buffers? this shouldn't happen BUGBUG"); return false; } - if (buffers.empty()) { + size_t currentQueueSize = buffers->size(); + + bool result = false; + if (m_fd < 0) { if (lenWritten != nullptr) { *lenWritten = -1; } + LogError(LOG_NET, "tried to write datagram with no file descriptor? this shouldn't happen BUGBUG"); return false; } - // bryanb: this is the same as above -- but for some assinine reason prevents - // weirdness - if (buffers.size() == 0U) { + if (buffers->empty()) { if (lenWritten != nullptr) { *lenWritten = -1; } @@ -513,11 +514,9 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept return false; } - // LogDebug(LOG_NET, "buffers len = %u", buffers.size()); - - if (buffers.size() > UINT16_MAX) { - LogError(LOG_NET, "Trying to send too many buffers?"); - + // bryanb: this is the same as above -- but for some assinine reason prevents + // weirdness + if (currentQueueSize == 0U) { if (lenWritten != nullptr) { *lenWritten = -1; } @@ -525,7 +524,9 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept return false; } - // LogDebug(LOG_NET, "Sending message(s) (to %s:%u) addrLen %u", Socket::address(address).c_str(), Socket::port(address), addrLen); + // LogDebugEx(LOG_NET, "Socket::write()", "buffers len = %u", currentQueueSize); + if (currentQueueSize > UINT16_MAX) + currentQueueSize = UINT16_MAX; // only send up to this many buffers // are we crypto wrapped? if (m_isCryptoWrapped) { @@ -540,114 +541,111 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept } } - int sent = 0; + int sent = 0, msgs = 0; + struct sockaddr_storage* addresses[MAX_BUFFER_COUNT]; struct mmsghdr headers[MAX_BUFFER_COUNT]; struct iovec chunks[MAX_BUFFER_COUNT]; // create mmsghdrs from input buffers and send them at once - int size = buffers.size(); - for (size_t i = 0U; i < buffers.size(); ++i) { - UDPDatagram* packet = buffers[i]; + for (size_t i = 0U; i < currentQueueSize; ++i) { + UDPDatagram* packet = buffers->front(); + buffers->pop(); if (packet == nullptr) { - --size; continue; } uint32_t length = packet->length; if (packet->buffer == nullptr) { LogError(LOG_NET, "discarding buffered message with len = %u, but deleted buffer?", length); - --size; + delete packet; continue; } - try { - // are we crypto wrapped? - if (m_isCryptoWrapped && m_presharedKey != nullptr) { - uint32_t cryptedLen = length * sizeof(uint8_t); - uint8_t* cryptoBuffer = packet->buffer; - - // do we need to pad the original buffer to be block aligned? - if (cryptedLen % crypto::AES::BLOCK_BYTES_LEN != 0) { - uint32_t alignment = crypto::AES::BLOCK_BYTES_LEN - (cryptedLen % crypto::AES::BLOCK_BYTES_LEN); - cryptedLen += alignment; - - // reallocate buffer and copy - cryptoBuffer = new uint8_t[cryptedLen]; - ::memset(cryptoBuffer, 0x00U, cryptedLen); - ::memcpy(cryptoBuffer, packet->buffer, length); - } - - // encrypt - uint8_t* crypted = m_aes->encryptECB(cryptoBuffer, cryptedLen, m_presharedKey); - delete[] cryptoBuffer; - - if (crypted == nullptr) { - --size; - continue; - } - - // Utils::dump(1U, "Socket::write(), crypted", crypted, cryptedLen); - - // finalize - DECLARE_UINT8_ARRAY(out, cryptedLen + 2U); - ::memcpy(out + 2U, crypted, cryptedLen); - SET_UINT16(AES_WRAPPED_PCKT_MAGIC, out, 0U); - - // cleanup buffers and replace with new - delete[] crypted; - //delete packet->buffer; - - // this should never happen... - if (packet == nullptr) { - --size; - continue; - } - - packet->buffer = new uint8_t[cryptedLen + 2U]; - ::memcpy(packet->buffer, out, cryptedLen + 2U); - packet->length = cryptedLen + 2U; - } + if (m_af != packet->address.ss_family) { + LogError(LOG_NET, "Socket::write() mismatched network address family? this isn't normal, aborting"); + if (packet->buffer != nullptr) + delete[] packet->buffer; + delete packet; + continue; + } - chunks[i].iov_len = packet->length; - chunks[i].iov_base = packet->buffer; - sent += packet->length; + uint8_t* iov_buffer = new uint8_t[packet->length]; + size_t iov_length = packet->length; + sockaddr_storage address; + ::memcpy(&address, &packet->address, sizeof(sockaddr_storage)); + uint32_t addrLen = packet->addrLen; - headers[i].msg_hdr.msg_name = (void*)&packet->address; - headers[i].msg_hdr.msg_namelen = packet->addrLen; - headers[i].msg_hdr.msg_iov = &chunks[i]; - headers[i].msg_hdr.msg_iovlen = 1; - headers[i].msg_hdr.msg_control = 0; - headers[i].msg_hdr.msg_controllen = 0; - } - catch (...) { - --size; - } - } + ::memcpy(iov_buffer, packet->buffer, packet->length); - bool skip = false; - for (auto& buffer : buffers) { - if (buffer == nullptr) { - LogError(LOG_NET, "Socket::write() missing network buffer data? this isn't normal, aborting"); - skip = true; - break; - } + // cleanup buffered packet + if (packet != nullptr) { + if (packet->buffer != nullptr) { + delete[] packet->buffer; + packet->length = 0; + } - if (m_af != buffer->address.ss_family) { - LogError(LOG_NET, "Socket::write() mismatched network address family? this isn't normal, aborting"); - skip = true; - break; + delete packet; } - } - if (skip) { - if (lenWritten != nullptr) { - *lenWritten = -1; + // are we crypto wrapped? + if (m_isCryptoWrapped && m_presharedKey != nullptr) { + uint32_t cryptedLen = length * sizeof(uint8_t); + uint8_t *cryptoBuffer = iov_buffer; + + // do we need to pad the original buffer to be block aligned? + if (cryptedLen % crypto::AES::BLOCK_BYTES_LEN != 0) { + uint32_t alignment = crypto::AES::BLOCK_BYTES_LEN - (cryptedLen % crypto::AES::BLOCK_BYTES_LEN); + cryptedLen += alignment; + + // reallocate buffer and copy + cryptoBuffer = new uint8_t[cryptedLen]; + ::memset(cryptoBuffer, 0x00U, cryptedLen); + ::memcpy(cryptoBuffer, iov_buffer, length); + } + + // encrypt + uint8_t* crypted = m_aes->encryptECB(cryptoBuffer, cryptedLen, m_presharedKey); + delete[] cryptoBuffer; + + if (crypted == nullptr) { + if (iov_buffer != nullptr) + delete[] iov_buffer; + continue; + } + + // Utils::dump(1U, "Socket::write(), crypted", crypted, cryptedLen); + + // finalize + DECLARE_UINT8_ARRAY(out, cryptedLen + 2U); + ::memcpy(out + 2U, crypted, cryptedLen); + SET_UINT16(AES_WRAPPED_PCKT_MAGIC, out, 0U); + + // cleanup buffers and replace with new + delete[] crypted; + delete[] iov_buffer; + iov_buffer = new uint8_t[cryptedLen + 2U]; + ::memcpy(iov_buffer, out, cryptedLen + 2U); + iov_length = cryptedLen + 2U; } - return false; + addresses[i] = new sockaddr_storage; + ::memcpy(addresses[i], &address, sizeof(sockaddr_storage)); + + chunks[i].iov_len = iov_length; + chunks[i].iov_base = iov_buffer; + sent += iov_length; + + headers[i].msg_hdr.msg_name = (void*)addresses[i]; + headers[i].msg_hdr.msg_namelen = addrLen; + headers[i].msg_hdr.msg_iov = &chunks[i]; + headers[i].msg_hdr.msg_iovlen = 1; + headers[i].msg_hdr.msg_control = 0; + headers[i].msg_hdr.msg_controllen = 0; + + ++msgs; } - if (sendmmsg(m_fd, headers, size, 0) < 0) { + if (sendmmsg(m_fd, headers, msgs, 0) < 0) { #if defined(_WIN32) LogError(LOG_NET, "Error returned from sendmmsg, err: %lu", ::GetLastError()); #else @@ -675,6 +673,20 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept } } + // cleanup buffers + for (size_t i = 0U; i < currentQueueSize; i++) { + if (addresses[i] != nullptr) { + delete addresses[i]; + addresses[i] = nullptr; + } + + if (chunks[i].iov_base != nullptr) { + uint8_t* iov_buffer = (uint8_t*)chunks[i].iov_base; + delete[] iov_buffer; + chunks[i].iov_base = nullptr; + } + } + return result; } diff --git a/src/common/network/udp/Socket.h b/src/common/network/udp/Socket.h index 261f5779..b094c4bf 100644 --- a/src/common/network/udp/Socket.h +++ b/src/common/network/udp/Socket.h @@ -25,7 +25,7 @@ #include "common/AESCrypto.h" #include -#include +#include #if defined(_WIN32) #pragma comment(lib, "Ws2_32.lib") @@ -167,8 +167,8 @@ namespace network uint32_t addrLen; //!< Length of address structure }; - /** @brief Vector of buffers that contain a full frames */ - typedef std::vector BufferVector; + /** @brief Queue of buffers that contain a UDP datagram. */ + typedef std::queue BufferQueue; // --------------------------------------------------------------------------- // Class Declaration @@ -261,11 +261,11 @@ namespace network virtual bool write(const uint8_t* buffer, uint32_t length, const sockaddr_storage& address, uint32_t addrLen, ssize_t* lenWritten = nullptr) noexcept; /** * @brief Write data to the UDP socket. - * @param[in] buffers Vector of buffers to write to socket. + * @param[in] buffers Queue of buffers to write to socket. * @param[out] lenWritten Total number of bytes written. * @returns bool True, if messages were sent otherwise, false. */ - virtual bool write(BufferVector& buffers, ssize_t* lenWritten = nullptr) noexcept; + virtual bool write(BufferQueue* buffers, ssize_t* lenWritten = nullptr) noexcept; /** * @brief Sets the preshared encryption key. diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 74af0f3e..c5190383 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -271,7 +271,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_ACTIVITY }, - req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false, true, pktPeerId); + req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, true, pktPeerId); } } } @@ -360,7 +360,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_STATUS }, - req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false, true, pktPeerId); + req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, true, pktPeerId); } } } diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 379bda27..777d6488 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -54,7 +54,7 @@ const uint32_t FIXED_HA_UPDATE_INTERVAL = 30U; // 30s // Static Class Members // --------------------------------------------------------------------------- -std::timed_mutex FNENetwork::m_keyQueueMutex; +std::timed_mutex FNENetwork::s_keyQueueMutex; // --------------------------------------------------------------------------- // Public Class Members @@ -153,7 +153,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_p25OTARService = new P25OTARService(this, m_tagP25->packetData(), kmfDebug, verbose); - SpanningTree::m_maxUpdatesBeforeReparent = (uint8_t)host->m_maxMissedPings; + SpanningTree::s_maxUpdatesBeforeReparent = (uint8_t)host->m_maxMissedPings; m_treeRoot = new SpanningTree(peerId, peerId, nullptr); m_treeRoot->identity(identity); @@ -661,7 +661,7 @@ void FNENetwork::close() uint32_t streamId = createStreamId(); for (auto peer : m_peers) { writePeer(peer.first, m_peerId, { NET_FUNC::MST_DISC, NET_SUBFUNC::NOP }, buffer, 1U, RTP_END_OF_CALL_SEQ, - streamId, false); + streamId); } } @@ -1541,7 +1541,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) modifyKeyRsp.encode(buffer + 11U); network->writePeer(peerId, network->m_peerId, { NET_FUNC::KEY_RSP, NET_SUBFUNC::NOP }, buffer, modifyKeyRsp.length() + 11U, - RTP_END_OF_CALL_SEQ, network->createStreamId(), false, false, true); + RTP_END_OF_CALL_SEQ, network->createStreamId()); } else { // attempt to forward KMM key request to replica masters if (network->m_host->m_peerNetworks.size() > 0) { @@ -1551,14 +1551,14 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogInfoEx(LOG_PEER, "PEER %u (%s) no local key or container, requesting key from upstream master, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); - bool locked = network->m_keyQueueMutex.try_lock_for(std::chrono::milliseconds(60)); + bool locked = network->s_keyQueueMutex.try_lock_for(std::chrono::milliseconds(60)); network->m_peerReplicaKeyQueue[peerId] = modifyKey->getKId(); if (locked) - network->m_keyQueueMutex.unlock(); + network->s_keyQueueMutex.unlock(); peer.second->writeMaster({ NET_FUNC::KEY_REQ, NET_SUBFUNC::NOP }, - req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false, false); + req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false); } } } @@ -1613,7 +1613,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_GRP_AFFIL }, - req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); + req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false); } } } @@ -1650,7 +1650,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_UNIT_REG }, - req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false, 0U, ssrc); + req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, 0U, ssrc); } } } @@ -1686,7 +1686,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_UNIT_DEREG }, - req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); + req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false); } } } @@ -1723,7 +1723,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_GRP_UNAFFIL }, - req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); + req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false); } } } @@ -1773,7 +1773,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_AFFILS }, - req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); + req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false); } } } @@ -1822,7 +1822,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isReplica()) { peer.second->writeMaster({ NET_FUNC::ANNOUNCE, NET_SUBFUNC::ANNC_SUBFUNC_SITE_VC }, - req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false, false); + req->buffer, req->length, req->rtpHeader.getSequence(), streamId, false); } } } @@ -2211,6 +2211,10 @@ void FNENetwork::taskMetadataUpdate(MetadataUpdateRequest* req) } } +/* +** ACL Message Writing +*/ + /* Helper to send the list of whitelisted RIDs to the specified peer. */ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sendReplica) @@ -2266,7 +2270,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sen if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_RID_LIST }, - frag.second->data, FRAG_SIZE, 0U, streamId, false, true, true); + frag.second->data, FRAG_SIZE, 0U, streamId); Thread::sleep(60U); // pace block transmission } } @@ -2467,7 +2471,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendReplica if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_TALKGROUP_LIST }, - frag.second->data, FRAG_SIZE, 0U, streamId, false, true, true); + frag.second->data, FRAG_SIZE, 0U, streamId); Thread::sleep(60U); // pace block transmission } } @@ -2659,7 +2663,7 @@ void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_PEER_LIST }, - frag.second->data, FRAG_SIZE, 0U, streamId, false, true, true); + frag.second->data, FRAG_SIZE, 0U, streamId); Thread::sleep(60U); // pace block transmission } } @@ -2704,7 +2708,7 @@ void FNENetwork::writeHAParameters(uint32_t peerId, uint32_t streamId, bool send if (connection != nullptr) { LogInfoEx(LOG_REPL, "PEER %u (%s) Peer Replication, HA parameters, streamId = %u", peerId, connection->identWithQualifier().c_str(), streamId); writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_HA_PARAMS}, - buffer, len, 0U, streamId, false, true, true); + buffer, len, 0U, streamId); } } @@ -2751,13 +2755,25 @@ bool FNENetwork::writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::E SET_UINT24(dstId, buffer, 11U); // Destination ID buffer[14U] = slotNo; // DMR Slot No - return writePeer(peerId, m_peerId, { NET_FUNC::INCALL_CTRL, subFunc }, buffer, 15U, RTP_END_OF_CALL_SEQ, streamId, false); + return writePeer(peerId, m_peerId, { NET_FUNC::INCALL_CTRL, subFunc }, buffer, 15U, RTP_END_OF_CALL_SEQ, streamId); } +/* +** Generic Message Writing +*/ + /* Helper to send a data message to the specified peer with a explicit packet sequence. */ bool FNENetwork::writePeer(uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePair opcode, const uint8_t* data, - uint32_t length, uint16_t pktSeq, uint32_t streamId, bool queueOnly, bool incPktSeq, bool directWrite) const + uint32_t length, uint16_t pktSeq, uint32_t streamId, bool incPktSeq) const +{ + return writePeerQueue(nullptr, peerId, ssrc, opcode, data, length, pktSeq, streamId, incPktSeq); +} + +/* Helper to queue a data message to the specified peer with a explicit packet sequence. */ + +bool FNENetwork::writePeerQueue(udp::BufferQueue* buffers, uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePair opcode, + const uint8_t* data, uint32_t length, uint16_t pktSeq, uint32_t streamId, bool incPktSeq) const { if (streamId == 0U) { LogError(LOG_NET, "BUGBUG: PEER %u, trying to send data with a streamId of 0?", peerId); @@ -2775,7 +2791,7 @@ bool FNENetwork::writePeer(uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePai } #if DEBUG_RTP_MUX if (m_debug) - LogDebugEx(LOG_NET, "FNENetwork::writePeer()", "PEER %u, streamId = %u, pktSeq = %u", peerId, streamId, pktSeq); + LogDebugEx(LOG_NET, "FNENetwork::writePeerQueue()", "PEER %u, streamId = %u, pktSeq = %u", peerId, streamId, pktSeq); #endif if (m_maskOutboundPeerID) ssrc = m_peerId; // mask the source SSRC to our own peer ID @@ -2793,13 +2809,11 @@ bool FNENetwork::writePeer(uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePai } } - if (directWrite) + if (buffers == nullptr) return m_frameQueue->write(data, length, streamId, peerId, ssrc, opcode, pktSeq, addr, addrLen); else { - m_frameQueue->enqueueMessage(data, length, streamId, peerId, ssrc, opcode, pktSeq, addr, addrLen); - if (queueOnly) - return true; - return m_frameQueue->flushQueue(); + m_frameQueue->enqueueMessage(buffers, data, length, streamId, peerId, ssrc, opcode, pktSeq, addr, addrLen); + return true; } } } @@ -2823,7 +2837,7 @@ bool FNENetwork::writePeerCommand(uint32_t peerId, FrameQueue::OpcodePair opcode } uint32_t len = length + 6U; - return writePeer(peerId, m_peerId, opcode, buffer, len, RTP_END_OF_CALL_SEQ, streamId, false, incPktSeq, true); + return writePeer(peerId, m_peerId, opcode, buffer, len, RTP_END_OF_CALL_SEQ, streamId, incPktSeq); } /* Helper to send a ACK response to the specified peer. */ @@ -2840,7 +2854,7 @@ bool FNENetwork::writePeerACK(uint32_t peerId, uint32_t streamId, const uint8_t* } return writePeer(peerId, m_peerId, { NET_FUNC::ACK, NET_SUBFUNC::NOP }, buffer, length + 10U, RTP_END_OF_CALL_SEQ, - streamId, false, false, true); + streamId); } /* Helper to log a warning specifying which NAK reason is being sent a peer. */ @@ -2900,7 +2914,7 @@ bool FNENetwork::writePeerNAK(uint32_t peerId, uint32_t streamId, const char* ta SET_UINT16((uint16_t)reason, buffer, 10U); // Reason logPeerNAKReason(peerId, tag, reason); - return writePeer(peerId, m_peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, buffer, 12U, RTP_END_OF_CALL_SEQ, streamId, false); + return writePeer(peerId, m_peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, buffer, 12U, RTP_END_OF_CALL_SEQ, streamId); } /* Helper to send a NAK response to the specified peer. */ @@ -2924,6 +2938,10 @@ bool FNENetwork::writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REA { NET_FUNC::NAK, NET_SUBFUNC::NOP }, 0U, addr, addrLen); } +/* +** Internal KMM Callback. +*/ + /* Helper to process a FNE KMM TEK response. */ void FNENetwork::processTEKResponse(p25::kmm::KeyItem* rspKi, uint8_t algId, uint8_t keyLength) @@ -2936,7 +2954,7 @@ void FNENetwork::processTEKResponse(p25::kmm::KeyItem* rspKi, uint8_t algId, uin LogInfoEx(LOG_PEER, "upstream master enc. key, algId = $%02X, kID = $%04X", algId, rspKi->kId()); - m_keyQueueMutex.lock(); + s_keyQueueMutex.lock(); std::vector peersToRemove; for (auto entry : m_peerReplicaKeyQueue) { @@ -2979,7 +2997,7 @@ void FNENetwork::processTEKResponse(p25::kmm::KeyItem* rspKi, uint8_t algId, uin modifyKeyRsp.encode(buffer + 11U); writePeer(peerId, m_peerId, { NET_FUNC::KEY_RSP, NET_SUBFUNC::NOP }, buffer, modifyKeyRsp.length() + 11U, - RTP_END_OF_CALL_SEQ, createStreamId(), false, false, true); + RTP_END_OF_CALL_SEQ, createStreamId()); peersToRemove.push_back(peerId); } diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index da07cca6..f7289278 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -68,6 +68,8 @@ namespace network // Constants // --------------------------------------------------------------------------- + #define MAX_QUEUED_PEER_MSGS 5U + /** * @brief DVM states. */ @@ -326,7 +328,7 @@ namespace network typedef std::pair PeerAffiliationMapPair; concurrent::unordered_map m_peerAffiliations; concurrent::unordered_map> m_ccPeerMap; - static std::timed_mutex m_keyQueueMutex; + static std::timed_mutex s_keyQueueMutex; std::unordered_map m_peerReplicaKeyQueue; SpanningTree* m_treeRoot; @@ -478,6 +480,10 @@ namespace network */ static void taskMetadataUpdate(MetadataUpdateRequest* req); + /* + ** ACL Message Writing + */ + /** * @brief Helper to send the list of whitelisted RIDs to the specified peer. * \code{.unparsed} @@ -647,6 +653,10 @@ namespace network bool writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::ENUM subFunc = NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::ENUM command = NET_ICC::NOP, uint32_t dstId = 0U, uint8_t slotNo = 0U); + /* + ** Generic Message Writing + */ + /** * @brief Helper to send a data message to the specified peer with a explicit packet sequence. * @param peerId Destination Peer ID. @@ -656,12 +666,26 @@ namespace network * @param length Length of buffer. * @param pktSeq RTP packet sequence for this message. * @param streamId Stream ID for this message. + * @param incPktSeq Flag indicating the message should increment the packet sequence after transmission. + */ + bool writePeer(uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, + uint16_t pktSeq, uint32_t streamId, bool incPktSeq = false) const; + /** + * @brief Helper to queue a data message to the specified peer with a explicit packet sequence. + * @param[in] buffers Buffer to contain queued messages. + * @param peerId Destination Peer ID. + * @param ssrc RTP synchronization source ID. + * @param opcode FNE network opcode pair. + * @param[in] data Buffer containing message to send to peer. + * @param length Length of buffer. + * @param pktSeq RTP packet sequence for this message. + * @param streamId Stream ID for this message. * @param queueOnly Flag indicating this message should be queued for transmission. * @param incPktSeq Flag indicating the message should increment the packet sequence after transmission. * @param directWrite Flag indicating this message should be immediately directly written. */ - bool writePeer(uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, - uint16_t pktSeq, uint32_t streamId, bool queueOnly, bool incPktSeq = false, bool directWrite = false) const; + bool writePeerQueue(udp::BufferQueue* buffers, uint32_t peerId, uint32_t ssrc, FrameQueue::OpcodePair opcode, + const uint8_t* data, uint32_t length, uint16_t pktSeq, uint32_t streamId, bool incPktSeq = false) const; /** * @brief Helper to send a command message to the specified peer. @@ -709,6 +733,10 @@ namespace network */ bool writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REASON reason, sockaddr_storage& addr, uint32_t addrLen); + /* + ** Internal KMM Callback. + */ + /** * @brief Helper to process a FNE KMM TEK response. * @param ki Key Item. diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 0c930f4f..36236a01 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -133,7 +133,7 @@ bool PeerNetwork::writePeerLinkPeers(json::array* peerList) if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writeMaster({ NET_FUNC::REPL, NET_SUBFUNC::REPL_ACT_PEER_LIST }, - frag.second->data, FRAG_SIZE, RTP_END_OF_CALL_SEQ, streamId, false, true); + frag.second->data, FRAG_SIZE, RTP_END_OF_CALL_SEQ, streamId, true); Thread::sleep(60U); // pace block transmission } } @@ -175,7 +175,7 @@ bool PeerNetwork::writeSpanningTree(SpanningTree* treeRoot) if (pkt.fragments.size() > 0U) { for (auto frag : pkt.fragments) { writeMaster({ NET_FUNC::NET_TREE, NET_SUBFUNC::NET_TREE_LIST }, - frag.second->data, FRAG_SIZE, RTP_END_OF_CALL_SEQ, streamId, false, true); + frag.second->data, FRAG_SIZE, RTP_END_OF_CALL_SEQ, streamId, true); Thread::sleep(60U); // pace block transmission } } @@ -215,7 +215,7 @@ bool PeerNetwork::writeHAParams(std::vector& haParams) // bryanb: this should probably be packet buffered writeMaster({ NET_FUNC::REPL, NET_SUBFUNC::REPL_HA_PARAMS }, - buffer, len, RTP_END_OF_CALL_SEQ, createStreamId(), false, true); + buffer, len, RTP_END_OF_CALL_SEQ, createStreamId(), true); return true; } diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 51c9e6b2..0fb0aaee 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -250,6 +250,8 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U) { uint32_t i = 0U; + udp::BufferQueue queue = udp::BufferQueue(); + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { if (peer.second == nullptr) @@ -265,9 +267,9 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee continue; } - // every 5 peers flush the queue - if (i % 5U == 0U) { - m_network->m_frameQueue->flushQueue(); + // every MAX_QUEUED_PEER_MSGS peers flush the queue + if (i % MAX_QUEUED_PEER_MSGS == 0U) { + m_network->m_frameQueue->flushQueue(&queue); } DECLARE_UINT8_ARRAY(outboundPeerBuffer, len); @@ -276,7 +278,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // perform TGID route rewrites if configured routeRewrite(outboundPeerBuffer, peer.first, dstId); - m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId, true); + m_network->writePeerQueue(&queue, peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { LogDebugEx(LOG_ANALOG, "TagAnalogData::processFrame()", "Master, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", ssrc, peerId, peer.first, seqNo, srcId, dstId, len, pktSeq, streamId, fromUpstream); @@ -285,7 +287,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee i++; } } - m_network->m_frameQueue->flushQueue(); + m_network->m_frameQueue->flushQueue(&queue); m_network->m_peers.shared_unlock(); } @@ -324,7 +326,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // are we a replica peer? if (peer.second->isReplica()) - peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); + peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { @@ -361,7 +363,7 @@ void TagAnalogData::playbackParrot() m_lastParrotDstId = pkt.dstId; if (m_network->m_parrotOnlyOriginating) { - m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); + m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebugEx(LOG_ANALOG, "TagAnalogData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", pkt.peerId, pkt.bufferLen, pkt.pktSeq, pkt.streamId); @@ -369,13 +371,26 @@ void TagAnalogData::playbackParrot() } else { // repeat traffic to the connected peers + uint32_t i = 0U; + udp::BufferQueue queue = udp::BufferQueue(); + + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { - m_network->writePeer(peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); + // every MAX_QUEUED_PEER_MSGS peers flush the queue + if (i % MAX_QUEUED_PEER_MSGS == 0U) { + m_network->m_frameQueue->flushQueue(&queue); + } + + m_network->writePeerQueue(&queue, peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebugEx(LOG_ANALOG, "TagAnalogData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", peer.first, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } + + i++; } + m_network->m_frameQueue->flushQueue(&queue); + m_network->m_peers.shared_unlock(); } delete[] pkt.buffer; diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index f239dba8..a2ff720e 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -412,6 +412,8 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; + udp::BufferQueue queue = udp::BufferQueue(); + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { if (peer.second == nullptr) @@ -453,9 +455,9 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId continue; } - // every 5 peers flush the queue - if (i % 5U == 0U) { - m_network->m_frameQueue->flushQueue(); + // every MAX_QUEUED_PEER_MSGS peers flush the queue + if (i % MAX_QUEUED_PEER_MSGS == 0U) { + m_network->m_frameQueue->flushQueue(&queue); } DECLARE_UINT8_ARRAY(outboundPeerBuffer, len); @@ -464,7 +466,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // perform TGID route rewrites if configured routeRewrite(outboundPeerBuffer, peer.first, dmrData, dataType, dstId, slotNo); - m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId, true); + m_network->writePeerQueue(&queue, peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { LogDebugEx(LOG_DMR, "TagDMRData::processFrame()", "Master, ssrc = %u, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, flco = $%02X, slotNo = %u, len = %u, pktSeq = %u, stream = %u, fromUpstream = %u", ssrc, peerId, peer.first, seqNo, srcId, dstId, flco, slotNo, len, pktSeq, streamId, fromUpstream); @@ -473,7 +475,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId i++; } } - m_network->m_frameQueue->flushQueue(); + m_network->m_frameQueue->flushQueue(&queue); m_network->m_peers.shared_unlock(); } @@ -518,7 +520,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // are we a replica peer? if (peer.second->isReplica()) - peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); + peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { @@ -601,7 +603,7 @@ void TagDMRData::playbackParrot() m_lastParrotDstId = pkt.dstId; if (m_network->m_parrotOnlyOriginating) { - m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); + m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebugEx(LOG_DMR, "TagDMRData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", pkt.peerId, pkt.bufferLen, pkt.pktSeq, pkt.streamId); @@ -609,13 +611,26 @@ void TagDMRData::playbackParrot() } else { // repeat traffic to the connected peers + uint32_t i = 0U; + udp::BufferQueue queue = udp::BufferQueue(); + + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { - m_network->writePeer(peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); + // every MAX_QUEUED_PEER_MSGS peers flush the queue + if (i % MAX_QUEUED_PEER_MSGS == 0U) { + m_network->m_frameQueue->flushQueue(&queue); + } + + m_network->writePeerQueue(&queue, peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebugEx(LOG_DMR, "TagDMRData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", peer.first, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } + + i++; } + m_network->m_frameQueue->flushQueue(&queue); + m_network->m_peers.shared_unlock(); } delete[] pkt.buffer; @@ -1351,25 +1366,29 @@ void TagDMRData::write_CSBK(uint32_t peerId, uint8_t slot, lc::CSBK* csbk) } if (peerId > 0U) { - m_network->writePeer(peerId, m_network->m_peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false); + m_network->writePeer(peerId, m_network->m_peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId); } else { // repeat traffic to the connected peers if (m_network->m_peers.size() > 0U) { uint32_t i = 0U; + udp::BufferQueue queue = udp::BufferQueue(); + + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { - // every 5 peers flush the queue - if (i % 5U == 0U) { - m_network->m_frameQueue->flushQueue(); + // every MAX_QUEUED_PEER_MSGS peers flush the queue + if (i % MAX_QUEUED_PEER_MSGS == 0U) { + m_network->m_frameQueue->flushQueue(&queue); } - m_network->writePeer(peer.first, m_network->m_peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, true); + m_network->writePeerQueue(&queue, peer.first, m_network->m_peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId); if (m_network->m_debug) { LogDebugEx(LOG_DMR, "TagDMRData::write_CSBK()", "peer = %u, slotNo = %u, len = %u, stream = %u", peer.first, slot, messageLength, streamId); } i++; } - m_network->m_frameQueue->flushQueue(); + m_network->m_frameQueue->flushQueue(&queue); + m_network->m_peers.shared_unlock(); } // repeat traffic to neighbor FNE peers diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index abadc4e9..4f208a67 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -442,6 +442,8 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // repeat traffic to nodes peered to us as master if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; + udp::BufferQueue queue = udp::BufferQueue(); + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { if (peer.second == nullptr) @@ -483,9 +485,9 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI continue; } - // every 5 peers flush the queue - if (i % 5U == 0U) { - m_network->m_frameQueue->flushQueue(); + // every MAX_QUEUED_PEER_MSGS peers flush the queue + if (i % MAX_QUEUED_PEER_MSGS == 0U) { + m_network->m_frameQueue->flushQueue(&queue); } DECLARE_UINT8_ARRAY(outboundPeerBuffer, len); @@ -494,7 +496,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // perform TGID route rewrites if configured routeRewrite(outboundPeerBuffer, peer.first, messageType, dstId); - m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId, true); + m_network->writePeerQueue(&queue, peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { LogDebugEx(LOG_NXDN, "TagNXDNData::processFrame()", "Master, ssrc = %u, srcPeer = %u, dstPeer = %u, messageType = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", ssrc, peerId, peer.first, messageType, srcId, dstId, len, pktSeq, streamId, fromUpstream); @@ -503,7 +505,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI i++; } } - m_network->m_frameQueue->flushQueue(); + m_network->m_frameQueue->flushQueue(&queue); m_network->m_peers.shared_unlock(); } @@ -548,7 +550,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI // are we a replica peer? if (peer.second->isReplica()) - peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); + peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { @@ -631,7 +633,7 @@ void TagNXDNData::playbackParrot() m_lastParrotDstId = pkt.dstId; if (m_network->m_parrotOnlyOriginating) { - m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); + m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebugEx(LOG_NXDN, "TagNXDNData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", pkt.peerId, pkt.bufferLen, pkt.pktSeq, pkt.streamId); @@ -639,13 +641,26 @@ void TagNXDNData::playbackParrot() } else { // repeat traffic to the connected peers + uint32_t i = 0U; + udp::BufferQueue queue = udp::BufferQueue(); + + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { - m_network->writePeer(peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); + // every MAX_QUEUED_PEER_MSGS peers flush the queue + if (i % MAX_QUEUED_PEER_MSGS == 0U) { + m_network->m_frameQueue->flushQueue(&queue); + } + + m_network->writePeerQueue(&queue, peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebugEx(LOG_NXDN, "TagNXDNData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", peer.first, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } + + i++; } + m_network->m_frameQueue->flushQueue(&queue); + m_network->m_peers.shared_unlock(); } delete[] pkt.buffer; @@ -1141,5 +1156,5 @@ void TagNXDNData::write_Message(uint32_t peerId, lc::RCCH* rcch) } uint32_t streamId = m_network->createStreamId(); - m_network->writePeer(peerId, m_network->m_peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false); + m_network->writePeer(peerId, m_network->m_peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId); } diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 0279c44e..154fbcb7 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -471,6 +471,8 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // repeat traffic to nodes connected to us as peers if (m_network->m_peers.size() > 0U && !noConnectedPeerRepeat) { uint32_t i = 0U; + udp::BufferQueue queue = udp::BufferQueue(); + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { if (peer.second == nullptr) @@ -517,9 +519,9 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId continue; } - // every 5 peers flush the queue - if (i % 5U == 0U) { - m_network->m_frameQueue->flushQueue(); + // every MAX_QUEUED_PEER_MSGS peers flush the queue + if (i % MAX_QUEUED_PEER_MSGS == 0U) { + m_network->m_frameQueue->flushQueue(&queue); } DECLARE_UINT8_ARRAY(outboundPeerBuffer, len); @@ -528,7 +530,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId // perform TGID route rewrites if configured routeRewrite(outboundPeerBuffer, peer.first, duid, dstId); - m_network->writePeer(peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId, true); + m_network->writePeerQueue(&queue, peer.first, ssrc, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { LogDebugEx(LOG_P25, "TagP25Data::processFrame()", "Master, ssrc = %u, srcPeer = %u, dstPeer = %u, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u, fromUpstream = %u", ssrc, peerId, peer.first, duid, lco, MFId, srcId, dstId, len, pktSeq, streamId, fromUpstream); @@ -537,7 +539,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId i++; } } - m_network->m_frameQueue->flushQueue(); + m_network->m_frameQueue->flushQueue(&queue); m_network->m_peers.shared_unlock(); } @@ -584,7 +586,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId if (processTSDUToNeighbor(outboundPeerBuffer, peerId, dstPeerId, duid)) { // are we a replica peer? if (peer.second->isReplica()) - peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId, false, false, 0U, ssrc); + peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId, false, 0U, ssrc); else peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId); if (m_network->m_debug) { @@ -687,13 +689,13 @@ void TagP25Data::playbackParrot() if (m_network->m_parrotOnlyOriginating) { LogInfoEx(LOG_P25, "Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", pkt.peerId, srcId, dstId); m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, - RTP_END_OF_CALL_SEQ, m_network->createStreamId(), false); + RTP_END_OF_CALL_SEQ, m_network->createStreamId()); } else { // repeat traffic to the connected peers for (auto peer : m_network->m_peers) { LogInfoEx(LOG_P25, "Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", peer.first, srcId, dstId); m_network->writePeer(peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, - RTP_END_OF_CALL_SEQ, m_network->createStreamId(), false); + RTP_END_OF_CALL_SEQ, m_network->createStreamId()); } } } @@ -707,20 +709,33 @@ void TagP25Data::playbackParrot() m_lastParrotDstId = pkt.dstId; if (m_network->m_parrotOnlyOriginating) { - m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); + m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebugEx(LOG_P25, "TagP25Data::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", pkt.peerId, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } } else { // repeat traffic to the connected peers + uint32_t i = 0U; + udp::BufferQueue queue = udp::BufferQueue(); + + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { - m_network->writePeer(peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId, false); + // every MAX_QUEUED_PEER_MSGS peers flush the queue + if (i % MAX_QUEUED_PEER_MSGS == 0U) { + m_network->m_frameQueue->flushQueue(&queue); + } + + m_network->writePeerQueue(&queue, peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebug(LOG_P25, "TagP25Data::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", peer.first, pkt.bufferLen, pkt.pktSeq, pkt.streamId); } + + i++; } + m_network->m_frameQueue->flushQueue(&queue); + m_network->m_peers.shared_unlock(); } delete[] pkt.buffer; @@ -1772,26 +1787,31 @@ void TagP25Data::write_TSDU(uint32_t peerId, lc::TSBK* tsbk) uint32_t streamId = m_network->createStreamId(); if (peerId > 0U) { m_network->writePeer(peerId, m_network->m_peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, - RTP_END_OF_CALL_SEQ, streamId, false); + RTP_END_OF_CALL_SEQ, streamId); } else { // repeat traffic to the connected peers if (m_network->m_peers.size() > 0U) { uint32_t i = 0U; + udp::BufferQueue queue = udp::BufferQueue(); + + m_network->m_peers.shared_lock(); for (auto peer : m_network->m_peers) { - // every 5 peers flush the queue - if (i % 5U == 0U) { - m_network->m_frameQueue->flushQueue(); + // every MAX_QUEUED_PEER_MSGS peers flush the queue + if (i % MAX_QUEUED_PEER_MSGS == 0U) { + m_network->m_frameQueue->flushQueue(&queue); } - m_network->writePeer(peer.first, m_network->m_peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, - RTP_END_OF_CALL_SEQ, streamId, true); + m_network->writePeerQueue(&queue, peer.first, m_network->m_peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, + RTP_END_OF_CALL_SEQ, streamId); if (m_network->m_debug) { LogDebugEx(LOG_P25, "TagP25Data::write_TSDU()", "P25, peer = %u, len = %u, streamId = %u", peer.first, messageLength, streamId); } + i++; } - m_network->m_frameQueue->flushQueue(); + m_network->m_frameQueue->flushQueue(&queue); + m_network->m_peers.shared_unlock(); } // repeat traffic to neighbor FNE peers diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp index 0cdcf92d..dee48cd3 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp @@ -282,7 +282,6 @@ void DMRPacketData::dispatchToFNE(uint32_t peerId, dmr::data::NetData& dmrData, // repeat traffic to the connected peers if (m_network->m_peers.size() > 0U) { - uint32_t i = 0U; for (auto peer : m_network->m_peers) { if (peerId != peer.first) { // is this peer ignored? @@ -290,21 +289,13 @@ void DMRPacketData::dispatchToFNE(uint32_t peerId, dmr::data::NetData& dmrData, continue; } - // every 5 peers flush the queue - if (i % 5U == 0U) { - m_network->m_frameQueue->flushQueue(); - } - - m_network->writePeer(peer.first, peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, data, len, pktSeq, streamId, true); + m_network->writePeer(peer.first, peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, data, len, pktSeq, streamId); if (m_network->m_debug) { LogDebugEx(LOG_DMR, "DMRPacketData::dispatchToFNE()", "Master, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, slotNo = %u, len = %u, pktSeq = %u, stream = %u", peerId, peer.first, seqNo, srcId, dstId, status->slotNo, len, pktSeq, streamId); } - - i++; } } - m_network->m_frameQueue->flushQueue(); } /* diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index 324c62ef..033cbc06 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -804,24 +804,15 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) // repeat traffic to the connected peers if (m_network->m_peers.size() > 0U) { - uint32_t i = 0U; for (auto peer : m_network->m_peers) { if (peerId != peer.first) { - // every 2 peers flush the queue - if (i % 2U == 0U) { - m_network->m_frameQueue->flushQueue(); - } - - write_PDU_User(peer.first, peerId, nullptr, status->header, status->extendedAddress, status->pduUserData, true); + write_PDU_User(peer.first, peerId, nullptr, status->header, status->extendedAddress, status->pduUserData); if (m_network->m_debug) { LogDebug(LOG_P25, "srcPeer = %u, dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", peerId, peer.first, DUID::PDU, srcId, dstId); } - - i++; } } - m_network->m_frameQueue->flushQueue(); } /* @@ -874,22 +865,13 @@ void P25PacketData::dispatchUserFrameToFNE(p25::data::DataHeader& dataHeader, bo // 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 2 peers flush the queue - if (i % 2U == 0U) { - m_network->m_frameQueue->flushQueue(); - } - - write_PDU_User(peer.first, m_network->m_peerId, nullptr, dataHeader, extendedAddress, pduUserData, true); + write_PDU_User(peer.first, m_network->m_peerId, nullptr, dataHeader, extendedAddress, pduUserData); if (m_network->m_debug) { LogDebug(LOG_P25, "dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", peer.first, DUID::PDU, srcId, dstId); } - - i++; } - m_network->m_frameQueue->flushQueue(); } } @@ -1079,7 +1061,7 @@ void P25PacketData::write_PDU_ARP_Reply(uint32_t targetAddr, uint32_t requestorL /* Helper to write user data as a P25 PDU packet. */ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network::PeerNetwork* peerNet, data::DataHeader& dataHeader, - bool extendedAddress, uint8_t* pduUserData, bool queueOnly) + bool extendedAddress, uint8_t* pduUserData) { uint32_t streamId = m_network->createStreamId(); uint16_t pktSeq = 0U; @@ -1100,7 +1082,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: // generate the PDU header and 1/2 rate Trellis dataHeader.encode(buffer); - writeNetwork(peerId, srcPeerId, peerNet, dataHeader, 0U, buffer, P25_PDU_FEC_LENGTH_BYTES, pktSeq, streamId, queueOnly); + writeNetwork(peerId, srcPeerId, peerNet, dataHeader, 0U, buffer, P25_PDU_FEC_LENGTH_BYTES, pktSeq, streamId); if (pduUserData == nullptr) return; @@ -1118,7 +1100,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); dataHeader.encodeExtAddr(buffer); - writeNetwork(peerId, srcPeerId, peerNet, dataHeader, 1U, buffer, P25_PDU_FEC_LENGTH_BYTES, pktSeq, streamId, queueOnly); + writeNetwork(peerId, srcPeerId, peerNet, dataHeader, 1U, buffer, P25_PDU_FEC_LENGTH_BYTES, pktSeq, streamId); ++pktSeq; dataOffset += P25_PDU_HEADER_LENGTH_BYTES; @@ -1175,7 +1157,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: /* Write data processed to the network. */ bool P25PacketData::writeNetwork(uint32_t peerId, uint32_t srcPeerId, network::PeerNetwork* peerNet, const p25::data::DataHeader& dataHeader, const uint8_t currentBlock, - const uint8_t *data, uint32_t len, uint16_t pktSeq, uint32_t streamId, bool queueOnly) + const uint8_t *data, uint32_t len, uint16_t pktSeq, uint32_t streamId) { assert(data != nullptr); @@ -1188,7 +1170,7 @@ bool P25PacketData::writeNetwork(uint32_t peerId, uint32_t srcPeerId, network::P if (peerNet != nullptr) { return peerNet->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq, streamId); } else { - return m_network->writePeer(peerId, srcPeerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq, streamId, false); + return m_network->writePeer(peerId, srcPeerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq, streamId); } } diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index 200ee508..452f99d1 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -253,7 +253,7 @@ namespace network * @param pduUserData Buffer containing user data to transmit. */ void write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network::PeerNetwork* peerNet, p25::data::DataHeader& dataHeader, - bool extendedAddress, uint8_t* pduUserData, bool queueOnly = false); + bool extendedAddress, uint8_t* pduUserData); /** * @brief Write data processed to the network. @@ -268,7 +268,7 @@ namespace network * @param streamId Stream ID. */ bool writeNetwork(uint32_t peerId, uint32_t srcPeerId, network::PeerNetwork* peerNet, const p25::data::DataHeader& dataHeader, const uint8_t currentBlock, - const uint8_t* data, uint32_t len, uint16_t pktSeq, uint32_t streamId, bool queueOnly = false); + const uint8_t* data, uint32_t len, uint16_t pktSeq, uint32_t streamId); /** * @brief Helper to determine if the logical link ID has an ARP entry. From dcd22d040481252582fba03d9b6e424fdf39c0ca Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 31 Oct 2025 13:24:49 -0400 Subject: [PATCH 119/200] relabel static class variables to use s_ and globals to use g_ --- src/bridge/ActivityLog.cpp | 34 +- src/bridge/HostBridge.cpp | 20 +- src/bridge/HostBridge.h | 4 +- src/common/Log.cpp | 88 ++-- src/common/Log.h | 10 +- src/common/dmr/lc/CSBK.cpp | 8 +- src/common/dmr/lc/CSBK.h | 12 +- src/common/dmr/lc/csbk/CSBK_ACK_RSP.cpp | 2 +- src/common/dmr/lc/csbk/CSBK_ALOHA.cpp | 6 +- src/common/dmr/lc/csbk/CSBK_BROADCAST.cpp | 6 +- src/common/lookups/AdjSiteMapLookup.cpp | 20 +- src/common/lookups/AdjSiteMapLookup.h | 4 +- src/common/lookups/IdenTableLookup.cpp | 8 +- src/common/lookups/IdenTableLookup.h | 2 +- src/common/lookups/PeerListLookup.cpp | 16 +- src/common/lookups/PeerListLookup.h | 4 +- src/common/lookups/RadioIdLookup.cpp | 16 +- src/common/lookups/RadioIdLookup.h | 4 +- src/common/lookups/TalkgroupRulesLookup.cpp | 22 +- src/common/lookups/TalkgroupRulesLookup.h | 4 +- src/common/network/tcp/SecureTcpClient.cpp | 2 +- src/common/network/tcp/SecureTcpClient.h | 6 +- src/common/network/viface/VIFace.cpp | 6 +- src/common/network/viface/VIFace.h | 2 +- src/common/nxdn/lc/RCCH.cpp | 26 +- src/common/nxdn/lc/RCCH.h | 16 +- src/common/nxdn/lc/RTCH.cpp | 6 +- src/common/nxdn/lc/RTCH.h | 4 +- .../nxdn/lc/rcch/MESSAGE_TYPE_DCALL_HDR.cpp | 4 +- .../nxdn/lc/rcch/MESSAGE_TYPE_DST_ID_INFO.cpp | 4 +- .../nxdn/lc/rcch/MESSAGE_TYPE_GRP_REG.cpp | 4 +- src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG.cpp | 4 +- .../nxdn/lc/rcch/MESSAGE_TYPE_REG_C.cpp | 4 +- .../nxdn/lc/rcch/MESSAGE_TYPE_REG_COMM.cpp | 4 +- .../nxdn/lc/rcch/MESSAGE_TYPE_SITE_INFO.cpp | 14 +- .../nxdn/lc/rcch/MESSAGE_TYPE_SRV_INFO.cpp | 14 +- .../nxdn/lc/rcch/MESSAGE_TYPE_VCALL_ASSGN.cpp | 4 +- .../nxdn/lc/rcch/MESSAGE_TYPE_VCALL_CONN.cpp | 4 +- src/common/p25/acl/AccessControl.cpp | 22 +- src/common/p25/acl/AccessControl.h | 4 +- src/common/p25/data/DataHeader.cpp | 8 +- src/common/p25/data/DataHeader.h | 4 +- src/common/p25/lc/AMBT.cpp | 2 +- src/common/p25/lc/LC.cpp | 22 +- src/common/p25/lc/LC.h | 6 +- src/common/p25/lc/TDULC.cpp | 14 +- src/common/p25/lc/TDULC.h | 10 +- src/common/p25/lc/TSBK.cpp | 34 +- src/common/p25/lc/TSBK.h | 18 +- src/common/p25/lc/tdulc/LC_ADJ_STS_BCAST.cpp | 4 +- src/common/p25/lc/tdulc/LC_CONV_FALLBACK.cpp | 12 +- src/common/p25/lc/tdulc/LC_GROUP_UPDT.cpp | 4 +- src/common/p25/lc/tdulc/LC_NET_STS_BCAST.cpp | 10 +- src/common/p25/lc/tdulc/LC_RFSS_STS_BCAST.cpp | 14 +- src/common/p25/lc/tdulc/LC_SYS_SRV_BCAST.cpp | 2 +- src/common/p25/lc/tdulc/TDULCFactory.cpp | 4 +- src/common/p25/lc/tdulc/TDULCFactory.h | 2 +- src/common/p25/lc/tsbk/IOSP_ACK_RSP.cpp | 4 +- src/common/p25/lc/tsbk/IOSP_GRP_VCH.cpp | 2 +- src/common/p25/lc/tsbk/IOSP_UU_VCH.cpp | 2 +- src/common/p25/lc/tsbk/IOSP_U_REG.cpp | 2 +- src/common/p25/lc/tsbk/OSP_ADJ_STS_BCAST.cpp | 4 +- .../p25/lc/tsbk/OSP_DVM_LC_CALL_TERM.cpp | 2 +- .../p25/lc/tsbk/OSP_GRP_VCH_GRANT_UPD.cpp | 4 +- src/common/p25/lc/tsbk/OSP_LOC_REG_RSP.cpp | 4 +- src/common/p25/lc/tsbk/OSP_MOT_CC_BSI.cpp | 8 +- .../p25/lc/tsbk/OSP_MOT_GRG_VCH_GRANT.cpp | 4 +- .../p25/lc/tsbk/OSP_MOT_GRG_VCH_UPD.cpp | 8 +- src/common/p25/lc/tsbk/OSP_NET_STS_BCAST.cpp | 12 +- src/common/p25/lc/tsbk/OSP_RFSS_STS_BCAST.cpp | 16 +- src/common/p25/lc/tsbk/OSP_SCCB.cpp | 8 +- src/common/p25/lc/tsbk/OSP_SCCB_EXP.cpp | 6 +- src/common/p25/lc/tsbk/OSP_SNDCP_CH_ANN.cpp | 8 +- src/common/p25/lc/tsbk/OSP_SNDCP_CH_GNT.cpp | 4 +- src/common/p25/lc/tsbk/OSP_SYNC_BCAST.cpp | 4 +- src/common/p25/lc/tsbk/OSP_SYS_SRV_BCAST.cpp | 2 +- src/common/p25/lc/tsbk/OSP_TIME_DATE_ANN.cpp | 2 +- .../p25/lc/tsbk/OSP_UU_VCH_GRANT_UPD.cpp | 2 +- src/common/p25/lc/tsbk/OSP_U_DEREG_ACK.cpp | 4 +- src/common/p25/lc/tsbk/TSBKFactory.cpp | 8 +- src/common/p25/lc/tsbk/TSBKFactory.h | 4 +- .../p25/lc/tsbk/mbt/MBT_IOSP_GRP_AFF.cpp | 10 +- .../p25/lc/tsbk/mbt/MBT_OSP_ADJ_STS_BCAST.cpp | 12 +- .../p25/lc/tsbk/mbt/MBT_OSP_AUTH_DMD.cpp | 10 +- .../p25/lc/tsbk/mbt/MBT_OSP_GRP_VCH_GRANT.cpp | 4 +- .../p25/lc/tsbk/mbt/MBT_OSP_NET_STS_BCAST.cpp | 24 +- .../lc/tsbk/mbt/MBT_OSP_RFSS_STS_BCAST.cpp | 24 +- .../p25/lc/tsbk/mbt/MBT_OSP_UU_VCH_GRANT.cpp | 24 +- src/fne/ActivityLog.cpp | 36 +- src/fne/CryptoContainer.cpp | 12 +- src/fne/CryptoContainer.h | 2 +- src/fne/network/FNENetwork.cpp | 2 +- src/fne/network/SpanningTree.cpp | 20 +- src/fne/network/SpanningTree.h | 4 +- src/host/ActivityLog.cpp | 36 +- src/host/dmr/Control.cpp | 14 +- src/host/dmr/Slot.cpp | 384 +++++++++--------- src/host/dmr/Slot.h | 60 +-- src/host/dmr/packet/ControlSignaling.cpp | 194 ++++----- src/host/dmr/packet/Data.cpp | 60 +-- src/host/dmr/packet/Voice.cpp | 110 ++--- src/host/nxdn/Control.cpp | 8 +- src/host/nxdn/Control.h | 2 +- src/host/p25/Control.cpp | 14 +- src/host/p25/Control.h | 4 +- src/host/p25/lc/tsbk/OSP_DVM_GIT_HASH.cpp | 4 +- src/host/p25/packet/Voice.cpp | 4 +- src/patch/ActivityLog.cpp | 34 +- src/patch/HostPatch.cpp | 12 +- src/patch/HostPatch.h | 2 +- src/remote/RESTClient.cpp | 64 +-- src/remote/RESTClient.h | 10 +- src/sysview/network/PeerNetwork.cpp | 4 +- src/sysview/network/PeerNetwork.h | 6 +- 114 files changed, 974 insertions(+), 974 deletions(-) diff --git a/src/bridge/ActivityLog.cpp b/src/bridge/ActivityLog.cpp index 506a279e..12c88b12 100644 --- a/src/bridge/ActivityLog.cpp +++ b/src/bridge/ActivityLog.cpp @@ -33,12 +33,12 @@ const uint32_t ACT_LOG_BUFFER_LEN = 501U; // Global Variables // --------------------------------------------------------------------------- -static std::string m_actFilePath; -static std::string m_actFileRoot; +static std::string g_actFilePath; +static std::string g_actFileRoot; -static FILE* m_actFpLog = nullptr; +static FILE* g_actFpLog = nullptr; -static struct tm m_actTm; +static struct tm g_actTm; // --------------------------------------------------------------------------- // Global Functions @@ -56,22 +56,22 @@ static bool ActivityLogOpen() struct tm* tm = ::gmtime(&now); - if (tm->tm_mday == m_actTm.tm_mday && tm->tm_mon == m_actTm.tm_mon && tm->tm_year == m_actTm.tm_year) { - if (m_actFpLog != nullptr) + if (tm->tm_mday == g_actTm.tm_mday && tm->tm_mon == g_actTm.tm_mon && tm->tm_year == g_actTm.tm_year) { + if (g_actFpLog != nullptr) return true; } else { - if (m_actFpLog != nullptr) - ::fclose(m_actFpLog); + if (g_actFpLog != nullptr) + ::fclose(g_actFpLog); } char filename[200U]; ::sprintf(filename, "%s/%s-%04d-%02d-%02d.activity.log", LogGetFilePath().c_str(), LogGetFileRoot().c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); - m_actFpLog = ::fopen(filename, "a+t"); - m_actTm = *tm; + g_actFpLog = ::fopen(filename, "a+t"); + g_actTm = *tm; - return m_actFpLog != nullptr; + return g_actFpLog != nullptr; } /* Initializes the activity log. */ @@ -81,8 +81,8 @@ bool ActivityLogInitialise(const std::string& filePath, const std::string& fileR #if defined(CATCH2_TEST_COMPILATION) return true; #endif - m_actFilePath = filePath; - m_actFileRoot = fileRoot; + g_actFilePath = filePath; + g_actFileRoot = fileRoot; return ::ActivityLogOpen(); } @@ -94,8 +94,8 @@ void ActivityLogFinalise() #if defined(CATCH2_TEST_COMPILATION) return; #endif - if (m_actFpLog != nullptr) - ::fclose(m_actFpLog); + if (g_actFpLog != nullptr) + ::fclose(g_actFpLog); } /* Writes a new entry to the activity log. */ @@ -117,8 +117,8 @@ void log_internal::ActivityLogInternal(const std::string& log) network->writeActLog(log.c_str()); } - ::fprintf(m_actFpLog, "%s\n", log.c_str()); - ::fflush(m_actFpLog); + ::fprintf(g_actFpLog, "%s\n", log.c_str()); + ::fflush(g_actFpLog); if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) { ::fprintf(stdout, "%s" EOL, log.c_str()); diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 8e233938..1ec650ce 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -70,8 +70,8 @@ const int NUMBER_OF_BUFFERS = 32; // Static Class Members // --------------------------------------------------------------------------- -std::mutex HostBridge::m_audioMutex; -std::mutex HostBridge::m_networkMutex; +std::mutex HostBridge::s_audioMutex; +std::mutex HostBridge::s_networkMutex; // --------------------------------------------------------------------------- // Global Functions @@ -89,7 +89,7 @@ void audioCallback(ma_device* device, void* output, const void* input, ma_uint32 // capture input audio if (frameCount > 0U) { - std::lock_guard lock(HostBridge::m_audioMutex); + std::lock_guard lock(HostBridge::s_audioMutex); int smpIdx = 0; short samples[AUDIO_SAMPLES_LENGTH]; @@ -591,7 +591,7 @@ int HostBridge::run() // ------------------------------------------------------ if (m_network != nullptr) { - std::lock_guard lock(HostBridge::m_networkMutex); + std::lock_guard lock(HostBridge::s_networkMutex); m_network->clock(ms); } @@ -2806,7 +2806,7 @@ void HostBridge::sendUsrpEot() void HostBridge::generatePreambleTone() { - std::lock_guard lock(m_audioMutex); + std::lock_guard lock(s_audioMutex); uint64_t frameCount = AnalogAudio::toSamples(SAMPLE_RATE, 1, m_preambleLength); if (frameCount > m_outputAudio.freeSpace()) { @@ -3043,7 +3043,7 @@ void* HostBridge::threadAudioProcess(void* arg) // scope is intentional { - std::lock_guard lock(m_audioMutex); + std::lock_guard lock(s_audioMutex); if (bridge->m_inputAudio.dataSize() >= AUDIO_SAMPLES_LENGTH) { short samples[AUDIO_SAMPLES_LENGTH]; @@ -3314,7 +3314,7 @@ void* HostBridge::threadUDPAudioProcess(void* arg) bridge->m_udpDropTime.start(); } - std::lock_guard lock(m_audioMutex); + std::lock_guard lock(s_audioMutex); uint8_t pcm[AUDIO_SAMPLES_LENGTH_BYTES]; ::memset(pcm, 0x00U, AUDIO_SAMPLES_LENGTH_BYTES); ::memcpy(pcm, req->pcm, AUDIO_SAMPLES_LENGTH_BYTES); @@ -3425,7 +3425,7 @@ void* HostBridge::threadNetworkProcess(void* arg) uint32_t length = 0U; bool netReadRet = false; if (bridge->m_txMode == TX_MODE_DMR) { - std::lock_guard lock(HostBridge::m_networkMutex); + std::lock_guard lock(HostBridge::s_networkMutex); UInt8Array dmrBuffer = bridge->m_network->readDMR(netReadRet, length); if (netReadRet) { bridge->processDMRNetwork(dmrBuffer.get(), length); @@ -3433,7 +3433,7 @@ void* HostBridge::threadNetworkProcess(void* arg) } if (bridge->m_txMode == TX_MODE_P25) { - std::lock_guard lock(HostBridge::m_networkMutex); + std::lock_guard lock(HostBridge::s_networkMutex); UInt8Array p25Buffer = bridge->m_network->readP25(netReadRet, length); if (netReadRet) { bridge->processP25Network(p25Buffer.get(), length); @@ -3441,7 +3441,7 @@ void* HostBridge::threadNetworkProcess(void* arg) } if (bridge->m_txMode == TX_MODE_ANALOG) { - std::lock_guard lock(HostBridge::m_networkMutex); + std::lock_guard lock(HostBridge::s_networkMutex); UInt8Array analogBuffer = bridge->m_network->readAnalog(netReadRet, length); if (netReadRet) { bridge->processAnalogNetwork(analogBuffer.get(), length); diff --git a/src/bridge/HostBridge.h b/src/bridge/HostBridge.h index 4897f1bd..0e6ed886 100644 --- a/src/bridge/HostBridge.h +++ b/src/bridge/HostBridge.h @@ -266,8 +266,8 @@ class HOST_SW_API HostBridge { uint32_t m_usrpSeqNo; - static std::mutex m_audioMutex; - static std::mutex m_networkMutex; + static std::mutex s_audioMutex; + static std::mutex s_networkMutex; #if defined(_WIN32) void* m_decoderState; diff --git a/src/common/Log.cpp b/src/common/Log.cpp index f4cf12b5..143b639e 100644 --- a/src/common/Log.cpp +++ b/src/common/Log.cpp @@ -38,13 +38,13 @@ const uint32_t LOG_BUFFER_LEN = 4096U; // Global Variables // --------------------------------------------------------------------------- -static uint32_t m_fileLevel = 0U; -static std::string m_filePath; -static std::string m_fileRoot; +static uint32_t g_fileLevel = 0U; +static std::string g_filePath; +static std::string g_fileRoot; -static network::BaseNetwork* m_network; +static network::BaseNetwork* g_network; -static FILE* m_fpLog = nullptr; +static FILE* g_fpLog = nullptr; uint32_t g_logDisplayLevel = 2U; bool g_disableTimeDisplay = false; @@ -52,11 +52,11 @@ bool g_disableTimeDisplay = false; bool g_useSyslog = false; bool g_disableNetworkLog = false; -static struct tm m_tm; +static struct tm g_tm; -static std::ostream m_outStream { std::cerr.rdbuf() }; +static std::ostream g_outStream { std::cerr.rdbuf() }; -bool log_stacktrace::SignalHandling::m_foreground = false; +bool log_stacktrace::SignalHandling::s_foreground = false; // --------------------------------------------------------------------------- // Global Functions @@ -64,15 +64,15 @@ bool log_stacktrace::SignalHandling::m_foreground = false; /* Helper to get the current log file level. */ -uint32_t CurrentLogFileLevel() { return m_fileLevel; } +uint32_t CurrentLogFileLevel() { return g_fileLevel; } /* Helper to get the current log file path. */ -std::string LogGetFilePath() { return m_filePath; } +std::string LogGetFilePath() { return g_filePath; } /* Helper to get the current log file root. */ -std::string LogGetFileRoot() { return m_fileRoot; } +std::string LogGetFileRoot() { return g_fileRoot; } /* Helper to open the detailed log file, file handle. */ @@ -81,7 +81,7 @@ static bool LogOpen() #if defined(CATCH2_TEST_COMPILATION) return true; #endif - if (m_fileLevel == 0U) + if (g_fileLevel == 0U) return true; if (!g_useSyslog) { @@ -90,26 +90,26 @@ static bool LogOpen() struct tm* tm = ::localtime(&now); - if (tm->tm_mday == m_tm.tm_mday && tm->tm_mon == m_tm.tm_mon && tm->tm_year == m_tm.tm_year) { - if (m_fpLog != nullptr) + if (tm->tm_mday == g_tm.tm_mday && tm->tm_mon == g_tm.tm_mon && tm->tm_year == g_tm.tm_year) { + if (g_fpLog != nullptr) return true; } else { - if (m_fpLog != nullptr) - ::fclose(m_fpLog); + if (g_fpLog != nullptr) + ::fclose(g_fpLog); } char filename[200U]; - ::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + ::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", g_filePath.c_str(), g_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); - m_fpLog = ::fopen(filename, "a+t"); - m_tm = *tm; + g_fpLog = ::fopen(filename, "a+t"); + g_tm = *tm; - return m_fpLog != nullptr; + return g_fpLog != nullptr; } else { #if !defined(_WIN32) - switch (m_fileLevel) { + switch (g_fileLevel) { case 1U: setlogmask(LOG_UPTO(LOG_DEBUG)); break; @@ -128,7 +128,7 @@ static bool LogOpen() break; } - openlog(m_fileRoot.c_str(), LOG_CONS | LOG_PID | LOG_NDELAY, LOG_DAEMON); + openlog(g_fileRoot.c_str(), LOG_CONS | LOG_PID | LOG_NDELAY, LOG_DAEMON); return true; #else return false; @@ -141,7 +141,7 @@ static bool LogOpen() void* LogGetNetwork() { // NO GOOD, VERY BAD, TERRIBLE HACK - return (void*)m_network; + return (void*)g_network; } /* Sets the instance of the Network class to transfer the activity log with. */ @@ -153,16 +153,16 @@ void LogSetNetwork(void* network) #endif // note: The Network class is passed here as a void so we can avoid including the Network.h // header in Log.h. This is dirty and probably terrible... - m_network = (network::BaseNetwork*)network; + g_network = (network::BaseNetwork*)network; } /* Initializes the diagnostics log. */ bool LogInitialise(const std::string& filePath, const std::string& fileRoot, uint32_t fileLevel, uint32_t displayLevel, bool disableTimeDisplay, bool useSyslog) { - m_filePath = filePath; - m_fileRoot = fileRoot; - m_fileLevel = fileLevel; + g_filePath = filePath; + g_fileRoot = fileRoot; + g_fileLevel = fileLevel; g_logDisplayLevel = displayLevel; g_disableTimeDisplay = disableTimeDisplay; #if defined(_WIN32) @@ -181,9 +181,9 @@ void LogFinalise() #if defined(CATCH2_TEST_COMPILATION) return; #endif - if (m_fpLog != nullptr) { - ::fclose(m_fpLog); - m_fpLog = nullptr; + if (g_fpLog != nullptr) { + ::fclose(g_fpLog); + g_fpLog = nullptr; } #if !defined(_WIN32) if (g_useSyslog) @@ -195,21 +195,21 @@ void LogFinalise() void log_internal::SetInternalOutputStream(std::ostream& stream) { - m_outStream.rdbuf(stream.rdbuf()); + g_outStream.rdbuf(stream.rdbuf()); } /* Writes a new entry to the diagnostics log. */ void log_internal::LogInternal(uint32_t level, const std::string& log) { - if (m_outStream && g_logDisplayLevel == 0U) { - m_outStream << log << std::endl; + if (g_outStream && g_logDisplayLevel == 0U) { + g_outStream << log << std::endl; } - if (m_network != nullptr && !g_disableNetworkLog) { + if (g_network != nullptr && !g_disableNetworkLog) { // don't transfer debug data... if (level > 1U) { - m_network->writeDiagLog(log.c_str()); + g_network->writeDiagLog(log.c_str()); } } @@ -218,15 +218,15 @@ void log_internal::LogInternal(uint32_t level, const std::string& log) return; #endif - if (level >= m_fileLevel && m_fileLevel != 0U) { + if (level >= g_fileLevel && g_fileLevel != 0U) { if (!g_useSyslog) { bool ret = ::LogOpen(); if (!ret) return; - if (m_fpLog != nullptr) { - ::fprintf(m_fpLog, "%s\n", log.c_str()); - ::fflush(m_fpLog); + if (g_fpLog != nullptr) { + ::fprintf(g_fpLog, "%s\n", log.c_str()); + ::fflush(g_fpLog); } } else { #if !defined(_WIN32) @@ -263,8 +263,8 @@ void log_internal::LogInternal(uint32_t level, const std::string& log) // fatal error (specially allow any log levels above 9999) if (level >= 5U && level < 9999U) { - if (m_fpLog != nullptr) - ::fclose(m_fpLog); + if (g_fpLog != nullptr) + ::fclose(g_fpLog); #if !defined(_WIN32) if (g_useSyslog) ::closelog(); @@ -277,19 +277,19 @@ void log_internal::LogInternal(uint32_t level, const std::string& log) std::string log_internal::GetLogFilePath() { - return m_filePath; + return g_filePath; } /* Internal helper to get the log file root name. */ std::string log_internal::GetLogFileRoot() { - return m_fileRoot; + return g_fileRoot; } /* Internal helper to get the log file handle pointer. */ FILE* log_internal::GetLogFile() { - return m_fpLog; + return g_fpLog; } diff --git a/src/common/Log.h b/src/common/Log.h index 2d356539..fc2c419b 100644 --- a/src/common/Log.h +++ b/src/common/Log.h @@ -266,7 +266,7 @@ namespace log_stacktrace { bool success = true; - m_foreground = foreground; + s_foreground = foreground; const size_t stackSize = 1024 * 1024 * 8; m_stackContent.reset(static_cast(malloc(stackSize))); @@ -368,7 +368,7 @@ namespace log_stacktrace p.color_mode = backward::ColorMode::never; log_internal::LogInternal(2U, "UNRECOVERABLE FATAL ERROR!"); - if (m_foreground > 0) { + if (s_foreground > 0) { p.print(st, stderr); } @@ -407,7 +407,7 @@ namespace log_stacktrace private: backward::details::handle m_stackContent; bool m_loaded; - static bool m_foreground; + static bool s_foreground; /** * @brief Internal helper to handle a signal. @@ -565,7 +565,7 @@ namespace log_stacktrace } std::thread m_reporterThread; - static bool m_foreground; + static bool s_foreground; // TODO: how not to hardcode these? static const constexpr int signalSkipRecs = @@ -742,7 +742,7 @@ namespace log_stacktrace bool loaded() { return false; } private: - static bool m_foreground; + static bool s_foreground; }; #endif // BACKWARD_SYSTEM_UNKNOWN #endif // !defined(CATCH2_TEST_COMPILATION) diff --git a/src/common/dmr/lc/CSBK.cpp b/src/common/dmr/lc/CSBK.cpp index 9c8a655d..230fa6fd 100644 --- a/src/common/dmr/lc/CSBK.cpp +++ b/src/common/dmr/lc/CSBK.cpp @@ -25,9 +25,9 @@ using namespace dmr::lc; // Static Class Members // --------------------------------------------------------------------------- -bool CSBK::m_verbose = false; +bool CSBK::s_verbose = false; -SiteData CSBK::m_siteData = SiteData(); +SiteData CSBK::s_siteData = SiteData(); // --------------------------------------------------------------------------- // Public Class Members @@ -271,7 +271,7 @@ bool CSBK::decode(const uint8_t* data, uint8_t* payload) break; } - if (m_verbose) { + if (s_verbose) { Utils::dump(2U, "CSBK::decode(), Decoded CSBK", csbk, DMR_CSBK_LENGTH_BYTES); } @@ -339,7 +339,7 @@ void CSBK::encode(uint8_t* data, const uint8_t* payload) break; } - if (m_verbose) { + if (s_verbose) { Utils::dump(2U, "CSBK::encode(), Encoded CSBK", csbk, DMR_CSBK_LENGTH_BYTES); } diff --git a/src/common/dmr/lc/CSBK.h b/src/common/dmr/lc/CSBK.h index 91d57da5..7f4c7e2b 100644 --- a/src/common/dmr/lc/CSBK.h +++ b/src/common/dmr/lc/CSBK.h @@ -92,24 +92,24 @@ namespace dmr * @brief Gets the flag indicating verbose log output. * @returns bool True, if the CSBK is verbose logging, otherwise false. */ - static bool getVerbose() { return m_verbose; } + static bool getVerbose() { return s_verbose; } /** * @brief Sets the flag indicating verbose log output. * @param verbose Flag indicating verbose log output. */ - static void setVerbose(bool verbose) { m_verbose = verbose; } + static void setVerbose(bool verbose) { s_verbose = verbose; } /** @name Local Site data */ /** * @brief Gets the local site data. * @returns SiteData Currently set site data for the CSBK class. */ - static SiteData getSiteData() { return m_siteData; } + static SiteData getSiteData() { return s_siteData; } /** * @brief Sets the local site data. * @param siteData Site data to set for the CSBK class. */ - static void setSiteData(SiteData siteData) { m_siteData = siteData; } + static void setSiteData(SiteData siteData) { s_siteData = siteData; } /** @} */ public: @@ -230,10 +230,10 @@ namespace dmr /** @} */ protected: - static bool m_verbose; + static bool s_verbose; // Local Site data - static SiteData m_siteData; + static SiteData s_siteData; /** * @brief Internal helper to convert payload bytes to a 64-bit long value. diff --git a/src/common/dmr/lc/csbk/CSBK_ACK_RSP.cpp b/src/common/dmr/lc/csbk/CSBK_ACK_RSP.cpp index f5d9c38a..faac3f75 100644 --- a/src/common/dmr/lc/csbk/CSBK_ACK_RSP.cpp +++ b/src/common/dmr/lc/csbk/CSBK_ACK_RSP.cpp @@ -63,7 +63,7 @@ void CSBK_ACK_RSP::encode(uint8_t* data) csbkValue = 0U; } else { csbkValue = (m_GI ? 0x40U : 0x00U) + // Source Type - (m_siteData.siteId() & 0x3FU); // Net + Site LSB + (s_siteData.siteId() & 0x3FU); // Net + Site LSB } csbkValue = (csbkValue << 7) + (m_response & 0x7FU); // Response Information csbkValue = (csbkValue << 8) + (m_reason & 0xFFU); // Reason Code diff --git a/src/common/dmr/lc/csbk/CSBK_ALOHA.cpp b/src/common/dmr/lc/csbk/CSBK_ALOHA.cpp index 39402c70..ffc3355d 100644 --- a/src/common/dmr/lc/csbk/CSBK_ALOHA.cpp +++ b/src/common/dmr/lc/csbk/CSBK_ALOHA.cpp @@ -55,13 +55,13 @@ void CSBK_ALOHA::encode(uint8_t* data) csbkValue = (csbkValue << 1) + ((m_siteTSSync) ? 1U : 0U); // Site Time Slot Synchronization csbkValue = (csbkValue << 3) + DMR_ALOHA_VER_151; // DMR Spec. Version (1.5.1) csbkValue = (csbkValue << 1) + ((m_siteOffsetTiming) ? 1U : 0U); // Site Timing: Aligned or Offset - csbkValue = (csbkValue << 1) + ((m_siteData.netActive()) ? 1U : 0U); // Site Networked + csbkValue = (csbkValue << 1) + ((s_siteData.netActive()) ? 1U : 0U); // Site Networked csbkValue = (csbkValue << 5) + (m_alohaMask & 0x1FU); // MS Mask csbkValue = (csbkValue << 2) + 0U; // Service Function csbkValue = (csbkValue << 4) + (m_nRandWait & 0x0FU); // Random Access Wait - csbkValue = (csbkValue << 1) + ((m_siteData.requireReg()) ? 1U : 0U); // Require Registration + csbkValue = (csbkValue << 1) + ((s_siteData.requireReg()) ? 1U : 0U); // Require Registration csbkValue = (csbkValue << 4) + (m_backoffNo & 0x0FU); // Backoff Number - csbkValue = (csbkValue << 16) + m_siteData.systemIdentity(); // Site Identity + csbkValue = (csbkValue << 16) + s_siteData.systemIdentity(); // Site Identity csbkValue = (csbkValue << 24) + m_srcId; // Source Radio Address std::unique_ptr csbk = CSBK::fromValue(csbkValue); diff --git a/src/common/dmr/lc/csbk/CSBK_BROADCAST.cpp b/src/common/dmr/lc/csbk/CSBK_BROADCAST.cpp index 51c35dac..3a3f0de7 100644 --- a/src/common/dmr/lc/csbk/CSBK_BROADCAST.cpp +++ b/src/common/dmr/lc/csbk/CSBK_BROADCAST.cpp @@ -142,11 +142,11 @@ void CSBK_BROADCAST::encode(uint8_t* data) break; case BroadcastAnncType::SITE_PARMS: // Broadcast Params 1 - csbkValue = (csbkValue << 14) + m_siteData.systemIdentity(true); // Site Identity (Broadcast Params 1) + csbkValue = (csbkValue << 14) + s_siteData.systemIdentity(true); // Site Identity (Broadcast Params 1) - csbkValue = (csbkValue << 1) + ((m_siteData.requireReg()) ? 1U : 0U); // Require Registration + csbkValue = (csbkValue << 1) + ((s_siteData.requireReg()) ? 1U : 0U); // Require Registration csbkValue = (csbkValue << 4) + (m_backoffNo & 0x0FU); // Backoff Number - csbkValue = (csbkValue << 16) + m_siteData.systemIdentity(); // Site Identity + csbkValue = (csbkValue << 16) + s_siteData.systemIdentity(); // Site Identity // Broadcast Params 2 csbkValue = (csbkValue << 1) + 0U; // Roaming TG Subscription/Attach diff --git a/src/common/lookups/AdjSiteMapLookup.cpp b/src/common/lookups/AdjSiteMapLookup.cpp index bcd0d784..6a43c90e 100644 --- a/src/common/lookups/AdjSiteMapLookup.cpp +++ b/src/common/lookups/AdjSiteMapLookup.cpp @@ -21,8 +21,8 @@ using namespace lookups; // Static Class Members // --------------------------------------------------------------------------- -std::mutex AdjSiteMapLookup::m_mutex; -bool AdjSiteMapLookup::m_locked = false; +std::mutex AdjSiteMapLookup::s_mutex; +bool AdjSiteMapLookup::s_locked = false; // --------------------------------------------------------------------------- // Macros @@ -30,16 +30,16 @@ bool AdjSiteMapLookup::m_locked = false; // Lock the table. #define __LOCK_TABLE() \ - std::lock_guard lock(m_mutex); \ - m_locked = true; + std::lock_guard lock(s_mutex); \ + s_locked = true; // Unlock the table. -#define __UNLOCK_TABLE() m_locked = false; +#define __UNLOCK_TABLE() s_locked = false; // Spinlock wait for table to be read unlocked. #define __SPINLOCK() \ - if (m_locked) { \ - while (m_locked) \ + if (s_locked) { \ + while (s_locked) \ Thread::sleep(2U); \ } @@ -171,7 +171,7 @@ AdjPeerMapEntry AdjSiteMapLookup::find(uint32_t id) __SPINLOCK(); - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); auto it = std::find_if(m_adjPeerMap.begin(), m_adjPeerMap.end(), [&](AdjPeerMapEntry& x) { return x.peerId() == id; @@ -225,7 +225,7 @@ bool AdjSiteMapLookup::load() if (peerList.size() == 0U) { ::LogError(LOG_HOST, "No adj site map peer list defined!"); - m_locked = false; + s_locked = false; return false; } @@ -255,7 +255,7 @@ bool AdjSiteMapLookup::save() return false; } - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); // New list for our new group voice rules yaml::Node peerList; diff --git a/src/common/lookups/AdjSiteMapLookup.h b/src/common/lookups/AdjSiteMapLookup.h index 7cbe8a6b..d560162c 100644 --- a/src/common/lookups/AdjSiteMapLookup.h +++ b/src/common/lookups/AdjSiteMapLookup.h @@ -233,8 +233,8 @@ namespace lookups bool m_stop; - static std::mutex m_mutex; //!< Mutex used for change locking. - static bool m_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. + static std::mutex s_mutex; //!< Mutex used for change locking. + static bool s_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. /** * @brief Loads the table from the passed lookup table file. diff --git a/src/common/lookups/IdenTableLookup.cpp b/src/common/lookups/IdenTableLookup.cpp index 313fb59b..3d1a4dd1 100644 --- a/src/common/lookups/IdenTableLookup.cpp +++ b/src/common/lookups/IdenTableLookup.cpp @@ -22,7 +22,7 @@ using namespace lookups; // Static Class Members // --------------------------------------------------------------------------- -std::mutex IdenTableLookup::m_mutex; +std::mutex IdenTableLookup::s_mutex; // --------------------------------------------------------------------------- // Public Class Members @@ -39,7 +39,7 @@ IdenTableLookup::IdenTableLookup(const std::string& filename, uint32_t reloadTim void IdenTableLookup::clear() { - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); m_table.clear(); } @@ -49,7 +49,7 @@ IdenTable IdenTableLookup::find(uint32_t id) { IdenTable entry; - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); try { entry = m_table.at(id); } catch (...) { @@ -103,7 +103,7 @@ bool IdenTableLookup::load() // clear table clear(); - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); // read lines from file std::string line; diff --git a/src/common/lookups/IdenTableLookup.h b/src/common/lookups/IdenTableLookup.h index f9752dc7..864155b4 100644 --- a/src/common/lookups/IdenTableLookup.h +++ b/src/common/lookups/IdenTableLookup.h @@ -160,7 +160,7 @@ namespace lookups bool save(bool quiet = false) override; private: - static std::mutex m_mutex; + static std::mutex s_mutex; }; } // namespace lookups diff --git a/src/common/lookups/PeerListLookup.cpp b/src/common/lookups/PeerListLookup.cpp index 849d2f49..29744a1b 100644 --- a/src/common/lookups/PeerListLookup.cpp +++ b/src/common/lookups/PeerListLookup.cpp @@ -22,8 +22,8 @@ using namespace lookups; // Static Class Members // --------------------------------------------------------------------------- -std::mutex PeerListLookup::m_mutex; -bool PeerListLookup::m_locked = false; +std::mutex PeerListLookup::s_mutex; +bool PeerListLookup::s_locked = false; // --------------------------------------------------------------------------- // Macros @@ -31,16 +31,16 @@ bool PeerListLookup::m_locked = false; // Lock the table. #define __LOCK_TABLE() \ - std::lock_guard lock(m_mutex); \ - m_locked = true; + std::lock_guard lock(s_mutex); \ + s_locked = true; // Unlock the table. -#define __UNLOCK_TABLE() m_locked = false; +#define __UNLOCK_TABLE() s_locked = false; // Spinlock wait for table to be read unlocked. #define __SPINLOCK() \ - if (m_locked) { \ - while (m_locked) \ + if (s_locked) { \ + while (s_locked) \ Thread::sleep(2U); \ } @@ -291,7 +291,7 @@ bool PeerListLookup::save(bool quiet) // Counter for lines written unsigned int lines = 0; - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); // String for writing std::string line; diff --git a/src/common/lookups/PeerListLookup.h b/src/common/lookups/PeerListLookup.h index 48f09383..13bb3f0e 100644 --- a/src/common/lookups/PeerListLookup.h +++ b/src/common/lookups/PeerListLookup.h @@ -243,8 +243,8 @@ namespace lookups bool save(bool quiet = false) override; private: - static std::mutex m_mutex; //!< Mutex used for change locking. - static bool m_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. + static std::mutex s_mutex; //!< Mutex used for change locking. + static bool s_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. }; } // namespace lookups diff --git a/src/common/lookups/RadioIdLookup.cpp b/src/common/lookups/RadioIdLookup.cpp index ecebab10..1e1a2bb9 100644 --- a/src/common/lookups/RadioIdLookup.cpp +++ b/src/common/lookups/RadioIdLookup.cpp @@ -24,8 +24,8 @@ using namespace lookups; // Static Class Members // --------------------------------------------------------------------------- -std::mutex RadioIdLookup::m_mutex; -bool RadioIdLookup::m_locked = false; +std::mutex RadioIdLookup::s_mutex; +bool RadioIdLookup::s_locked = false; // --------------------------------------------------------------------------- // Macros @@ -33,16 +33,16 @@ bool RadioIdLookup::m_locked = false; // Lock the table. #define __LOCK_TABLE() \ - std::lock_guard lock(m_mutex); \ - m_locked = true; + std::lock_guard lock(s_mutex); \ + s_locked = true; // Unlock the table. -#define __UNLOCK_TABLE() m_locked = false; +#define __UNLOCK_TABLE() s_locked = false; // Spinlock wait for table to be read unlocked. #define __SPINLOCK() \ - if (m_locked) { \ - while (m_locked) \ + if (s_locked) { \ + while (s_locked) \ Thread::sleep(2U); \ } @@ -259,7 +259,7 @@ bool RadioIdLookup::save(bool quiet) // Counter for lines written unsigned int lines = 0; - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); // String for writing std::string line; diff --git a/src/common/lookups/RadioIdLookup.h b/src/common/lookups/RadioIdLookup.h index cc4ea191..54afb475 100644 --- a/src/common/lookups/RadioIdLookup.h +++ b/src/common/lookups/RadioIdLookup.h @@ -210,8 +210,8 @@ namespace lookups bool save(bool quiet = false) override; private: - static std::mutex m_mutex; //!< Mutex used for change locking. - static bool m_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. + static std::mutex s_mutex; //!< Mutex used for change locking. + static bool s_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. }; } // namespace lookups diff --git a/src/common/lookups/TalkgroupRulesLookup.cpp b/src/common/lookups/TalkgroupRulesLookup.cpp index 187fed4f..14689043 100644 --- a/src/common/lookups/TalkgroupRulesLookup.cpp +++ b/src/common/lookups/TalkgroupRulesLookup.cpp @@ -22,8 +22,8 @@ using namespace lookups; // Static Class Members // --------------------------------------------------------------------------- -std::mutex TalkgroupRulesLookup::m_mutex; -bool TalkgroupRulesLookup::m_locked = false; +std::mutex TalkgroupRulesLookup::s_mutex; +bool TalkgroupRulesLookup::s_locked = false; // --------------------------------------------------------------------------- // Macros @@ -31,16 +31,16 @@ bool TalkgroupRulesLookup::m_locked = false; // Lock the table. #define __LOCK_TABLE() \ - std::lock_guard lock(m_mutex); \ - m_locked = true; + std::lock_guard lock(s_mutex); \ + s_locked = true; // Unlock the table. -#define __UNLOCK_TABLE() m_locked = false; +#define __UNLOCK_TABLE() s_locked = false; // Spinlock wait for table to be read unlocked. #define __SPINLOCK() \ - if (m_locked) { \ - while (m_locked) \ + if (s_locked) { \ + while (s_locked) \ Thread::sleep(2U); \ } @@ -233,7 +233,7 @@ TalkgroupRuleGroupVoice TalkgroupRulesLookup::find(uint32_t id, uint8_t slot) __SPINLOCK(); - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(), [&](TalkgroupRuleGroupVoice& x) { @@ -260,7 +260,7 @@ TalkgroupRuleGroupVoice TalkgroupRulesLookup::findByRewrite(uint32_t peerId, uin __SPINLOCK(); - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(), [&](TalkgroupRuleGroupVoice& x) { if (x.config().rewrite().size() == 0) @@ -336,7 +336,7 @@ bool TalkgroupRulesLookup::load() if (groupVoiceList.size() == 0U) { ::LogError(LOG_HOST, "No group voice rules list defined!"); - m_locked = false; + s_locked = false; return false; } @@ -390,7 +390,7 @@ bool TalkgroupRulesLookup::save(bool quiet) return false; } - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); // New list for our new group voice rules yaml::Node groupVoiceList; diff --git a/src/common/lookups/TalkgroupRulesLookup.h b/src/common/lookups/TalkgroupRulesLookup.h index d981a710..4d68de15 100644 --- a/src/common/lookups/TalkgroupRulesLookup.h +++ b/src/common/lookups/TalkgroupRulesLookup.h @@ -644,8 +644,8 @@ namespace lookups bool m_acl; bool m_stop; - static std::mutex m_mutex; //!< Mutex used for change locking. - static bool m_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. + static std::mutex s_mutex; //!< Mutex used for change locking. + static bool s_locked; //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used. /** * @brief Loads the table from the passed lookup table file. diff --git a/src/common/network/tcp/SecureTcpClient.cpp b/src/common/network/tcp/SecureTcpClient.cpp index 0b3d4b0f..a928ecb2 100644 --- a/src/common/network/tcp/SecureTcpClient.cpp +++ b/src/common/network/tcp/SecureTcpClient.cpp @@ -18,6 +18,6 @@ using namespace network::tcp; // Static Class Members // --------------------------------------------------------------------------- -std::string SecureTcpClient::m_sslHostname = std::string(); +std::string SecureTcpClient::s_sslHostname = std::string(); #endif // ENABLE_SSL \ No newline at end of file diff --git a/src/common/network/tcp/SecureTcpClient.h b/src/common/network/tcp/SecureTcpClient.h index 657ce8c8..f8a654c8 100644 --- a/src/common/network/tcp/SecureTcpClient.h +++ b/src/common/network/tcp/SecureTcpClient.h @@ -244,11 +244,11 @@ namespace network * @brief Sets the hostname for the SSL certificate. * @param hostname Hostname. */ - static void setHostname(std::string hostname) { m_sslHostname = hostname; } + static void setHostname(std::string hostname) { s_sslHostname = hostname; } private: sockaddr_storage m_sockaddr; - static std::string m_sslHostname; + static std::string s_sslHostname; SSL* m_pSSL; SSL_CTX* m_pSSLCtx; @@ -284,7 +284,7 @@ namespace network } SSL_set_hostflags(m_pSSL, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); - if (!SSL_set1_host(m_pSSL, SecureTcpClient::m_sslHostname.c_str())) { + if (!SSL_set1_host(m_pSSL, SecureTcpClient::s_sslHostname.c_str())) { LogError(LOG_NET, "Failed to set SSL hostname, %s err: %d", ERR_error_string(ERR_get_error(), NULL), errno); throw std::runtime_error("Failed to set SSL hostname"); } diff --git a/src/common/network/viface/VIFace.cpp b/src/common/network/viface/VIFace.cpp index 530b2b13..69034d93 100644 --- a/src/common/network/viface/VIFace.cpp +++ b/src/common/network/viface/VIFace.cpp @@ -49,7 +49,7 @@ using namespace network::viface; // Static Class Members // --------------------------------------------------------------------------- -uint32_t VIFace::m_idSeq = 0U; +uint32_t VIFace::s_idSeq = 0U; // --------------------------------------------------------------------------- // Global Functions @@ -336,8 +336,8 @@ VIFace::VIFace(std::string name, bool tap, int id) : // set id if (id < 0) { - m_id = m_idSeq; - m_idSeq++; + m_id = s_idSeq; + s_idSeq++; } else { m_id = id; } diff --git a/src/common/network/viface/VIFace.h b/src/common/network/viface/VIFace.h index 804cf376..bfbef0fb 100644 --- a/src/common/network/viface/VIFace.h +++ b/src/common/network/viface/VIFace.h @@ -237,7 +237,7 @@ namespace network uint32_t m_mtu; uint32_t m_id; - static uint32_t m_idSeq; + static uint32_t s_idSeq; /** * @brief Internal helper that performs a kernel IOCTL to get the IPv4 address by request. diff --git a/src/common/nxdn/lc/RCCH.cpp b/src/common/nxdn/lc/RCCH.cpp index b5a69671..38727e3b 100644 --- a/src/common/nxdn/lc/RCCH.cpp +++ b/src/common/nxdn/lc/RCCH.cpp @@ -22,10 +22,10 @@ using namespace nxdn::lc; // Static Class Members // --------------------------------------------------------------------------- -bool RCCH::m_verbose = false; +bool RCCH::s_verbose = false; -uint8_t* RCCH::m_siteCallsign = nullptr; -SiteData RCCH::m_siteData = SiteData(); +uint8_t* RCCH::s_siteCallsign = nullptr; +SiteData RCCH::s_siteData = SiteData(); // --------------------------------------------------------------------------- // Public Class Members @@ -51,9 +51,9 @@ RCCH::RCCH() : m_transmissionMode(TransmissionMode::MODE_4800), m_siteIdenEntry() { - if (m_siteCallsign == nullptr) { - m_siteCallsign = new uint8_t[CALLSIGN_LENGTH_BYTES]; - ::memset(m_siteCallsign, 0x00U, CALLSIGN_LENGTH_BYTES); + if (s_siteCallsign == nullptr) { + s_siteCallsign = new uint8_t[CALLSIGN_LENGTH_BYTES]; + ::memset(s_siteCallsign, 0x00U, CALLSIGN_LENGTH_BYTES); } } @@ -82,19 +82,19 @@ std::string RCCH::toString(bool isp) void RCCH::setCallsign(std::string callsign) { - if (m_siteCallsign == nullptr) { - m_siteCallsign = new uint8_t[CALLSIGN_LENGTH_BYTES]; - ::memset(m_siteCallsign, 0x00U, CALLSIGN_LENGTH_BYTES); + if (s_siteCallsign == nullptr) { + s_siteCallsign = new uint8_t[CALLSIGN_LENGTH_BYTES]; + ::memset(s_siteCallsign, 0x00U, CALLSIGN_LENGTH_BYTES); } uint32_t idLength = callsign.length(); if (idLength > 0) { - ::memset(m_siteCallsign, 0x20U, CALLSIGN_LENGTH_BYTES); + ::memset(s_siteCallsign, 0x20U, CALLSIGN_LENGTH_BYTES); if (idLength > CALLSIGN_LENGTH_BYTES) idLength = CALLSIGN_LENGTH_BYTES; for (uint32_t i = 0; i < idLength; i++) - m_siteCallsign[i] = callsign[i]; + s_siteCallsign[i] = callsign[i]; } } @@ -114,7 +114,7 @@ void RCCH::decode(const uint8_t* data, uint8_t* rcch, uint32_t length, uint32_t WRITE_BIT(rcch, i, b); } - if (m_verbose) { + if (s_verbose) { Utils::dump(2U, "NXDN, RCCH::decode(), Decoded RCCH Data", rcch, NXDN_RCCH_LC_LENGTH_BYTES); } @@ -137,7 +137,7 @@ void RCCH::encode(uint8_t* data, const uint8_t* rcch, uint32_t length, uint32_t data[0U] = m_messageType & 0x3FU; // Message Type } - if (m_verbose) { + if (s_verbose) { Utils::dump(2U, "NXDN, RCCH::encode(), Encoded RCCH Data", data, NXDN_RCCH_LC_LENGTH_BYTES); } } diff --git a/src/common/nxdn/lc/RCCH.h b/src/common/nxdn/lc/RCCH.h index c56cde62..b497397a 100644 --- a/src/common/nxdn/lc/RCCH.h +++ b/src/common/nxdn/lc/RCCH.h @@ -71,14 +71,14 @@ namespace nxdn /** * @brief Gets the flag indicating verbose log output. - * @returns bool True, if the TSBK is verbose logging, otherwise false. + * @returns bool True, if the RCCH is verbose logging, otherwise false. */ - static bool getVerbose() { return m_verbose; } + static bool getVerbose() { return s_verbose; } /** * @brief Sets the flag indicating verbose log output. * @param verbose Flag indicating verbose log output. */ - static void setVerbose(bool verbose) { m_verbose = verbose; } + static void setVerbose(bool verbose) { s_verbose = verbose; } /** @name Local Site data */ /** @@ -91,12 +91,12 @@ namespace nxdn * @brief Gets the local site data. * @returns SiteData Currently set site data for the RCCH class. */ - static SiteData getSiteData() { return m_siteData; } + static SiteData getSiteData() { return s_siteData; } /** * @brief Sets the local site data. * @param siteData Site data to set for the RCCH class. */ - static void setSiteData(SiteData siteData) { m_siteData = siteData; } + static void setSiteData(SiteData siteData) { s_siteData = siteData; } /** @} */ public: @@ -183,11 +183,11 @@ namespace nxdn /** @} */ protected: - static bool m_verbose; + static bool s_verbose; // Local Site data - static uint8_t* m_siteCallsign; - static SiteData m_siteData; + static uint8_t* s_siteCallsign; + static SiteData s_siteData; /** * @brief Internal helper to decode a RCCH link control message. diff --git a/src/common/nxdn/lc/RTCH.cpp b/src/common/nxdn/lc/RTCH.cpp index a476dc62..14486cc2 100644 --- a/src/common/nxdn/lc/RTCH.cpp +++ b/src/common/nxdn/lc/RTCH.cpp @@ -24,7 +24,7 @@ using namespace nxdn::lc; // Static Class Members // --------------------------------------------------------------------------- -bool RTCH::m_verbose = false; +bool RTCH::s_verbose = false; // --------------------------------------------------------------------------- // Public Class Members @@ -113,7 +113,7 @@ void RTCH::decode(const uint8_t* data, uint32_t length, uint32_t offset) WRITE_BIT(rtch, i, b); } - if (m_verbose) { + if (s_verbose) { Utils::dump(2U, "NXDN, RTCH::decode(), Decoded RTCH Data", rtch, NXDN_RTCH_LC_LENGTH_BYTES); } @@ -136,7 +136,7 @@ void RTCH::encode(uint8_t* data, uint32_t length, uint32_t offset) WRITE_BIT(data, offset, b); } - if (m_verbose) { + if (s_verbose) { Utils::dump(2U, "NXDN, RTCH::encode(), Encoded RTCH Data", data, length); } } diff --git a/src/common/nxdn/lc/RTCH.h b/src/common/nxdn/lc/RTCH.h index abfd87c1..84de99cd 100644 --- a/src/common/nxdn/lc/RTCH.h +++ b/src/common/nxdn/lc/RTCH.h @@ -82,7 +82,7 @@ namespace nxdn * @brief Sets the flag indicating verbose log output. * @param verbose Flag indicating verbose log output. */ - static void setVerbose(bool verbose) { m_verbose = verbose; } + static void setVerbose(bool verbose) { s_verbose = verbose; } public: /** @name Common Data */ @@ -177,7 +177,7 @@ namespace nxdn DECLARE_PROPERTY(uint8_t, causeRsp, CauseResponse); private: - static bool m_verbose; + static bool s_verbose; // Encryption data uint8_t* m_mi; diff --git a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_DCALL_HDR.cpp b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_DCALL_HDR.cpp index 21929537..64075afc 100644 --- a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_DCALL_HDR.cpp +++ b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_DCALL_HDR.cpp @@ -69,8 +69,8 @@ void MESSAGE_TYPE_DCALL_HDR::encode(uint8_t* data, uint32_t length, uint32_t off rcch[6U] = (m_dstId >> 0U) & 0xFFU; // ... rcch[7U] = m_causeRsp; // Cause (VD) - rcch[9U] = (m_siteData.locId() >> 8) & 0xFFU; // Location ID - rcch[10U] = (m_siteData.locId() >> 0) & 0xFFU; // ... + rcch[9U] = (s_siteData.locId() >> 8) & 0xFFU; // Location ID + rcch[10U] = (s_siteData.locId() >> 0) & 0xFFU; // ... RCCH::encode(data, rcch, length, offset); } diff --git a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_DST_ID_INFO.cpp b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_DST_ID_INFO.cpp index f1d9ff54..6d48beee 100644 --- a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_DST_ID_INFO.cpp +++ b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_DST_ID_INFO.cpp @@ -50,9 +50,9 @@ void MESSAGE_TYPE_DST_ID_INFO::encode(uint8_t* data, uint32_t length, uint32_t o ::memset(rcch, 0x00U, NXDN_RCCH_LC_LENGTH_BYTES + 4U); rcch[1U] = 0xC0U + CALLSIGN_LENGTH_BYTES; // Station ID Option - Start / End / Character Count - rcch[2U] = (m_siteCallsign[0]); // Character 0 + rcch[2U] = (s_siteCallsign[0]); // Character 0 for (uint8_t i = 1; i < CALLSIGN_LENGTH_BYTES; i++) { - rcch[i + 2U] = m_siteCallsign[i]; // Character 1 - 7 + rcch[i + 2U] = s_siteCallsign[i]; // Character 1 - 7 } RCCH::encode(data, rcch, length, offset); diff --git a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_GRP_REG.cpp b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_GRP_REG.cpp index 62849107..46e390ad 100644 --- a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_GRP_REG.cpp +++ b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_GRP_REG.cpp @@ -58,8 +58,8 @@ void MESSAGE_TYPE_GRP_REG::encode(uint8_t* data, uint32_t length, uint32_t offse rcch[4U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address rcch[5U] = (m_dstId >> 0U) & 0xFFU; // ... rcch[6U] = m_causeRsp; // Cause (MM) - rcch[8U] = (m_siteData.locId() >> 8) & 0xFFU; // Location ID - rcch[9U] = (m_siteData.locId() >> 0) & 0xFFU; // ... + rcch[8U] = (s_siteData.locId() >> 8) & 0xFFU; // Location ID + rcch[9U] = (s_siteData.locId() >> 0) & 0xFFU; // ... RCCH::encode(data, rcch, length, offset); } diff --git a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG.cpp b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG.cpp index 3b3002f9..f5c5f23d 100644 --- a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG.cpp +++ b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG.cpp @@ -60,9 +60,9 @@ void MESSAGE_TYPE_REG::encode(uint8_t* data, uint32_t length, uint32_t offset) ::memset(rcch, 0x00U, NXDN_RCCH_LC_LENGTH_BYTES + 4U); rcch[1U] = (m_regOption << 3) + // Registration Option - ((m_siteData.locId() >> 22U) & 0x03U); // Location ID + ((s_siteData.locId() >> 22U) & 0x03U); // Location ID - uint16_t systemCode = (m_siteData.locId() >> 12U) << 7U; + uint16_t systemCode = (s_siteData.locId() >> 12U) << 7U; rcch[2U] = (systemCode >> 8U) & 0x03U; // ... rcch[3U] = systemCode & 0xFFU; // ... diff --git a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG_C.cpp b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG_C.cpp index d90eeffc..8da27c81 100644 --- a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG_C.cpp +++ b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG_C.cpp @@ -53,8 +53,8 @@ void MESSAGE_TYPE_REG_C::encode(uint8_t* data, uint32_t length, uint32_t offset) uint8_t rcch[NXDN_RCCH_LC_LENGTH_BYTES + 4U]; ::memset(rcch, 0x00U, NXDN_RCCH_LC_LENGTH_BYTES + 4U); - rcch[2U] = (m_siteData.locId() >> 8) & 0xFFU; // Location ID - rcch[3U] = (m_siteData.locId() >> 0) & 0xFFU; // ... + rcch[2U] = (s_siteData.locId() >> 8) & 0xFFU; // Location ID + rcch[3U] = (s_siteData.locId() >> 0) & 0xFFU; // ... rcch[4U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address rcch[5U] = (m_dstId >> 0U) & 0xFFU; // ... rcch[6U] = m_causeRsp; // Cause (MM) diff --git a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG_COMM.cpp b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG_COMM.cpp index e116b7b5..f611e552 100644 --- a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG_COMM.cpp +++ b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_REG_COMM.cpp @@ -49,8 +49,8 @@ void MESSAGE_TYPE_REG_COMM::encode(uint8_t* data, uint32_t length, uint32_t offs uint8_t rcch[NXDN_RCCH_LC_LENGTH_BYTES + 4U]; ::memset(rcch, 0x00U, NXDN_RCCH_LC_LENGTH_BYTES + 4U); - rcch[2U] = (m_siteData.locId() >> 8) & 0xFFU; // Location ID - rcch[3U] = (m_siteData.locId() >> 0) & 0xFFU; // ... + rcch[2U] = (s_siteData.locId() >> 8) & 0xFFU; // Location ID + rcch[3U] = (s_siteData.locId() >> 0) & 0xFFU; // ... rcch[4U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address rcch[5U] = (m_dstId >> 0U) & 0xFFU; // ... diff --git a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_SITE_INFO.cpp b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_SITE_INFO.cpp index 82bc54f1..ea9ddfbb 100644 --- a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_SITE_INFO.cpp +++ b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_SITE_INFO.cpp @@ -51,16 +51,16 @@ void MESSAGE_TYPE_SITE_INFO::encode(uint8_t* data, uint32_t length, uint32_t off { assert(data != nullptr); - uint8_t siteInfo2 = m_siteData.siteInfo2(); + uint8_t siteInfo2 = s_siteData.siteInfo2(); if ((siteInfo2 & SiteInformation2::IP_NETWORK) == SiteInformation2::IP_NETWORK) siteInfo2 &= ~SiteInformation2::IP_NETWORK; // clear the IP_NETWORK bit -- that will be provided by netActive() uint8_t rcch[NXDN_RCCH_LC_LENGTH_BYTES + 4U]; ::memset(rcch, 0x00U, NXDN_RCCH_LC_LENGTH_BYTES + 4U); - rcch[1U] = (m_siteData.locId() >> 16) & 0xFFU; // Location ID - rcch[2U] = (m_siteData.locId() >> 8) & 0xFFU; // ... - rcch[3U] = (m_siteData.locId() >> 0) & 0xFFU; // ... + rcch[1U] = (s_siteData.locId() >> 16) & 0xFFU; // Location ID + rcch[2U] = (s_siteData.locId() >> 8) & 0xFFU; // ... + rcch[3U] = (s_siteData.locId() >> 0) & 0xFFU; // ... rcch[4U] = ((m_bcchCnt & 0x03U) << 6) + // Channel Structure - Number of BCCH ((m_rcchGroupingCnt & 0x07U) << 3) + // ... - Number of Grouping (((m_ccchPagingCnt >> 1) & 0x07U) << 0); // ... - Number of Paging Frames @@ -68,8 +68,8 @@ void MESSAGE_TYPE_SITE_INFO::encode(uint8_t* data, uint32_t length, uint32_t off ((m_ccchMultiCnt & 0x07U) << 4) + // ... - Number of Multipurpose Frames ((m_rcchIterateCnt & 0x0FU) << 0); // ... - Number of Iteration - rcch[6U] = m_siteData.siteInfo1(); // Site Information 1 - rcch[7U] = (m_siteData.netActive() ? SiteInformation2::IP_NETWORK : 0x00U) + // Site Information 2 + rcch[6U] = s_siteData.siteInfo1(); // Site Information 1 + rcch[7U] = (s_siteData.netActive() ? SiteInformation2::IP_NETWORK : 0x00U) + // Site Information 2 siteInfo2; // bryanb: this is currently fixed -- maybe dynamic in the future @@ -84,7 +84,7 @@ void MESSAGE_TYPE_SITE_INFO::encode(uint8_t* data, uint32_t length, uint32_t off rcch[14U] = 1U; // Version - uint16_t channelNo = m_siteData.channelNo() & 0x3FFU; + uint16_t channelNo = s_siteData.channelNo() & 0x3FFU; rcch[15U] = (channelNo >> 6) & 0x0FU; // 1st Control Channel rcch[16U] = (channelNo & 0x3FU) << 2; // ... diff --git a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_SRV_INFO.cpp b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_SRV_INFO.cpp index f5ab9db0..931cb84d 100644 --- a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_SRV_INFO.cpp +++ b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_SRV_INFO.cpp @@ -46,24 +46,24 @@ void MESSAGE_TYPE_SRV_INFO::encode(uint8_t* data, uint32_t length, uint32_t offs { assert(data != nullptr); - uint8_t siteInfo2 = m_siteData.siteInfo2(); + uint8_t siteInfo2 = s_siteData.siteInfo2(); if ((siteInfo2 & SiteInformation2::IP_NETWORK) == SiteInformation2::IP_NETWORK) siteInfo2 &= ~SiteInformation2::IP_NETWORK; // clear the IP_NETWORK bit -- that will be provided by netActive() uint8_t rcch[NXDN_RCCH_LC_LENGTH_BYTES + 4U]; ::memset(rcch, 0x00U, NXDN_RCCH_LC_LENGTH_BYTES + 4U); - rcch[1U] = (m_siteData.locId() >> 16) & 0xFFU; // Location ID - rcch[2U] = (m_siteData.locId() >> 8) & 0xFFU; // ... - rcch[3U] = (m_siteData.locId() >> 0) & 0xFFU; // ... - rcch[4U] = m_siteData.siteInfo1(); // Site Information 1 - rcch[5U] = (m_siteData.netActive() ? SiteInformation2::IP_NETWORK : 0x00U) + // Site Information 2 + rcch[1U] = (s_siteData.locId() >> 16) & 0xFFU; // Location ID + rcch[2U] = (s_siteData.locId() >> 8) & 0xFFU; // ... + rcch[3U] = (s_siteData.locId() >> 0) & 0xFFU; // ... + rcch[4U] = s_siteData.siteInfo1(); // Site Information 1 + rcch[5U] = (s_siteData.netActive() ? SiteInformation2::IP_NETWORK : 0x00U) + // Site Information 2 siteInfo2; // bryanb: this is currently fixed -- maybe dynamic in the future rcch[8U] = 0U; // Restriction Information - No access restriction / No cycle restriction rcch[9U] = 0U; // ... - No group restriction / No Location Registration Restriction - rcch[10U] = (!m_siteData.netActive() ? 0x01U : 0x00U); // ... - No group ratio restriction / No delay time extension / ISO + rcch[10U] = (!s_siteData.netActive() ? 0x01U : 0x00U); // ... - No group ratio restriction / No delay time extension / ISO RCCH::encode(data, rcch, length, offset); } diff --git a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_VCALL_ASSGN.cpp b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_VCALL_ASSGN.cpp index b11c352d..005eda64 100644 --- a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_VCALL_ASSGN.cpp +++ b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_VCALL_ASSGN.cpp @@ -63,8 +63,8 @@ void MESSAGE_TYPE_VCALL_ASSGN::encode(uint8_t* data, uint32_t length, uint32_t o rcch[7U] = (m_grpVchNo >> 10) & 0x03U; // Channel rcch[8U] = (m_grpVchNo & 0xFFU); // ... - rcch[10U] = (m_siteData.locId() >> 8) & 0xFFU; // Location ID - rcch[11U] = (m_siteData.locId() >> 0) & 0xFFU; // ... + rcch[10U] = (s_siteData.locId() >> 8) & 0xFFU; // Location ID + rcch[11U] = (s_siteData.locId() >> 0) & 0xFFU; // ... RCCH::encode(data, rcch, length, offset); } diff --git a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_VCALL_CONN.cpp b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_VCALL_CONN.cpp index 7ec7b7a6..e6200af4 100644 --- a/src/common/nxdn/lc/rcch/MESSAGE_TYPE_VCALL_CONN.cpp +++ b/src/common/nxdn/lc/rcch/MESSAGE_TYPE_VCALL_CONN.cpp @@ -69,8 +69,8 @@ void MESSAGE_TYPE_VCALL_CONN::encode(uint8_t* data, uint32_t length, uint32_t of rcch[6U] = (m_dstId >> 0U) & 0xFFU; // ... rcch[7U] = m_causeRsp; // Cause (VD) - rcch[9U] = (m_siteData.locId() >> 8) & 0xFFU; // Location ID - rcch[10U] = (m_siteData.locId() >> 0) & 0xFFU; // ... + rcch[9U] = (s_siteData.locId() >> 8) & 0xFFU; // Location ID + rcch[10U] = (s_siteData.locId() >> 0) & 0xFFU; // ... RCCH::encode(data, rcch, length, offset); } diff --git a/src/common/p25/acl/AccessControl.cpp b/src/common/p25/acl/AccessControl.cpp index 2c0fd791..aa29cde0 100644 --- a/src/common/p25/acl/AccessControl.cpp +++ b/src/common/p25/acl/AccessControl.cpp @@ -18,15 +18,15 @@ using namespace p25::acl; // Static Class Members // --------------------------------------------------------------------------- -RadioIdLookup* AccessControl::m_ridLookup; -TalkgroupRulesLookup* AccessControl::m_tidLookup; +RadioIdLookup* AccessControl::s_ridLookup; +TalkgroupRulesLookup* AccessControl::s_tidLookup; /* Initializes the P25 access control. */ void AccessControl::init(RadioIdLookup* ridLookup, TalkgroupRulesLookup* tidLookup) { - m_ridLookup = ridLookup; - m_tidLookup = tidLookup; + s_ridLookup = ridLookup; + s_tidLookup = tidLookup; } /* Helper to validate a source radio ID. */ @@ -34,8 +34,8 @@ void AccessControl::init(RadioIdLookup* ridLookup, TalkgroupRulesLookup* tidLook bool AccessControl::validateSrcId(uint32_t id) { // check if RID ACLs are enabled - if (!m_ridLookup->getACL()) { - RadioId rid = m_ridLookup->find(id); + if (!s_ridLookup->getACL()) { + RadioId rid = s_ridLookup->find(id); if (!rid.radioDefault() && !rid.radioEnabled()) { return false; } @@ -44,7 +44,7 @@ bool AccessControl::validateSrcId(uint32_t id) } // lookup RID and perform test for validity - RadioId rid = m_ridLookup->find(id); + RadioId rid = s_ridLookup->find(id); if (!rid.radioEnabled()) return false; @@ -64,12 +64,12 @@ bool AccessControl::validateTGId(uint32_t id, bool allowZero) return true; // check if TID ACLs are enabled - if (!m_tidLookup->getACL()) { + if (!s_tidLookup->getACL()) { return true; } // lookup TID and perform test for validity - TalkgroupRuleGroupVoice tid = m_tidLookup->find(id); + TalkgroupRuleGroupVoice tid = s_tidLookup->find(id); if (tid.isInvalid()) return false; @@ -88,12 +88,12 @@ bool AccessControl::tgidNonPreferred(uint32_t id) return false; // check if TID ACLs are enabled - if (!m_tidLookup->getACL()) { + if (!s_tidLookup->getACL()) { return false; } // lookup TID and perform test for validity - TalkgroupRuleGroupVoice tid = m_tidLookup->find(id); + TalkgroupRuleGroupVoice tid = s_tidLookup->find(id); if (tid.config().nonPreferred()) return true; diff --git a/src/common/p25/acl/AccessControl.h b/src/common/p25/acl/AccessControl.h index 7f1baf76..de5e82b6 100644 --- a/src/common/p25/acl/AccessControl.h +++ b/src/common/p25/acl/AccessControl.h @@ -67,8 +67,8 @@ namespace p25 static bool tgidNonPreferred(uint32_t id); private: - static RadioIdLookup* m_ridLookup; - static TalkgroupRulesLookup* m_tidLookup; + static RadioIdLookup* s_ridLookup; + static TalkgroupRulesLookup* s_tidLookup; }; } // namespace acl } // namespace p25 diff --git a/src/common/p25/data/DataHeader.cpp b/src/common/p25/data/DataHeader.cpp index 211953da..ecdf6c79 100644 --- a/src/common/p25/data/DataHeader.cpp +++ b/src/common/p25/data/DataHeader.cpp @@ -27,9 +27,9 @@ using namespace p25::data; // --------------------------------------------------------------------------- #if FORCE_TSBK_CRC_WARN -bool DataHeader::m_warnCRC = true; +bool DataHeader::s_warnCRC = true; #else -bool DataHeader::m_warnCRC = false; +bool DataHeader::s_warnCRC = false; #endif // --------------------------------------------------------------------------- @@ -97,7 +97,7 @@ bool DataHeader::decode(const uint8_t* data, bool noTrellis) if (valid) { valid = edac::CRC::checkCCITT162(m_data, P25_PDU_HEADER_LENGTH_BYTES); if (!valid) { - if (m_warnCRC) { + if (s_warnCRC) { // if we're already warning instead of erroring CRC, don't announce invalid CRC in the // case where no CRC is defined if ((m_data[P25_PDU_HEADER_LENGTH_BYTES - 2U] != 0x00U) && (m_data[P25_PDU_HEADER_LENGTH_BYTES - 1U] != 0x00U)) { @@ -294,7 +294,7 @@ bool DataHeader::decodeExtAddr(const uint8_t* data, bool noTrellis) if (valid) { valid = edac::CRC::checkCCITT162(m_extAddrData, P25_PDU_HEADER_LENGTH_BYTES); if (!valid) { - if (m_warnCRC) { + if (s_warnCRC) { // if we're already warning instead of erroring CRC, don't announce invalid CRC in the // case where no CRC is defined if ((m_extAddrData[P25_PDU_HEADER_LENGTH_BYTES - 2U] != 0x00U) && (m_extAddrData[P25_PDU_HEADER_LENGTH_BYTES - 1U] != 0x00U)) { diff --git a/src/common/p25/data/DataHeader.h b/src/common/p25/data/DataHeader.h index d59c8c5c..26433a45 100644 --- a/src/common/p25/data/DataHeader.h +++ b/src/common/p25/data/DataHeader.h @@ -115,7 +115,7 @@ namespace p25 * @brief Sets the flag indicating CRC-errors should be warnings and not errors. * @param warnCRC Flag indicating CRC-errors should be treated as warnings. */ - static void setWarnCRC(bool warnCRC) { m_warnCRC = warnCRC; } + static void setWarnCRC(bool warnCRC) { s_warnCRC = warnCRC; } /** * @brief Helper to determine the pad length for a given packet length. @@ -232,7 +232,7 @@ namespace p25 uint8_t* m_data; uint8_t* m_extAddrData; - static bool m_warnCRC; + static bool s_warnCRC; }; } // namespace data } // namespace p25 diff --git a/src/common/p25/lc/AMBT.cpp b/src/common/p25/lc/AMBT.cpp index 54cd1573..96f12220 100644 --- a/src/common/p25/lc/AMBT.cpp +++ b/src/common/p25/lc/AMBT.cpp @@ -113,7 +113,7 @@ bool AMBT::decode(const data::DataHeader& dataHeader, const data::DataBlock* blo dataOffset += P25_PDU_UNCONFIRMED_LENGTH_BYTES; } - if (m_verbose) { + if (s_verbose) { LogDebugEx(LOG_P25, "AMBT::decode()", "mfId = $%02X, lco = $%02X, ambt8 = $%02X, ambt9 = $%02X", m_mfId, m_lco, dataHeader.getAMBTField8(), dataHeader.getAMBTField9()); Utils::dump(2U, "P25, AMBT::decode(), pduUserData", pduUserData, P25_PDU_UNCONFIRMED_LENGTH_BYTES * dataHeader.getBlocksToFollow()); } diff --git a/src/common/p25/lc/LC.cpp b/src/common/p25/lc/LC.cpp index a0003e05..df443fb7 100644 --- a/src/common/p25/lc/LC.cpp +++ b/src/common/p25/lc/LC.cpp @@ -29,7 +29,7 @@ using namespace p25::lc; // Static Class Members // --------------------------------------------------------------------------- -SiteData LC::m_siteData = SiteData(); +SiteData LC::s_siteData = SiteData(); // --------------------------------------------------------------------------- // Public Class Members @@ -673,10 +673,10 @@ void LC::encodeLC(uint8_t* rs) break; case LCO::GROUP_UPDT: rs[0U] |= 0x40U; // Implicit Operation - rsValue = m_siteData.channelId(); // Group A - Channel ID + rsValue = s_siteData.channelId(); // Group A - Channel ID rsValue = (rsValue << 12) + m_grpVchNo; // Group A - Channel Number rsValue = (rsValue << 16) + m_dstId; // Group A - Talkgroup Address - rsValue = (rsValue << 4) + m_siteData.channelId(); // Group B - Channel ID + rsValue = (rsValue << 4) + s_siteData.channelId(); // Group B - Channel ID rsValue = (rsValue << 12) + m_grpVchNoB; // Group B - Channel Number rsValue = (rsValue << 16) + m_dstIdB; // Group B - Talkgroup Address break; @@ -716,13 +716,13 @@ void LC::encodeLC(uint8_t* rs) break; case LCO::RFSS_STS_BCAST: rs[0U] |= 0x40U; // Implicit Operation - rsValue = m_siteData.lra(); // Location Registration Area - rsValue = (rsValue << 12) + m_siteData.sysId(); // System ID - rsValue = (rsValue << 8) + m_siteData.rfssId(); // RF Sub-System ID - rsValue = (rsValue << 8) + m_siteData.siteId(); // Site ID - rsValue = (rsValue << 4) + m_siteData.channelId(); // Channel ID - rsValue = (rsValue << 12) + m_siteData.channelNo(); // Channel Number - rsValue = (rsValue << 8) + m_siteData.serviceClass(); // System Service Class + rsValue = s_siteData.lra(); // Location Registration Area + rsValue = (rsValue << 12) + s_siteData.sysId(); // System ID + rsValue = (rsValue << 8) + s_siteData.rfssId(); // RF Sub-System ID + rsValue = (rsValue << 8) + s_siteData.siteId(); // Site ID + rsValue = (rsValue << 4) + s_siteData.channelId(); // Channel ID + rsValue = (rsValue << 12) + s_siteData.channelNo(); // Channel Number + rsValue = (rsValue << 8) + s_siteData.serviceClass(); // System Service Class break; default: LogError(LOG_P25, "LC::encodeLC(), unknown LC value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); @@ -881,7 +881,7 @@ void LC::copy(const LC& data) m_gotUserAlias = false; } - m_siteData = data.m_siteData; + s_siteData = data.s_siteData; } /* Decode LDU hamming FEC. */ diff --git a/src/common/p25/lc/LC.h b/src/common/p25/lc/LC.h index 1af45054..37960a84 100644 --- a/src/common/p25/lc/LC.h +++ b/src/common/p25/lc/LC.h @@ -158,12 +158,12 @@ namespace p25 * @brief Gets the local site data. * @returns SiteData Currently set site data for the LC class. */ - static SiteData getSiteData() { return m_siteData; } + static SiteData getSiteData() { return s_siteData; } /** * @brief Sets the local site data. * @param siteData Site data to set for the LC class. */ - static void setSiteData(SiteData siteData) { m_siteData = siteData; } + static void setSiteData(SiteData siteData) { s_siteData = siteData; } /** @} */ public: @@ -274,7 +274,7 @@ namespace p25 bool m_gotUserAlias; // Local Site data - static SiteData m_siteData; + static SiteData s_siteData; /** * @brief Internal helper to copy the class. diff --git a/src/common/p25/lc/TDULC.cpp b/src/common/p25/lc/TDULC.cpp index 94025eb0..87f18606 100644 --- a/src/common/p25/lc/TDULC.cpp +++ b/src/common/p25/lc/TDULC.cpp @@ -26,9 +26,9 @@ using namespace p25::lc; // Static Class Members // --------------------------------------------------------------------------- -bool TDULC::m_verbose = false; +bool TDULC::s_verbose = false; -SiteData TDULC::m_siteData = SiteData(); +SiteData TDULC::s_siteData = SiteData(); // --------------------------------------------------------------------------- // Public Class Members @@ -85,7 +85,7 @@ TDULC::TDULC() : m_callTimer(0U), m_raw(nullptr) { - m_grpVchNo = m_siteData.channelNo(); + m_grpVchNo = s_siteData.channelNo(); } /* Finalizes a instance of TDULC class. */ @@ -186,7 +186,7 @@ bool TDULC::decode(const uint8_t* data, uint8_t* payload, bool rawTDULC) return false; } - if (m_verbose) { + if (s_verbose) { Utils::dump(2U, "P25, TDULC::decode(), TDULC Value", rs, P25_TDULC_LENGTH_BYTES); } @@ -219,7 +219,7 @@ void TDULC::encode(uint8_t* data, const uint8_t* payload, bool rawTDULC) if (m_implicit) rs[0U] |= 0x40U; // Implicit Operation - if (m_verbose) { + if (s_verbose) { Utils::dump(2U, "P25, TDULC::encode(), TDULC Value", rs, P25_TDULC_LENGTH_BYTES); } @@ -249,7 +249,7 @@ void TDULC::encode(uint8_t* data, const uint8_t* payload, bool rawTDULC) void TDULC::copy(const TDULC& data) { - m_verbose = data.m_verbose; + s_verbose = data.s_verbose; m_protect = data.m_protect; m_lco = data.m_lco; m_mfId = data.m_mfId; @@ -272,6 +272,6 @@ void TDULC::copy(const TDULC& data) m_callTimer = data.m_callTimer; - m_siteData = data.m_siteData; + s_siteData = data.s_siteData; m_siteIdenEntry = data.m_siteIdenEntry; } diff --git a/src/common/p25/lc/TDULC.h b/src/common/p25/lc/TDULC.h index 9e1ccf4d..217883dc 100644 --- a/src/common/p25/lc/TDULC.h +++ b/src/common/p25/lc/TDULC.h @@ -90,19 +90,19 @@ namespace p25 * @brief Sets the flag indicating verbose log output. * @param verbose Flag indicating verbose log output. */ - static void setVerbose(bool verbose) { m_verbose = verbose; } + static void setVerbose(bool verbose) { s_verbose = verbose; } /** @name Local Site data */ /** * @brief Gets the local site data. * @returns SiteData Currently set site data for the TDULC class. */ - static SiteData getSiteData() { return m_siteData; } + static SiteData getSiteData() { return s_siteData; } /** * @brief Sets the local site data. * @param siteData Site data to set for the TDULC class. */ - static void setSiteData(SiteData siteData) { m_siteData = siteData; } + static void setSiteData(SiteData siteData) { s_siteData = siteData; } /** @} */ public: @@ -183,10 +183,10 @@ namespace p25 bool m_implicit; uint32_t m_callTimer; - static bool m_verbose; + static bool s_verbose; // Local Site data - static SiteData m_siteData; + static SiteData s_siteData; /** * @brief Internal helper to convert payload bytes to a 64-bit long value. diff --git a/src/common/p25/lc/TSBK.cpp b/src/common/p25/lc/TSBK.cpp index da39bc4f..52e49cfa 100644 --- a/src/common/p25/lc/TSBK.cpp +++ b/src/common/p25/lc/TSBK.cpp @@ -25,15 +25,15 @@ using namespace p25::lc; // Static Class Members // --------------------------------------------------------------------------- -bool TSBK::m_verbose = false; +bool TSBK::s_verbose = false; #if FORCE_TSBK_CRC_WARN -bool TSBK::m_warnCRC = true; +bool TSBK::s_warnCRC = true; #else -bool TSBK::m_warnCRC = false; +bool TSBK::s_warnCRC = false; #endif -uint8_t* TSBK::m_siteCallsign = nullptr; -SiteData TSBK::m_siteData = SiteData(); +uint8_t* TSBK::s_siteCallsign = nullptr; +SiteData TSBK::s_siteData = SiteData(); // --------------------------------------------------------------------------- // Public Class Members @@ -90,9 +90,9 @@ TSBK::TSBK() : m_trellis(), m_raw(nullptr) { - if (m_siteCallsign == nullptr) { - m_siteCallsign = new uint8_t[MOT_CALLSIGN_LENGTH_BYTES]; - ::memset(m_siteCallsign, 0x00U, MOT_CALLSIGN_LENGTH_BYTES); + if (s_siteCallsign == nullptr) { + s_siteCallsign = new uint8_t[MOT_CALLSIGN_LENGTH_BYTES]; + ::memset(s_siteCallsign, 0x00U, MOT_CALLSIGN_LENGTH_BYTES); } #if FORCE_TSBK_CRC_WARN @@ -126,19 +126,19 @@ uint8_t* TSBK::getDecodedRaw() const void TSBK::setCallsign(std::string callsign) { - if (m_siteCallsign == nullptr) { - m_siteCallsign = new uint8_t[MOT_CALLSIGN_LENGTH_BYTES]; - ::memset(m_siteCallsign, 0x00U, MOT_CALLSIGN_LENGTH_BYTES); + if (s_siteCallsign == nullptr) { + s_siteCallsign = new uint8_t[MOT_CALLSIGN_LENGTH_BYTES]; + ::memset(s_siteCallsign, 0x00U, MOT_CALLSIGN_LENGTH_BYTES); } uint32_t idLength = callsign.length(); if (idLength > 0) { - ::memset(m_siteCallsign, 0x20U, MOT_CALLSIGN_LENGTH_BYTES); + ::memset(s_siteCallsign, 0x20U, MOT_CALLSIGN_LENGTH_BYTES); if (idLength > MOT_CALLSIGN_LENGTH_BYTES) idLength = MOT_CALLSIGN_LENGTH_BYTES; for (uint32_t i = 0; i < idLength; i++) - m_siteCallsign[i] = callsign[i]; + s_siteCallsign[i] = callsign[i]; } } @@ -201,7 +201,7 @@ bool TSBK::decode(const uint8_t* data, uint8_t* payload, bool rawTSBK) bool ret = edac::CRC::checkCCITT162(tsbk, P25_TSBK_LENGTH_BYTES); if (!ret) { - if (m_warnCRC) { + if (s_warnCRC) { // if we're already warning instead of erroring CRC, don't announce invalid CRC in the // case where no CRC is defined if ((tsbk[P25_TSBK_LENGTH_BYTES - 2U] != 0x00U) && (tsbk[P25_TSBK_LENGTH_BYTES - 1U] != 0x00U)) { @@ -228,7 +228,7 @@ bool TSBK::decode(const uint8_t* data, uint8_t* payload, bool rawTSBK) if (ret) { ret = edac::CRC::checkCCITT162(tsbk, P25_TSBK_LENGTH_BYTES); if (!ret) { - if (m_warnCRC) { + if (s_warnCRC) { LogWarning(LOG_P25, "TSBK::decode(), failed CRC CCITT-162 check"); ret = true; // ignore CRC error } @@ -247,7 +247,7 @@ bool TSBK::decode(const uint8_t* data, uint8_t* payload, bool rawTSBK) } } - if (m_verbose) { + if (s_verbose) { Utils::dump(2U, "P25, TSBK::decode(), TSBK Value", tsbk, P25_TSBK_LENGTH_BYTES); } @@ -282,7 +282,7 @@ void TSBK::encode(uint8_t* data, const uint8_t* payload, bool rawTSBK, bool noTr // compute CRC-CCITT 16 edac::CRC::addCCITT162(tsbk, P25_TSBK_LENGTH_BYTES); - if (m_verbose) { + if (s_verbose) { Utils::dump(2U, "P25, TSBK::encode(), TSBK Value", tsbk, P25_TSBK_LENGTH_BYTES); } diff --git a/src/common/p25/lc/TSBK.h b/src/common/p25/lc/TSBK.h index 8b7453a3..b869ded7 100644 --- a/src/common/p25/lc/TSBK.h +++ b/src/common/p25/lc/TSBK.h @@ -108,17 +108,17 @@ namespace p25 * @brief Gets the flag indicating verbose log output. * @returns bool True, if the TSBK is verbose logging, otherwise false. */ - static bool getVerbose() { return m_verbose; } + static bool getVerbose() { return s_verbose; } /** * @brief Sets the flag indicating verbose log output. * @param verbose Flag indicating verbose log output. */ - static void setVerbose(bool verbose) { m_verbose = verbose; } + static void setVerbose(bool verbose) { s_verbose = verbose; } /** * @brief Sets the flag indicating CRC-errors should be warnings and not errors. * @param warnCRC Flag indicating CRC-errors should be treated as warnings. */ - static void setWarnCRC(bool warnCRC) { m_warnCRC = warnCRC; } + static void setWarnCRC(bool warnCRC) { s_warnCRC = warnCRC; } /** @name Local Site data */ /** @@ -131,12 +131,12 @@ namespace p25 * @brief Gets the local site data. * @returns SiteData Currently set site data for the TSBK class. */ - static SiteData getSiteData() { return m_siteData; } + static SiteData getSiteData() { return s_siteData; } /** * @brief Sets the local site data. * @param siteData Site data to set for the TSBK class. */ - static void setSiteData(SiteData siteData) { m_siteData = siteData; } + static void setSiteData(SiteData siteData) { s_siteData = siteData; } /** @} */ public: @@ -237,12 +237,12 @@ namespace p25 edac::RS634717 m_rs; edac::Trellis m_trellis; - static bool m_verbose; - static bool m_warnCRC; + static bool s_verbose; + static bool s_warnCRC; // Local Site data - static uint8_t* m_siteCallsign; - static SiteData m_siteData; + static uint8_t* s_siteCallsign; + static SiteData s_siteData; /** * @brief Internal helper to convert payload bytes to a 64-bit long value. diff --git a/src/common/p25/lc/tdulc/LC_ADJ_STS_BCAST.cpp b/src/common/p25/lc/tdulc/LC_ADJ_STS_BCAST.cpp index 0d59cb45..ec851d88 100644 --- a/src/common/p25/lc/tdulc/LC_ADJ_STS_BCAST.cpp +++ b/src/common/p25/lc/tdulc/LC_ADJ_STS_BCAST.cpp @@ -59,10 +59,10 @@ void LC_ADJ_STS_BCAST::encode(uint8_t* data) if ((m_adjRfssId != 0U) && (m_adjSiteId != 0U) && (m_adjChannelNo != 0U)) { if (m_adjSysId == 0U) { - m_adjSysId = m_siteData.sysId(); + m_adjSysId = s_siteData.sysId(); } - rsValue = m_siteData.lra(); // Location Registration Area + rsValue = s_siteData.lra(); // Location Registration Area rsValue = (rsValue << 12) + m_adjSysId; // System ID rsValue = (rsValue << 8) + m_adjRfssId; // RF Sub-System ID rsValue = (rsValue << 8) + m_adjSiteId; // Site ID diff --git a/src/common/p25/lc/tdulc/LC_CONV_FALLBACK.cpp b/src/common/p25/lc/tdulc/LC_CONV_FALLBACK.cpp index c89242b4..9cf02513 100644 --- a/src/common/p25/lc/tdulc/LC_CONV_FALLBACK.cpp +++ b/src/common/p25/lc/tdulc/LC_CONV_FALLBACK.cpp @@ -47,12 +47,12 @@ void LC_CONV_FALLBACK::encode(uint8_t* data) ulong64_t rsValue = 0U; - rsValue = (rsValue << 48) + m_siteData.channelId(); // Channel ID 6 - rsValue = (rsValue << 40) + m_siteData.channelId(); // Channel ID 5 - rsValue = (rsValue << 32) + m_siteData.channelId(); // Channel ID 4 - rsValue = (rsValue << 24) + m_siteData.channelId(); // Channel ID 3 - rsValue = (rsValue << 16) + m_siteData.channelId(); // Channel ID 2 - rsValue = (rsValue << 8) + m_siteData.channelId(); // Channel ID 1 + rsValue = (rsValue << 48) + s_siteData.channelId(); // Channel ID 6 + rsValue = (rsValue << 40) + s_siteData.channelId(); // Channel ID 5 + rsValue = (rsValue << 32) + s_siteData.channelId(); // Channel ID 4 + rsValue = (rsValue << 24) + s_siteData.channelId(); // Channel ID 3 + rsValue = (rsValue << 16) + s_siteData.channelId(); // Channel ID 2 + rsValue = (rsValue << 8) + s_siteData.channelId(); // Channel ID 1 std::unique_ptr rs = TDULC::fromValue(rsValue); TDULC::encode(data, rs.get()); diff --git a/src/common/p25/lc/tdulc/LC_GROUP_UPDT.cpp b/src/common/p25/lc/tdulc/LC_GROUP_UPDT.cpp index aa840662..e0fa5862 100644 --- a/src/common/p25/lc/tdulc/LC_GROUP_UPDT.cpp +++ b/src/common/p25/lc/tdulc/LC_GROUP_UPDT.cpp @@ -49,10 +49,10 @@ void LC_GROUP_UPDT::encode(uint8_t* data) m_implicit = true; - rsValue = m_siteData.channelId(); // Group A - Channel ID + rsValue = s_siteData.channelId(); // Group A - Channel ID rsValue = (rsValue << 12) + m_grpVchNo; // Group A - Channel Number rsValue = (rsValue << 16) + m_dstId; // Group A - Talkgroup Address - rsValue = (rsValue << 4) + m_siteData.channelId(); // Group B - Channel ID + rsValue = (rsValue << 4) + s_siteData.channelId(); // Group B - Channel ID rsValue = (rsValue << 12) + m_grpVchNo; // Group B - Channel Number rsValue = (rsValue << 16) + m_dstId; // Group B - Talkgroup Address diff --git a/src/common/p25/lc/tdulc/LC_NET_STS_BCAST.cpp b/src/common/p25/lc/tdulc/LC_NET_STS_BCAST.cpp index 05214f0b..0c782dca 100644 --- a/src/common/p25/lc/tdulc/LC_NET_STS_BCAST.cpp +++ b/src/common/p25/lc/tdulc/LC_NET_STS_BCAST.cpp @@ -49,11 +49,11 @@ void LC_NET_STS_BCAST::encode(uint8_t* data) m_implicit = true; - rsValue = (rsValue << 20) + m_siteData.netId(); // Network ID - rsValue = (rsValue << 12) + m_siteData.sysId(); // System ID - rsValue = (rsValue << 4) + m_siteData.channelId(); // Channel ID - rsValue = (rsValue << 12) + m_siteData.channelNo(); // Channel Number - rsValue = (rsValue << 8) + m_siteData.serviceClass(); // System Service Class + rsValue = (rsValue << 20) + s_siteData.netId(); // Network ID + rsValue = (rsValue << 12) + s_siteData.sysId(); // System ID + rsValue = (rsValue << 4) + s_siteData.channelId(); // Channel ID + rsValue = (rsValue << 12) + s_siteData.channelNo(); // Channel Number + rsValue = (rsValue << 8) + s_siteData.serviceClass(); // System Service Class std::unique_ptr rs = TDULC::fromValue(rsValue); TDULC::encode(data, rs.get()); diff --git a/src/common/p25/lc/tdulc/LC_RFSS_STS_BCAST.cpp b/src/common/p25/lc/tdulc/LC_RFSS_STS_BCAST.cpp index 449a79af..0600fc4a 100644 --- a/src/common/p25/lc/tdulc/LC_RFSS_STS_BCAST.cpp +++ b/src/common/p25/lc/tdulc/LC_RFSS_STS_BCAST.cpp @@ -49,13 +49,13 @@ void LC_RFSS_STS_BCAST::encode(uint8_t* data) m_implicit = true; - rsValue = m_siteData.lra(); // Location Registration Area - rsValue = (rsValue << 12) + m_siteData.sysId(); // System ID - rsValue = (rsValue << 8) + m_siteData.rfssId(); // RF Sub-System ID - rsValue = (rsValue << 8) + m_siteData.siteId(); // Site ID - rsValue = (rsValue << 4) + m_siteData.channelId(); // Channel ID - rsValue = (rsValue << 12) + m_siteData.channelNo(); // Channel Number - rsValue = (rsValue << 8) + m_siteData.serviceClass(); // System Service Class + rsValue = s_siteData.lra(); // Location Registration Area + rsValue = (rsValue << 12) + s_siteData.sysId(); // System ID + rsValue = (rsValue << 8) + s_siteData.rfssId(); // RF Sub-System ID + rsValue = (rsValue << 8) + s_siteData.siteId(); // Site ID + rsValue = (rsValue << 4) + s_siteData.channelId(); // Channel ID + rsValue = (rsValue << 12) + s_siteData.channelNo(); // Channel Number + rsValue = (rsValue << 8) + s_siteData.serviceClass(); // System Service Class std::unique_ptr rs = TDULC::fromValue(rsValue); TDULC::encode(data, rs.get()); diff --git a/src/common/p25/lc/tdulc/LC_SYS_SRV_BCAST.cpp b/src/common/p25/lc/tdulc/LC_SYS_SRV_BCAST.cpp index 15ea3c0c..0ede0a2a 100644 --- a/src/common/p25/lc/tdulc/LC_SYS_SRV_BCAST.cpp +++ b/src/common/p25/lc/tdulc/LC_SYS_SRV_BCAST.cpp @@ -45,7 +45,7 @@ void LC_SYS_SRV_BCAST::encode(uint8_t* data) { assert(data != nullptr); - const uint32_t services = (m_siteData.netActive()) ? SystemService::NET_ACTIVE : 0U | SYS_SRV_DEFAULT; + const uint32_t services = (s_siteData.netActive()) ? SystemService::NET_ACTIVE : 0U | SYS_SRV_DEFAULT; ulong64_t rsValue = 0U; diff --git a/src/common/p25/lc/tdulc/TDULCFactory.cpp b/src/common/p25/lc/tdulc/TDULCFactory.cpp index e8a05088..4dcb8082 100644 --- a/src/common/p25/lc/tdulc/TDULCFactory.cpp +++ b/src/common/p25/lc/tdulc/TDULCFactory.cpp @@ -24,7 +24,7 @@ using namespace p25::lc::tdulc; // Static Class Members // --------------------------------------------------------------------------- -::edac::RS634717 TDULCFactory::m_rs = ::edac::RS634717(); +::edac::RS634717 TDULCFactory::s_rs = ::edac::RS634717(); // --------------------------------------------------------------------------- // Public Class Members @@ -58,7 +58,7 @@ std::unique_ptr TDULCFactory::createTDULC(const uint8_t* data) // decode RS (24,12,13) FEC try { - bool ret = m_rs.decode241213(rs); + bool ret = s_rs.decode241213(rs); if (!ret) { LogError(LOG_P25, "TDULCFactory::createTDULC(), failed to decode RS (24,12,13) FEC"); return nullptr; diff --git a/src/common/p25/lc/tdulc/TDULCFactory.h b/src/common/p25/lc/tdulc/TDULCFactory.h index 632ecd17..60c33298 100644 --- a/src/common/p25/lc/tdulc/TDULCFactory.h +++ b/src/common/p25/lc/tdulc/TDULCFactory.h @@ -69,7 +69,7 @@ namespace p25 static std::unique_ptr createTDULC(const uint8_t* data); private: - static edac::RS634717 m_rs; + static edac::RS634717 s_rs; /** * @brief Decode a TDULC. diff --git a/src/common/p25/lc/tsbk/IOSP_ACK_RSP.cpp b/src/common/p25/lc/tsbk/IOSP_ACK_RSP.cpp index 1973e6a6..f8c78dfc 100644 --- a/src/common/p25/lc/tsbk/IOSP_ACK_RSP.cpp +++ b/src/common/p25/lc/tsbk/IOSP_ACK_RSP.cpp @@ -63,8 +63,8 @@ void IOSP_ACK_RSP::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue |= (m_aivFlag) ? 0x80U : 0x00U; // Additional Info. Valid Flag tsbkValue |= (m_extendedAddrFlag) ? 0x40U : 0x00U; // Extended Addressing Flag if (m_aivFlag && m_extendedAddrFlag) { - tsbkValue = (tsbkValue << 20) + m_siteData.netId(); // Network ID - tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID + tsbkValue = (tsbkValue << 20) + s_siteData.netId(); // Network ID + tsbkValue = (tsbkValue << 12) + s_siteData.sysId(); // System ID } else { tsbkValue = (tsbkValue << 32) + m_dstId; // Target Radio Address diff --git a/src/common/p25/lc/tsbk/IOSP_GRP_VCH.cpp b/src/common/p25/lc/tsbk/IOSP_GRP_VCH.cpp index 84d9427a..d60be652 100644 --- a/src/common/p25/lc/tsbk/IOSP_GRP_VCH.cpp +++ b/src/common/p25/lc/tsbk/IOSP_GRP_VCH.cpp @@ -71,7 +71,7 @@ void IOSP_GRP_VCH::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = (tsbkValue << 4) + m_grpVchId; // Channel ID } else { - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel ID } tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number tsbkValue = (tsbkValue << 16) + m_dstId; // Talkgroup Address diff --git a/src/common/p25/lc/tsbk/IOSP_UU_VCH.cpp b/src/common/p25/lc/tsbk/IOSP_UU_VCH.cpp index f91e2b6c..ccce24a4 100644 --- a/src/common/p25/lc/tsbk/IOSP_UU_VCH.cpp +++ b/src/common/p25/lc/tsbk/IOSP_UU_VCH.cpp @@ -70,7 +70,7 @@ void IOSP_UU_VCH::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = (tsbkValue << 4) + m_grpVchId; // Channel ID } else { - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel ID } tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number tsbkValue = (tsbkValue << 24) + m_dstId; // Target ID diff --git a/src/common/p25/lc/tsbk/IOSP_U_REG.cpp b/src/common/p25/lc/tsbk/IOSP_U_REG.cpp index 2c25b7ee..f0d12714 100644 --- a/src/common/p25/lc/tsbk/IOSP_U_REG.cpp +++ b/src/common/p25/lc/tsbk/IOSP_U_REG.cpp @@ -59,7 +59,7 @@ void IOSP_U_REG::encode(uint8_t* data, bool rawTSBK, bool noTrellis) ulong64_t tsbkValue = 0U; tsbkValue = (tsbkValue << 2) + (m_response & 0x3U); // Unit Registration Response - tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID + tsbkValue = (tsbkValue << 12) + s_siteData.sysId(); // System ID tsbkValue = (tsbkValue << 24) + m_dstId; // Source ID tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address diff --git a/src/common/p25/lc/tsbk/OSP_ADJ_STS_BCAST.cpp b/src/common/p25/lc/tsbk/OSP_ADJ_STS_BCAST.cpp index f845e9ed..51019e28 100644 --- a/src/common/p25/lc/tsbk/OSP_ADJ_STS_BCAST.cpp +++ b/src/common/p25/lc/tsbk/OSP_ADJ_STS_BCAST.cpp @@ -68,9 +68,9 @@ void OSP_ADJ_STS_BCAST::encode(uint8_t* data, bool rawTSBK, bool noTrellis) ulong64_t tsbkValue = 0U; - tsbkValue = m_siteData.lra(); // Location Registration Area + tsbkValue = s_siteData.lra(); // Location Registration Area tsbkValue = (tsbkValue << 4) + m_adjCFVA; // CFVA - tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID + tsbkValue = (tsbkValue << 12) + s_siteData.sysId(); // System ID tsbkValue = (tsbkValue << 8) + m_adjRfssId; // RF Sub-System ID tsbkValue = (tsbkValue << 8) + m_adjSiteId; // Site ID tsbkValue = (tsbkValue << 4) + m_adjChannelId; // Channel ID diff --git a/src/common/p25/lc/tsbk/OSP_DVM_LC_CALL_TERM.cpp b/src/common/p25/lc/tsbk/OSP_DVM_LC_CALL_TERM.cpp index 4f2c01e6..d7a98348 100644 --- a/src/common/p25/lc/tsbk/OSP_DVM_LC_CALL_TERM.cpp +++ b/src/common/p25/lc/tsbk/OSP_DVM_LC_CALL_TERM.cpp @@ -61,7 +61,7 @@ void OSP_DVM_LC_CALL_TERM::encode(uint8_t* data, bool rawTSBK, bool noTrellis) m_mfId = MFG_DVM_OCS; - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel ID tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number tsbkValue = (tsbkValue << 16) + m_dstId; // Talkgroup Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address diff --git a/src/common/p25/lc/tsbk/OSP_GRP_VCH_GRANT_UPD.cpp b/src/common/p25/lc/tsbk/OSP_GRP_VCH_GRANT_UPD.cpp index cdc0f34f..c3ff75eb 100644 --- a/src/common/p25/lc/tsbk/OSP_GRP_VCH_GRANT_UPD.cpp +++ b/src/common/p25/lc/tsbk/OSP_GRP_VCH_GRANT_UPD.cpp @@ -68,7 +68,7 @@ void OSP_GRP_VCH_GRANT_UPD::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = m_grpVchId; // Channel ID (A) } else { - tsbkValue = m_siteData.channelId(); // Channel ID (Site) + tsbkValue = s_siteData.channelId(); // Channel ID (Site) } tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number (A) tsbkValue = (tsbkValue << 16) + m_dstId; // Talkgroup Address (A) @@ -77,7 +77,7 @@ void OSP_GRP_VCH_GRANT_UPD::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = (tsbkValue << 4) + m_grpVchIdB; // Channel ID (B) } else { - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID (Site) + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel ID (Site) } tsbkValue = (tsbkValue << 12) + m_grpVchNoB; // Channel Number (A) tsbkValue = (tsbkValue << 16) + m_dstIdB; // Talkgroup Address (B) diff --git a/src/common/p25/lc/tsbk/OSP_LOC_REG_RSP.cpp b/src/common/p25/lc/tsbk/OSP_LOC_REG_RSP.cpp index b7723dcf..a4f427d3 100644 --- a/src/common/p25/lc/tsbk/OSP_LOC_REG_RSP.cpp +++ b/src/common/p25/lc/tsbk/OSP_LOC_REG_RSP.cpp @@ -49,8 +49,8 @@ void OSP_LOC_REG_RSP::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = (tsbkValue << 6) + (m_response & 0x3U); // Registration Response tsbkValue = (tsbkValue << 16) + (m_dstId & 0xFFFFU); // Talkgroup Address - tsbkValue = (tsbkValue << 8) + m_siteData.rfssId(); // RF Sub-System ID - tsbkValue = (tsbkValue << 8) + m_siteData.sysId(); // Site ID + tsbkValue = (tsbkValue << 8) + s_siteData.rfssId(); // RF Sub-System ID + tsbkValue = (tsbkValue << 8) + s_siteData.sysId(); // Site ID tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address std::unique_ptr tsbk = TSBK::fromValue(tsbkValue); diff --git a/src/common/p25/lc/tsbk/OSP_MOT_CC_BSI.cpp b/src/common/p25/lc/tsbk/OSP_MOT_CC_BSI.cpp index 585615ee..ff0552b2 100644 --- a/src/common/p25/lc/tsbk/OSP_MOT_CC_BSI.cpp +++ b/src/common/p25/lc/tsbk/OSP_MOT_CC_BSI.cpp @@ -49,12 +49,12 @@ void OSP_MOT_CC_BSI::encode(uint8_t* data, bool rawTSBK, bool noTrellis) m_mfId = MFG_MOT; - tsbkValue = (m_siteCallsign[0] - 43U) & 0x3F; // Character 0 + tsbkValue = (s_siteCallsign[0] - 43U) & 0x3F; // Character 0 for (uint8_t i = 1; i < MOT_CALLSIGN_LENGTH_BYTES; i++) { - tsbkValue = (tsbkValue << 6) + ((m_siteCallsign[i] - 43U) & 0x3F); // Character 1 - 7 + tsbkValue = (tsbkValue << 6) + ((s_siteCallsign[i] - 43U) & 0x3F); // Character 1 - 7 } - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID - tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel Number + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 12) + s_siteData.channelNo(); // Channel Number std::unique_ptr tsbk = TSBK::fromValue(tsbkValue); TSBK::encode(data, tsbk.get(), rawTSBK, noTrellis); diff --git a/src/common/p25/lc/tsbk/OSP_MOT_GRG_VCH_GRANT.cpp b/src/common/p25/lc/tsbk/OSP_MOT_GRG_VCH_GRANT.cpp index ef051a1c..ca4ebbb3 100644 --- a/src/common/p25/lc/tsbk/OSP_MOT_GRG_VCH_GRANT.cpp +++ b/src/common/p25/lc/tsbk/OSP_MOT_GRG_VCH_GRANT.cpp @@ -53,8 +53,8 @@ void OSP_MOT_GRG_VCH_GRANT::encode(uint8_t* data, bool rawTSBK, bool noTrellis) if (m_patchSuperGroupId != 0U) { tsbkValue = 0U; // Priority - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID - tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel Number + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 12) + s_siteData.channelNo(); // Channel Number tsbkValue = (tsbkValue << 16) + m_patchSuperGroupId; // Patch Supergroup Address tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address } diff --git a/src/common/p25/lc/tsbk/OSP_MOT_GRG_VCH_UPD.cpp b/src/common/p25/lc/tsbk/OSP_MOT_GRG_VCH_UPD.cpp index c3c23f1b..bf95dac4 100644 --- a/src/common/p25/lc/tsbk/OSP_MOT_GRG_VCH_UPD.cpp +++ b/src/common/p25/lc/tsbk/OSP_MOT_GRG_VCH_UPD.cpp @@ -51,11 +51,11 @@ void OSP_MOT_GRG_VCH_UPD::encode(uint8_t* data, bool rawTSBK, bool noTrellis) m_mfId = MFG_MOT; - tsbkValue = m_siteData.channelId(); // Channel ID - tsbkValue = (tsbkValue << 4) + m_siteData.channelNo(); // Channel Number + tsbkValue = s_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 4) + s_siteData.channelNo(); // Channel Number tsbkValue = (tsbkValue << 12) + m_patchGroup1Id; // Patch Group 1 - tsbkValue = (tsbkValue << 16) + m_siteData.channelId(); // Channel ID - tsbkValue = (tsbkValue << 4) + m_siteData.channelNo(); // Channel Number + tsbkValue = (tsbkValue << 16) + s_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 4) + s_siteData.channelNo(); // Channel Number tsbkValue = (tsbkValue << 12) + m_patchGroup2Id; // Patch Group 2 std::unique_ptr tsbk = TSBK::fromValue(tsbkValue); diff --git a/src/common/p25/lc/tsbk/OSP_NET_STS_BCAST.cpp b/src/common/p25/lc/tsbk/OSP_NET_STS_BCAST.cpp index a49b441f..b73b0199 100644 --- a/src/common/p25/lc/tsbk/OSP_NET_STS_BCAST.cpp +++ b/src/common/p25/lc/tsbk/OSP_NET_STS_BCAST.cpp @@ -47,12 +47,12 @@ void OSP_NET_STS_BCAST::encode(uint8_t* data, bool rawTSBK, bool noTrellis) ulong64_t tsbkValue = 0U; - tsbkValue = m_siteData.lra(); // Location Registration Area - tsbkValue = (tsbkValue << 20) + m_siteData.netId(); // Network ID - tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID - tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel Number - tsbkValue = (tsbkValue << 8) + m_siteData.serviceClass(); // System Service Class + tsbkValue = s_siteData.lra(); // Location Registration Area + tsbkValue = (tsbkValue << 20) + s_siteData.netId(); // Network ID + tsbkValue = (tsbkValue << 12) + s_siteData.sysId(); // System ID + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 12) + s_siteData.channelNo(); // Channel Number + tsbkValue = (tsbkValue << 8) + s_siteData.serviceClass(); // System Service Class std::unique_ptr tsbk = TSBK::fromValue(tsbkValue); TSBK::encode(data, tsbk.get(), rawTSBK, noTrellis); diff --git a/src/common/p25/lc/tsbk/OSP_RFSS_STS_BCAST.cpp b/src/common/p25/lc/tsbk/OSP_RFSS_STS_BCAST.cpp index db151e89..975024cd 100644 --- a/src/common/p25/lc/tsbk/OSP_RFSS_STS_BCAST.cpp +++ b/src/common/p25/lc/tsbk/OSP_RFSS_STS_BCAST.cpp @@ -48,16 +48,16 @@ void OSP_RFSS_STS_BCAST::encode(uint8_t* data, bool rawTSBK, bool noTrellis) ulong64_t tsbkValue = 0U; - tsbkValue = m_siteData.lra(); // Location Registration Area + tsbkValue = s_siteData.lra(); // Location Registration Area tsbkValue = (tsbkValue << 4) + ((m_roamerReaccess) ? 0x02U : 0x00U) + // Roamer Reaccess Method - ((m_siteData.netActive()) ? 0x01U : 0x00U); // Network Active - tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID - tsbkValue = (tsbkValue << 8) + m_siteData.rfssId(); // RF Sub-System ID - tsbkValue = (tsbkValue << 8) + m_siteData.siteId(); // Site ID - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID - tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel Number - tsbkValue = (tsbkValue << 8) + m_siteData.serviceClass(); // System Service Class + ((s_siteData.netActive()) ? 0x01U : 0x00U); // Network Active + tsbkValue = (tsbkValue << 12) + s_siteData.sysId(); // System ID + tsbkValue = (tsbkValue << 8) + s_siteData.rfssId(); // RF Sub-System ID + tsbkValue = (tsbkValue << 8) + s_siteData.siteId(); // Site ID + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 12) + s_siteData.channelNo(); // Channel Number + tsbkValue = (tsbkValue << 8) + s_siteData.serviceClass(); // System Service Class std::unique_ptr tsbk = TSBK::fromValue(tsbkValue); TSBK::encode(data, tsbk.get(), rawTSBK, noTrellis); diff --git a/src/common/p25/lc/tsbk/OSP_SCCB.cpp b/src/common/p25/lc/tsbk/OSP_SCCB.cpp index b8f9e9ad..c962bc3d 100644 --- a/src/common/p25/lc/tsbk/OSP_SCCB.cpp +++ b/src/common/p25/lc/tsbk/OSP_SCCB.cpp @@ -49,18 +49,18 @@ void OSP_SCCB::encode(uint8_t* data, bool rawTSBK, bool noTrellis) ulong64_t tsbkValue = 0U; - tsbkValue = m_siteData.rfssId(); // RF Sub-System ID - tsbkValue = (tsbkValue << 8) + m_siteData.siteId(); // Site ID + tsbkValue = s_siteData.rfssId(); // RF Sub-System ID + tsbkValue = (tsbkValue << 8) + s_siteData.siteId(); // Site ID tsbkValue = (tsbkValue << 16) + m_sccbChannelId1; // SCCB Channel ID 1 if (m_sccbChannelId1 > 0) { - tsbkValue = (tsbkValue << 8) + m_siteData.serviceClass(); // System Service Class + tsbkValue = (tsbkValue << 8) + s_siteData.serviceClass(); // System Service Class } else { tsbkValue = (tsbkValue << 8) + (ServiceClass::INVALID); // System Service Class } tsbkValue = (tsbkValue << 16) + m_sccbChannelId2; // SCCB Channel ID 2 if (m_sccbChannelId2 > 0) { - tsbkValue = (tsbkValue << 8) + m_siteData.serviceClass(); // System Service Class + tsbkValue = (tsbkValue << 8) + s_siteData.serviceClass(); // System Service Class } else { tsbkValue = (tsbkValue << 8) + (ServiceClass::INVALID); // System Service Class diff --git a/src/common/p25/lc/tsbk/OSP_SCCB_EXP.cpp b/src/common/p25/lc/tsbk/OSP_SCCB_EXP.cpp index 84e5b460..93680c23 100644 --- a/src/common/p25/lc/tsbk/OSP_SCCB_EXP.cpp +++ b/src/common/p25/lc/tsbk/OSP_SCCB_EXP.cpp @@ -49,8 +49,8 @@ void OSP_SCCB_EXP::encode(uint8_t* data, bool rawTSBK, bool noTrellis) ulong64_t tsbkValue = 0U; - tsbkValue = m_siteData.rfssId(); // RF Sub-System ID - tsbkValue = (tsbkValue << 8) + m_siteData.siteId(); // Site ID + tsbkValue = s_siteData.rfssId(); // RF Sub-System ID + tsbkValue = (tsbkValue << 8) + s_siteData.siteId(); // Site ID tsbkValue = (tsbkValue << 4) + m_sccbChannelId1; // Channel (T) ID tsbkValue = (tsbkValue << 12) + m_sccbChannelNo; // Channel (T) Number @@ -58,7 +58,7 @@ void OSP_SCCB_EXP::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = (tsbkValue << 12) + m_sccbChannelNo; // Channel (R) Number if (m_sccbChannelId1 > 0) { - tsbkValue = (tsbkValue << 8) + m_siteData.serviceClass(); // System Service Class + tsbkValue = (tsbkValue << 8) + s_siteData.serviceClass(); // System Service Class } else { tsbkValue = (tsbkValue << 8) + (ServiceClass::INVALID); // System Service Class diff --git a/src/common/p25/lc/tsbk/OSP_SNDCP_CH_ANN.cpp b/src/common/p25/lc/tsbk/OSP_SNDCP_CH_ANN.cpp index 63050218..9d6b18fb 100644 --- a/src/common/p25/lc/tsbk/OSP_SNDCP_CH_ANN.cpp +++ b/src/common/p25/lc/tsbk/OSP_SNDCP_CH_ANN.cpp @@ -54,7 +54,7 @@ void OSP_SNDCP_CH_ANN::encode(uint8_t* data, bool rawTSBK, bool noTrellis) uint32_t calcSpace = (uint32_t)(m_siteIdenEntry.chSpaceKhz() / 0.125); float calcTxOffset = m_siteIdenEntry.txOffsetMhz() * 1000000.0; - uint32_t txFrequency = (uint32_t)((m_siteIdenEntry.baseFrequency() + ((calcSpace * 125) * m_siteData.channelNo()))); + uint32_t txFrequency = (uint32_t)((m_siteIdenEntry.baseFrequency() + ((calcSpace * 125) * s_siteData.channelNo()))); uint32_t rxFrequency = (uint32_t)(txFrequency + (int32_t)calcTxOffset); uint32_t rootFreq = rxFrequency - m_siteIdenEntry.baseFrequency(); @@ -70,14 +70,14 @@ void OSP_SNDCP_CH_ANN::encode(uint8_t* data, bool rawTSBK, bool noTrellis) if (m_implicitChannel) { tsbkValue = (tsbkValue << 16) + 0xFFFFU; } else { - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (T) ID - tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel (T) Number + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel (T) ID + tsbkValue = (tsbkValue << 12) + s_siteData.channelNo(); // Channel (T) Number } if (m_implicitChannel) { tsbkValue = (tsbkValue << 16) + 0xFFFFU; } else { - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (R) ID + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel (R) ID tsbkValue = (tsbkValue << 12) + (rxChNo & 0xFFFU); // Channel (R) Number } diff --git a/src/common/p25/lc/tsbk/OSP_SNDCP_CH_GNT.cpp b/src/common/p25/lc/tsbk/OSP_SNDCP_CH_GNT.cpp index fe0f13ca..cfa81f3d 100644 --- a/src/common/p25/lc/tsbk/OSP_SNDCP_CH_GNT.cpp +++ b/src/common/p25/lc/tsbk/OSP_SNDCP_CH_GNT.cpp @@ -64,14 +64,14 @@ void OSP_SNDCP_CH_GNT::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = (tsbkValue << 4) + m_grpVchId; // Channel (T) ID } else { - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (T) ID + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel (T) ID } tsbkValue = (tsbkValue << 12) + m_dataChannelNo; // Channel (T) Number if (m_grpVchId != 0U) { tsbkValue = (tsbkValue << 4) + m_grpVchId; // Channel (R) ID } else { - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (R) ID + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel (R) ID } tsbkValue = (tsbkValue << 12) + (rxChNo & 0xFFFU); // Channel (R) Number tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address diff --git a/src/common/p25/lc/tsbk/OSP_SYNC_BCAST.cpp b/src/common/p25/lc/tsbk/OSP_SYNC_BCAST.cpp index c4b1b6bd..215849e3 100644 --- a/src/common/p25/lc/tsbk/OSP_SYNC_BCAST.cpp +++ b/src/common/p25/lc/tsbk/OSP_SYNC_BCAST.cpp @@ -64,8 +64,8 @@ void OSP_SYNC_BCAST::encode(uint8_t* data, bool rawTSBK, bool noTrellis) // determine LTO and direction (positive or negative) bool negativeLTO = false; - uint8_t lto = fabs(m_siteData.lto()) * 2U; // this will cause a bug for half-hour timezone intervals... - if (m_siteData.lto() < 0) + uint8_t lto = fabs(s_siteData.lto()) * 2U; // this will cause a bug for half-hour timezone intervals... + if (s_siteData.lto() < 0) negativeLTO = true; // mark the LTO as valid if its non-zero diff --git a/src/common/p25/lc/tsbk/OSP_SYS_SRV_BCAST.cpp b/src/common/p25/lc/tsbk/OSP_SYS_SRV_BCAST.cpp index 24d00b0c..dee51739 100644 --- a/src/common/p25/lc/tsbk/OSP_SYS_SRV_BCAST.cpp +++ b/src/common/p25/lc/tsbk/OSP_SYS_SRV_BCAST.cpp @@ -45,7 +45,7 @@ void OSP_SYS_SRV_BCAST::encode(uint8_t* data, bool rawTSBK, bool noTrellis) { assert(data != nullptr); - const uint32_t services = (m_siteData.netActive()) ? SystemService::NET_ACTIVE : 0U | SYS_SRV_DEFAULT; + const uint32_t services = (s_siteData.netActive()) ? SystemService::NET_ACTIVE : 0U | SYS_SRV_DEFAULT; ulong64_t tsbkValue = 0U; diff --git a/src/common/p25/lc/tsbk/OSP_TIME_DATE_ANN.cpp b/src/common/p25/lc/tsbk/OSP_TIME_DATE_ANN.cpp index 3f585623..7ca72613 100644 --- a/src/common/p25/lc/tsbk/OSP_TIME_DATE_ANN.cpp +++ b/src/common/p25/lc/tsbk/OSP_TIME_DATE_ANN.cpp @@ -70,7 +70,7 @@ void OSP_TIME_DATE_ANN::encode(uint8_t* data, bool rawTSBK, bool noTrellis) LogDebug(LOG_P25, "TSBKO, OSP_TIME_DATE_ANN, tmM = %u / %u, tmY = %u / %u", local_tm.tm_mon, tmM, local_tm.tm_year, tmY); #endif - uint16_t lto = abs(m_siteData.lto()) * 2U; // this will cause a bug for half-hour timezone intervals... + uint16_t lto = abs(s_siteData.lto()) * 2U; // this will cause a bug for half-hour timezone intervals... // mark the LTO as valid if its non-zero bool vl = false; diff --git a/src/common/p25/lc/tsbk/OSP_UU_VCH_GRANT_UPD.cpp b/src/common/p25/lc/tsbk/OSP_UU_VCH_GRANT_UPD.cpp index f7b188c2..370b56dc 100644 --- a/src/common/p25/lc/tsbk/OSP_UU_VCH_GRANT_UPD.cpp +++ b/src/common/p25/lc/tsbk/OSP_UU_VCH_GRANT_UPD.cpp @@ -63,7 +63,7 @@ void OSP_UU_VCH_GRANT_UPD::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = m_grpVchId; // Channel ID } else { - tsbkValue = m_siteData.channelId(); // Channel ID + tsbkValue = s_siteData.channelId(); // Channel ID } tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number tsbkValue = (tsbkValue << 24) + m_dstId; // Target Address diff --git a/src/common/p25/lc/tsbk/OSP_U_DEREG_ACK.cpp b/src/common/p25/lc/tsbk/OSP_U_DEREG_ACK.cpp index 5cd92136..b8df17b6 100644 --- a/src/common/p25/lc/tsbk/OSP_U_DEREG_ACK.cpp +++ b/src/common/p25/lc/tsbk/OSP_U_DEREG_ACK.cpp @@ -58,8 +58,8 @@ void OSP_U_DEREG_ACK::encode(uint8_t* data, bool rawTSBK, bool noTrellis) ulong64_t tsbkValue = 0U; - tsbkValue = (tsbkValue << 8) + m_siteData.netId(); // Network ID - tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID + tsbkValue = (tsbkValue << 8) + s_siteData.netId(); // Network ID + tsbkValue = (tsbkValue << 12) + s_siteData.sysId(); // System ID tsbkValue = (tsbkValue << 24) + m_dstId; // Destination Radio Address std::unique_ptr tsbk = TSBK::fromValue(tsbkValue); diff --git a/src/common/p25/lc/tsbk/TSBKFactory.cpp b/src/common/p25/lc/tsbk/TSBKFactory.cpp index 742d7d49..e8700473 100644 --- a/src/common/p25/lc/tsbk/TSBKFactory.cpp +++ b/src/common/p25/lc/tsbk/TSBKFactory.cpp @@ -25,9 +25,9 @@ using namespace p25::lc::tsbk; // --------------------------------------------------------------------------- #if FORCE_TSBK_CRC_WARN -bool TSBKFactory::m_warnCRC = true; +bool TSBKFactory::s_warnCRC = true; #else -bool TSBKFactory::m_warnCRC = false; +bool TSBKFactory::s_warnCRC = false; #endif // --------------------------------------------------------------------------- @@ -58,7 +58,7 @@ std::unique_ptr TSBKFactory::createTSBK(const uint8_t* data, bool rawTSBK) bool ret = edac::CRC::checkCCITT162(tsbk, P25_TSBK_LENGTH_BYTES); if (!ret) { - if (m_warnCRC) { + if (s_warnCRC) { // if we're already warning instead of erroring CRC, don't announce invalid CRC in the // case where no CRC is defined if ((tsbk[P25_TSBK_LENGTH_BYTES - 2U] != 0x00U) && (tsbk[P25_TSBK_LENGTH_BYTES - 1U] != 0x00U)) { @@ -87,7 +87,7 @@ std::unique_ptr TSBKFactory::createTSBK(const uint8_t* data, bool rawTSBK) if (ret) { ret = edac::CRC::checkCCITT162(tsbk, P25_TSBK_LENGTH_BYTES); if (!ret) { - if (m_warnCRC) { + if (s_warnCRC) { LogWarning(LOG_P25, "TSBKFactory::createTSBK(), failed CRC CCITT-162 check"); ret = true; // ignore CRC error } diff --git a/src/common/p25/lc/tsbk/TSBKFactory.h b/src/common/p25/lc/tsbk/TSBKFactory.h index b86728d7..a9e88585 100644 --- a/src/common/p25/lc/tsbk/TSBKFactory.h +++ b/src/common/p25/lc/tsbk/TSBKFactory.h @@ -144,10 +144,10 @@ namespace p25 * @brief Sets the flag indicating CRC-errors should be warnings and not errors. * @param warnCRC Flag indicating CRC-errors should be treated as warnings. */ - static void setWarnCRC(bool warnCRC) { m_warnCRC = warnCRC; } + static void setWarnCRC(bool warnCRC) { s_warnCRC = warnCRC; } private: - static bool m_warnCRC; + static bool s_warnCRC; /** * @brief Decode a TSBK. diff --git a/src/common/p25/lc/tsbk/mbt/MBT_IOSP_GRP_AFF.cpp b/src/common/p25/lc/tsbk/mbt/MBT_IOSP_GRP_AFF.cpp index 591b25c6..7be4cd60 100644 --- a/src/common/p25/lc/tsbk/mbt/MBT_IOSP_GRP_AFF.cpp +++ b/src/common/p25/lc/tsbk/mbt/MBT_IOSP_GRP_AFF.cpp @@ -59,13 +59,13 @@ void MBT_IOSP_GRP_AFF::encodeMBT(data::DataHeader& dataHeader, uint8_t* pduUserD dataHeader.setBlocksToFollow(2U); - dataHeader.setAMBTField8((m_siteData.netId() >> 12) & 0xFFU); // Network ID (b19-12) - dataHeader.setAMBTField9((m_siteData.netId() >> 4) & 0xFFU); // Network ID (b11-b4) + dataHeader.setAMBTField8((s_siteData.netId() >> 12) & 0xFFU); // Network ID (b19-12) + dataHeader.setAMBTField9((s_siteData.netId() >> 4) & 0xFFU); // Network ID (b11-b4) /** Block 1 */ - pduUserData[0U] = ((m_siteData.netId() & 0x0FU) << 4) + // Network ID (b3-b0) - ((m_siteData.sysId() >> 8) & 0xFFU); // System ID (b11-b8) - pduUserData[1U] = (m_siteData.sysId() & 0xFFU); // System ID (b7-b0) + pduUserData[0U] = ((s_siteData.netId() & 0x0FU) << 4) + // Network ID (b3-b0) + ((s_siteData.sysId() >> 8) & 0xFFU); // System ID (b11-b8) + pduUserData[1U] = (s_siteData.sysId() & 0xFFU); // System ID (b7-b0) SET_UINT16(m_dstId, pduUserData, 2U); // Group ID SET_UINT16(m_announceGroup, pduUserData, 4U); // Announcement Group diff --git a/src/common/p25/lc/tsbk/mbt/MBT_OSP_ADJ_STS_BCAST.cpp b/src/common/p25/lc/tsbk/mbt/MBT_OSP_ADJ_STS_BCAST.cpp index 4db5bc92..45e1287c 100644 --- a/src/common/p25/lc/tsbk/mbt/MBT_OSP_ADJ_STS_BCAST.cpp +++ b/src/common/p25/lc/tsbk/mbt/MBT_OSP_ADJ_STS_BCAST.cpp @@ -55,13 +55,13 @@ void MBT_OSP_ADJ_STS_BCAST::encodeMBT(data::DataHeader& dataHeader, uint8_t* pdu if ((m_adjRfssId != 0U) && (m_adjSiteId != 0U) && (m_adjChannelNo != 0U)) { if (m_adjSysId == 0U) { - m_adjSysId = m_siteData.sysId(); + m_adjSysId = s_siteData.sysId(); } // pack LRA, CFVA and system ID into LLID - uint32_t llId = m_siteData.lra(); // Location Registration Area + uint32_t llId = s_siteData.lra(); // Location Registration Area llId = (llId << 8) + m_adjCFVA; // CFVA - llId = (llId << 4) + m_siteData.siteId(); // System ID + llId = (llId << 4) + s_siteData.siteId(); // System ID dataHeader.setLLId(llId); dataHeader.setAMBTField8(m_adjRfssId); // RF Sub-System ID @@ -75,9 +75,9 @@ void MBT_OSP_ADJ_STS_BCAST::encodeMBT(data::DataHeader& dataHeader, uint8_t* pdu ((m_adjChannelNo >> 8) & 0xFFU); pduUserData[3U] = (m_adjChannelNo >> 0) & 0xFFU; // Receive Channel Number LSB pduUserData[4U] = m_adjServiceClass; // System Service Class - pduUserData[5U] = (m_siteData.netId() >> 12) & 0xFFU; // Network ID (b19-12) - pduUserData[6U] = (m_siteData.netId() >> 4) & 0xFFU; // Network ID (b11-b4) - pduUserData[7U] = (m_siteData.netId() & 0x0FU) << 4; // Network ID (b3-b0) + pduUserData[5U] = (s_siteData.netId() >> 12) & 0xFFU; // Network ID (b19-12) + pduUserData[6U] = (s_siteData.netId() >> 4) & 0xFFU; // Network ID (b11-b4) + pduUserData[7U] = (s_siteData.netId() & 0x0FU) << 4; // Network ID (b3-b0) } else { LogError(LOG_P25, "MBT_OSP_ADJ_STS_BCAST::encodeMBT(), invalid values for OSP_ADJ_STS_BCAST, adjRfssId = $%02X, adjSiteId = $%02X, adjChannelId = %u, adjChannelNo = $%02X, adjSvcClass = $%02X", diff --git a/src/common/p25/lc/tsbk/mbt/MBT_OSP_AUTH_DMD.cpp b/src/common/p25/lc/tsbk/mbt/MBT_OSP_AUTH_DMD.cpp index eb23b1bf..1fa9b261 100644 --- a/src/common/p25/lc/tsbk/mbt/MBT_OSP_AUTH_DMD.cpp +++ b/src/common/p25/lc/tsbk/mbt/MBT_OSP_AUTH_DMD.cpp @@ -68,13 +68,13 @@ void MBT_OSP_AUTH_DMD::encodeMBT(data::DataHeader& dataHeader, uint8_t* pduUserD dataHeader.setBlocksToFollow(2U); - dataHeader.setAMBTField8((m_siteData.netId() >> 12) & 0xFFU); // Network ID (b19-12) - dataHeader.setAMBTField9((m_siteData.netId() >> 4) & 0xFFU); // Network ID (b11-b4) + dataHeader.setAMBTField8((s_siteData.netId() >> 12) & 0xFFU); // Network ID (b19-12) + dataHeader.setAMBTField9((s_siteData.netId() >> 4) & 0xFFU); // Network ID (b11-b4) /** Block 1 */ - pduUserData[0U] = ((m_siteData.netId() & 0x0FU) << 4) + // Network ID (b3-b0) - ((m_siteData.sysId() >> 8) & 0xFFU); // System ID (b11-b8) - pduUserData[1U] = (m_siteData.sysId() & 0xFFU); // System ID (b7-b0) + pduUserData[0U] = ((s_siteData.netId() & 0x0FU) << 4) + // Network ID (b3-b0) + ((s_siteData.sysId() >> 8) & 0xFFU); // System ID (b11-b8) + pduUserData[1U] = (s_siteData.sysId() & 0xFFU); // System ID (b7-b0) SET_UINT24(m_dstId, pduUserData, 2U); // Target Radio Address diff --git a/src/common/p25/lc/tsbk/mbt/MBT_OSP_GRP_VCH_GRANT.cpp b/src/common/p25/lc/tsbk/mbt/MBT_OSP_GRP_VCH_GRANT.cpp index 84860504..99a8fc59 100644 --- a/src/common/p25/lc/tsbk/mbt/MBT_OSP_GRP_VCH_GRANT.cpp +++ b/src/common/p25/lc/tsbk/mbt/MBT_OSP_GRP_VCH_GRANT.cpp @@ -60,7 +60,7 @@ void MBT_OSP_GRP_VCH_GRANT::encodeMBT(data::DataHeader& dataHeader, uint8_t* pdu txFrequency = (txFrequency << 4) + m_grpVchId; // Tx Channel ID } else { - txFrequency = (txFrequency << 4) + m_siteData.channelId(); // Tx Channel ID + txFrequency = (txFrequency << 4) + s_siteData.channelId(); // Tx Channel ID } txFrequency = (txFrequency << 12) + m_grpVchNo; // Tx Channel Number @@ -70,7 +70,7 @@ void MBT_OSP_GRP_VCH_GRANT::encodeMBT(data::DataHeader& dataHeader, uint8_t* pdu rxFrequency = (rxFrequency << 4) + m_rxGrpVchId; // Rx Channel ID } else { - rxFrequency = (rxFrequency << 4) + m_siteData.channelId(); // Rx Channel ID + rxFrequency = (rxFrequency << 4) + s_siteData.channelId(); // Rx Channel ID } rxFrequency = (rxFrequency << 12) + m_rxGrpVchNo; // Rx Channel Number diff --git a/src/common/p25/lc/tsbk/mbt/MBT_OSP_NET_STS_BCAST.cpp b/src/common/p25/lc/tsbk/mbt/MBT_OSP_NET_STS_BCAST.cpp index ae97ac7c..aa95c918 100644 --- a/src/common/p25/lc/tsbk/mbt/MBT_OSP_NET_STS_BCAST.cpp +++ b/src/common/p25/lc/tsbk/mbt/MBT_OSP_NET_STS_BCAST.cpp @@ -46,21 +46,21 @@ void MBT_OSP_NET_STS_BCAST::encodeMBT(data::DataHeader& dataHeader, uint8_t* pdu assert(pduUserData != nullptr); // pack LRA and system ID into LLID - uint32_t llId = m_siteData.lra(); // Location Registration Area - llId = (llId << 12) + m_siteData.siteId(); // System ID + uint32_t llId = s_siteData.lra(); // Location Registration Area + llId = (llId << 12) + s_siteData.siteId(); // System ID dataHeader.setLLId(llId); /** Block 1 */ - pduUserData[0U] = (m_siteData.netId() >> 12) & 0xFFU; // Network ID (b19-12) - pduUserData[1U] = (m_siteData.netId() >> 4) & 0xFFU; // Network ID (b11-b4) - pduUserData[2U] = (m_siteData.netId() & 0x0FU) << 4; // Network ID (b3-b0) - pduUserData[3U] = ((m_siteData.channelId() & 0x0FU) << 4) + // Transmit Channel ID & Channel Number MSB - ((m_siteData.channelNo() >> 8) & 0xFFU); - pduUserData[4U] = (m_siteData.channelNo() >> 0) & 0xFFU; // Transmit Channel Number LSB - pduUserData[5U] = ((m_siteData.channelId() & 0x0FU) << 4) + // Receive Channel ID & Channel Number MSB - ((m_siteData.channelNo() >> 8) & 0xFFU); - pduUserData[6U] = (m_siteData.channelNo() >> 0) & 0xFFU; // Receive Channel Number LSB - pduUserData[7U] = m_siteData.serviceClass(); // System Service Class + pduUserData[0U] = (s_siteData.netId() >> 12) & 0xFFU; // Network ID (b19-12) + pduUserData[1U] = (s_siteData.netId() >> 4) & 0xFFU; // Network ID (b11-b4) + pduUserData[2U] = (s_siteData.netId() & 0x0FU) << 4; // Network ID (b3-b0) + pduUserData[3U] = ((s_siteData.channelId() & 0x0FU) << 4) + // Transmit Channel ID & Channel Number MSB + ((s_siteData.channelNo() >> 8) & 0xFFU); + pduUserData[4U] = (s_siteData.channelNo() >> 0) & 0xFFU; // Transmit Channel Number LSB + pduUserData[5U] = ((s_siteData.channelId() & 0x0FU) << 4) + // Receive Channel ID & Channel Number MSB + ((s_siteData.channelNo() >> 8) & 0xFFU); + pduUserData[6U] = (s_siteData.channelNo() >> 0) & 0xFFU; // Receive Channel Number LSB + pduUserData[7U] = s_siteData.serviceClass(); // System Service Class AMBT::encode(dataHeader, pduUserData); } diff --git a/src/common/p25/lc/tsbk/mbt/MBT_OSP_RFSS_STS_BCAST.cpp b/src/common/p25/lc/tsbk/mbt/MBT_OSP_RFSS_STS_BCAST.cpp index 9fd9e721..7856e6c4 100644 --- a/src/common/p25/lc/tsbk/mbt/MBT_OSP_RFSS_STS_BCAST.cpp +++ b/src/common/p25/lc/tsbk/mbt/MBT_OSP_RFSS_STS_BCAST.cpp @@ -46,23 +46,23 @@ void MBT_OSP_RFSS_STS_BCAST::encodeMBT(data::DataHeader& dataHeader, uint8_t* pd assert(pduUserData != nullptr); // pack LRA and system ID into LLID - uint32_t llId = m_siteData.lra(); // Location Registration Area - llId = (llId << 12) + m_siteData.siteId(); // System ID - if (m_siteData.netActive()) { + uint32_t llId = s_siteData.lra(); // Location Registration Area + llId = (llId << 12) + s_siteData.siteId(); // System ID + if (s_siteData.netActive()) { llId |= 0x1000U; // Network Active Flag } dataHeader.setLLId(llId); /** Block 1 */ - pduUserData[0U] = (m_siteData.rfssId()) & 0xFFU; // RF Sub-System ID - pduUserData[1U] = (m_siteData.siteId()) & 0xFFU; // Site ID - pduUserData[2U] = ((m_siteData.channelId() & 0x0FU) << 4) + // Transmit Channel ID & Channel Number MSB - ((m_siteData.channelNo() >> 8) & 0xFFU); - pduUserData[3U] = (m_siteData.channelNo() >> 0) & 0xFFU; // Transmit Channel Number LSB - pduUserData[4U] = ((m_siteData.channelId() & 0x0FU) << 4) + // Receive Channel ID & Channel Number MSB - ((m_siteData.channelNo() >> 8) & 0xFFU); - pduUserData[5U] = (m_siteData.channelNo() >> 0) & 0xFFU; // Receive Channel Number LSB - pduUserData[6U] = m_siteData.serviceClass(); // System Service Class + pduUserData[0U] = (s_siteData.rfssId()) & 0xFFU; // RF Sub-System ID + pduUserData[1U] = (s_siteData.siteId()) & 0xFFU; // Site ID + pduUserData[2U] = ((s_siteData.channelId() & 0x0FU) << 4) + // Transmit Channel ID & Channel Number MSB + ((s_siteData.channelNo() >> 8) & 0xFFU); + pduUserData[3U] = (s_siteData.channelNo() >> 0) & 0xFFU; // Transmit Channel Number LSB + pduUserData[4U] = ((s_siteData.channelId() & 0x0FU) << 4) + // Receive Channel ID & Channel Number MSB + ((s_siteData.channelNo() >> 8) & 0xFFU); + pduUserData[5U] = (s_siteData.channelNo() >> 0) & 0xFFU; // Receive Channel Number LSB + pduUserData[6U] = s_siteData.serviceClass(); // System Service Class AMBT::encode(dataHeader, pduUserData); } diff --git a/src/common/p25/lc/tsbk/mbt/MBT_OSP_UU_VCH_GRANT.cpp b/src/common/p25/lc/tsbk/mbt/MBT_OSP_UU_VCH_GRANT.cpp index 8c6278c8..0e7c0636 100644 --- a/src/common/p25/lc/tsbk/mbt/MBT_OSP_UU_VCH_GRANT.cpp +++ b/src/common/p25/lc/tsbk/mbt/MBT_OSP_UU_VCH_GRANT.cpp @@ -56,14 +56,14 @@ void MBT_OSP_UU_VCH_GRANT::encodeMBT(data::DataHeader& dataHeader, uint8_t* pduU (m_priority & 0x07U); // Priority dataHeader.setAMBTField8(serviceOptions); - dataHeader.setAMBTField9((m_siteData.netId() >> 12) & 0xFFU); // Target Network ID (b19-12) + dataHeader.setAMBTField9((s_siteData.netId() >> 12) & 0xFFU); // Target Network ID (b19-12) uint16_t txFrequency = 0U; if ((m_grpVchId != 0U) || m_forceChannelId) { txFrequency = (txFrequency << 4) + m_grpVchId; // Tx Channel ID } else { - txFrequency = (txFrequency << 4) + m_siteData.channelId(); // Tx Channel ID + txFrequency = (txFrequency << 4) + s_siteData.channelId(); // Tx Channel ID } txFrequency = (txFrequency << 12) + m_grpVchNo; // Tx Channel Number @@ -73,26 +73,26 @@ void MBT_OSP_UU_VCH_GRANT::encodeMBT(data::DataHeader& dataHeader, uint8_t* pduU rxFrequency = (rxFrequency << 4) + m_rxGrpVchId; // Rx Channel ID } else { - rxFrequency = (rxFrequency << 4) + m_siteData.channelId(); // Rx Channel ID + rxFrequency = (rxFrequency << 4) + s_siteData.channelId(); // Rx Channel ID } rxFrequency = (rxFrequency << 12) + m_rxGrpVchNo; // Rx Channel Number /** Block 1 */ - pduUserData[0U] = ((m_siteData.netId() >> 12) & 0xFFU); // Source Network ID (b19-12) - pduUserData[1U] = ((m_siteData.netId() >> 4) & 0xFFU); // Source Network ID (b11-b4) - pduUserData[2U] = ((m_siteData.netId() & 0x0FU) << 4) + // Source Network ID (b3-b0) - ((m_siteData.sysId() >> 8) & 0xFFU); // Source System ID (b11-b8) - pduUserData[3U] = (m_siteData.sysId() & 0xFFU); // Source System ID (b7-b0) + pduUserData[0U] = ((s_siteData.netId() >> 12) & 0xFFU); // Source Network ID (b19-12) + pduUserData[1U] = ((s_siteData.netId() >> 4) & 0xFFU); // Source Network ID (b11-b4) + pduUserData[2U] = ((s_siteData.netId() & 0x0FU) << 4) + // Source Network ID (b3-b0) + ((s_siteData.sysId() >> 8) & 0xFFU); // Source System ID (b11-b8) + pduUserData[3U] = (s_siteData.sysId() & 0xFFU); // Source System ID (b7-b0) SET_UINT24(m_srcId, pduUserData, 4U); // Source Radio Address SET_UINT24(m_dstId, pduUserData, 7U); // Target Radio Address SET_UINT16(txFrequency, pduUserData, 10U); // Transmit Frequency /** Block 2 */ SET_UINT16(rxFrequency, pduUserData, 12U); // Receive Frequency - pduUserData[14U] = ((m_siteData.netId() >> 4) & 0xFFU); // Target Network ID (b11-b4) - pduUserData[15U] = ((m_siteData.netId() & 0x0FU) << 4) + // Target Network ID (b3-b0) - ((m_siteData.sysId() >> 8) & 0xFFU); // Target System ID (b11-b8) - pduUserData[16U] = (m_siteData.sysId() & 0xFFU); // Target System ID (b7-b0) + pduUserData[14U] = ((s_siteData.netId() >> 4) & 0xFFU); // Target Network ID (b11-b4) + pduUserData[15U] = ((s_siteData.netId() & 0x0FU) << 4) + // Target Network ID (b3-b0) + ((s_siteData.sysId() >> 8) & 0xFFU); // Target System ID (b11-b8) + pduUserData[16U] = (s_siteData.sysId() & 0xFFU); // Target System ID (b7-b0) SET_UINT24(m_dstId, pduUserData, 17U); // Target Radio Address AMBT::encode(dataHeader, pduUserData); diff --git a/src/fne/ActivityLog.cpp b/src/fne/ActivityLog.cpp index 9ab11657..518190e5 100644 --- a/src/fne/ActivityLog.cpp +++ b/src/fne/ActivityLog.cpp @@ -31,12 +31,12 @@ const uint32_t ACT_LOG_BUFFER_LEN = 501U; // Global Variables // --------------------------------------------------------------------------- -static std::string m_actFilePath; -static std::string m_actFileRoot; +static std::string g_actFilePath; +static std::string g_aclFileRoot; -static FILE* m_actFpLog = nullptr; +static FILE* g_actFpLog = nullptr; -static struct tm m_actTm; +static struct tm g_actTm; // --------------------------------------------------------------------------- // Global Functions @@ -54,22 +54,22 @@ static bool ActivityLogOpen() struct tm* tm = ::localtime(&now); - if (tm->tm_mday == m_actTm.tm_mday && tm->tm_mon == m_actTm.tm_mon && tm->tm_year == m_actTm.tm_year) { - if (m_actFpLog != nullptr) + if (tm->tm_mday == g_actTm.tm_mday && tm->tm_mon == g_actTm.tm_mon && tm->tm_year == g_actTm.tm_year) { + if (g_actFpLog != nullptr) return true; } else { - if (m_actFpLog != nullptr) - ::fclose(m_actFpLog); + if (g_actFpLog != nullptr) + ::fclose(g_actFpLog); } char filename[200U]; - ::sprintf(filename, "%s/%s-%04d-%02d-%02d.activity.log", m_actFilePath.c_str(), m_actFileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + ::sprintf(filename, "%s/%s-%04d-%02d-%02d.activity.log", g_actFilePath.c_str(), g_aclFileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); - m_actFpLog = ::fopen(filename, "a+t"); - m_actTm = *tm; + g_actFpLog = ::fopen(filename, "a+t"); + g_actTm = *tm; - return m_actFpLog != nullptr; + return g_actFpLog != nullptr; } /* Initializes the activity log. */ @@ -79,8 +79,8 @@ bool ActivityLogInitialise(const std::string& filePath, const std::string& fileR #if defined(CATCH2_TEST_COMPILATION) return true; #endif - m_actFilePath = filePath; - m_actFileRoot = fileRoot; + g_actFilePath = filePath; + g_aclFileRoot = fileRoot; return ::ActivityLogOpen(); } @@ -92,8 +92,8 @@ void ActivityLogFinalise() #if defined(CATCH2_TEST_COMPILATION) return; #endif - if (m_actFpLog != nullptr) - ::fclose(m_actFpLog); + if (g_actFpLog != nullptr) + ::fclose(g_actFpLog); } /* Writes a new entry to the activity log. */ @@ -110,8 +110,8 @@ void log_internal::ActivityLogInternal(const std::string& log) if (CurrentLogFileLevel() == 0U) return; - ::fprintf(m_actFpLog, "%s\n", log.c_str()); - ::fflush(m_actFpLog); + ::fprintf(g_actFpLog, "%s\n", log.c_str()); + ::fflush(g_actFpLog); if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) { ::fprintf(stdout, "%s" EOL, log.c_str()); diff --git a/src/fne/CryptoContainer.cpp b/src/fne/CryptoContainer.cpp index 1d7be4e3..29db98c0 100644 --- a/src/fne/CryptoContainer.cpp +++ b/src/fne/CryptoContainer.cpp @@ -133,7 +133,7 @@ int findLastChar(const uint8_t* buffer, uint32_t len, char target) // Static Class Members // --------------------------------------------------------------------------- -std::mutex CryptoContainer::m_mutex; +std::mutex CryptoContainer::s_mutex; // --------------------------------------------------------------------------- // Public Class Members @@ -220,7 +220,7 @@ bool CryptoContainer::read() void CryptoContainer::clear() { - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); m_keys.clear(); } @@ -235,7 +235,7 @@ void CryptoContainer::addEntry(KeyItem key) uint32_t id = entry.id(); uint32_t kId = entry.kId(); - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); auto it = std::find_if(m_keys.begin(), m_keys.end(), [&](KeyItem& x) { @@ -253,7 +253,7 @@ void CryptoContainer::addEntry(KeyItem key) void CryptoContainer::eraseEntry(uint32_t id) { - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); auto it = std::find_if(m_keys.begin(), m_keys.end(), [&](KeyItem& x) { return x.id() == id; @@ -269,7 +269,7 @@ KeyItem CryptoContainer::find(uint32_t kId) { KeyItem entry; - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); auto it = std::find_if(m_keys.begin(), m_keys.end(), [&](KeyItem& x) { return x.kId() == kId; @@ -498,7 +498,7 @@ bool CryptoContainer::load() // clear table clear(); - std::lock_guard lock(m_mutex); + std::lock_guard lock(s_mutex); // get keys node rapidxml::xml_node<>* keys = innerRoot->first_node("Keys"); diff --git a/src/fne/CryptoContainer.h b/src/fne/CryptoContainer.h index 705651e7..a78866a6 100644 --- a/src/fne/CryptoContainer.h +++ b/src/fne/CryptoContainer.h @@ -240,7 +240,7 @@ class HOST_SW_API CryptoContainer : public Thread { bool m_enabled; bool m_stop; - static std::mutex m_mutex; + static std::mutex s_mutex; /** * @brief Loads the table from the passed lookup table file. diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 777d6488..85177449 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -3007,5 +3007,5 @@ void FNENetwork::processTEKResponse(p25::kmm::KeyItem* rspKi, uint8_t algId, uin for (auto peerId : peersToRemove) m_peerReplicaKeyQueue.erase(peerId); - m_keyQueueMutex.unlock(); + s_keyQueueMutex.unlock(); } diff --git a/src/fne/network/SpanningTree.cpp b/src/fne/network/SpanningTree.cpp index 1ef692fd..67e5cee7 100644 --- a/src/fne/network/SpanningTree.cpp +++ b/src/fne/network/SpanningTree.cpp @@ -17,8 +17,8 @@ using namespace network; // Static Class Members // --------------------------------------------------------------------------- -std::unordered_map SpanningTree::m_SpanningTrees = std::unordered_map(); -uint8_t SpanningTree::m_maxUpdatesBeforeReparent = 5U; +std::unordered_map SpanningTree::s_spanningTrees = std::unordered_map(); +uint8_t SpanningTree::s_maxUpdatesBeforeReparent = 5U; // --------------------------------------------------------------------------- // Public Class Members @@ -34,7 +34,7 @@ SpanningTree::SpanningTree(uint32_t id, uint32_t masterId, SpanningTree* parent) m_masterId(masterId), m_updatesBeforeReparent(0U) { - m_SpanningTrees[id] = this; + s_spanningTrees[id] = this; if (m_parent != nullptr) m_parent->m_children.push_back(this); } @@ -56,8 +56,8 @@ SpanningTree::~SpanningTree() SpanningTree* SpanningTree::findByPeerID(const uint32_t peerId) { - auto it = m_SpanningTrees.find(peerId); - if (it != m_SpanningTrees.end()) { + auto it = s_spanningTrees.find(peerId); + if (it != s_spanningTrees.end()) { return it->second; } @@ -68,7 +68,7 @@ SpanningTree* SpanningTree::findByPeerID(const uint32_t peerId) SpanningTree* SpanningTree::findByMasterID(const uint32_t masterId) { - for (auto it : m_SpanningTrees) { + for (auto it : s_spanningTrees) { SpanningTree* tree = it.second; if (tree != nullptr) { if (tree->masterId() == masterId) { @@ -102,8 +102,8 @@ uint32_t SpanningTree::countChildren(SpanningTree* node) void SpanningTree::erasePeer(const uint32_t peerId) { - auto it = m_SpanningTrees.find(peerId); - if (it != m_SpanningTrees.end()) { + auto it = s_spanningTrees.find(peerId); + if (it != s_spanningTrees.end()) { SpanningTree* tree = it->second; if (tree != nullptr) { if (tree->m_parent != nullptr) { @@ -118,7 +118,7 @@ void SpanningTree::erasePeer(const uint32_t peerId) delete tree; } - m_SpanningTrees.erase(it); + s_spanningTrees.erase(it); } } @@ -189,7 +189,7 @@ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, else { // are the parents different? if so, start counting down to reparenting if (existingNode->m_parent != parent) { - if (existingNode->m_updatesBeforeReparent >= m_maxUpdatesBeforeReparent) { + if (existingNode->m_updatesBeforeReparent >= s_maxUpdatesBeforeReparent) { existingNode->m_updatesBeforeReparent = 0U; // reparent the node if necessary diff --git a/src/fne/network/SpanningTree.h b/src/fne/network/SpanningTree.h index 4c488b7d..9fd0fc37 100644 --- a/src/fne/network/SpanningTree.h +++ b/src/fne/network/SpanningTree.h @@ -149,8 +149,8 @@ namespace network SpanningTree* m_parent; //!< Parent tree node. (i.e. master FNE above this) std::vector m_children; //!< Child tree nodes. (i.e. peer FNEs below this) - static std::unordered_map m_SpanningTrees; //!< Static map of all trees by peer ID. - static uint8_t m_maxUpdatesBeforeReparent; //!< Maximum count of updates before allowing node reparenting. + static std::unordered_map s_spanningTrees; //!< Static map of all trees by peer ID. + static uint8_t s_maxUpdatesBeforeReparent; //!< Maximum count of updates before allowing node reparenting. /** * @brief Peer Identity. diff --git a/src/host/ActivityLog.cpp b/src/host/ActivityLog.cpp index 5b4a5223..c44261b0 100644 --- a/src/host/ActivityLog.cpp +++ b/src/host/ActivityLog.cpp @@ -33,12 +33,12 @@ const uint32_t ACT_LOG_BUFFER_LEN = 501U; // Global Variables // --------------------------------------------------------------------------- -static std::string m_actFilePath; -static std::string m_actFileRoot; +static std::string g_actFilePath; +static std::string g_actFileRoot; -static FILE* m_actFpLog = nullptr; +static FILE* g_actFpLog = nullptr; -static struct tm m_actTm; +static struct tm g_actTm; // --------------------------------------------------------------------------- // Global Functions @@ -56,22 +56,22 @@ static bool ActivityLogOpen() struct tm* tm = ::localtime(&now); - if (tm->tm_mday == m_actTm.tm_mday && tm->tm_mon == m_actTm.tm_mon && tm->tm_year == m_actTm.tm_year) { - if (m_actFpLog != nullptr) + if (tm->tm_mday == g_actTm.tm_mday && tm->tm_mon == g_actTm.tm_mon && tm->tm_year == g_actTm.tm_year) { + if (g_actFpLog != nullptr) return true; } else { - if (m_actFpLog != nullptr) - ::fclose(m_actFpLog); + if (g_actFpLog != nullptr) + ::fclose(g_actFpLog); } char filename[200U]; - ::sprintf(filename, "%s/%s-%04d-%02d-%02d.activity.log", m_actFilePath.c_str(), m_actFileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + ::sprintf(filename, "%s/%s-%04d-%02d-%02d.activity.log", g_actFilePath.c_str(), g_actFileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); - m_actFpLog = ::fopen(filename, "a+t"); - m_actTm = *tm; + g_actFpLog = ::fopen(filename, "a+t"); + g_actTm = *tm; - return m_actFpLog != nullptr; + return g_actFpLog != nullptr; } /* Initializes the activity log. */ @@ -81,8 +81,8 @@ bool ActivityLogInitialise(const std::string& filePath, const std::string& fileR #if defined(CATCH2_TEST_COMPILATION) return true; #endif - m_actFilePath = filePath; - m_actFileRoot = fileRoot; + g_actFilePath = filePath; + g_actFileRoot = fileRoot; return ::ActivityLogOpen(); } @@ -94,8 +94,8 @@ void ActivityLogFinalise() #if defined(CATCH2_TEST_COMPILATION) return; #endif - if (m_actFpLog != nullptr) - ::fclose(m_actFpLog); + if (g_actFpLog != nullptr) + ::fclose(g_actFpLog); } /* Writes a new entry to the activity log. */ @@ -117,8 +117,8 @@ void log_internal::ActivityLogInternal(const std::string& log) network->writeActLog(log.c_str()); } - ::fprintf(m_actFpLog, "%s\n", log.c_str()); - ::fflush(m_actFpLog); + ::fprintf(g_actFpLog, "%s\n", log.c_str()); + ::fflush(g_actFpLog); if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) { ::fprintf(stdout, "%s" EOL, log.c_str()); diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index 9e30eb1c..903cfd9e 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -94,7 +94,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa m_supervisor = supervisor; - Slot::m_verifyReg = dmrProtocol["verifyReg"].as(false); + Slot::s_verifyReg = dmrProtocol["verifyReg"].as(false); uint8_t nRandWait = (uint8_t)dmrProtocol["nRandWait"].as(DEFAULT_NRAND_WAIT); if (nRandWait > 15U) @@ -153,8 +153,8 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa m_slot2->setNotifyCC(notifyCC); bool disableUnitRegTimeout = dmrProtocol["disableUnitRegTimeout"].as(false); - m_slot1->m_affiliations->setDisableUnitRegTimeout(disableUnitRegTimeout); - m_slot2->m_affiliations->setDisableUnitRegTimeout(disableUnitRegTimeout); + m_slot1->s_affiliations->setDisableUnitRegTimeout(disableUnitRegTimeout); + m_slot2->s_affiliations->setDisableUnitRegTimeout(disableUnitRegTimeout); /* ** Voice Silence and Frame Loss Thresholds @@ -236,7 +236,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa LogInfo(" Silence Threshold: %u (%.1f%%)", silenceThreshold, float(silenceThreshold) / 1.41F); LogInfo(" Frame Loss Threshold: %u", frameLossThreshold); - LogInfo(" Verify Registration: %s", Slot::m_verifyReg ? "yes" : "no"); + LogInfo(" Verify Registration: %s", Slot::s_verifyReg ? "yes" : "no"); LogInfo(" Conventional Network Grant Demand: %s", convNetGrantDemand ? "yes" : "no"); } } @@ -467,9 +467,9 @@ dmr::lookups::DMRAffiliationLookup* Control::affiliations() { switch (m_tsccSlotNo) { case 1U: - return m_slot1->m_affiliations; + return m_slot1->s_affiliations; case 2U: - return m_slot2->m_affiliations; + return m_slot2->s_affiliations; default: LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", m_tsccSlotNo); break; @@ -769,7 +769,7 @@ void Control::processNetwork() if (m_enableTSCC) { Slot* tscc = getTSCCSlot(); if (tscc != nullptr) { - if (!tscc->m_affiliations->isGranted(dstId)) { + if (!tscc->s_affiliations->isGranted(dstId)) { tscc->m_control->writeRF_CSBK_Grant(srcId, dstId, 4U, (flco == FLCO::GROUP) ? true : false, true); } } diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index e81c2ec0..5eb1a122 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -35,51 +35,51 @@ using namespace dmr::packet; // Static Class Members // --------------------------------------------------------------------------- -Control* Slot::m_dmr = nullptr; +Control* Slot::s_dmr = nullptr; -bool Slot::m_authoritative = true; +bool Slot::s_authoritative = true; -uint32_t Slot::m_colorCode = 0U; +uint32_t Slot::s_colorCode = 0U; -SiteData Slot::m_siteData = SiteData(); -uint32_t Slot::m_channelNo = 0U; +SiteData Slot::s_siteData = SiteData(); +uint32_t Slot::s_channelNo = 0U; -bool Slot::m_embeddedLCOnly = false; -bool Slot::m_dumpTAData = true; +bool Slot::s_embeddedLCOnly = false; +bool Slot::s_dumpTAData = true; -modem::Modem* Slot::m_modem = nullptr; -network::Network* Slot::m_network = nullptr; +modem::Modem* Slot::s_modem = nullptr; +network::Network* Slot::s_network = nullptr; -bool Slot::m_duplex = true; +bool Slot::s_duplex = true; -::lookups::IdenTableLookup* Slot::m_idenTable = nullptr; -::lookups::RadioIdLookup* Slot::m_ridLookup = nullptr; -::lookups::TalkgroupRulesLookup* Slot::m_tidLookup = nullptr; -dmr::lookups::DMRAffiliationLookup *Slot::m_affiliations = nullptr; -::lookups::VoiceChData Slot::m_controlChData = ::lookups::VoiceChData(); +::lookups::IdenTableLookup* Slot::s_idenTable = nullptr; +::lookups::RadioIdLookup* Slot::s_ridLookup = nullptr; +::lookups::TalkgroupRulesLookup* Slot::s_tidLookup = nullptr; +dmr::lookups::DMRAffiliationLookup *Slot::s_affiliations = nullptr; +::lookups::VoiceChData Slot::s_controlChData = ::lookups::VoiceChData(); -::lookups::IdenTable Slot::m_idenEntry = ::lookups::IdenTable(); +::lookups::IdenTable Slot::s_idenEntry = ::lookups::IdenTable(); -uint32_t Slot::m_hangCount = 3U * 17U; +uint32_t Slot::s_hangCount = 3U * 17U; -::lookups::RSSIInterpolator* Slot::m_rssiMapper = nullptr; +::lookups::RSSIInterpolator* Slot::s_rssiMapper = nullptr; -uint32_t Slot::m_jitterTime = 360U; -uint32_t Slot::m_jitterSlots = 6U; +uint32_t Slot::s_jitterTime = 360U; +uint32_t Slot::s_jitterSlots = 6U; -uint8_t* Slot::m_idle = nullptr; +uint8_t* Slot::s_idle = nullptr; -FLCO::E Slot::m_flco1; -uint8_t Slot::m_id1 = 0U; -Slot::SLCO_ACT_TYPE Slot::m_actType1 = Slot::SLCO_ACT_TYPE::VOICE; -FLCO::E Slot::m_flco2; -uint8_t Slot::m_id2 = 0U; -Slot::SLCO_ACT_TYPE Slot::m_actType2 = Slot::SLCO_ACT_TYPE::VOICE; +FLCO::E Slot::s_flco1; +uint8_t Slot::s_id1 = 0U; +Slot::SLCO_ACT_TYPE Slot::s_actType1 = Slot::SLCO_ACT_TYPE::VOICE; +FLCO::E Slot::s_flco2; +uint8_t Slot::s_id2 = 0U; +Slot::SLCO_ACT_TYPE Slot::s_actType2 = Slot::SLCO_ACT_TYPE::VOICE; -bool Slot::m_verifyReg = false; +bool Slot::s_verifyReg = false; -uint8_t Slot::m_alohaNRandWait = DEFAULT_NRAND_WAIT; -uint8_t Slot::m_alohaBackOff = 1U; +uint8_t Slot::s_alohaNRandWait = DEFAULT_NRAND_WAIT; +uint8_t Slot::s_alohaBackOff = 1U; // --------------------------------------------------------------------------- // Constants @@ -179,9 +179,9 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz m_adjSiteUpdateTimer.setTimeout(m_adjSiteUpdateInterval); m_adjSiteUpdateTimer.start(); - m_voice = new Voice(this, m_network, m_embeddedLCOnly, m_dumpTAData, debug, verbose); - m_data = new Data(this, m_network, dumpDataPacket, repeatDataPacket, debug, verbose); - m_control = new ControlSignaling(this, m_network, dumpCSBKData, debug, verbose); + m_voice = new Voice(this, s_network, s_embeddedLCOnly, s_dumpTAData, debug, verbose); + m_data = new Data(this, s_network, dumpDataPacket, repeatDataPacket, debug, verbose); + m_control = new ControlSignaling(this, s_network, dumpCSBKData, debug, verbose); } /* Finalizes a instance of the Slot class. */ @@ -241,7 +241,7 @@ bool Slot::processFrame(uint8_t *data, uint32_t len) raw |= (data[36U] << 0) & 0x00FFU; // Convert the raw RSSI to dBm - int rssi = m_rssiMapper->interpolate(raw); + int rssi = s_rssiMapper->interpolate(raw); if (m_verbose) { LogInfoEx(LOG_RF, "DMR Slot %u, raw RSSI = %u, reported RSSI = %d dBm", m_slotNo, raw, rssi); } @@ -391,7 +391,7 @@ void Slot::processNetwork(const data::NetData& dmrData) { // don't process network frames if the RF modem isn't in a listening state if (m_rfState != RS_RF_LISTENING) { - m_network->resetDMR(m_slotNo); + s_network->resetDMR(m_slotNo); return; } @@ -406,7 +406,7 @@ void Slot::processNetwork(const data::NetData& dmrData) } } - if (m_authoritative) { + if (s_authoritative) { // don't process network frames if the destination ID's don't match and the network TG hang timer is running if (m_netLastDstId != 0U && dmrData.getDstId() != 0U && m_netState != RS_NET_IDLE) { if (m_netLastDstId != dmrData.getDstId() && (m_netTGHang.isRunning() && !m_netTGHang.hasExpired())) { @@ -420,7 +420,7 @@ void Slot::processNetwork(const data::NetData& dmrData) } // don't process network frames if this modem isn't authoritative - if (!m_authoritative && m_permittedDstId != dmrData.getDstId()) { + if (!s_authoritative && m_permittedDstId != dmrData.getDstId()) { if (!g_disableNonAuthoritativeLogging) LogWarning(LOG_NET, "DMR Slot %u, [NON-AUTHORITATIVE] Ignoring network traffic, destination not permitted!", m_slotNo); return; @@ -430,7 +430,7 @@ void Slot::processNetwork(const data::NetData& dmrData) DataType::E dataType = dmrData.getDataType(); - Slot* tscc = m_dmr->getTSCCSlot(); + Slot* tscc = s_dmr->getTSCCSlot(); bool enableTSCC = false; if (tscc != nullptr) @@ -493,7 +493,7 @@ void Slot::processNetwork(const data::NetData& dmrData) if (dataType != DataType::CSBK) return; else { - if (m_slotNo != m_dmr->m_tsccSlotNo) + if (m_slotNo != s_dmr->m_tsccSlotNo) return; } } @@ -530,8 +530,8 @@ void Slot::processInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId) { if (m_rfState == RS_RF_AUDIO && m_rfLC->getDstId() == dstId) { LogWarning(LOG_DMR, "Slot %u, network requested in-call traffic reject, dstId = %u", m_slotNo, dstId); - if (m_affiliations->isGranted(dstId)) { - m_affiliations->releaseGrant(dstId, false); + if (s_affiliations->isGranted(dstId)) { + s_affiliations->releaseGrant(dstId, false); if (!m_enableTSCC) { notifyCC_ReleaseGrant(dstId); } @@ -559,30 +559,30 @@ void Slot::clock() uint32_t ms = m_interval.elapsed(); m_interval.start(); - if (m_network != nullptr) { - if (m_network->getStatus() == network::NET_STAT_RUNNING) { - m_siteData.setNetActive(true); + if (s_network != nullptr) { + if (s_network->getStatus() == network::NET_STAT_RUNNING) { + s_siteData.setNetActive(true); } else { - m_siteData.setNetActive(false); + s_siteData.setNetActive(false); } - lc::CSBK::setSiteData(m_siteData); + lc::CSBK::setSiteData(s_siteData); } // if we have control enabled; do clocking to generate a CC data stream if (m_enableTSCC) { - m_dmr->m_tsccCntInterval.clock(ms); - if (m_dmr->m_tsccCntInterval.isRunning() && m_dmr->m_tsccCntInterval.hasExpired()) { - m_dmr->m_tsccCnt++; - if (m_dmr->m_tsccCnt == TSCC_MAX_CSC_CNT) { - m_dmr->m_tsccCnt = 0U; + s_dmr->m_tsccCntInterval.clock(ms); + if (s_dmr->m_tsccCntInterval.isRunning() && s_dmr->m_tsccCntInterval.hasExpired()) { + s_dmr->m_tsccCnt++; + if (s_dmr->m_tsccCnt == TSCC_MAX_CSC_CNT) { + s_dmr->m_tsccCnt = 0U; } - m_dmr->m_tsccCntInterval.start(); + s_dmr->m_tsccCntInterval.start(); } - m_modem->setDMRIgnoreCACH_AT(m_slotNo); + s_modem->setDMRIgnoreCACH_AT(m_slotNo); if (m_ccRunning && !m_ccPacketInterval.isRunning()) { m_ccPacketInterval.start(); @@ -607,16 +607,16 @@ void Slot::clock() m_ccSeq = 0U; } - if (m_dmr->m_tsccPayloadActive) { - if ((m_dmr->m_tsccCnt % 2) == 0) { - setShortLC_Payload(m_siteData, m_dmr->m_tsccCnt); + if (s_dmr->m_tsccPayloadActive) { + if ((s_dmr->m_tsccCnt % 2) == 0) { + setShortLC_Payload(s_siteData, s_dmr->m_tsccCnt); } } else { - setShortLC_TSCC(m_siteData, m_dmr->m_tsccCnt); + setShortLC_TSCC(s_siteData, s_dmr->m_tsccCnt); } - writeRF_ControlData(m_dmr->m_tsccCnt, m_ccSeq); + writeRF_ControlData(s_dmr->m_tsccCnt, m_ccSeq); m_ccSeq++; } @@ -632,7 +632,7 @@ void Slot::clock() } // activate payload channel if requested from the TSCC - if (m_dmr->m_tsccPayloadActive) { + if (s_dmr->m_tsccPayloadActive) { if (m_rfState == RS_RF_LISTENING && m_netState == RS_NET_IDLE) { if (m_tsccPayloadDstId > 0U) { if (m_tsccPayloadActRetry.isRunning()) { @@ -644,7 +644,7 @@ void Slot::clock() } } - if ((m_dmr->m_tsccCnt % 2) > 0) { + if ((s_dmr->m_tsccCnt % 2) > 0) { if (m_tsccPayloadVoice) setShortLC(m_slotNo, m_tsccPayloadDstId, m_tsccPayloadGroup ? FLCO::GROUP : FLCO::PRIVATE, SLCO_ACT_TYPE::VOICE); else @@ -689,7 +689,7 @@ void Slot::clock() m_rfLastSrcId = 0U; // reset permitted ID and clear permission state - if (!m_authoritative && m_permittedDstId != 0U) { + if (!s_authoritative && m_permittedDstId != 0U) { m_permittedDstId = 0U; } } @@ -702,7 +702,7 @@ void Slot::clock() } } - if (m_authoritative) { + if (s_authoritative) { if (m_netTGHang.isRunning()) { m_netTGHang.clock(ms); @@ -743,9 +743,9 @@ void Slot::clock() if (m_packetTimer.isRunning() && m_packetTimer.hasExpired()) { uint32_t elapsed = m_elapsed.elapsed(); - if (elapsed >= m_jitterTime) { + if (elapsed >= s_jitterTime) { LogWarning(LOG_NET, "DMR Slot %u, lost audio for %ums filling in", m_slotNo, elapsed); - m_voice->insertSilence(m_jitterSlots); + m_voice->insertSilence(s_jitterSlots); m_elapsed.start(); } @@ -771,7 +771,7 @@ void Slot::clockSiteData(uint32_t ms) { if (m_enableTSCC) { // clock all the grant timers - m_affiliations->clock(ms); + s_affiliations->clock(ms); // do we need to network announce ourselves? if (!m_adjSiteUpdateTimer.isRunning()) { @@ -783,10 +783,10 @@ void Slot::clockSiteData(uint32_t ms) if (m_adjSiteUpdateTimer.isRunning() && m_adjSiteUpdateTimer.hasExpired()) { if (m_rfState == RS_RF_LISTENING && m_netState == RS_NET_IDLE) { m_control->writeAdjSSNetwork(); - if (m_network != nullptr) { - if (m_affiliations->grpAffSize() > 0) { - auto affs = m_affiliations->grpAffTable(); - m_network->announceAffiliationUpdate(affs); + if (s_network != nullptr) { + if (s_affiliations->grpAffSize() > 0) { + auto affs = s_affiliations->grpAffTable(); + s_network->announceAffiliationUpdate(affs); } } m_adjSiteUpdateTimer.start(); @@ -823,7 +823,7 @@ void Slot::clockSiteData(uint32_t ms) void Slot::permittedTG(uint32_t dstId) { - if (m_authoritative) { + if (s_authoritative) { return; } @@ -864,16 +864,16 @@ void Slot::releaseGrantTG(uint32_t dstId) LogInfoEx(LOG_DMR, "DMR Slot %u, VC request, release TG grant, dstId = %u", m_slotNo, dstId); } - if (m_affiliations->isGranted(dstId)) { - uint32_t chNo = m_affiliations->getGrantedCh(dstId); - uint32_t srcId = m_affiliations->getGrantedSrcId(dstId); - ::lookups::VoiceChData voiceCh = m_affiliations->rfCh()->getRFChData(chNo); + if (s_affiliations->isGranted(dstId)) { + uint32_t chNo = s_affiliations->getGrantedCh(dstId); + uint32_t srcId = s_affiliations->getGrantedSrcId(dstId); + ::lookups::VoiceChData voiceCh = s_affiliations->rfCh()->getRFChData(chNo); if (m_verbose) { LogInfoEx(LOG_DMR, "DMR Slot %u, VC %s:%u, TG grant released, srcId = %u, dstId = %u, chNo = %u-%u", m_slotNo, voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); } - m_affiliations->releaseGrant(dstId, false); + s_affiliations->releaseGrant(dstId, false); } } @@ -885,16 +885,16 @@ void Slot::touchGrantTG(uint32_t dstId) return; } - if (m_affiliations->isGranted(dstId)) { - uint32_t chNo = m_affiliations->getGrantedCh(dstId); - uint32_t srcId = m_affiliations->getGrantedSrcId(dstId); - ::lookups::VoiceChData voiceCh = m_affiliations->rfCh()->getRFChData(chNo); + if (s_affiliations->isGranted(dstId)) { + uint32_t chNo = s_affiliations->getGrantedCh(dstId); + uint32_t srcId = s_affiliations->getGrantedSrcId(dstId); + ::lookups::VoiceChData voiceCh = s_affiliations->rfCh()->getRFChData(chNo); if (m_verbose) { LogInfoEx(LOG_DMR, "DMR Slot %u, VC %s:%u, call in progress, srcId = %u, dstId = %u, chNo = %u-%u", m_slotNo, voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); } - m_affiliations->touchGrant(dstId); + s_affiliations->touchGrant(dstId); } } @@ -914,8 +914,8 @@ void Slot::clearRFReject() m_netFrames = 0U; m_netLost = 0U; - if (m_network != nullptr) - m_network->resetDMR(m_slotNo); + if (s_network != nullptr) + s_network->resetDMR(m_slotNo); m_rfState = RS_RF_LISTENING; } @@ -936,8 +936,8 @@ void Slot::setTSCC(bool enable, bool dedicated) m_enableTSCC = enable; m_dedicatedTSCC = dedicated; if (m_enableTSCC) { - m_modem->setDMRIgnoreCACH_AT(m_slotNo); - m_affiliations->setSlotForChannelTSCC(m_channelNo, m_slotNo); + s_modem->setDMRIgnoreCACH_AT(m_slotNo); + s_affiliations->setSlotForChannelTSCC(s_channelNo, m_slotNo); } } @@ -951,8 +951,8 @@ void Slot::setTSCCActivated(uint32_t dstId, uint32_t srcId, bool group, bool voi m_tsccPayloadVoice = voice; // start payload channel transmit - if (!m_modem->hasTX()) { - m_modem->writeDMRStart(true); + if (!s_modem->hasTX()) { + s_modem->writeDMRStart(true); } if (m_tsccPayloadDstId != 0U && !m_tsccPayloadActRetry.isRunning()) { @@ -1004,37 +1004,37 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s assert(idenTable != nullptr); assert(rssiMapper != nullptr); - m_dmr = dmr; + s_dmr = dmr; - m_authoritative = authoritative; + s_authoritative = authoritative; - m_colorCode = colorCode; + s_colorCode = colorCode; - m_siteData = siteData; + s_siteData = siteData; - m_embeddedLCOnly = embeddedLCOnly; - m_dumpTAData = dumpTAData; + s_embeddedLCOnly = embeddedLCOnly; + s_dumpTAData = dumpTAData; - m_modem = modem; - m_network = network; + s_modem = modem; + s_network = network; - m_duplex = duplex; + s_duplex = duplex; - m_idenTable = idenTable; - m_ridLookup = ridLookup; - m_tidLookup = tidLookup; - m_affiliations = new dmr::lookups::DMRAffiliationLookup(chLookup, verbose); + s_idenTable = idenTable; + s_ridLookup = ridLookup; + s_tidLookup = tidLookup; + s_affiliations = new dmr::lookups::DMRAffiliationLookup(chLookup, verbose); // set the grant release callback - m_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t srcId, uint32_t dstId, uint8_t slot) { - Slot* tscc = m_dmr->getTSCCSlot(); + s_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t srcId, uint32_t dstId, uint8_t slot) { + Slot* tscc = s_dmr->getTSCCSlot(); if (tscc != nullptr) { - if (chNo == tscc->m_channelNo) { - m_dmr->tsccClearActivatedSlot(slot); + if (chNo == tscc->s_channelNo) { + s_dmr->tsccClearActivatedSlot(slot); return; } - ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); + ::lookups::VoiceChData voiceChData = tscc->s_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["slot"].set(slot); @@ -1048,7 +1048,7 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s } // callback REST API to clear TG permit for the granted TG on the specified voice channel - if (m_authoritative && m_dmr->m_supervisor) { + if (s_authoritative && s_dmr->m_supervisor) { if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); dstId = 0U; // clear TG value @@ -1065,56 +1065,56 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s }); // set the unit deregistration callback - m_affiliations->setUnitDeregCallback([=](uint32_t srcId, bool automatic) { - if (m_network != nullptr) - m_network->announceUnitDeregistration(srcId); + s_affiliations->setUnitDeregCallback([=](uint32_t srcId, bool automatic) { + if (s_network != nullptr) + s_network->announceUnitDeregistration(srcId); }); - m_hangCount = callHang * 17U; + s_hangCount = callHang * 17U; - m_rssiMapper = rssiMapper; + s_rssiMapper = rssiMapper; - m_jitterTime = jitter; + s_jitterTime = jitter; float jitter_tmp = float(jitter) / 360.0F; - m_jitterSlots = (uint32_t)(std::ceil(jitter_tmp) * 6.0F); + s_jitterSlots = (uint32_t)(std::ceil(jitter_tmp) * 6.0F); - m_idle = new uint8_t[DMR_FRAME_LENGTH_BYTES + 2U]; - ::memcpy(m_idle, IDLE_DATA, DMR_FRAME_LENGTH_BYTES + 2U); + s_idle = new uint8_t[DMR_FRAME_LENGTH_BYTES + 2U]; + ::memcpy(s_idle, IDLE_DATA, DMR_FRAME_LENGTH_BYTES + 2U); // Generate the Slot Type for the Idle frame SlotType slotType; slotType.setColorCode(colorCode); slotType.setDataType(DataType::IDLE); - slotType.encode(m_idle + 2U); + slotType.encode(s_idle + 2U); } /* Sets local configured site data. */ void Slot::setSiteData(::lookups::VoiceChData controlChData, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReg) { - m_siteData = SiteData(SiteModel::SM_SMALL, netId, siteId, 3U, requireReg); - m_channelNo = channelNo; + s_siteData = SiteData(SiteModel::SM_SMALL, netId, siteId, 3U, requireReg); + s_channelNo = channelNo; - std::vector<::lookups::IdenTable> entries = m_idenTable->list(); + std::vector<::lookups::IdenTable> entries = s_idenTable->list(); for (auto entry : entries) { if (entry.channelId() == channelId) { - m_idenEntry = entry; + s_idenEntry = entry; break; } } - m_controlChData = controlChData; + s_controlChData = controlChData; - lc::CSBK::setSiteData(m_siteData); + lc::CSBK::setSiteData(s_siteData); } /* Sets TSCC Aloha configuration. */ void Slot::setAlohaConfig(uint8_t nRandWait, uint8_t backOff) { - m_alohaNRandWait = nRandWait; - m_alohaBackOff = backOff; + s_alohaNRandWait = nRandWait; + s_alohaBackOff = backOff; } // --------------------------------------------------------------------------- @@ -1141,9 +1141,9 @@ void Slot::addFrame(const uint8_t *data, bool net, bool imm) uint32_t fifoSpace = 0U; if (m_slotNo == 1U) { - fifoSpace = m_modem->getDMRSpace1(); + fifoSpace = s_modem->getDMRSpace1(); } else { - fifoSpace = m_modem->getDMRSpace2(); + fifoSpace = s_modem->getDMRSpace2(); } //LogDebugEx(LOG_DMR, "Slot::addFrame()", "Slot %u, fifoSpace = %u", m_slotNo, fifoSpace); @@ -1206,10 +1206,10 @@ void Slot::processFrameLoss() m_slotNo, m_rfFrames, m_rfBits, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); // release trunked grant (if necessary) - Slot* tscc = m_dmr->getTSCCSlot(); + Slot* tscc = s_dmr->getTSCCSlot(); if (tscc != nullptr) { if (tscc->m_enableTSCC && m_rfLC != nullptr) { - tscc->m_affiliations->releaseGrant(m_rfLC->getDstId(), false); + tscc->s_affiliations->releaseGrant(m_rfLC->getDstId(), false); } clearTSCCActivated(); @@ -1242,11 +1242,11 @@ void Slot::processFrameLoss() void Slot::notifyCC_ReleaseGrant(uint32_t dstId) { - if (m_controlChData.address().empty()) { + if (s_controlChData.address().empty()) { return; } - if (m_controlChData.port() == 0) { + if (s_controlChData.port() == 0) { return; } @@ -1255,7 +1255,7 @@ void Slot::notifyCC_ReleaseGrant(uint32_t dstId) } if (m_verbose) { - LogInfoEx(LOG_DMR, "DMR Slot %u, CC %s:%u, notifying CC of call termination, dstId = %u", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); + LogInfoEx(LOG_DMR, "DMR Slot %u, CC %s:%u, notifying CC of call termination, dstId = %u", m_slotNo, s_controlChData.address().c_str(), s_controlChData.port(), dstId); } // callback REST API to release the granted TG on the specified control channel @@ -1266,21 +1266,21 @@ void Slot::notifyCC_ReleaseGrant(uint32_t dstId) g_RPC->req(RPC_RELEASE_DMR_TG, req, [=](json::object& req, json::object& reply) { if (!req["status"].is()) { - ::LogError(LOG_DMR, "DMR Slot %u, failed to notify the CC %s:%u of the release of, dstId = %u, invalid RPC response", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); + ::LogError(LOG_DMR, "DMR Slot %u, failed to notify the CC %s:%u of the release of, dstId = %u, invalid RPC response", m_slotNo, s_controlChData.address().c_str(), s_controlChData.port(), dstId); return; } int status = req["status"].get(); if (status != network::NetRPC::OK) { - ::LogError(LOG_DMR, "DMR Slot %u, failed to notify the CC %s:%u of the release of, dstId = %u", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); + ::LogError(LOG_DMR, "DMR Slot %u, failed to notify the CC %s:%u of the release of, dstId = %u", m_slotNo, s_controlChData.address().c_str(), s_controlChData.port(), dstId); if (req["message"].is()) { std::string retMsg = req["message"].get(); ::LogError(LOG_DMR, "DMR Slot %u, RPC failed, %s", m_slotNo, retMsg.c_str()); } } else - ::LogInfoEx(LOG_DMR, "DMR Slot %u, CC %s:%u, released grant, dstId = %u", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); - }, m_controlChData.address(), m_controlChData.port()); + ::LogInfoEx(LOG_DMR, "DMR Slot %u, CC %s:%u, released grant, dstId = %u", m_slotNo, s_controlChData.address().c_str(), s_controlChData.port(), dstId); + }, s_controlChData.address(), s_controlChData.port()); m_rfLastDstId = 0U; m_rfLastSrcId = 0U; @@ -1292,11 +1292,11 @@ void Slot::notifyCC_ReleaseGrant(uint32_t dstId) void Slot::notifyCC_TouchGrant(uint32_t dstId) { - if (m_controlChData.address().empty()) { + if (s_controlChData.address().empty()) { return; } - if (m_controlChData.port() == 0) { + if (s_controlChData.port() == 0) { return; } @@ -1313,21 +1313,21 @@ void Slot::notifyCC_TouchGrant(uint32_t dstId) g_RPC->req(RPC_TOUCH_DMR_TG, req, [=](json::object& req, json::object& reply) { // validate channelNo is a string within the JSON blob if (!req["status"].is()) { - ::LogError(LOG_DMR, "DMR Slot %u, failed to notify the CC %s:%u of the touch of, dstId = %u, invalid RPC response", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); + ::LogError(LOG_DMR, "DMR Slot %u, failed to notify the CC %s:%u of the touch of, dstId = %u, invalid RPC response", m_slotNo, s_controlChData.address().c_str(), s_controlChData.port(), dstId); return; } int status = req["status"].get(); if (status != network::NetRPC::OK) { - ::LogError(LOG_DMR, "DMR Slot %u, failed to notify the CC %s:%u of the touch of, dstId = %u", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); + ::LogError(LOG_DMR, "DMR Slot %u, failed to notify the CC %s:%u of the touch of, dstId = %u", m_slotNo, s_controlChData.address().c_str(), s_controlChData.port(), dstId); if (req["message"].is()) { std::string retMsg = req["message"].get(); ::LogError(LOG_DMR, "DMR Slot %u, RPC failed, %s", m_slotNo, retMsg.c_str()); } } else - ::LogInfoEx(LOG_DMR, "DMR Slot %u, CC %s:%u, touched grant, dstId = %u", m_slotNo, m_controlChData.address().c_str(), m_controlChData.port(), dstId); - }, m_controlChData.address(), m_controlChData.port()); + ::LogInfoEx(LOG_DMR, "DMR Slot %u, CC %s:%u, touched grant, dstId = %u", m_slotNo, s_controlChData.address().c_str(), s_controlChData.port(), dstId); + }, s_controlChData.address(), s_controlChData.port()); } /* Write data frame to the network. */ @@ -1350,7 +1350,7 @@ void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, FLCO::E flco, if (m_netState != RS_NET_IDLE) return; - if (m_network == nullptr) + if (s_network == nullptr) return; data::NetData dmrData; @@ -1369,7 +1369,7 @@ void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, FLCO::E flco, dmrData.setData(data + 2U); - m_network->writeDMR(dmrData, noSequence); + s_network->writeDMR(dmrData, noSequence); } /* Helper to write RF end of frame data. */ @@ -1380,38 +1380,38 @@ void Slot::writeEndRF(bool writeEnd) if (m_netState == RS_NET_IDLE) { if (m_enableTSCC) - setShortLC_Payload(m_siteData, m_dmr->m_tsccCnt); + setShortLC_Payload(s_siteData, s_dmr->m_tsccCnt); else setShortLC(m_slotNo, 0U); } if (writeEnd) { - if (m_netState == RS_NET_IDLE && m_duplex && !m_rfTimeout) { + if (m_netState == RS_NET_IDLE && s_duplex && !m_rfTimeout) { // Create a dummy start end frame uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; - Sync::addDMRDataSync(data + 2U, m_duplex); + Sync::addDMRDataSync(data + 2U, s_duplex); lc::FullLC fullLC; fullLC.encode(*m_rfLC, data + 2U, DataType::TERMINATOR_WITH_LC); SlotType slotType; - slotType.setColorCode(m_colorCode); + slotType.setColorCode(s_colorCode); slotType.setDataType(DataType::TERMINATOR_WITH_LC); slotType.encode(data + 2U); data[0U] = modem::TAG_EOT; data[1U] = 0x00U; - for (uint32_t i = 0U; i < m_hangCount; i++) + for (uint32_t i = 0U; i < s_hangCount; i++) addFrame(data); } } m_data->m_pduDataOffset = 0U; - if (m_network != nullptr) - m_network->resetDMR(m_slotNo); + if (s_network != nullptr) + s_network->resetDMR(m_slotNo); m_rfTimeoutTimer.stop(); m_rfTimeout = false; @@ -1438,21 +1438,21 @@ void Slot::writeEndNet(bool writeEnd) // Create a dummy start end frame uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; - Sync::addDMRDataSync(data + 2U, m_duplex); + Sync::addDMRDataSync(data + 2U, s_duplex); lc::FullLC fullLC; fullLC.encode(*m_netLC, data + 2U, DataType::TERMINATOR_WITH_LC); SlotType slotType; - slotType.setColorCode(m_colorCode); + slotType.setColorCode(s_colorCode); slotType.setDataType(DataType::TERMINATOR_WITH_LC); slotType.encode(data + 2U); data[0U] = modem::TAG_EOT; data[1U] = 0x00U; - if (m_duplex) { - for (uint32_t i = 0U; i < m_hangCount; i++) + if (s_duplex) { + for (uint32_t i = 0U; i < s_hangCount; i++) addFrame(data, true); } else { @@ -1462,10 +1462,10 @@ void Slot::writeEndNet(bool writeEnd) } // release trunked grant (if necessary) - Slot* tscc = m_dmr->getTSCCSlot(); + Slot* tscc = s_dmr->getTSCCSlot(); if (tscc != nullptr) { if (tscc->m_enableTSCC && m_netLC != nullptr) { - tscc->m_affiliations->releaseGrant(m_netLC->getDstId(), false); + tscc->s_affiliations->releaseGrant(m_netLC->getDstId(), false); } clearTSCCActivated(); @@ -1477,8 +1477,8 @@ void Slot::writeEndNet(bool writeEnd) m_data->m_pduDataOffset = 0U; - if (m_network != nullptr) - m_network->resetDMR(m_slotNo); + if (s_network != nullptr) + s_network->resetDMR(m_slotNo); m_networkWatchdog.stop(); m_netTimeoutTimer.stop(); @@ -1557,11 +1557,11 @@ void Slot::writeRF_ControlData(uint16_t frameCnt, uint8_t n) m_control->writeRF_TSCC_Aloha(); break; case 2: - m_control->writeRF_TSCC_Bcast_Ann_Wd(m_channelNo, true, m_siteData.systemIdentity(), m_siteData.requireReg()); + m_control->writeRF_TSCC_Bcast_Ann_Wd(s_channelNo, true, s_siteData.systemIdentity(), s_siteData.requireReg()); break; case 3: { - std::unordered_map grants = m_affiliations->grantTable(); + std::unordered_map grants = s_affiliations->grantTable(); if (grants.size() > 0) { uint32_t j = 0U; if (m_lastLateEntry > grants.size()) { @@ -1571,8 +1571,8 @@ void Slot::writeRF_ControlData(uint16_t frameCnt, uint8_t n) for (auto entry : grants) { if (j == m_lastLateEntry) { uint32_t dstId = entry.first; - uint32_t srcId = m_affiliations->getGrantedSrcId(dstId); - bool grp = m_affiliations->isGroup(dstId); + uint32_t srcId = s_affiliations->getGrantedSrcId(dstId); + bool grp = s_affiliations->isGroup(dstId); if (m_debug) { LogDebugEx(LOG_DMR, "Slot::writeRF_ControlData()", "frameCnt = %u, seq = %u, late entry, dstId = %u, srcId = %u", frameCnt, n, dstId, srcId); @@ -1646,31 +1646,31 @@ void Slot::clearTSCCActivated() void Slot::setShortLC(uint32_t slotNo, uint32_t id, FLCO::E flco, SLCO_ACT_TYPE actType) { - assert(m_modem != nullptr); + assert(s_modem != nullptr); switch (slotNo) { case 1U: - m_id1 = 0U; - m_flco1 = flco; - m_actType1 = actType; + s_id1 = 0U; + s_flco1 = flco; + s_actType1 = actType; if (id != 0U) { uint8_t buffer[3U]; buffer[0U] = (id << 16) & 0xFFU; buffer[1U] = (id << 8) & 0xFFU; buffer[2U] = (id << 0) & 0xFFU; - m_id1 = edac::CRC::crc8(buffer, 3U); + s_id1 = edac::CRC::crc8(buffer, 3U); } break; case 2U: - m_id2 = 0U; - m_flco2 = flco; - m_actType2 = actType; + s_id2 = 0U; + s_flco2 = flco; + s_actType2 = actType; if (id != 0U) { uint8_t buffer[3U]; buffer[0U] = (id << 16) & 0xFFU; buffer[1U] = (id << 8) & 0xFFU; buffer[2U] = (id << 0) & 0xFFU; - m_id2 = edac::CRC::crc8(buffer, 3U); + s_id2 = edac::CRC::crc8(buffer, 3U); } break; default: @@ -1679,7 +1679,7 @@ void Slot::setShortLC(uint32_t slotNo, uint32_t id, FLCO::E flco, SLCO_ACT_TYPE } // If we have no activity to report, let the modem send the null Short LC when it's ready - if (m_id1 == 0U && m_id2 == 0U) + if (s_id1 == 0U && s_id2 == 0U) return; uint8_t lc[5U]; @@ -1688,35 +1688,35 @@ void Slot::setShortLC(uint32_t slotNo, uint32_t id, FLCO::E flco, SLCO_ACT_TYPE lc[2U] = 0x00U; lc[3U] = 0x00U; - if (m_id1 != 0U) { - lc[2U] = m_id1; - if (m_actType1 == SLCO_ACT_TYPE::VOICE && m_flco1 == FLCO::GROUP) + if (s_id1 != 0U) { + lc[2U] = s_id1; + if (s_actType1 == SLCO_ACT_TYPE::VOICE && s_flco1 == FLCO::GROUP) lc[1U] |= 0x08U; - else if (m_actType1 == SLCO_ACT_TYPE::VOICE && m_flco1 == FLCO::PRIVATE) + else if (s_actType1 == SLCO_ACT_TYPE::VOICE && s_flco1 == FLCO::PRIVATE) lc[1U] |= 0x09U; - else if (m_actType1 == SLCO_ACT_TYPE::DATA && m_flco1 == FLCO::GROUP) + else if (s_actType1 == SLCO_ACT_TYPE::DATA && s_flco1 == FLCO::GROUP) lc[1U] |= 0x0BU; - else if (m_actType1 == SLCO_ACT_TYPE::DATA && m_flco1 == FLCO::PRIVATE) + else if (s_actType1 == SLCO_ACT_TYPE::DATA && s_flco1 == FLCO::PRIVATE) lc[1U] |= 0x0AU; - else if (m_actType1 == SLCO_ACT_TYPE::CSBK && m_flco1 == FLCO::GROUP) + else if (s_actType1 == SLCO_ACT_TYPE::CSBK && s_flco1 == FLCO::GROUP) lc[1U] |= 0x02U; - else if (m_actType1 == SLCO_ACT_TYPE::CSBK && m_flco1 == FLCO::PRIVATE) + else if (s_actType1 == SLCO_ACT_TYPE::CSBK && s_flco1 == FLCO::PRIVATE) lc[1U] |= 0x03U; } - if (m_id2 != 0U) { - lc[3U] = m_id2; - if (m_actType2 == SLCO_ACT_TYPE::VOICE && m_flco2 == FLCO::GROUP) + if (s_id2 != 0U) { + lc[3U] = s_id2; + if (s_actType2 == SLCO_ACT_TYPE::VOICE && s_flco2 == FLCO::GROUP) lc[1U] |= 0x08U; - else if (m_actType2 == SLCO_ACT_TYPE::VOICE && m_flco2 == FLCO::PRIVATE) + else if (s_actType2 == SLCO_ACT_TYPE::VOICE && s_flco2 == FLCO::PRIVATE) lc[1U] |= 0x09U; - else if (m_actType2 == SLCO_ACT_TYPE::DATA && m_flco2 == FLCO::GROUP) + else if (s_actType2 == SLCO_ACT_TYPE::DATA && s_flco2 == FLCO::GROUP) lc[1U] |= 0x0BU; - else if (m_actType2 == SLCO_ACT_TYPE::DATA && m_flco2 == FLCO::PRIVATE) + else if (s_actType2 == SLCO_ACT_TYPE::DATA && s_flco2 == FLCO::PRIVATE) lc[1U] |= 0x0AU; - else if (m_actType2 == SLCO_ACT_TYPE::CSBK && m_flco2 == FLCO::GROUP) + else if (s_actType2 == SLCO_ACT_TYPE::CSBK && s_flco2 == FLCO::GROUP) lc[1U] |= 0x02U; - else if (m_actType2 == SLCO_ACT_TYPE::CSBK && m_flco2 == FLCO::PRIVATE) + else if (s_actType2 == SLCO_ACT_TYPE::CSBK && s_flco2 == FLCO::PRIVATE) lc[1U] |= 0x03U; } @@ -1727,14 +1727,14 @@ void Slot::setShortLC(uint32_t slotNo, uint32_t id, FLCO::E flco, SLCO_ACT_TYPE lc::ShortLC shortLC; shortLC.encode(lc, sLC); - m_modem->writeDMRShortLC(sLC); + s_modem->writeDMRShortLC(sLC); } /* Helper to set the DMR short LC for TSCC. */ void Slot::setShortLC_TSCC(SiteData siteData, uint16_t counter) { - assert(m_modem != nullptr); + assert(s_modem != nullptr); uint8_t lc[5U]; uint32_t lcValue = 0U; @@ -1787,14 +1787,14 @@ void Slot::setShortLC_TSCC(SiteData siteData, uint16_t counter) lc::ShortLC shortLC; shortLC.encode(lc, sLC); - m_modem->writeDMRShortLC(sLC); + s_modem->writeDMRShortLC(sLC); } /* Helper to set the DMR short LC for payload. */ void Slot::setShortLC_Payload(SiteData siteData, uint16_t counter) { - assert(m_modem != nullptr); + assert(s_modem != nullptr); uint8_t lc[5U]; uint32_t lcValue = 0U; @@ -1847,5 +1847,5 @@ void Slot::setShortLC_Payload(SiteData siteData, uint16_t counter) lc::ShortLC shortLC; shortLC.encode(lc, sLC); - m_modem->writeDMRShortLC(sLC); + s_modem->writeDMRShortLC(sLC); } diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 88430dee..3f139536 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -444,39 +444,39 @@ namespace dmr bool m_verbose; bool m_debug; - static Control* m_dmr; + static Control* s_dmr; - static bool m_authoritative; + static bool s_authoritative; - static uint32_t m_colorCode; + static uint32_t s_colorCode; - static SiteData m_siteData; - static uint32_t m_channelNo; + static SiteData s_siteData; + static uint32_t s_channelNo; - static bool m_embeddedLCOnly; - static bool m_dumpTAData; + static bool s_embeddedLCOnly; + static bool s_dumpTAData; - static modem::Modem* m_modem; - static network::Network* m_network; + static modem::Modem* s_modem; + static network::Network* s_network; - static bool m_duplex; + static bool s_duplex; - static ::lookups::IdenTableLookup* m_idenTable; - static ::lookups::RadioIdLookup* m_ridLookup; - static ::lookups::TalkgroupRulesLookup* m_tidLookup; - static lookups::DMRAffiliationLookup* m_affiliations; - static ::lookups::VoiceChData m_controlChData; + static ::lookups::IdenTableLookup* s_idenTable; + static ::lookups::RadioIdLookup* s_ridLookup; + static ::lookups::TalkgroupRulesLookup* s_tidLookup; + static lookups::DMRAffiliationLookup* s_affiliations; + static ::lookups::VoiceChData s_controlChData; - static ::lookups::IdenTable m_idenEntry; + static ::lookups::IdenTable s_idenEntry; - static uint32_t m_hangCount; + static uint32_t s_hangCount; - static ::lookups::RSSIInterpolator* m_rssiMapper; + static ::lookups::RSSIInterpolator* s_rssiMapper; - static uint32_t m_jitterTime; - static uint32_t m_jitterSlots; + static uint32_t s_jitterTime; + static uint32_t s_jitterSlots; - static uint8_t* m_idle; + static uint8_t* s_idle; /** * @brief Short LC Activity Type @@ -488,18 +488,18 @@ namespace dmr CSBK //!< Slot Activity Type - CSBK }; - static defines::FLCO::E m_flco1; - static uint8_t m_id1; - static SLCO_ACT_TYPE m_actType1; + static defines::FLCO::E s_flco1; + static uint8_t s_id1; + static SLCO_ACT_TYPE s_actType1; - static defines::FLCO::E m_flco2; - static uint8_t m_id2; - static SLCO_ACT_TYPE m_actType2; + static defines::FLCO::E s_flco2; + static uint8_t s_id2; + static SLCO_ACT_TYPE s_actType2; - static bool m_verifyReg; + static bool s_verifyReg; - static uint8_t m_alohaNRandWait; - static uint8_t m_alohaBackOff; + static uint8_t s_alohaNRandWait; + static uint8_t s_alohaBackOff; /** * @brief Add data frame to the data ring buffer. diff --git a/src/host/dmr/packet/ControlSignaling.cpp b/src/host/dmr/packet/ControlSignaling.cpp index 1d3cb5dc..572f9ca3 100644 --- a/src/host/dmr/packet/ControlSignaling.cpp +++ b/src/host/dmr/packet/ControlSignaling.cpp @@ -35,7 +35,7 @@ using namespace dmr::packet; // Make sure control data is supported. #define IS_SUPPORT_CONTROL_CHECK(_PCKT_STR, _PCKT, _SRCID) \ - if (!m_slot->m_dmr->getTSCCSlot()->m_enableTSCC) { \ + if (!m_slot->s_dmr->getTSCCSlot()->m_enableTSCC) { \ LogWarning(LOG_RF, "DMR Slot %u, %s denial, unsupported service, srcId = %u", m_slot->m_slotNo, _PCKT_STR.c_str(), _SRCID); \ writeRF_CSBK_ACK_RSP(_SRCID, ReasonCode::TS_DENY_RSN_SYS_UNSUPPORTED_SVC, 0U); \ return false; \ @@ -67,7 +67,7 @@ using namespace dmr::packet; // Verify the source RID is registered. #define VERIFY_SRCID_REG(_PCKT_STR, _PCKT, _SRCID) \ - if (!m_slot->m_affiliations->isUnitReg(_SRCID) && m_slot->m_verifyReg) { \ + if (!m_slot->s_affiliations->isUnitReg(_SRCID) && m_slot->s_verifyReg) { \ LogWarning(LOG_RF, "DMR Slot %u, %s denial, RID not registered, srcId = %u", m_slot->m_slotNo, _PCKT_STR.c_str(), _SRCID); \ writeRF_CSBK_ACK_RSP(_SRCID, ReasonCode::TS_DENY_RSN_PERM_USER_REFUSED, 0U); \ return false; \ @@ -118,7 +118,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) DataType::E dataType = (DataType::E)(data[1U] & 0x0FU); SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(dataType); if (dataType == DataType::CSBK) { @@ -135,7 +135,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) uint32_t srcId = csbk->getSrcId(); uint32_t dstId = csbk->getDstId(); - m_slot->m_affiliations->touchUnitReg(srcId); + m_slot->s_affiliations->touchUnitReg(srcId); if (srcId != 0U || dstId != 0U) { // don't process RF frames if the network isn't in a idle state and the RF destination is the network destination @@ -165,7 +165,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) if (m_slot->m_netState == RS_NET_IDLE && csbk->getDataContent()) { m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, Slot::SLCO_ACT_TYPE::DATA); } else { - m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, Slot::SLCO_ACT_TYPE::CSBK); + m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, Slot::SLCO_ACT_TYPE::CSBK); } bool handled = false; @@ -210,11 +210,11 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_WAIT_RSN, 1U); - if (m_slot->m_authoritative) { + if (m_slot->s_authoritative) { writeRF_CSBK_Grant(srcId, dstId, isp->getServiceOptions(), false); } else { - if (m_slot->m_network != nullptr) - m_slot->m_network->writeGrantReq(modem::DVM_STATE::STATE_DMR, srcId, dstId, m_slot->m_slotNo, true); + if (m_slot->s_network != nullptr) + m_slot->s_network->writeGrantReq(modem::DVM_STATE::STATE_DMR, srcId, dstId, m_slot->m_slotNo, true); } break; case ServiceKind::GRP_VOICE_CALL: @@ -229,11 +229,11 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_WAIT_RSN, 1U); - if (m_slot->m_authoritative) { + if (m_slot->s_authoritative) { writeRF_CSBK_Grant(srcId, dstId, isp->getServiceOptions(), true); } else { - if (m_slot->m_network != nullptr) - m_slot->m_network->writeGrantReq(modem::DVM_STATE::STATE_DMR, srcId, dstId, m_slot->m_slotNo, false); + if (m_slot->s_network != nullptr) + m_slot->s_network->writeGrantReq(modem::DVM_STATE::STATE_DMR, srcId, dstId, m_slot->m_slotNo, false); } break; case ServiceKind::IND_DATA_CALL: @@ -359,14 +359,14 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) slotType.encode(data + 2U); // convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); m_slot->m_rfSeqNo = 0U; data[0U] = modem::TAG_DATA; data[1U] = 0x00U; - if (m_slot->m_duplex) + if (m_slot->s_duplex) m_slot->addFrame(data); m_slot->writeNetwork(data, DataType::CSBK, gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId, 0U, 0U, true); @@ -406,7 +406,7 @@ void ControlSignaling::processNetwork(const data::NetData& dmrData) return; } - if (osp->getSystemId() != m_slot->m_siteData.systemIdentity()) { + if (osp->getSystemId() != m_slot->s_siteData.systemIdentity()) { // update site table data AdjSiteData site; try { @@ -481,14 +481,14 @@ void ControlSignaling::processNetwork(const data::NetData& dmrData) case ServiceKind::IND_VOICE_CALL: writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_WAIT_RSN, 1U); - if (!m_slot->m_affiliations->isGranted(dstId)) { + if (!m_slot->s_affiliations->isGranted(dstId)) { writeRF_CSBK_Grant(srcId, dstId, isp->getServiceOptions(), false, true); } break; case ServiceKind::GRP_VOICE_CALL: writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_WAIT_RSN, 1U); - if (!m_slot->m_affiliations->isGranted(dstId)) { + if (!m_slot->s_affiliations->isGranted(dstId)) { writeRF_CSBK_Grant(srcId, dstId, isp->getServiceOptions(), true, true); } break; @@ -582,11 +582,11 @@ void ControlSignaling::processNetwork(const data::NetData& dmrData) // regenerate the Slot Type SlotType slotType; slotType.decode(data + 2U); - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.encode(data + 2U); // convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); data[0U] = modem::TAG_DATA; data[1U] = 0x00U; @@ -608,20 +608,20 @@ void ControlSignaling::writeAdjSSNetwork() return; } - if (m_slot->m_network != nullptr) { + if (m_slot->s_network != nullptr) { // transmit adjacent site broadcast std::unique_ptr csbk = std::make_unique(); - csbk->siteIdenEntry(m_slot->m_idenEntry); + csbk->siteIdenEntry(m_slot->s_idenEntry); csbk->setCdef(false); csbk->setAnncType(BroadcastAnncType::ANN_WD_TSCC); - csbk->setLogicalCh1(m_slot->m_channelNo); + csbk->setLogicalCh1(m_slot->s_channelNo); csbk->setAnnWdCh1(true); - csbk->setSystemId(m_slot->m_siteData.systemIdentity()); - csbk->setRequireReg(m_slot->m_siteData.requireReg()); + csbk->setSystemId(m_slot->s_siteData.systemIdentity()); + csbk->setRequireReg(m_slot->s_siteData.requireReg()); if (m_verbose) { LogInfoEx(LOG_NET, "DMR Slot %u, CSBK, %s, network announce, sysId = $%03X, chNo = %u", m_slot->m_slotNo, csbk->toString().c_str(), - m_slot->m_siteData.systemIdentity(), m_slot->m_channelNo); + m_slot->s_siteData.systemIdentity(), m_slot->s_channelNo); } writeNet_CSBK(csbk.get()); @@ -710,7 +710,7 @@ void ControlSignaling::writeRF_CSBK(lc::CSBK* csbk, bool imm) ::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES); SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(DataType::CSBK); // Regenerate the CSBK data @@ -720,14 +720,14 @@ void ControlSignaling::writeRF_CSBK(lc::CSBK* csbk, bool imm) slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); m_slot->m_rfSeqNo = 0U; data[0U] = modem::TAG_DATA; data[1U] = 0x00U; - if (m_slot->m_duplex) + if (m_slot->s_duplex) m_slot->addFrame(data, false, imm); } @@ -739,7 +739,7 @@ void ControlSignaling::writeNet_CSBK(lc::CSBK* csbk) ::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES); SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(DataType::CSBK); // Regenerate the CSBK data @@ -756,7 +756,7 @@ void ControlSignaling::writeNet_CSBK(lc::CSBK* csbk) data[0U] = modem::TAG_DATA; data[1U] = 0x00U; - if (m_slot->m_duplex) + if (m_slot->s_duplex) m_slot->addFrame(data); m_slot->writeNetwork(data, DataType::CSBK, csbk->getGI() ? FLCO::GROUP : FLCO::PRIVATE, csbk->getSrcId(), csbk->getDstId(), 0U, 0U, true); @@ -808,7 +808,7 @@ void ControlSignaling::writeRF_CSBK_NACK_RSP(uint32_t dstId, uint8_t reason, uin bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp, bool net, bool skip, uint32_t chNo) { - Slot* tscc = m_slot->m_dmr->getTSCCSlot(); + Slot* tscc = m_slot->s_dmr->getTSCCSlot(); uint8_t slot = 0U; @@ -859,15 +859,15 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } - if (!tscc->m_affiliations->isGranted(dstId)) { - ::lookups::TalkgroupRuleGroupVoice groupVoice = tscc->m_tidLookup->find(dstId); + if (!tscc->s_affiliations->isGranted(dstId)) { + ::lookups::TalkgroupRuleGroupVoice groupVoice = tscc->s_tidLookup->find(dstId); slot = groupVoice.source().tgSlot(); if (grp && !tscc->m_ignoreAffiliationCheck) { // is this an affiliation required group? - ::lookups::TalkgroupRuleGroupVoice tid = tscc->m_tidLookup->find(dstId, slot); + ::lookups::TalkgroupRuleGroupVoice tid = tscc->s_tidLookup->find(dstId, slot); if (tid.config().affiliated()) { - if (!tscc->m_affiliations->hasGroupAff(dstId)) { + if (!tscc->s_affiliations->hasGroupAff(dstId)) { LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_VOICE_CALL (Group Voice Call) ignored, no group affiliations, dstId = %u", tscc->m_slotNo, dstId); return false; } @@ -876,14 +876,14 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (!grp && !tscc->m_ignoreAffiliationCheck) { // is this the target registered? - if (!tscc->m_affiliations->isUnitReg(dstId)) { + if (!tscc->s_affiliations->isUnitReg(dstId)) { LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_VOICE_CALL (Individual Voice Call) ignored, no unit registration, dstId = %u", tscc->m_slotNo, dstId); return false; } } - uint32_t availChNo = tscc->m_affiliations->getAvailableChannelForSlot(slot); - if (!tscc->m_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) { + uint32_t availChNo = tscc->s_affiliations->getAvailableChannelForSlot(slot); + if (!tscc->s_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) { if (grp) { if (!net) { LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_VOICE_CALL (Group Voice Call) queued, no channels available, dstId = %u", tscc->m_slotNo, dstId); @@ -908,10 +908,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } else { - if (tscc->m_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) { - chNo = tscc->m_affiliations->getGrantedCh(dstId); - slot = tscc->m_affiliations->getGrantedSlot(dstId); - //tscc->m_siteData.setChCnt(tscc->m_affiliations->getRFChCnt() + tscc->m_affiliations->getGrantedRFChCnt()); + if (tscc->s_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) { + chNo = tscc->s_affiliations->getGrantedCh(dstId); + slot = tscc->s_affiliations->getGrantedSlot(dstId); + //tscc->s_siteData.setChCnt(tscc->s_affiliations->getRFChCnt() + tscc->s_affiliations->getGrantedRFChCnt()); } } } @@ -919,7 +919,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (!tscc->m_disableGrantSrcIdCheck && !net) { // do collision check between grants to see if a SU is attempting a "grant retry" or if this is a // different source from the original grant - uint32_t grantedSrcId = tscc->m_affiliations->getGrantedSrcId(dstId); + uint32_t grantedSrcId = tscc->s_affiliations->getGrantedSrcId(dstId); if (srcId != grantedSrcId) { if (!net) { LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", tscc->m_slotNo, dstId); @@ -933,18 +933,18 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } - chNo = tscc->m_affiliations->getGrantedCh(dstId); - slot = tscc->m_affiliations->getGrantedSlot(dstId); + chNo = tscc->s_affiliations->getGrantedCh(dstId); + slot = tscc->s_affiliations->getGrantedSlot(dstId); - tscc->m_affiliations->touchGrant(dstId); + tscc->s_affiliations->touchGrant(dstId); } } else { - if (tscc->m_affiliations->isGranted(dstId)) { - chNo = tscc->m_affiliations->getGrantedCh(dstId); - slot = tscc->m_affiliations->getGrantedSlot(dstId); + if (tscc->s_affiliations->isGranted(dstId)) { + chNo = tscc->s_affiliations->getGrantedCh(dstId); + slot = tscc->s_affiliations->getGrantedSlot(dstId); - tscc->m_affiliations->touchGrant(dstId); + tscc->s_affiliations->touchGrant(dstId); } else { return false; @@ -957,9 +957,9 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } // callback RPC to permit the granted TG on the specified voice channel - if (tscc->m_authoritative && tscc->m_supervisor && - tscc->m_channelNo != chNo) { - ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (tscc->s_authoritative && tscc->m_supervisor && + tscc->s_channelNo != chNo) { + ::lookups::VoiceChData voiceChData = tscc->s_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); @@ -987,7 +987,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (requestFailed) { ::LogError((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); - tscc->m_affiliations->releaseGrant(dstId, false); + tscc->s_affiliations->releaseGrant(dstId, false); if (!net) { writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); m_slot->m_rfState = RS_RF_REJECTED; @@ -1023,8 +1023,8 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ writeRF_CSBK_Imm(csbk.get()); // if the channel granted isn't the same as the TSCC; remote activate the payload channel - if (chNo != tscc->m_channelNo) { - ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (chNo != tscc->s_channelNo) { + ::lookups::VoiceChData voiceChData = tscc->s_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); @@ -1043,7 +1043,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } else { - m_slot->m_dmr->tsccActivateSlot(slot, dstId, srcId, grp, true); + m_slot->s_dmr->tsccActivateSlot(slot, dstId, srcId, grp, true); } } else { @@ -1052,9 +1052,9 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } // callback RPC to permit the granted TG on the specified voice channel - if (tscc->m_authoritative && tscc->m_supervisor && - tscc->m_channelNo != chNo) { - ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (tscc->s_authoritative && tscc->m_supervisor && + tscc->s_channelNo != chNo) { + ::lookups::VoiceChData voiceChData = tscc->s_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); @@ -1082,7 +1082,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (requestFailed) { ::LogError((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); - tscc->m_affiliations->releaseGrant(dstId, false); + tscc->s_affiliations->releaseGrant(dstId, false); if (!net) { writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); m_slot->m_rfState = RS_RF_REJECTED; @@ -1116,8 +1116,8 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ writeRF_CSBK_Imm(csbk.get()); // if the channel granted isn't the same as the TSCC; remote activate the payload channel - if (chNo != tscc->m_channelNo) { - ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (chNo != tscc->s_channelNo) { + ::lookups::VoiceChData voiceChData = tscc->s_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); @@ -1136,7 +1136,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } else { - m_slot->m_dmr->tsccActivateSlot(slot, dstId, srcId, grp, true); + m_slot->s_dmr->tsccActivateSlot(slot, dstId, srcId, grp, true); } } @@ -1147,7 +1147,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp, bool net, bool skip, uint32_t chNo) { - Slot* tscc = m_slot->m_dmr->getTSCCSlot(); + Slot* tscc = m_slot->s_dmr->getTSCCSlot(); uint8_t slot = 0U; @@ -1198,12 +1198,12 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u } } - if (!tscc->m_affiliations->isGranted(dstId)) { - ::lookups::TalkgroupRuleGroupVoice groupVoice = tscc->m_tidLookup->find(dstId); + if (!tscc->s_affiliations->isGranted(dstId)) { + ::lookups::TalkgroupRuleGroupVoice groupVoice = tscc->s_tidLookup->find(dstId); slot = groupVoice.source().tgSlot(); - uint32_t availChNo = tscc->m_affiliations->getAvailableChannelForSlot(slot); - if (!tscc->m_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) { + uint32_t availChNo = tscc->s_affiliations->getAvailableChannelForSlot(slot); + if (!tscc->s_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) { if (grp) { if (!net) { LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_DATA_CALL (Group Data Call) queued, no channels available, dstId = %u", tscc->m_slotNo, dstId); @@ -1228,19 +1228,19 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u } } else { - if (tscc->m_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) { - chNo = tscc->m_affiliations->getGrantedCh(dstId); - slot = tscc->m_affiliations->getGrantedSlot(dstId); + if (tscc->s_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) { + chNo = tscc->s_affiliations->getGrantedCh(dstId); + slot = tscc->s_affiliations->getGrantedSlot(dstId); //tscc->m_siteData.setChCnt(tscc->m_affiliations->getRFChCnt() + tscc->m_affiliations->getGrantedRFChCnt()); } } } else { - chNo = tscc->m_affiliations->getGrantedCh(dstId); - slot = tscc->m_affiliations->getGrantedSlot(dstId); + chNo = tscc->s_affiliations->getGrantedCh(dstId); + slot = tscc->s_affiliations->getGrantedSlot(dstId); - tscc->m_affiliations->touchGrant(dstId); + tscc->s_affiliations->touchGrant(dstId); } } @@ -1270,8 +1270,8 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u writeRF_CSBK_Imm(csbk.get()); // if the channel granted isn't the same as the TSCC; remote activate the payload channel - if (chNo != tscc->m_channelNo) { - ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (chNo != tscc->s_channelNo) { + ::lookups::VoiceChData voiceChData = tscc->s_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); @@ -1290,7 +1290,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u } } else { - m_slot->m_dmr->tsccActivateSlot(slot, dstId, srcId, grp, false); + m_slot->s_dmr->tsccActivateSlot(slot, dstId, srcId, grp, false); } } else { @@ -1318,8 +1318,8 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u writeRF_CSBK_Imm(csbk.get()); // if the channel granted isn't the same as the TSCC; remote activate the payload channel - if (chNo != tscc->m_channelNo) { - ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (chNo != tscc->s_channelNo) { + ::lookups::VoiceChData voiceChData = tscc->s_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); @@ -1338,7 +1338,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u } } else { - m_slot->m_dmr->tsccActivateSlot(slot, dstId, srcId, grp, false); + m_slot->s_dmr->tsccActivateSlot(slot, dstId, srcId, grp, false); } } @@ -1349,7 +1349,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOptions) { - Slot* tscc = m_slot->m_dmr->getTSCCSlot(); + Slot* tscc = m_slot->s_dmr->getTSCCSlot(); bool dereg = (serviceOptions & 0x01U) == 0x01U; uint8_t powerSave = (serviceOptions >> 1) & 0x07U; @@ -1380,10 +1380,10 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt } // remove dynamic unit registration table entry - m_slot->m_affiliations->unitDereg(srcId); + m_slot->s_affiliations->unitDereg(srcId); -// if (m_slot->m_network != nullptr) -// m_slot->m_network->announceUnitDeregistration(srcId); +// if (m_slot->s_network != nullptr) +// m_slot->s_network->announceUnitDeregistration(srcId); csbk->setReason(ReasonCode::TS_ACK_RSN_REG); } @@ -1406,12 +1406,12 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt ::ActivityLog("DMR", true, "unit registration request from %u", srcId); // update dynamic unit registration table - if (!m_slot->m_affiliations->isUnitReg(srcId)) { - m_slot->m_affiliations->unitReg(srcId); + if (!m_slot->s_affiliations->isUnitReg(srcId)) { + m_slot->s_affiliations->unitReg(srcId); } - if (m_slot->m_network != nullptr) - m_slot->m_network->announceUnitRegistration(srcId); + if (m_slot->s_network != nullptr) + m_slot->s_network->announceUnitRegistration(srcId); } } @@ -1425,10 +1425,10 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt void ControlSignaling::writeRF_CSBK_Grant_LateEntry(uint32_t dstId, uint32_t srcId, bool grp) { - Slot* tscc = m_slot->m_dmr->getTSCCSlot(); + Slot* tscc = m_slot->s_dmr->getTSCCSlot(); - uint32_t chNo = tscc->m_affiliations->getGrantedCh(dstId); - uint8_t slot = tscc->m_affiliations->getGrantedSlot(dstId); + uint32_t chNo = tscc->s_affiliations->getGrantedCh(dstId); + uint8_t slot = tscc->s_affiliations->getGrantedSlot(dstId); if (grp) { std::unique_ptr csbk = std::make_unique(); @@ -1480,7 +1480,7 @@ void ControlSignaling::writeRF_CSBK_Payload_Activate(uint32_t dstId, uint32_t sr csbk->setLastBlock(true); - csbk->setLogicalCh1(m_slot->m_channelNo); + csbk->setLogicalCh1(m_slot->s_channelNo); csbk->setSlotNo(m_slot->m_slotNo); csbk->setSrcId(srcId); @@ -1491,7 +1491,7 @@ void ControlSignaling::writeRF_CSBK_Payload_Activate(uint32_t dstId, uint32_t sr m_slot->m_slotNo, csbk->toString().c_str(), csbk->getCSBKO(), csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } - m_slot->setShortLC_Payload(m_slot->m_siteData, 1U); + m_slot->setShortLC_Payload(m_slot->s_siteData, 1U); for (uint8_t i = 0; i < 2U; i++) writeRF_CSBK(csbk.get(), imm); } @@ -1506,7 +1506,7 @@ void ControlSignaling::writeRF_CSBK_Payload_Clear(uint32_t dstId, uint32_t srcId csbk->setLastBlock(true); - csbk->setLogicalCh1(m_slot->m_channelNo); + csbk->setLogicalCh1(m_slot->s_channelNo); csbk->setSlotNo(m_slot->m_slotNo); csbk->setSrcId(srcId); @@ -1527,8 +1527,8 @@ void ControlSignaling::writeRF_TSCC_Aloha() { std::unique_ptr csbk = std::make_unique(); DEBUG_LOG_CSBK(csbk->toString()); - csbk->setNRandWait(m_slot->m_alohaNRandWait); - csbk->setBackoffNo(m_slot->m_alohaBackOff); + csbk->setNRandWait(m_slot->s_alohaNRandWait); + csbk->setBackoffNo(m_slot->s_alohaBackOff); writeRF_CSBK(csbk.get()); } @@ -1540,7 +1540,7 @@ void ControlSignaling::writeRF_TSCC_Bcast_Ann_Wd(uint32_t channelNo, bool annWd, m_slot->m_rfSeqNo = 0U; std::unique_ptr csbk = std::make_unique(); - csbk->siteIdenEntry(m_slot->m_idenEntry); + csbk->siteIdenEntry(m_slot->s_idenEntry); csbk->setCdef(false); csbk->setAnncType(BroadcastAnncType::ANN_WD_TSCC); csbk->setLogicalCh1(channelNo); diff --git a/src/host/dmr/packet/Data.cpp b/src/host/dmr/packet/Data.cpp index 7ee6f386..6d602d55 100644 --- a/src/host/dmr/packet/Data.cpp +++ b/src/host/dmr/packet/Data.cpp @@ -45,7 +45,7 @@ bool Data::process(uint8_t* data, uint32_t len) DataType::E dataType = (DataType::E)(data[1U] & 0x0FU); SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(dataType); if (dataType == DataType::TERMINATOR_WITH_LC) { @@ -60,7 +60,7 @@ bool Data::process(uint8_t* data, uint32_t len) slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); if (!m_slot->m_rfTimeout) { data[0U] = modem::TAG_EOT; @@ -68,8 +68,8 @@ bool Data::process(uint8_t* data, uint32_t len) m_slot->writeNetwork(data, DataType::TERMINATOR_WITH_LC, 0U); - if (m_slot->m_duplex) { - for (uint32_t i = 0U; i < m_slot->m_hangCount; i++) + if (m_slot->s_duplex) { + for (uint32_t i = 0U; i < m_slot->s_hangCount; i++) m_slot->addFrame(data); } } @@ -79,10 +79,10 @@ bool Data::process(uint8_t* data, uint32_t len) } // release trunked grant (if necessary) - Slot *m_tscc = m_slot->m_dmr->getTSCCSlot(); + Slot *m_tscc = m_slot->s_dmr->getTSCCSlot(); if (m_tscc != nullptr) { if (m_tscc->m_enableTSCC) { - m_tscc->m_affiliations->releaseGrant(m_slot->m_rfLC->getDstId(), false); + m_tscc->s_affiliations->releaseGrant(m_slot->m_rfLC->getDstId(), false); m_slot->clearTSCCActivated(); } } @@ -100,7 +100,7 @@ bool Data::process(uint8_t* data, uint32_t len) LogInfoEx(LOG_RF, "DMR Slot %u, total frames: %d, total bits: %d, errors: %d, BER: %.4f%%", m_slot->m_slotNo, m_slot->m_rfFrames, m_slot->m_rfBits, m_slot->m_rfErrs, float(m_slot->m_rfErrs * 100U) / float(m_slot->m_rfBits)); - m_slot->m_dmr->tsccClearActivatedSlot(m_slot->m_slotNo); + m_slot->s_dmr->tsccClearActivatedSlot(m_slot->m_slotNo); if (m_slot->m_rfTimeout) { m_slot->writeEndRF(); @@ -124,7 +124,7 @@ bool Data::process(uint8_t* data, uint32_t len) uint32_t srcId = m_rfDataHeader.getSrcId(); uint32_t dstId = m_rfDataHeader.getDstId(); - if (!m_slot->m_authoritative && m_slot->m_permittedDstId != dstId) { + if (!m_slot->s_authoritative && m_slot->m_permittedDstId != dstId) { if (!g_disableNonAuthoritativeLogging) LogWarning(LOG_RF, "[NON-AUTHORITATIVE] Ignoring RF traffic, destination not permitted!"); m_slot->m_rfState = RS_RF_LISTENING; @@ -139,7 +139,7 @@ bool Data::process(uint8_t* data, uint32_t len) } if (m_slot->m_enableTSCC && dstId == m_slot->m_netLastDstId) { - if (m_slot->m_affiliations->isNetGranted(dstId)) { + if (m_slot->s_affiliations->isNetGranted(dstId)) { LogWarning(LOG_RF, "DMR Slot %u, Traffic collision detect, preempting new RF traffic to existing granted network traffic (Are we in a voting condition?)", m_slot->m_slotNo); m_slot->m_rfState = RS_RF_LISTENING; return false; @@ -222,12 +222,12 @@ bool Data::process(uint8_t* data, uint32_t len) slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); data[0U] = m_slot->m_rfFrames == 0U ? modem::TAG_EOT : modem::TAG_DATA; data[1U] = 0x00U; - if (m_slot->m_duplex && m_repeatDataPacket) + if (m_slot->s_duplex && m_repeatDataPacket) m_slot->addFrame(data); uint8_t controlByte = 0U; @@ -319,11 +319,11 @@ bool Data::process(uint8_t* data, uint32_t len) slotType.encode(data + 2U); // convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); m_slot->writeNetwork(data, dataType, 0U); - if (m_slot->m_duplex && m_repeatDataPacket) { + if (m_slot->s_duplex && m_repeatDataPacket) { m_slot->addFrame(data); } @@ -357,19 +357,19 @@ void Data::processNetwork(const data::NetData& dmrData) // Regenerate the Slot Type SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(DataType::TERMINATOR_WITH_LC); slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); if (!m_slot->m_netTimeout) { data[0U] = modem::TAG_EOT; data[1U] = 0x00U; - if (m_slot->m_duplex) { - for (uint32_t i = 0U; i < m_slot->m_hangCount; i++) + if (m_slot->s_duplex) { + for (uint32_t i = 0U; i < m_slot->s_hangCount; i++) m_slot->addFrame(data, true); } else { @@ -383,10 +383,10 @@ void Data::processNetwork(const data::NetData& dmrData) } // release trunked grant (if necessary) - Slot *m_tscc = m_slot->m_dmr->getTSCCSlot(); + Slot *m_tscc = m_slot->s_dmr->getTSCCSlot(); if (m_tscc != nullptr) { if (m_tscc->m_enableTSCC) { - m_tscc->m_affiliations->releaseGrant(m_slot->m_netLC->getDstId(), false); + m_tscc->s_affiliations->releaseGrant(m_slot->m_netLC->getDstId(), false); m_slot->clearTSCCActivated(); } } @@ -396,7 +396,7 @@ void Data::processNetwork(const data::NetData& dmrData) ::ActivityLog("DMR", false, "Slot %u network end of voice transmission, %.1f seconds, %u%% packet loss, BER: %.1f%%", m_slot->m_slotNo, float(m_slot->m_netFrames) / 16.667F, (m_slot->m_netLost * 100U) / m_slot->m_netFrames, float(m_slot->m_netErrs * 100U) / float(m_slot->m_netBits)); - m_slot->m_dmr->tsccClearActivatedSlot(m_slot->m_slotNo); + m_slot->s_dmr->tsccClearActivatedSlot(m_slot->m_slotNo); m_slot->writeEndNet(); } @@ -415,7 +415,7 @@ void Data::processNetwork(const data::NetData& dmrData) uint32_t srcId = m_netDataHeader.getSrcId(); uint32_t dstId = m_netDataHeader.getDstId(); - if (!m_slot->m_authoritative && m_slot->m_permittedDstId != dstId) { + if (!m_slot->s_authoritative && m_slot->m_permittedDstId != dstId) { return; } @@ -476,19 +476,19 @@ void Data::processNetwork(const data::NetData& dmrData) // Regenerate the Slot Type SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(DataType::DATA_HEADER); slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); data[0U] = m_slot->m_netFrames == 0U ? modem::TAG_EOT : modem::TAG_DATA; data[1U] = 0x00U; // Put a small delay into starting transmission - m_slot->addFrame(m_slot->m_idle, true); - m_slot->addFrame(m_slot->m_idle, true); + m_slot->addFrame(m_slot->s_idle, true); + m_slot->addFrame(m_slot->s_idle, true); m_slot->addFrame(data, true); @@ -577,11 +577,11 @@ void Data::processNetwork(const data::NetData& dmrData) // regenerate the Slot Type SlotType slotType; slotType.decode(data + 2U); - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.encode(data + 2U); // convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); data[0U] = m_slot->m_netFrames == 0U ? modem::TAG_EOT : modem::TAG_DATA; data[1U] = 0x00U; @@ -655,21 +655,21 @@ void Data::writeRF_PDU(DataType::E dataType, const uint8_t* pdu) ::memcpy(data, pdu, DMR_FRAME_LENGTH_BYTES); SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(dataType); // Regenerate the Slot Type slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); m_slot->m_rfSeqNo = 0U; data[0U] = modem::TAG_DATA; data[1U] = 0x00U; - if (m_slot->m_duplex) + if (m_slot->s_duplex) m_slot->addFrame(data); } diff --git a/src/host/dmr/packet/Voice.cpp b/src/host/dmr/packet/Voice.cpp index 1d5fb164..e5b55e09 100644 --- a/src/host/dmr/packet/Voice.cpp +++ b/src/host/dmr/packet/Voice.cpp @@ -35,7 +35,7 @@ using namespace dmr::packet; // Helper macro to check if the host is authoritative and the destination ID is permitted. #define CHECK_AUTHORITATIVE(_DST_ID) \ - if (!m_slot->m_authoritative && m_slot->m_permittedDstId != _DST_ID) { \ + if (!m_slot->s_authoritative && m_slot->m_permittedDstId != _DST_ID) { \ if (!g_disableNonAuthoritativeLogging) \ LogWarning(LOG_RF, "[NON-AUTHORITATIVE] Ignoring RF traffic, destination not permitted, dstId = %u", _DST_ID); \ m_slot->m_rfState = RS_RF_LISTENING; \ @@ -44,7 +44,7 @@ using namespace dmr::packet; // Helper macro to check if the host is authoritative and the destination ID is permitted. #define CHECK_NET_AUTHORITATIVE(_DST_ID) \ - if (!m_slot->m_authoritative && m_slot->m_permittedDstId != _DST_ID) { \ + if (!m_slot->s_authoritative && m_slot->m_permittedDstId != _DST_ID) { \ return; \ } @@ -65,7 +65,7 @@ bool Voice::process(uint8_t* data, uint32_t len) DataType::E dataType = (DataType::E)(data[1U] & 0x0FU); SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(dataType); if (dataType == DataType::VOICE_LC_HEADER) { @@ -123,20 +123,20 @@ bool Voice::process(uint8_t* data, uint32_t len) // are we auto-registering legacy radios to groups? if (m_slot->m_legacyGroupReg) { - if (!m_slot->m_affiliations->isGroupAff(srcId, dstId)) { + if (!m_slot->s_affiliations->isGroupAff(srcId, dstId)) { // update dynamic unit registration table - if (!m_slot->m_affiliations->isUnitReg(srcId)) { - m_slot->m_affiliations->unitReg(srcId); + if (!m_slot->s_affiliations->isUnitReg(srcId)) { + m_slot->s_affiliations->unitReg(srcId); } - if (m_slot->m_network != nullptr) - m_slot->m_network->announceUnitRegistration(srcId); + if (m_slot->s_network != nullptr) + m_slot->s_network->announceUnitRegistration(srcId); // update dynamic affiliation table - m_slot->m_affiliations->groupAff(srcId, dstId); + m_slot->s_affiliations->groupAff(srcId, dstId); - if (m_slot->m_network != nullptr) - m_slot->m_network->announceGroupAffiliation(srcId, dstId); + if (m_slot->s_network != nullptr) + m_slot->s_network->announceGroupAffiliation(srcId, dstId); } } } @@ -163,7 +163,7 @@ bool Voice::process(uint8_t* data, uint32_t len) slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); data[0U] = modem::TAG_DATA; data[1U] = 0x00U; @@ -185,9 +185,9 @@ bool Voice::process(uint8_t* data, uint32_t len) m_slot->m_aveRSSI = m_slot->m_rssi; m_slot->m_rssiCount = 1U; - if (m_slot->m_duplex) { + if (m_slot->s_duplex) { m_slot->m_txQueue.clear(); - m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); + m_slot->s_modem->writeDMRAbort(m_slot->m_slotNo); for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) m_slot->addFrame(data); @@ -239,12 +239,12 @@ bool Voice::process(uint8_t* data, uint32_t len) slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); data[0U] = modem::TAG_DATA; data[1U] = 0x00U; - if (m_slot->m_duplex) + if (m_slot->s_duplex) m_slot->addFrame(data); m_slot->writeNetwork(data, DataType::VOICE_PI_HEADER, 0U); @@ -271,7 +271,7 @@ bool Voice::process(uint8_t* data, uint32_t len) m_lastRfN = 0U; // convert the Audio Sync to be from the BS or MS as needed - Sync::addDMRAudioSync(data + 2U, m_slot->m_duplex); + Sync::addDMRAudioSync(data + 2U, m_slot->s_duplex); uint32_t errors = 0U; uint8_t fid = m_slot->m_rfLC->getFID(); @@ -314,7 +314,7 @@ bool Voice::process(uint8_t* data, uint32_t len) data[0U] = modem::TAG_DATA; data[1U] = 0x00U; - if (m_slot->m_duplex) + if (m_slot->s_duplex) m_slot->addFrame(data); m_slot->writeNetwork(data, DataType::VOICE_SYNC, 0U, errors); @@ -469,7 +469,7 @@ bool Voice::process(uint8_t* data, uint32_t len) lcss = m_rfEmbeddedLC.getData(data + 2U, m_rfN); // Regenerate the EMB - emb.setColorCode(m_slot->m_colorCode); + emb.setColorCode(m_slot->s_colorCode); emb.setLCSS(lcss); emb.encode(data + 2U); @@ -484,12 +484,12 @@ bool Voice::process(uint8_t* data, uint32_t len) lcss = m_rfEmbeddedLC.getData(data + 2U, m_rfN); // Regenerate the EMB - emb.setColorCode(m_slot->m_colorCode); + emb.setColorCode(m_slot->s_colorCode); emb.setLCSS(lcss); emb.encode(data + 2U); } - if (m_slot->m_duplex) + if (m_slot->s_duplex) m_slot->addFrame(data); return true; @@ -503,7 +503,7 @@ bool Voice::process(uint8_t* data, uint32_t len) // If we haven't received an LC yet, then be strict on the color code uint8_t colorCode = emb.getColorCode(); - if (colorCode != m_slot->m_colorCode) + if (colorCode != m_slot->s_colorCode) return false; m_rfEmbeddedLC.addData(data + 2U, emb.getLCSS()); @@ -544,13 +544,13 @@ bool Voice::process(uint8_t* data, uint32_t len) // Create a dummy start frame to replace the received frame uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U]; - Sync::addDMRDataSync(start + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(start + 2U, m_slot->s_duplex); lc::FullLC fullLC; fullLC.encode(*m_slot->m_rfLC, start + 2U, DataType::VOICE_LC_HEADER); SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(DataType::VOICE_LC_HEADER); slotType.encode(start + 2U); @@ -574,9 +574,9 @@ bool Voice::process(uint8_t* data, uint32_t len) m_slot->m_aveRSSI = m_slot->m_rssi; m_slot->m_rssiCount = 1U; - if (m_slot->m_duplex) { + if (m_slot->s_duplex) { m_slot->m_txQueue.clear(); - m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); + m_slot->s_modem->writeDMRAbort(m_slot->m_slotNo); for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) m_slot->addFrame(start); @@ -640,7 +640,7 @@ bool Voice::process(uint8_t* data, uint32_t len) data[0U] = modem::TAG_DATA; data[1U] = 0x00U; - if (m_slot->m_duplex) + if (m_slot->s_duplex) m_slot->addFrame(data); m_slot->writeNetwork(data, DataType::VOICE, 0U, errors); @@ -720,12 +720,12 @@ void Voice::processNetwork(const data::NetData& dmrData) // Regenerate the Slot Type SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(DataType::VOICE_LC_HEADER); slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); data[0U] = modem::TAG_DATA; data[1U] = 0x00U; @@ -744,15 +744,15 @@ void Voice::processNetwork(const data::NetData& dmrData) m_slot->m_voice->m_netEmbeddedWriteN = 1U; m_slot->m_voice->m_netTalkerId = TalkerID::NONE; - if (m_slot->m_duplex) { + if (m_slot->s_duplex) { m_slot->m_txQueue.clear(); - m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); + m_slot->s_modem->writeDMRAbort(m_slot->m_slotNo); } - for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++) - m_slot->addFrame(m_slot->m_idle, true); + for (uint32_t i = 0U; i < m_slot->s_jitterSlots; i++) + m_slot->addFrame(m_slot->s_idle, true); - if (m_slot->m_duplex) { + if (m_slot->s_duplex) { for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) m_slot->addFrame(data, true); } @@ -795,31 +795,31 @@ void Voice::processNetwork(const data::NetData& dmrData) m_slot->m_netTimeoutTimer.start(); m_slot->m_netTimeout = false; - if (m_slot->m_duplex) { + if (m_slot->s_duplex) { m_slot->m_txQueue.clear(); - m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); + m_slot->s_modem->writeDMRAbort(m_slot->m_slotNo); } - for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++) - m_slot->addFrame(m_slot->m_idle, true); + for (uint32_t i = 0U; i < m_slot->s_jitterSlots; i++) + m_slot->addFrame(m_slot->s_idle, true); // Create a dummy start frame uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U]; - Sync::addDMRDataSync(start + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(start + 2U, m_slot->s_duplex); lc::FullLC fullLC; fullLC.encode(*m_slot->m_netLC, start + 2U, DataType::VOICE_LC_HEADER); SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(DataType::VOICE_LC_HEADER); slotType.encode(start + 2U); start[0U] = modem::TAG_DATA; start[1U] = 0x00U; - if (m_slot->m_duplex) { + if (m_slot->s_duplex) { for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) m_slot->addFrame(start); } @@ -860,12 +860,12 @@ void Voice::processNetwork(const data::NetData& dmrData) // Regenerate the Slot Type SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(DataType::VOICE_PI_HEADER); slotType.encode(data + 2U); // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(data + 2U, m_slot->s_duplex); data[0U] = modem::TAG_DATA; data[1U] = 0x00U; @@ -904,31 +904,31 @@ void Voice::processNetwork(const data::NetData& dmrData) m_slot->m_netTimeoutTimer.start(); m_slot->m_netTimeout = false; - if (m_slot->m_duplex) { + if (m_slot->s_duplex) { m_slot->m_txQueue.clear(); - m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); + m_slot->s_modem->writeDMRAbort(m_slot->m_slotNo); } - for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++) - m_slot->addFrame(m_slot->m_idle, true); + for (uint32_t i = 0U; i < m_slot->s_jitterSlots; i++) + m_slot->addFrame(m_slot->s_idle, true); // Create a dummy start frame uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U]; - Sync::addDMRDataSync(start + 2U, m_slot->m_duplex); + Sync::addDMRDataSync(start + 2U, m_slot->s_duplex); lc::FullLC fullLC; fullLC.encode(*m_slot->m_netLC, start + 2U, DataType::VOICE_LC_HEADER); SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); + slotType.setColorCode(m_slot->s_colorCode); slotType.setDataType(DataType::VOICE_LC_HEADER); slotType.encode(start + 2U); start[0U] = modem::TAG_DATA; start[1U] = 0x00U; - if (m_slot->m_duplex) { + if (m_slot->s_duplex) { for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) m_slot->addFrame(start); } @@ -984,7 +984,7 @@ void Voice::processNetwork(const data::NetData& dmrData) data[1U] = 0x00U; // Convert the Audio Sync to be from the BS or MS as needed - Sync::addDMRAudioSync(data + 2U, m_slot->m_duplex); + Sync::addDMRAudioSync(data + 2U, m_slot->s_duplex); // Initialise the lost packet data if (m_slot->m_netFrames == 0U) { @@ -1119,7 +1119,7 @@ void Voice::processNetwork(const data::NetData& dmrData) } // Regenerate the EMB - emb.setColorCode(m_slot->m_colorCode); + emb.setColorCode(m_slot->s_colorCode); emb.setLCSS(lcss); emb.encode(data + 2U); @@ -1210,7 +1210,7 @@ bool Voice::checkRFTrafficCollision(uint32_t dstId) } if (m_slot->m_enableTSCC && dstId == m_slot->m_netLastDstId) { - if (m_slot->m_affiliations->isNetGranted(dstId)) { + if (m_slot->s_affiliations->isNetGranted(dstId)) { LogWarning(LOG_RF, "DMR Slot %u, Traffic collision detect, preempting new RF traffic to existing granted network traffic (Are we in a voting condition?)", m_slot->m_slotNo); m_slot->m_rfState = RS_RF_LISTENING; return true; @@ -1374,7 +1374,7 @@ void Voice::insertSilence(uint32_t count) uint8_t fid = m_slot->m_netLC->getFID(); data::EMB emb; - emb.setColorCode(m_slot->m_colorCode); + emb.setColorCode(m_slot->s_colorCode); for (uint32_t i = 0U; i < count; i++) { // only use our silence frame if its AMBE audio data @@ -1386,7 +1386,7 @@ void Voice::insertSilence(uint32_t count) } if (n == 0U) { - Sync::addDMRAudioSync(data + 2U, m_slot->m_duplex); + Sync::addDMRAudioSync(data + 2U, m_slot->s_duplex); } else { uint8_t lcss = m_netEmbeddedLC.getData(data + 2U, n); diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index ec08879c..6fea2cf5 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -46,7 +46,7 @@ const uint8_t SCRAMBLER[] = { // Static Class Members // --------------------------------------------------------------------------- -std::mutex Control::m_queueLock; +std::mutex Control::s_queueLock; // --------------------------------------------------------------------------- // Public Class Members @@ -507,7 +507,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) uint32_t Control::peekFrameLength() { - std::lock_guard lock(m_queueLock); + std::lock_guard lock(s_queueLock); if (m_txQueue.isEmpty() && m_txImmQueue.isEmpty()) return 0U; @@ -553,7 +553,7 @@ uint32_t Control::getFrame(uint8_t* data) { assert(data != nullptr); - std::lock_guard lock(m_queueLock); + std::lock_guard lock(s_queueLock); if (m_txQueue.isEmpty() && m_txImmQueue.isEmpty()) return 0U; @@ -864,7 +864,7 @@ void Control::addFrame(const uint8_t *data, bool net, bool imm) { assert(data != nullptr); - std::lock_guard lock(m_queueLock); + std::lock_guard lock(s_queueLock); if (!net) { if (m_rfTimeout.isRunning() && m_rfTimeout.hasExpired()) diff --git a/src/host/nxdn/Control.h b/src/host/nxdn/Control.h index f65fa1db..e02de635 100644 --- a/src/host/nxdn/Control.h +++ b/src/host/nxdn/Control.h @@ -301,7 +301,7 @@ namespace nxdn RingBuffer m_txImmQueue; RingBuffer m_txQueue; - static std::mutex m_queueLock; + static std::mutex s_queueLock; RPT_RF_STATE m_rfState; uint32_t m_rfLastDstId; diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index 0edb3c08..7e43d9a6 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -41,8 +41,8 @@ const uint32_t MAX_PREAMBLE_TDU_CNT = 64U; // Static Class Members // --------------------------------------------------------------------------- -std::mutex Control::m_queueLock; -std::mutex Control::m_activeTGLock; +std::mutex Control::s_queueLock; +std::mutex Control::s_activeTGLock; // --------------------------------------------------------------------------- // Public Class Members @@ -760,7 +760,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) uint32_t Control::peekFrameLength() { - std::lock_guard lock(m_queueLock); + std::lock_guard lock(s_queueLock); if (m_txQueue.isEmpty() && m_txImmQueue.isEmpty()) return 0U; @@ -811,7 +811,7 @@ uint32_t Control::getFrame(uint8_t* data) { assert(data != nullptr); - std::lock_guard lock(m_queueLock); + std::lock_guard lock(s_queueLock); if (m_txQueue.isEmpty() && m_txImmQueue.isEmpty()) return 0U; @@ -1316,7 +1316,7 @@ void Control::addFrame(const uint8_t* data, uint32_t length, bool net, bool imm) { assert(data != nullptr); - std::lock_guard lock(m_queueLock); + std::lock_guard lock(s_queueLock); if (!net) { if (m_rfTimeout.isRunning() && m_rfTimeout.hasExpired()) @@ -1998,7 +1998,7 @@ void Control::RPC_activeTG(json::object& req, json::object& reply) } json::array active = req["active"].get(); - std::lock_guard lock(m_activeTGLock); + std::lock_guard lock(s_activeTGLock); m_activeTG.clear(); if (active.size() > 0) { @@ -2022,7 +2022,7 @@ void Control::RPC_clearActiveTG(json::object& req, json::object& reply) g_RPC->defaultResponse(reply, "OK", network::NetRPC::OK); if (m_activeTG.size() > 0) { - std::lock_guard lock(m_activeTGLock); + std::lock_guard lock(s_activeTGLock); m_activeTG.clear(); } } diff --git a/src/host/p25/Control.h b/src/host/p25/Control.h index 8ebc3038..e14665cc 100644 --- a/src/host/p25/Control.h +++ b/src/host/p25/Control.h @@ -330,7 +330,7 @@ namespace p25 RingBuffer m_txImmQueue; RingBuffer m_txQueue; - static std::mutex m_queueLock; + static std::mutex s_queueLock; RPT_RF_STATE m_rfState; uint32_t m_rfLastDstId; @@ -387,7 +387,7 @@ namespace p25 uint32_t m_aveRSSI; uint32_t m_rssiCount; - static std::mutex m_activeTGLock; + static std::mutex s_activeTGLock; bool m_ccNotifyActiveTG; bool m_disableAdjSiteBroadcast; diff --git a/src/host/p25/lc/tsbk/OSP_DVM_GIT_HASH.cpp b/src/host/p25/lc/tsbk/OSP_DVM_GIT_HASH.cpp index a38f0d25..0e4132f2 100644 --- a/src/host/p25/lc/tsbk/OSP_DVM_GIT_HASH.cpp +++ b/src/host/p25/lc/tsbk/OSP_DVM_GIT_HASH.cpp @@ -55,8 +55,8 @@ void OSP_DVM_GIT_HASH::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = (tsbkValue << 8) + (g_gitHashBytes[2U]); // ... tsbkValue = (tsbkValue << 8) + (g_gitHashBytes[3U]); // ... tsbkValue = (tsbkValue << 16) + 0U; - tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID - tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel Number + tsbkValue = (tsbkValue << 4) + s_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 12) + s_siteData.channelNo(); // Channel Number std::unique_ptr tsbk = TSBK::fromValue(tsbkValue); TSBK::encode(data, tsbk.get(), rawTSBK, noTrellis); diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index 989a65e3..d1576e9b 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -672,7 +672,7 @@ bool Voice::process(uint8_t* data, uint32_t len) m_rfLC.setLCO(LCO::RFSS_STS_BCAST); } else { - std::lock_guard lock(m_p25->m_activeTGLock); + std::lock_guard lock(m_p25->s_activeTGLock); if (m_p25->m_activeTG.size() > 0) { if (m_grpUpdtCount > m_p25->m_activeTG.size()) m_grpUpdtCount = 0U; @@ -2026,7 +2026,7 @@ void Voice::writeNet_LDU1() m_netLC.setLCO(LCO::RFSS_STS_BCAST); } else { - std::lock_guard lock(m_p25->m_activeTGLock); + std::lock_guard lock(m_p25->s_activeTGLock); if (m_p25->m_activeTG.size() > 0) { if (m_grpUpdtCount > m_p25->m_activeTG.size()) m_grpUpdtCount = 0U; diff --git a/src/patch/ActivityLog.cpp b/src/patch/ActivityLog.cpp index 0ee90191..cb1693a6 100644 --- a/src/patch/ActivityLog.cpp +++ b/src/patch/ActivityLog.cpp @@ -33,12 +33,12 @@ const uint32_t ACT_LOG_BUFFER_LEN = 501U; // Global Variables // --------------------------------------------------------------------------- -static std::string m_actFilePath; -static std::string m_actFileRoot; +static std::string g_actFilePath; +static std::string g_actFileRoot; -static FILE* m_actFpLog = nullptr; +static FILE* g_actFpLog = nullptr; -static struct tm m_actTm; +static struct tm g_actTm; // --------------------------------------------------------------------------- // Global Functions @@ -56,22 +56,22 @@ static bool ActivityLogOpen() struct tm* tm = ::gmtime(&now); - if (tm->tm_mday == m_actTm.tm_mday && tm->tm_mon == m_actTm.tm_mon && tm->tm_year == m_actTm.tm_year) { - if (m_actFpLog != nullptr) + if (tm->tm_mday == g_actTm.tm_mday && tm->tm_mon == g_actTm.tm_mon && tm->tm_year == g_actTm.tm_year) { + if (g_actFpLog != nullptr) return true; } else { - if (m_actFpLog != nullptr) - ::fclose(m_actFpLog); + if (g_actFpLog != nullptr) + ::fclose(g_actFpLog); } char filename[200U]; ::sprintf(filename, "%s/%s-%04d-%02d-%02d.activity.log", LogGetFilePath().c_str(), LogGetFileRoot().c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); - m_actFpLog = ::fopen(filename, "a+t"); - m_actTm = *tm; + g_actFpLog = ::fopen(filename, "a+t"); + g_actTm = *tm; - return m_actFpLog != nullptr; + return g_actFpLog != nullptr; } /* Initializes the activity log. */ @@ -81,8 +81,8 @@ bool ActivityLogInitialise(const std::string& filePath, const std::string& fileR #if defined(CATCH2_TEST_COMPILATION) return true; #endif - m_actFilePath = filePath; - m_actFileRoot = fileRoot; + g_actFilePath = filePath; + g_actFileRoot = fileRoot; return ::ActivityLogOpen(); } @@ -94,8 +94,8 @@ void ActivityLogFinalise() #if defined(CATCH2_TEST_COMPILATION) return; #endif - if (m_actFpLog != nullptr) - ::fclose(m_actFpLog); + if (g_actFpLog != nullptr) + ::fclose(g_actFpLog); } /* Writes a new entry to the activity log. */ @@ -117,8 +117,8 @@ void log_internal::ActivityLogInternal(const std::string& log) network->writeActLog(log.c_str()); } - ::fprintf(m_actFpLog, "%s\n", log.c_str()); - ::fflush(m_actFpLog); + ::fprintf(g_actFpLog, "%s\n", log.c_str()); + ::fflush(g_actFpLog); if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) { ::fprintf(stdout, "%s" EOL, log.c_str()); diff --git a/src/patch/HostPatch.cpp b/src/patch/HostPatch.cpp index b5b57d5e..c12eaa64 100644 --- a/src/patch/HostPatch.cpp +++ b/src/patch/HostPatch.cpp @@ -56,7 +56,7 @@ using namespace network::udp; // Static Class Members // --------------------------------------------------------------------------- -std::mutex HostPatch::m_networkMutex; +std::mutex HostPatch::s_networkMutex; // --------------------------------------------------------------------------- // Public Class Members @@ -247,12 +247,12 @@ int HostPatch::run() // ------------------------------------------------------ if (m_network != nullptr) { - std::lock_guard lock(HostPatch::m_networkMutex); + std::lock_guard lock(HostPatch::s_networkMutex); m_network->clock(ms); } if (m_mmdvmP25Reflector) { - std::lock_guard lock(HostPatch::m_networkMutex); + std::lock_guard lock(HostPatch::s_networkMutex); m_mmdvmP25Net->clock(ms); } @@ -1619,7 +1619,7 @@ void* HostPatch::threadNetworkProcess(void* arg) uint32_t length = 0U; bool netReadRet = false; if (patch->m_digiMode == TX_MODE_DMR) { - std::lock_guard lock(HostPatch::m_networkMutex); + std::lock_guard lock(HostPatch::s_networkMutex); UInt8Array dmrBuffer = patch->m_network->readDMR(netReadRet, length); if (netReadRet) { patch->processDMRNetwork(dmrBuffer.get(), length); @@ -1627,7 +1627,7 @@ void* HostPatch::threadNetworkProcess(void* arg) } if (patch->m_digiMode == TX_MODE_P25) { - std::lock_guard lock(HostPatch::m_networkMutex); + std::lock_guard lock(HostPatch::s_networkMutex); UInt8Array p25Buffer = patch->m_network->readP25(netReadRet, length); if (netReadRet) { patch->processP25Network(p25Buffer.get(), length); @@ -1692,7 +1692,7 @@ void* HostPatch::threadMMDVMProcess(void* arg) stopWatch.start(); if (patch->m_digiMode == TX_MODE_P25) { - std::lock_guard lock(HostPatch::m_networkMutex); + std::lock_guard lock(HostPatch::s_networkMutex); DECLARE_UINT8_ARRAY(buffer, 100U); uint32_t len = patch->m_mmdvmP25Net->read(buffer, 100U); diff --git a/src/patch/HostPatch.h b/src/patch/HostPatch.h index 2bc36374..b48e2206 100644 --- a/src/patch/HostPatch.h +++ b/src/patch/HostPatch.h @@ -116,7 +116,7 @@ class HOST_SW_API HostPatch { bool m_trace; bool m_debug; - static std::mutex m_networkMutex; + static std::mutex s_networkMutex; /** * @brief Reads basic configuration parameters from the INI. diff --git a/src/remote/RESTClient.cpp b/src/remote/RESTClient.cpp index 01fbe510..59d43a7b 100644 --- a/src/remote/RESTClient.cpp +++ b/src/remote/RESTClient.cpp @@ -44,12 +44,12 @@ using namespace network::rest::http; // Static Class Members // --------------------------------------------------------------------------- -bool RESTClient::m_responseAvailable = false; -HTTPPayload RESTClient::m_response; +bool RESTClient::s_responseAvailable = false; +HTTPPayload RESTClient::s_response; -bool RESTClient::m_console = false; -bool RESTClient::m_enableSSL = false; -bool RESTClient::m_debug = false; +bool RESTClient::s_console = false; +bool RESTClient::s_enableSSL = false; +bool RESTClient::s_debug = false; // --------------------------------------------------------------------------- // Global Functions @@ -94,9 +94,9 @@ RESTClient::RESTClient(const std::string& address, uint32_t port, const std::str assert(!address.empty()); assert(port > 0U); - m_console = true; - m_enableSSL = enableSSL; - m_debug = debug; + s_console = true; + s_enableSSL = enableSSL; + s_debug = debug; } /* Finalizes a instance of the RESTClient class. */ @@ -115,7 +115,7 @@ int RESTClient::send(const std::string method, const std::string endpoint, json: int RESTClient::send(const std::string method, const std::string endpoint, json::object payload, json::object& response) { - return send(m_address, m_port, m_password, method, endpoint, payload, response, m_enableSSL, m_debug); + return send(m_address, m_port, m_password, method, endpoint, payload, response, s_enableSSL, s_debug); } /* Sends remote control command to the specified modem. */ @@ -146,8 +146,8 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin } int ret = EXIT_SUCCESS; - m_enableSSL = enableSSL; - m_debug = debug; + s_enableSSL = enableSSL; + s_debug = debug; typedef network::rest::BasicRequestDispatcher RESTDispatcherType; RESTDispatcherType m_dispatcher(RESTClient::responseHandler); @@ -159,7 +159,7 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin try { // setup HTTP client for authentication payload #if defined(ENABLE_SSL) - if (m_enableSSL) { + if (s_enableSSL) { sslClient = new SecureHTTPClient(address, port); if (!sslClient->open()) { delete sslClient; @@ -208,7 +208,7 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin HTTPPayload httpPayload = HTTPPayload::requestPayload(HTTP_PUT, "/auth"); httpPayload.payload(request); #if defined(ENABLE_SSL) - if (m_enableSSL) { + if (s_enableSSL) { sslClient->request(httpPayload); } else { #endif // ENABLE_SSL @@ -220,7 +220,7 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin // wait for response and parse if (wait()) { #if defined(ENABLE_SSL) - if (m_enableSSL) { + if (s_enableSSL) { sslClient->close(); delete sslClient; } else { @@ -234,7 +234,7 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin } json::object rsp = json::object(); - if (!parseResponseBody(m_response, rsp)) { + if (!parseResponseBody(s_response, rsp)) { return ERRNO_BAD_API_RESPONSE; } @@ -245,7 +245,7 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin } else { #if defined(ENABLE_SSL) - if (m_enableSSL) { + if (s_enableSSL) { sslClient->close(); delete sslClient; } else { @@ -259,7 +259,7 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin } #if defined(ENABLE_SSL) - if (m_enableSSL) { + if (s_enableSSL) { sslClient->close(); delete sslClient; } else { @@ -272,7 +272,7 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin // reset the HTTP client and setup for actual payload request #if defined(ENABLE_SSL) - if (m_enableSSL) { + if (s_enableSSL) { sslClient = new SecureHTTPClient(address, port); if (!sslClient->open()) { delete sslClient; @@ -296,7 +296,7 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin httpPayload.headers.add("X-DVM-Auth-Token", token); httpPayload.payload(payload); #if defined(ENABLE_SSL) - if (m_enableSSL) { + if (s_enableSSL) { sslClient->request(httpPayload); } else { #endif // ENABLE_SSL @@ -308,7 +308,7 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin // wait for response and parse if (wait()) { #if defined(ENABLE_SSL) - if (m_enableSSL) { + if (s_enableSSL) { sslClient->close(); delete sslClient; } else { @@ -322,25 +322,25 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin } response = json::object(); - if (!parseResponseBody(m_response, response)) { + if (!parseResponseBody(s_response, response)) { return ERRNO_BAD_API_RESPONSE; } ret = response["status"].get(); - if (m_console) { - fprintf(stdout, "%s\r\n", m_response.content.c_str()); + if (s_console) { + fprintf(stdout, "%s\r\n", s_response.content.c_str()); } else { - if (m_debug) { - if (m_response.content.size() < 4095) { - ::LogDebug(LOG_REST, "REST Response: %s", m_response.content.c_str()); + if (s_debug) { + if (s_response.content.size() < 4095) { + ::LogDebug(LOG_REST, "REST Response: %s", s_response.content.c_str()); } // bryanb: this will cause REST responses >4095 characters to simply not print... } } #if defined(ENABLE_SSL) - if (m_enableSSL) { + if (s_enableSSL) { sslClient->close(); delete sslClient; } else { @@ -353,7 +353,7 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin } catch (std::exception&) { #if defined(ENABLE_SSL) - if (m_enableSSL) { + if (s_enableSSL) { if (sslClient != nullptr) { delete sslClient; } @@ -379,18 +379,18 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin void RESTClient::responseHandler(const HTTPPayload& request, HTTPPayload& reply) { - m_responseAvailable = true; - m_response = request; + s_responseAvailable = true; + s_response = request; } /* Helper to wait for a HTTP response. */ bool RESTClient::wait(const int t) { - m_responseAvailable = false; + s_responseAvailable = false; int timeout = t; - while (!m_responseAvailable && timeout > 0) { + while (!s_responseAvailable && timeout > 0) { timeout--; Thread::sleep(1); } diff --git a/src/remote/RESTClient.h b/src/remote/RESTClient.h index 87706491..db92d945 100644 --- a/src/remote/RESTClient.h +++ b/src/remote/RESTClient.h @@ -122,13 +122,13 @@ class HOST_SW_API RESTClient uint32_t m_port; std::string m_password; - static bool m_console; + static bool s_console; - static bool m_responseAvailable; - static HTTPPayload m_response; + static bool s_responseAvailable; + static HTTPPayload s_response; - static bool m_enableSSL; - static bool m_debug; + static bool s_enableSSL; + static bool s_debug; }; #endif // __REMOTE_COMMAND_H__ diff --git a/src/sysview/network/PeerNetwork.cpp b/src/sysview/network/PeerNetwork.cpp index eeaa9456..fbdd262f 100644 --- a/src/sysview/network/PeerNetwork.cpp +++ b/src/sysview/network/PeerNetwork.cpp @@ -26,7 +26,7 @@ using namespace network; // Static Class Members // --------------------------------------------------------------------------- -std::mutex PeerNetwork::m_peerStatusMutex; +std::mutex PeerNetwork::s_peerStatusMutex; // --------------------------------------------------------------------------- // Public Class Members @@ -105,7 +105,7 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco json::object obj = v.get(); uint32_t actualPeerId = obj["peerId"].getDefault(peerId); - std::lock_guard lock(m_peerStatusMutex); + std::lock_guard lock(s_peerStatusMutex); peerStatus[actualPeerId] = obj; } break; diff --git a/src/sysview/network/PeerNetwork.h b/src/sysview/network/PeerNetwork.h index 82a63e1f..8b5bef94 100644 --- a/src/sysview/network/PeerNetwork.h +++ b/src/sysview/network/PeerNetwork.h @@ -64,11 +64,11 @@ namespace network /** * @brief Helper to lock the peer status mutex. */ - void lockPeerStatus() { m_peerStatusMutex.lock(); } + void lockPeerStatus() { s_peerStatusMutex.lock(); } /** * @brief Helper to unlock the peer status mutex. */ - void unlockPeerStatus() { m_peerStatusMutex.unlock(); } + void unlockPeerStatus() { s_peerStatusMutex.unlock(); } /** * @brief Map of peer status. @@ -96,7 +96,7 @@ namespace network bool writeConfig() override; private: - static std::mutex m_peerStatusMutex; + static std::mutex s_peerStatusMutex; bool m_peerReplica; PacketBuffer m_tgidPkt; From fd923b49ac59aaed00224dd1610a6ed80d18ce72 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 31 Oct 2025 17:09:41 -0400 Subject: [PATCH 120/200] enhance colorize-host.sh to terminate color properly; --- tools/colorize-host.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/colorize-host.sh b/tools/colorize-host.sh index f5166876..2707af5c 100755 --- a/tools/colorize-host.sh +++ b/tools/colorize-host.sh @@ -23,6 +23,7 @@ #* along with this program; if not, write to the Free Software #* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #*/ +EOL_CLEAR="s/$/\x1b[0m/;" LOG_COLOR="s#W:#\x1b[1m\x1b[33m&#; s#E:#\x1b[1m\x1b[31m&#; s#M:#\x1b[0m&#; s#I:#\x1b[0m&#; s#D:#\x1b[1m\x1b[34m&\x1b[0m\x1b[1m#;" DMR_COLOR="s#VOICE#\x1b[36m&#; s#TERMINATOR_WITH_LC#\x1b[0m\x1b[32m&#; s#CSBK#\x1b[0m\x1b[35m&#" @@ -33,4 +34,4 @@ AFF_COLOR="s#Affiliations#\x1b[1m\x1b[36m&#; s#Affiliation#\x1b[1m\x1b[36m&#;" RF_HIGHLIGHT="s#RF#\x1b[1m\x1b[35m&\x1b[0m#;" NET_HIGHLIGHT="s#NET#\x1b[1m\x1b[36m&\x1b[0m#;" -sed "${LOG_COLOR}; ${RF_HIGHLIGHT}; ${NET_HIGHLIGHT}; ${DMR_COLOR}; ${P25_COLOR}; ${NXDN_COLOR}; ${AFF_COLOR}" +sed "${LOG_COLOR}; ${RF_HIGHLIGHT}; ${NET_HIGHLIGHT}; ${DMR_COLOR}; ${P25_COLOR}; ${NXDN_COLOR}; ${AFF_COLOR}; ${EOL_COLOR};" From 69e8fbc7e42b0f3bb754a5b4912ffeaab22150c0 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 31 Oct 2025 17:17:17 -0400 Subject: [PATCH 121/200] enhance colorize-host.sh to terminate color properly; --- tools/colorize-host.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/colorize-host.sh b/tools/colorize-host.sh index 2707af5c..951c123a 100755 --- a/tools/colorize-host.sh +++ b/tools/colorize-host.sh @@ -24,7 +24,7 @@ #* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #*/ EOL_CLEAR="s/$/\x1b[0m/;" -LOG_COLOR="s#W:#\x1b[1m\x1b[33m&#; s#E:#\x1b[1m\x1b[31m&#; s#M:#\x1b[0m&#; s#I:#\x1b[0m&#; s#D:#\x1b[1m\x1b[34m&\x1b[0m\x1b[1m#;" +LOG_COLOR="s#W:#\x1b[0m\x1b[1m\x1b[33m&\x1b[0m#; s#E:#\x1b[0m\x1b[1m\x1b[31m&\x1b[0m#; s#M:#\x1b[0m&#; s#I:#\x1b[0m&#; s#D:#\x1b[1m\x1b[34m&\x1b[0m#; s#U:#\x1b[44m\x1b[1m\x1b[33m&#;" DMR_COLOR="s#VOICE#\x1b[36m&#; s#TERMINATOR_WITH_LC#\x1b[0m\x1b[32m&#; s#CSBK#\x1b[0m\x1b[35m&#" P25_COLOR="s#LDU#\x1b[36m&#; s#TDU#\x1b[0m\x1b[32m&#; s#HDU#\x1b[0m\x1b[32m&#; s#TSDU#\x1b[0m\x1b[35m&#" From 9e4a0ca53bc85ff6afc7b33a25eba4b85d5f8ac7 Mon Sep 17 00:00:00 2001 From: "Lorenzo L. Romero" Date: Sat, 1 Nov 2025 18:12:36 -0400 Subject: [PATCH 122/200] RTS PTT will only assert when audio is present. Add holdoff timer before removing RTS. (#103) --- configs/bridge-config.example.yml | 2 ++ src/bridge/HostBridge.cpp | 27 ++++++++++++++++++++++----- src/bridge/HostBridge.h | 4 ++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/configs/bridge-config.example.yml b/configs/bridge-config.example.yml index 8a9cb0bc..aed45381 100644 --- a/configs/bridge-config.example.yml +++ b/configs/bridge-config.example.yml @@ -179,3 +179,5 @@ system: rtsPttEnable: false # Serial port device for RTS PTT control (e.g., /dev/ttyUSB0). rtsPttPort: "/dev/ttyUSB0" + # Hold-off time (ms) before clearing RTS PTT after last audio output. + rtsPttHoldoffMs: 250 diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 1ec650ce..006e8703 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -6,6 +6,7 @@ * * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2025 Caleb, K4PHP + * Copyright (C) 2025 Lorenzo L Romero, K2LLR * */ #include "Defines.h" @@ -114,12 +115,9 @@ void audioCallback(ma_device* device, void* output, const void* input, ma_uint32 pcmIdx += 2; } - // Assert RTS PTT when audio is being sent to output + // Assert RTS PTT when audio is being sent to output and record last output time bridge->assertRtsPtt(); - } - else { - // Deassert RTS PTT when no audio is being sent to output - bridge->deassertRtsPtt(); + bridge->m_lastAudioOut = system_clock::hrc::now(); } } @@ -285,6 +283,10 @@ HostBridge::HostBridge(const std::string& confFile) : ::memset(m_netLDU2, 0x00U, 9U * 25U); m_p25Crypto = new p25::crypto::P25Crypto(); + + // initialize RTS PTT timing + m_lastAudioOut = system_clock::hrc::now(); + m_rtsPttHoldoffMs = 250U; } /* Finalizes a instance of the HostBridge class. */ @@ -946,6 +948,7 @@ bool HostBridge::readParams() // RTS PTT Configuration m_rtsPttEnable = systemConf["rtsPttEnable"].as(false); m_rtsPttPort = systemConf["rtsPttPort"].as("/dev/ttyUSB0"); + m_rtsPttHoldoffMs = (uint32_t)systemConf["rtsPttHoldoffMs"].as(m_rtsPttHoldoffMs); std::string txModeStr = "DMR"; if (m_txMode == TX_MODE_P25) @@ -975,6 +978,7 @@ bool HostBridge::readParams() LogInfo(" RTS PTT Enable: %s", m_rtsPttEnable ? "yes" : "no"); if (m_rtsPttEnable) { LogInfo(" RTS PTT Port: %s", m_rtsPttPort.c_str()); + LogInfo(" RTS PTT Hold-off: %ums", m_rtsPttHoldoffMs); } if (m_debug) { @@ -2970,6 +2974,11 @@ void HostBridge::callEnd(uint32_t srcId, uint32_t dstId) m_trafficFromUDP = false; m_udpFrameCnt = 0U; + // ensure PTT is dropped at call end + if (m_rtsPttEnable) { + deassertRtsPtt(); + } + m_dmrSeqNo = 0U; m_dmrN = 0U; m_p25SeqNo = 0U; @@ -3758,6 +3767,14 @@ void* HostBridge::threadCallWatchdog(void* arg) bridge->m_udpDropTime.clock(ms); } + // Debounce RTS PTT clear using hold-off after last audio output + if (bridge->m_rtsPttEnable && bridge->m_rtsPttActive) { + uint64_t sinceLastOut = system_clock::hrc::diffNow(bridge->m_lastAudioOut); + if (sinceLastOut >= bridge->m_rtsPttHoldoffMs) { + bridge->deassertRtsPtt(); + } + } + std::string trafficType = LOCAL_CALL; if (bridge->m_trafficFromUDP) trafficType = UDP_CALL; diff --git a/src/bridge/HostBridge.h b/src/bridge/HostBridge.h index 0e6ed886..088c30e7 100644 --- a/src/bridge/HostBridge.h +++ b/src/bridge/HostBridge.h @@ -26,6 +26,7 @@ #include "common/yaml/Yaml.h" #include "common/RingBuffer.h" #include "common/Timer.h" +#include "common/Clock.h" #include "vocoder/MBEDecoder.h" #include "vocoder/MBEEncoder.h" #define MINIAUDIO_IMPLEMENTATION @@ -260,6 +261,9 @@ class HOST_SW_API HostBridge { std::string m_rtsPttPort; RtsPttController* m_rtsPttController; bool m_rtsPttActive; + // Timestamp of last audio written to output and hold-off before clearing PTT + system_clock::hrc::hrc_t m_lastAudioOut; + uint32_t m_rtsPttHoldoffMs; uint16_t m_rtpSeqNo; uint32_t m_rtpTimestamp; From e746e7f8a02a6a8f988aed39b424801c36f079fd Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 2 Nov 2025 11:22:25 -0500 Subject: [PATCH 123/200] add option to enable/disable upstream call start/end event logging; --- configs/fne-config.example.yml | 2 ++ src/fne/network/FNENetwork.cpp | 3 ++ src/fne/network/FNENetwork.h | 1 + src/fne/network/callhandler/TagAnalogData.cpp | 13 +++++-- src/fne/network/callhandler/TagDMRData.cpp | 33 +++++++++++++----- src/fne/network/callhandler/TagNXDNData.cpp | 34 +++++++++++++------ src/fne/network/callhandler/TagP25Data.cpp | 34 +++++++++++++------ 7 files changed, 88 insertions(+), 32 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 49be4b99..7133e7f3 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -64,6 +64,8 @@ master: # Flag indicating whether or not denied traffic will be logged. # (This is useful for debugging talkgroup rules and other ACL issues, but can be very noisy on a busy system.) logDenials: false + # Flag indicating whether or not calls start/end events from a upstream peer will be logged. + logUpstreamCallStartEnd: true # Maximum number of concurrent packet processing workers. workers: 16 diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 85177449..d1dfed66 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -138,6 +138,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_dumpPacketData(false), m_verbosePacketData(false), m_logDenials(false), + m_logUpstreamCallStartEnd(true), m_reportPeerPing(reportPeerPing), m_verbose(verbose) { @@ -252,6 +253,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) m_verbosePacketData = conf["verbosePacketData"].as(false); m_logDenials = conf["logDenials"].as(false); + m_logUpstreamCallStartEnd = conf["logUpstreamCallStartEnd"].as(true); /* ** Drop Unit to Unit Peers @@ -297,6 +299,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogInfo(" Enable In-Call Control: %s", m_enableInCallCtrl ? "yes" : "no"); LogInfo(" Reject Unknown RIDs: %s", m_rejectUnknownRID ? "yes" : "no"); LogInfo(" Log Traffic Denials: %s", m_logDenials ? "yes" : "no"); + LogInfo(" Log Upstream Call Start/End Events: %s", m_logUpstreamCallStartEnd ? "yes" : "no"); LogInfo(" Mask Outbound Traffic Peer ID: %s", m_maskOutboundPeerID ? "yes" : "no"); if (m_maskOutboundPeerIDForNonPL) { LogInfo(" Mask Outbound Traffic Peer ID for Non-Peer Link: yes"); diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index f7289278..65952339 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -386,6 +386,7 @@ namespace network bool m_verbosePacketData; bool m_logDenials; + bool m_logUpstreamCallStartEnd; bool m_reportPeerPing; bool m_verbose; diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 0fb0aaee..0a73cb1a 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -121,8 +121,11 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } } - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); + #define CALL_END_LOG "Analog, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, CALL_END_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, CALL_END_LOG); // report call event to InfluxDB if (m_network->m_enableInfluxDB) { @@ -211,7 +214,11 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status[dstId].activeCall = true; m_status.unlock(); - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); + #define CALL_START_LOG "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, CALL_START_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, CALL_START_LOG); } } diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index a2ff720e..4b60b417 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -193,12 +193,19 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream); + #define PRV_CALL_END_LOG "DMR, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, PRV_CALL_END_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, PRV_CALL_END_LOG); + } + else { + #define CALL_END_LOG "DMR, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, CALL_END_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, CALL_END_LOG); } - else - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB if (m_network->m_enableInfluxDB) { @@ -323,11 +330,19 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, srcId, dstId, streamId, fromUpstream); + #define PRV_CALL_START_LOG "DMR, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, PRV_CALL_START_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, PRV_CALL_START_LOG); + } + else { + #define CALL_START_LOG "DMR, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, CALL_START_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, CALL_START_LOG); } - else - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); } } diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 4f208a67..c0af7cc8 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -232,12 +232,19 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); + #define PRV_CALL_END_LOG "NXDN, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, PRV_CALL_END_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, PRV_CALL_END_LOG); + } + else { + #define CALL_END_LOG "NXDN, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, CALL_END_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, CALL_END_LOG); } - else - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB if (m_network->m_enableInfluxDB) { @@ -358,12 +365,19 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, srcId, dstId, streamId, fromUpstream); + #define PRV_CALL_START_LOG "NXDN, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, PRV_CALL_START_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, PRV_CALL_START_LOG); + } + else { + #define CALL_START_LOG "NXDN, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, CALL_START_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, CALL_START_LOG); } - else - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, srcId, dstId, streamId, fromUpstream); } } } diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 154fbcb7..367fad41 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -241,12 +241,19 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_statusPVCall.end()) { m_statusPVCall[dstId].reset(); - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Private Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream); + #define PRV_CALL_END_LOG "P25, Private Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, PRV_CALL_END_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, PRV_CALL_END_LOG); + } + else { + #define CALL_END_LOG "P25, Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, CALL_END_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, CALL_END_LOG); } - else - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream); // report call event to InfluxDB if (m_network->m_enableInfluxDB) { @@ -368,12 +375,19 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall.unlock(); - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Private Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream); + #define PRV_CALL_START_LOG "P25, Private Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, PRV_CALL_START_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, PRV_CALL_START_LOG); + } + else { + #define CALL_START_LOG "P25, Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, CALL_START_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, CALL_START_LOG); } - else - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", - peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream); } } } From 185aaf2e371451fd6d7ef226f3e3eb94ffe60691 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 3 Nov 2025 15:42:59 -0500 Subject: [PATCH 124/200] more work towards a working DLD-type OTAR service for P25; --- src/common/p25/P25Defines.h | 3 + src/common/p25/kmm/KMMFactory.cpp | 4 + src/common/p25/kmm/KMMFactory.h | 2 + src/common/p25/kmm/KMMRekeyAck.cpp | 122 +++++++++++++++ src/common/p25/kmm/KMMRekeyAck.h | 103 +++++++++++++ src/common/p25/kmm/KMMRekeyCommand.cpp | 201 +++++++++++++++++++++++++ src/common/p25/kmm/KMMRekeyCommand.h | 125 +++++++++++++++ src/common/p25/kmm/KeysetItem.h | 62 ++++++++ src/fne/network/P25OTARService.cpp | 189 +++++++++++++++++++++++ src/fne/network/P25OTARService.h | 35 +++++ 10 files changed, 846 insertions(+) create mode 100644 src/common/p25/kmm/KMMRekeyAck.cpp create mode 100644 src/common/p25/kmm/KMMRekeyAck.h create mode 100644 src/common/p25/kmm/KMMRekeyCommand.cpp create mode 100644 src/common/p25/kmm/KMMRekeyCommand.h diff --git a/src/common/p25/P25Defines.h b/src/common/p25/P25Defines.h index 22dba8e7..e4395ca4 100644 --- a/src/common/p25/P25Defines.h +++ b/src/common/p25/P25Defines.h @@ -444,6 +444,9 @@ namespace p25 NAK = 0x16U, //!< Negative Ack NO_SERVICE = 0x17U, //!< No Service + REKEY_ACK = 0x1DU, //!< Rekey Ack + REKEY_CMD = 0x1EU, //!< Rekey Command + ZEROIZE_CMD = 0x21U, //!< Zeroize Command ZEROIZE_RSP = 0x22U, //!< Zeroize Response diff --git a/src/common/p25/kmm/KMMFactory.cpp b/src/common/p25/kmm/KMMFactory.cpp index eec2a2fa..8879d1ea 100644 --- a/src/common/p25/kmm/KMMFactory.cpp +++ b/src/common/p25/kmm/KMMFactory.cpp @@ -87,6 +87,10 @@ std::unique_ptr KMMFactory::create(const uint8_t* data) return decode(new KMMRegistrationCommand(), data); case KMM_MessageType::REG_RSP: return decode(new KMMRegistrationResponse(), data); + case KMM_MessageType::REKEY_ACK: + return decode(new KMMRekeyAck(), data); + case KMM_MessageType::REKEY_CMD: + return decode(new KMMRekeyCommand(), data); default: LogError(LOG_P25, "KMMFactory::create(), unknown KMM message ID value, messageId = $%02X", messageId); break; diff --git a/src/common/p25/kmm/KMMFactory.h b/src/common/p25/kmm/KMMFactory.h index ce9dd010..cfdeb1e3 100644 --- a/src/common/p25/kmm/KMMFactory.h +++ b/src/common/p25/kmm/KMMFactory.h @@ -33,6 +33,8 @@ #include "common/p25/kmm/KMMNoService.h" #include "common/p25/kmm/KMMRegistrationCommand.h" #include "common/p25/kmm/KMMRegistrationResponse.h" +#include "common/p25/kmm/KMMRekeyAck.h" +#include "common/p25/kmm/KMMRekeyCommand.h" #include "common/p25/kmm/KMMZeroize.h" namespace p25 diff --git a/src/common/p25/kmm/KMMRekeyAck.cpp b/src/common/p25/kmm/KMMRekeyAck.cpp new file mode 100644 index 00000000..d17f27f7 --- /dev/null +++ b/src/common/p25/kmm/KMMRekeyAck.cpp @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "p25/P25Defines.h" +#include "p25/kmm/KMMRekeyAck.h" +#include "Log.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::kmm; + +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the KMMRekeyAck class. */ + +KMMRekeyAck::KMMRekeyAck() : KMMFrame(), + m_messageId(0U), + m_numberOfKeyStatus(0U), + m_keystatus() +{ + m_messageId = KMM_MessageType::NAK; + m_respKind = KMM_ResponseKind::NONE; +} + +/* Finalizes a instance of the KMMRekeyAck class. */ + +KMMRekeyAck::~KMMRekeyAck() = default; + +/* Gets the byte length of this KMMRekeyAck. */ + +uint32_t KMMRekeyAck::length() const +{ + uint32_t len = KMM_REKEY_ACK_LENGTH; + len += m_keystatus.size() * 4U; + + return len; +} + +/* Decode a KMM NAK. */ + +bool KMMRekeyAck::decode(const uint8_t* data) +{ + assert(data != nullptr); + + KMMFrame::decodeHeader(data); + + m_messageId = data[10U]; // Message ID + m_numberOfKeyStatus = data[11U]; // Number of Key Status + + uint16_t offset = 0U; + for (uint8_t i = 0U; i < m_numberOfKeyStatus; i++) { + KeyStatus keyStatus = KeyStatus(); + + keyStatus.algId(data[12U + offset]); + + uint16_t kId = GET_UINT16(data, 13U + offset); + keyStatus.kId(kId); + + keyStatus.status(data[15U + offset]); + + m_keystatus.push_back(keyStatus); + offset += 4U; + } + + return true; +} + +/* Encode a KMM NAK. */ + +void KMMRekeyAck::encode(uint8_t* data) +{ + assert(data != nullptr); + m_messageLength = length(); + + KMMFrame::encodeHeader(data); + + data[10U] = m_messageId; // Message ID + data[11U] = m_numberOfKeyStatus; // Number of Key Status + + uint16_t offset = 0U; + for (auto keyStatus : m_keystatus) { + data[12U + offset] = keyStatus.algId(); + SET_UINT16(keyStatus.kId(), data, 13U + offset); + + data[15U + offset] = keyStatus.status(); + offset += 4U; + } +} + +/* Returns a string that represents the current KMM frame. */ + +std::string KMMRekeyAck::toString() +{ + return std::string("KMM, REKEY_ACK (Rekey Acknowledge)"); +} + +// --------------------------------------------------------------------------- +// Protected Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to copy the the class. */ + +void KMMRekeyAck::copy(const KMMRekeyAck& data) +{ + KMMFrame::copy(data); + + m_messageId = data.m_messageId; + m_numberOfKeyStatus = data.m_numberOfKeyStatus; + + m_keystatus = data.m_keystatus; +} diff --git a/src/common/p25/kmm/KMMRekeyAck.h b/src/common/p25/kmm/KMMRekeyAck.h new file mode 100644 index 00000000..13d741f4 --- /dev/null +++ b/src/common/p25/kmm/KMMRekeyAck.h @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file KMMRekeyAck.h + * @ingroup p25_kmm + * @file KMMRekeyAck.cpp + * @ingroup p25_kmm + */ +#if !defined(__P25_KMM__KMM_REKEY_ACK_H__) +#define __P25_KMM__KMM_REKEY_ACK_H__ + +#include "common/Defines.h" +#include "common/p25/kmm/KMMFrame.h" +#include "common/p25/kmm/KeysetItem.h" +#include "common/Utils.h" + +#include +#include + +namespace p25 +{ + namespace kmm + { + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + + /** + * @addtogroup p25_kmm + * @{ + */ + + const uint32_t KMM_REKEY_ACK_LENGTH = KMM_FRAME_LENGTH + 2U; + + /** @} */ + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + class HOST_SW_API KMMRekeyAck : public KMMFrame { + public: + /** + * @brief Initializes a new instance of the KMMRekeyAck class. + */ + KMMRekeyAck(); + /** + * @brief Finalizes a instance of the KMMRekeyAck class. + */ + ~KMMRekeyAck(); + + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + + /** + * @brief Decode a KMM rekey Ack. + * @param[in] data Buffer containing KMM frame data to decode. + * @returns bool True, if decoded, otherwise false. + */ + bool decode(const uint8_t* data) override; + /** + * @brief Encode a KMM rekey Ack. + * @param[out] data Buffer to encode KMM frame data to. + */ + void encode(uint8_t* data) override; + + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + + public: + /** + * @brief + */ + DECLARE_PROPERTY(uint8_t, messageId, MessageId); + /** + * @brief + */ + DECLARE_PROPERTY(uint8_t, numberOfKeyStatus, NumberOfKeyStatus); + + /** + * @brief List of key status. + */ + DECLARE_PROPERTY(std::vector, keystatus, KeyStatus); + + DECLARE_COPY(KMMRekeyAck); + }; + } // namespace kmm +} // namespace p25 + +#endif // __P25_KMM__KMM_REKEY_ACK_H__ diff --git a/src/common/p25/kmm/KMMRekeyCommand.cpp b/src/common/p25/kmm/KMMRekeyCommand.cpp new file mode 100644 index 00000000..cc0ed405 --- /dev/null +++ b/src/common/p25/kmm/KMMRekeyCommand.cpp @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "p25/P25Defines.h" +#include "p25/kmm/KMMRekeyCommand.h" +#include "Log.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::kmm; + +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the KMMRekeyCommand class. */ + +KMMRekeyCommand::KMMRekeyCommand() : KMMFrame(), + m_decryptInfoFmt(KMM_DECRYPT_INSTRUCT_NONE), + m_algId(ALGO_UNENCRYPT), + m_kId(0U), + m_keysets(), + m_miSet(false), + m_mi(nullptr) +{ + m_messageId = KMM_MessageType::REKEY_CMD; + + m_mi = new uint8_t[MI_LENGTH_BYTES]; + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); +} + +/* Finalizes a instance of the KMMRekeyCommand class. */ + +KMMRekeyCommand::~KMMRekeyCommand() +{ + if (m_mi != nullptr) { + delete[] m_mi; + m_mi = nullptr; + } +} + +/* Gets the byte length of this KMMRekeyCommand. */ + +uint32_t KMMRekeyCommand::length() const +{ + uint32_t len = KMM_REKEY_CMD_LENGTH; + if (m_miSet) + len += MI_LENGTH_BYTES; + + for (auto keysetItem : m_keysets) + len += keysetItem.length(); + + return len; +} + +/* Decode a KMM modify key. */ + +bool KMMRekeyCommand::decode(const uint8_t* data) +{ + assert(data != nullptr); + + KMMFrame::decodeHeader(data); + + m_decryptInfoFmt = data[10U]; // Decryption Instruction Format + m_algId = data[11U]; // Algorithm ID + m_kId = GET_UINT16(data, 12U); // Key ID + + uint16_t offset = 0U; + if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + ::memcpy(m_mi, data + 14U, MI_LENGTH_BYTES); + offset += 9U; + } + + uint8_t keysetCount = data[14U + offset]; + for (uint8_t n = 0U; n < keysetCount; n++) { + KeysetItem keysetItem = KeysetItem(); + + keysetItem.keysetId(data[16U + offset]); + keysetItem.algId(data[17U + offset]); + keysetItem.keyLength(data[18U + offset]); + + uint8_t keyCount = data[19U + offset]; + for (uint8_t i = 0U; i < keyCount; i++) { + KeyItem key = KeyItem(); + + DECLARE_UINT8_ARRAY(keyPayload, keysetItem.keyLength()); + + uint8_t keyFormat = data[20U + offset]; + uint8_t keyNameLen = keyFormat & 0x1FU; + + key.keyFormat(keyFormat & 0xE0U); + + uint16_t sln = GET_UINT16(data, 21U + offset); + key.sln(sln); + + uint16_t kId = GET_UINT16(data, 23U + offset); + key.kId(kId); + + ::memcpy(keyPayload, data + (25U + offset), keysetItem.keyLength()); + key.setKey(keyPayload, keysetItem.keyLength()); + + keysetItem.push_back(key); + + offset += 5U + keyNameLen + keysetItem.keyLength(); + } + + m_keysets.push_back(keysetItem); + offset += 4U; + } + + return true; +} + +/* Encode a KMM modify key. */ + +void KMMRekeyCommand::encode(uint8_t* data) +{ + assert(data != nullptr); + m_messageLength = length(); + + KMMFrame::encodeHeader(data); + + if (!m_miSet && m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { + m_decryptInfoFmt = KMM_DECRYPT_INSTRUCT_NONE; + } + + data[10U] = m_decryptInfoFmt; // Decryption Instruction Format + data[11U] = m_algId; // Algorithm ID + SET_UINT16(m_kId, data, 12U); // Key ID + + uint16_t offset = 0U; + if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { + ::memcpy(data + 14U, m_mi, MI_LENGTH_BYTES); + offset += 9U; + } + + uint8_t keysetCount = m_keysets.size(); + data[14U + offset] = keysetCount; + for (auto keysetItem : m_keysets) { + data[16U + offset] = keysetItem.keysetId(); + data[17U + offset] = keysetItem.algId(); + data[18U + offset] = keysetItem.keyLength(); + + uint8_t keyCount = keysetItem.keys().size(); + data[19U + offset] = keyCount; + for (auto key : keysetItem.keys()) { + uint8_t keyNameLen = key.keyFormat() & 0x1FU; + data[18U + offset] = key.keyFormat(); + SET_UINT16(key.sln(), data, 19U + offset); + SET_UINT16(key.kId(), data, 21U + offset); + + DECLARE_UINT8_ARRAY(keyPayload, keysetItem.keyLength()); + key.getKey(keyPayload); + + ::memcpy(data + (23U + offset), keyPayload, keysetItem.keyLength()); + + offset += 5U + keyNameLen + keysetItem.keyLength(); + } + + offset += 4U; + } +} + +/* Returns a string that represents the current KMM frame. */ + +std::string KMMRekeyCommand::toString() +{ + return std::string("KMM, REKEY_CMD (Rekey Command)"); +} + +// --------------------------------------------------------------------------- +// Protected Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to copy the the class. */ + +void KMMRekeyCommand::copy(const KMMRekeyCommand& data) +{ + KMMFrame::copy(data); + + m_decryptInfoFmt = data.m_decryptInfoFmt; + m_algId = data.m_algId; + m_kId = data.m_kId; + + if (data.m_mi != nullptr) { + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + ::memcpy(m_mi, data.m_mi, MI_LENGTH_BYTES); + } + + m_keysets = data.m_keysets; +} diff --git a/src/common/p25/kmm/KMMRekeyCommand.h b/src/common/p25/kmm/KMMRekeyCommand.h new file mode 100644 index 00000000..82e0e7e5 --- /dev/null +++ b/src/common/p25/kmm/KMMRekeyCommand.h @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file KMMRekeyCommand.h + * @ingroup p25_kmm + * @file KMMRekeyCommand.cpp + * @ingroup p25_kmm + */ +#if !defined(__P25_KMM__KMM_REKEY_COMMAND_H__) +#define __P25_KMM__KMM_REKEY_COMMAND_H__ + +#include "common/Defines.h" +#include "common/p25/kmm/KMMFrame.h" +#include "common/p25/kmm/KeysetItem.h" +#include "common/Utils.h" + +#include +#include + +namespace p25 +{ + namespace kmm + { + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + + /** + * @addtogroup p25_kmm + * @{ + */ + + const uint32_t KMM_REKEY_CMD_LENGTH = KMM_FRAME_LENGTH + 5U; + + /** @} */ + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + class HOST_SW_API KMMRekeyCommand : public KMMFrame { + public: + /** + * @brief Initializes a new instance of the KMMRekeyCommand class. + */ + KMMRekeyCommand(); + /** + * @brief Finalizes a instance of the KMMRekeyCommand class. + */ + ~KMMRekeyCommand(); + + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + + /** + * @brief Decode a KMM rekey command. + * @param[in] data Buffer containing KMM frame data to decode. + * @returns bool True, if decoded, otherwise false. + */ + bool decode(const uint8_t* data) override; + /** + * @brief Encode a KMM rekey command. + * @param[out] data Buffer to encode KMM frame data to. + */ + void encode(uint8_t* data) override; + + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + + /** @name Encryption data */ + /** + * @brief Sets the encryption message indicator. + * @param[in] mi Buffer containing the 9-byte Message Indicator. + */ + void setMI(const uint8_t* mi); + /** + * @brief Gets the encryption message indicator. + * @param[out] mi Buffer containing the 9-byte Message Indicator. + */ + void getMI(uint8_t* mi) const; + /** @} */ + + public: + /** + * @brief + */ + DECLARE_PROPERTY(uint8_t, decryptInfoFmt, DecryptInfoFmt); + /** + * @brief Encryption algorithm ID. + */ + DECLARE_PROPERTY(uint8_t, algId, AlgId); + /** + * @brief Encryption key ID. + */ + DECLARE_PROPERTY(uint32_t, kId, KId); + + /** + * @brief List of keysets. + */ + DECLARE_PROPERTY(std::vector, keysets, Keysets); + + DECLARE_COPY(KMMRekeyCommand); + + private: + // Encryption data + bool m_miSet; + uint8_t* m_mi; + }; + } // namespace kmm +} // namespace p25 + +#endif // __P25_KMM__KMM_REKEY_COMMAND_H__ diff --git a/src/common/p25/kmm/KeysetItem.h b/src/common/p25/kmm/KeysetItem.h index 07391b9a..34cf98fe 100644 --- a/src/common/p25/kmm/KeysetItem.h +++ b/src/common/p25/kmm/KeysetItem.h @@ -210,6 +210,68 @@ namespace p25 */ DECLARE_PROPERTY_PLAIN(std::vector, keys); }; + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents a key status within a KMM frame packet. + * @ingroup p25_kmm + */ + class HOST_SW_API KeyStatus { + public: + /** + * @brief Initializes a new instance of the KeyStatus class. + */ + KeyStatus() : + m_algId(0U), + m_kId(0U), + m_status(0U) + { + /* stub */ + } + + /** + * @brief Equals operator. Copies this KeyStatus to another KeyStatus. + * @param data Instance of KeyStatus to copy. + */ + virtual KeyStatus& operator= (const KeyStatus& data) + { + if (this != &data) { + m_algId = data.m_algId; + m_kId = data.m_kId; + m_status = data.m_status; + } + + return *this; + } + + /** + * @brief Gets the byte length of this keyset item. + * @return uint32_t Length of keyset item. + */ + uint32_t length() const + { + uint32_t len = 3U; + return len; + } + + public: + /** + * @brief Encryption algorithm ID. + */ + DECLARE_PROPERTY_PLAIN(uint8_t, algId); + /** + * @brief Key ID. + */ + DECLARE_PROPERTY_PLAIN(uint16_t, kId); + + /** + * @brief Key ID. + */ + DECLARE_PROPERTY_PLAIN(uint8_t, status); + }; } // namespace kmm } // namespace p25 diff --git a/src/fne/network/P25OTARService.cpp b/src/fne/network/P25OTARService.cpp index 89bfddad..17ae4796 100644 --- a/src/fne/network/P25OTARService.cpp +++ b/src/fne/network/P25OTARService.cpp @@ -359,6 +359,14 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ if (m_verbose) { LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, flag = $%02X", kmm->toString().c_str(), llId, kmm->getFlag()); + switch (kmm->getFlag()) { + case KMM_HelloFlag::REKEY_REQUEST_UKEK: + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, rekey requested with UKEK, llId = %u", kmm->toString().c_str(), llId); + break; + case KMM_HelloFlag::REKEY_REQUEST_NO_UKEK: + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, rekey requested with no UKEK, llId = %u", kmm->toString().c_str(), llId); + break; + } } // respond with No-Service if KMF services are disabled @@ -367,6 +375,53 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ } break; + case KMM_MessageType::NAK: + { + KMMNegativeAck* kmm = static_cast(frame.get()); + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, messageId = $%02X, messageNo = %u, status = $%02X", kmm->toString().c_str(), + llId, kmm->getMessageId(), kmm->getMessageNumber(), kmm->getStatus()); + logResponseStatus(llId, kmm->toString(), kmm->getStatus()); + } + break; + + case KMM_MessageType::REKEY_ACK: + { + KMMRekeyAck* kmm = static_cast(frame.get()); + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, messageId = $%02X, numOfStatus = %u", kmm->toString().c_str(), + llId, kmm->getMessageId(), kmm->getNumberOfKeyStatus()); + + if (kmm->getNumberOfKeyStatus() > 0U) { + for (auto entry : kmm->getKeyStatus()) { + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, algId = $%02X, kId = $%04X, status = $%02X", kmm->toString().c_str(), + llId, entry.algId(), entry.kId(), entry.status()); + logResponseStatus(llId, kmm->toString(), entry.status()); + } + } + } + break; + + case KMM_MessageType::DEREG_CMD: + { + KMMDeregistrationCommand* kmm = static_cast(frame.get()); + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u", kmm->toString().c_str(), + llId); + + // respond with No-Service if KMF services are disabled + if (!m_network->m_kmfServicesEnabled) + return write_KMM_NoService(llId, kmm->getSrcLLId(), payloadSize); + else + return write_KMM_Dereg_Response(llId, kmm->getSrcLLId(), payloadSize); + } + break; + case KMM_MessageType::REG_RSP: + { + KMMRegistrationResponse* kmm = static_cast(frame.get()); + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, status = $%02X", kmm->toString().c_str(), + llId, kmm->getStatus()); + logResponseStatus(llId, kmm->toString(), kmm->getStatus()); + } + break; + default: break; } // switch (frame->getMessageId()) @@ -374,6 +429,57 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ return nullptr; } +/* Helper used to return a Registration-Command KMM to the calling SU. */ + +UInt8Array P25OTARService::write_KMM_Reg_Command(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize) +{ + if (payloadSize != nullptr) + *payloadSize = KMM_REGISTRATION_CMD_LENGTH; + + UInt8Array kmmFrame = std::make_unique(KMM_REGISTRATION_CMD_LENGTH); + + uint8_t buffer[KMM_REGISTRATION_CMD_LENGTH]; + KMMRegistrationCommand outKmm = KMMRegistrationCommand(); + outKmm.setSrcLLId(WUID_FNE); + outKmm.setDstLLId(kmmRSI); + + if (m_verbose) { + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, RSI = %u", outKmm.toString().c_str(), + outKmm.getSrcLLId(), outKmm.getDstLLId()); + } + + outKmm.encode(buffer); + + ::memcpy(kmmFrame.get(), buffer, KMM_REGISTRATION_CMD_LENGTH); + return kmmFrame; +} + +/* Helper used to return a Deregistration-Response KMM to the calling SU. */ + +UInt8Array P25OTARService::write_KMM_Dereg_Response(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize) +{ + if (payloadSize != nullptr) + *payloadSize = KMM_DEREGISTRATION_RSP_LENGTH; + + UInt8Array kmmFrame = std::make_unique(KMM_DEREGISTRATION_RSP_LENGTH); + + uint8_t buffer[KMM_DEREGISTRATION_RSP_LENGTH]; + KMMDeregistrationResponse outKmm = KMMDeregistrationResponse(); + outKmm.setSrcLLId(WUID_FNE); + outKmm.setDstLLId(kmmRSI); + outKmm.setStatus(KMM_Status::CMD_PERFORMED); + + if (m_verbose) { + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, RSI = %u", outKmm.toString().c_str(), + outKmm.getSrcLLId(), outKmm.getDstLLId()); + } + + outKmm.encode(buffer); + + ::memcpy(kmmFrame.get(), buffer, KMM_DEREGISTRATION_RSP_LENGTH); + return kmmFrame; +} + /* Helper used to return a No-Service KMM to the calling SU. */ UInt8Array P25OTARService::write_KMM_NoService(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize) @@ -398,3 +504,86 @@ UInt8Array P25OTARService::write_KMM_NoService(uint32_t llId, uint32_t kmmRSI, u ::memcpy(kmmFrame.get(), buffer, KMM_NO_SERVICE_LENGTH); return kmmFrame; } + +/* Helper used to return a Zeroize KMM to the calling SU. */ + +UInt8Array P25OTARService::write_KMM_Zeroize(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize) +{ + if (payloadSize != nullptr) + *payloadSize = KMM_ZEROIZE_LENGTH; + + UInt8Array kmmFrame = std::make_unique(KMM_ZEROIZE_LENGTH); + + uint8_t buffer[KMM_ZEROIZE_LENGTH]; + KMMZeroize outKmm = KMMZeroize(); + outKmm.setSrcLLId(WUID_FNE); + outKmm.setDstLLId(kmmRSI); + + if (m_verbose) { + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, RSI = %u", outKmm.toString().c_str(), + outKmm.getSrcLLId(), outKmm.getDstLLId()); + } + + outKmm.encode(buffer); + + ::memcpy(kmmFrame.get(), buffer, KMM_ZEROIZE_LENGTH); + return kmmFrame; +} + +/* Helper used to log a KMM response. */ + +void P25OTARService::logResponseStatus(uint32_t llId, std::string kmmString, uint8_t status) +{ + switch (status) { + case KMM_Status::CMD_PERFORMED: + if (m_verbose) { + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, command performed, llId = %u", kmmString.c_str(), llId); + } + break; + case KMM_Status::CMD_NOT_PERFORMED: + LogWarning(LOG_P25, P25_KMM_STR ", %s, command not performed, llId = %u", kmmString.c_str(), llId); + break; + + case KMM_Status::ITEM_NOT_EXIST: + LogWarning(LOG_P25, P25_KMM_STR ", %s, item does not exist, llId = %u", kmmString.c_str(), llId); + break; + case KMM_Status::INVALID_MSG_ID: + LogWarning(LOG_P25, P25_KMM_STR ", %s, invalid message ID, llId = %u", kmmString.c_str(), llId); + break; + case KMM_Status::INVALID_MAC: + LogWarning(LOG_P25, P25_KMM_STR ", %s, invalid auth code, llId = %u", kmmString.c_str(), llId); + break; + + case KMM_Status::OUT_OF_MEMORY: + LogWarning(LOG_P25, P25_KMM_STR ", %s, out of memory, llId = %u", kmmString.c_str(), llId); + break; + case KMM_Status::FAILED_TO_DECRYPT: + LogWarning(LOG_P25, P25_KMM_STR ", %s, failed to decrypt message, llId = %u", kmmString.c_str(), llId); + break; + + case KMM_Status::INVALID_MSG_NUMBER: + LogWarning(LOG_P25, P25_KMM_STR ", %s, invalid message number, llId = %u", kmmString.c_str(), llId); + break; + case KMM_Status::INVALID_KID: + LogWarning(LOG_P25, P25_KMM_STR ", %s, invalid key ID, llId = %u", kmmString.c_str(), llId); + break; + case KMM_Status::INVALID_ALGID: + LogWarning(LOG_P25, P25_KMM_STR ", %s, invalid algorithm ID, llId = %u", kmmString.c_str(), llId); + break; + case KMM_Status::INVALID_MFID: + LogWarning(LOG_P25, P25_KMM_STR ", %s, invalid manufacturer ID, llId = %u", kmmString.c_str(), llId); + break; + + case KMM_Status::MI_ALL_ZERO: + LogWarning(LOG_P25, P25_KMM_STR ", %s, message indicator was all zeros, llId = %u", kmmString.c_str(), llId); + break; + case KMM_Status::KEY_FAIL: + LogWarning(LOG_P25, P25_KMM_STR ", %s, key identified by algo/key is erased, llId = %u", kmmString.c_str(), llId); + break; + + case KMM_Status::UNKNOWN: + default: + LogWarning(LOG_P25, P25_KMM_STR ", llId = %u, status = $%02X; unknown status", llId, status); + break; + } +} diff --git a/src/fne/network/P25OTARService.h b/src/fne/network/P25OTARService.h index b07cd02d..0697ff15 100644 --- a/src/fne/network/P25OTARService.h +++ b/src/fne/network/P25OTARService.h @@ -136,6 +136,24 @@ namespace network */ UInt8Array processKMM(const uint8_t* data, uint32_t len, uint32_t llId, bool encrypted, uint32_t* payloadSize); + /** + * @brief Helper used to return a Registration-Command KMM to the calling SU. + * @param llId Logical Link Address. + * @param kmmRSI KMM Radio Set Identifier. + * @param[out] payloadSize Size of the returned KMM payload. + * @returns UInt8Array Buffer containing the processed KMM frame (if any). + */ + UInt8Array write_KMM_Reg_Command(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize); + + /** + * @brief Helper used to return a Deregistration-Response KMM to the calling SU. + * @param llId Logical Link Address. + * @param kmmRSI KMM Radio Set Identifier. + * @param[out] payloadSize Size of the returned KMM payload. + * @returns UInt8Array Buffer containing the processed KMM frame (if any). + */ + UInt8Array write_KMM_Dereg_Response(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize); + /** * @brief Helper used to return a No-Service KMM to the calling SU. * @param llId Logical Link Address. @@ -144,6 +162,23 @@ namespace network * @returns UInt8Array Buffer containing the processed KMM frame (if any). */ UInt8Array write_KMM_NoService(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize); + + /** + * @brief Helper used to return a Zeroize KMM to the calling SU. + * @param llId Logical Link Address. + * @param kmmRSI KMM Radio Set Identifier. + * @param[out] payloadSize Size of the returned KMM payload. + * @returns UInt8Array Buffer containing the processed KMM frame (if any). + */ + UInt8Array write_KMM_Zeroize(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize); + + /** + * @brief Helper used to log a KMM response. + * @param llId Logical Link Address. + * @param kmmString + * @param status Status. + */ + void logResponseStatus(uint32_t llId, std::string kmmString, uint8_t status); }; } // namespace network From 901ae6c10a950c5548887ff11c5dd29400a9a74c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 3 Nov 2025 15:44:14 -0500 Subject: [PATCH 125/200] fix typos; --- src/common/p25/kmm/KMMRekeyAck.cpp | 4 ++-- src/common/p25/kmm/KMMRekeyAck.h | 4 ++-- src/common/p25/kmm/KMMRekeyCommand.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/common/p25/kmm/KMMRekeyAck.cpp b/src/common/p25/kmm/KMMRekeyAck.cpp index d17f27f7..c1b0e9df 100644 --- a/src/common/p25/kmm/KMMRekeyAck.cpp +++ b/src/common/p25/kmm/KMMRekeyAck.cpp @@ -47,7 +47,7 @@ uint32_t KMMRekeyAck::length() const return len; } -/* Decode a KMM NAK. */ +/* Decode a KMM rekey ack. */ bool KMMRekeyAck::decode(const uint8_t* data) { @@ -76,7 +76,7 @@ bool KMMRekeyAck::decode(const uint8_t* data) return true; } -/* Encode a KMM NAK. */ +/* Encode a KMM rekey ack. */ void KMMRekeyAck::encode(uint8_t* data) { diff --git a/src/common/p25/kmm/KMMRekeyAck.h b/src/common/p25/kmm/KMMRekeyAck.h index 13d741f4..0637c084 100644 --- a/src/common/p25/kmm/KMMRekeyAck.h +++ b/src/common/p25/kmm/KMMRekeyAck.h @@ -63,13 +63,13 @@ namespace p25 uint32_t length() const override; /** - * @brief Decode a KMM rekey Ack. + * @brief Decode a KMM rekey ack. * @param[in] data Buffer containing KMM frame data to decode. * @returns bool True, if decoded, otherwise false. */ bool decode(const uint8_t* data) override; /** - * @brief Encode a KMM rekey Ack. + * @brief Encode a KMM rekey ack. * @param[out] data Buffer to encode KMM frame data to. */ void encode(uint8_t* data) override; diff --git a/src/common/p25/kmm/KMMRekeyCommand.cpp b/src/common/p25/kmm/KMMRekeyCommand.cpp index cc0ed405..b33373df 100644 --- a/src/common/p25/kmm/KMMRekeyCommand.cpp +++ b/src/common/p25/kmm/KMMRekeyCommand.cpp @@ -62,7 +62,7 @@ uint32_t KMMRekeyCommand::length() const return len; } -/* Decode a KMM modify key. */ +/* Decode a KMM rekey command. */ bool KMMRekeyCommand::decode(const uint8_t* data) { @@ -121,7 +121,7 @@ bool KMMRekeyCommand::decode(const uint8_t* data) return true; } -/* Encode a KMM modify key. */ +/* Encode a KMM rekey command. */ void KMMRekeyCommand::encode(uint8_t* data) { From c57f39c29f3001861948f733485403f38fb43f17 Mon Sep 17 00:00:00 2001 From: "Lorenzo L. Romero" Date: Tue, 4 Nov 2025 08:51:07 -0500 Subject: [PATCH 126/200] Add carrier operated relay support to dvmbridge. (#104) --- configs/bridge-config.example.yml | 10 + src/bridge/CtsCorController.cpp | 239 +++++++++++++++++++ src/bridge/CtsCorController.h | 83 +++++++ src/bridge/HostBridge.cpp | 377 +++++++++++++++++++++++++----- src/bridge/HostBridge.h | 21 ++ src/bridge/RtsPttController.cpp | 11 + src/bridge/RtsPttController.h | 6 + 7 files changed, 689 insertions(+), 58 deletions(-) create mode 100644 src/bridge/CtsCorController.cpp create mode 100644 src/bridge/CtsCorController.h diff --git a/configs/bridge-config.example.yml b/configs/bridge-config.example.yml index aed45381..978f9050 100644 --- a/configs/bridge-config.example.yml +++ b/configs/bridge-config.example.yml @@ -181,3 +181,13 @@ system: rtsPttPort: "/dev/ttyUSB0" # Hold-off time (ms) before clearing RTS PTT after last audio output. rtsPttHoldoffMs: 250 + + # CTS COR Configuration + # Flag indicating whether CTS-based COR detection is enabled. + ctsCorEnable: false + # Serial port device for CTS COR (e.g., /dev/ttyUSB0). Often same as RTS PTT. + ctsCorPort: "/dev/ttyUSB0" + # Flag indicating whether to invert COR logic (if true, COR LOW triggers instead of HIGH). + ctsCorInvert: false + # Hold-off time (ms) before ending call after CTS COR deasserts. + ctsCorHoldoffMs: 250 diff --git a/src/bridge/CtsCorController.cpp b/src/bridge/CtsCorController.cpp new file mode 100644 index 00000000..c00096a9 --- /dev/null +++ b/src/bridge/CtsCorController.cpp @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Bridge + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Lorenzo L. Romero, K2LLR + */ +/** + * @file CtsCorController.cpp + * @ingroup bridge + */ + +#include "Defines.h" +#include "CtsCorController.h" + +#if !defined(_WIN32) +#include +#endif + +CtsCorController::CtsCorController(const std::string& port) + : m_port(port), m_isOpen(false), m_ownsFd(true) +#if defined(_WIN32) + , m_fd(INVALID_HANDLE_VALUE) +#else + , m_fd(-1) +#endif // defined(_WIN32) +{ +} + +CtsCorController::~CtsCorController() +{ + close(); +} + +bool CtsCorController::open(int reuseFd) +{ + if (m_isOpen) + return true; + +#if defined(_WIN32) + std::string deviceName = m_port; + if (deviceName.find("\\\\.\\") == std::string::npos) { + deviceName = "\\\\." + m_port; + } + + m_fd = ::CreateFileA(deviceName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (m_fd == INVALID_HANDLE_VALUE) { + ::LogError(LOG_HOST, "Cannot open CTS COR device - %s, err=%04lx", m_port.c_str(), ::GetLastError()); + return false; + } + + DCB dcb; + if (::GetCommState(m_fd, &dcb) == 0) { + ::LogError(LOG_HOST, "Cannot get the attributes for %s, err=%04lx", m_port.c_str(), ::GetLastError()); + ::CloseHandle(m_fd); + m_fd = INVALID_HANDLE_VALUE; + return false; + } + + dcb.BaudRate = 9600; + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.fParity = FALSE; + dcb.StopBits = ONESTOPBIT; + dcb.fInX = FALSE; + dcb.fOutX = FALSE; + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDsrSensitivity = FALSE; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + + if (::SetCommState(m_fd, &dcb) == 0) { + ::LogError(LOG_HOST, "Cannot set the attributes for %s, err=%04lx", m_port.c_str(), ::GetLastError()); + ::CloseHandle(m_fd); + m_fd = INVALID_HANDLE_VALUE; + return false; + } + +#else + // If reusing an existing file descriptor from RTS PTT, don't open a new one + if (reuseFd >= 0) { + m_fd = reuseFd; + m_ownsFd = false; // Only COR can close file descriptor + ::LogInfo(LOG_HOST, "CTS COR Controller reusing file descriptor from RTS PTT on %s", m_port.c_str()); + m_isOpen = true; + return true; + } + + m_ownsFd = true; // COR owns the file descriptor + + // Open port if not available + m_fd = ::open(m_port.c_str(), O_RDONLY | O_NOCTTY | O_NDELAY, 0); + if (m_fd < 0) { + // Try rw if ro fails + m_fd = ::open(m_port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); + if (m_fd < 0) { + ::LogError(LOG_HOST, "Cannot open CTS COR device - %s", m_port.c_str()); + return false; + } + } + + if (::isatty(m_fd) == 0) { + ::LogError(LOG_HOST, "%s is not a TTY device", m_port.c_str()); + ::close(m_fd); + m_fd = -1; + return false; + } + + // Save current RTS state before configuring termios + int savedModemState = 0; + if (::ioctl(m_fd, TIOCMGET, &savedModemState) < 0) { + ::LogError(LOG_HOST, "Cannot get the control attributes for %s", m_port.c_str()); + ::close(m_fd); + m_fd = -1; + return false; + } + bool savedRtsState = (savedModemState & TIOCM_RTS) != 0; + + if (!setTermios()) { + ::close(m_fd); + m_fd = -1; + return false; + } + + // Restore RTS to its original state + int currentModemState = 0; + if (::ioctl(m_fd, TIOCMGET, ¤tModemState) < 0) { + ::LogError(LOG_HOST, "Cannot get the control attributes for %s after termios", m_port.c_str()); + ::close(m_fd); + m_fd = -1; + return false; + } + bool currentRtsState = (currentModemState & TIOCM_RTS) != 0; + if (currentRtsState != savedRtsState) { + // Restore RTS to original state + if (savedRtsState) { + currentModemState |= TIOCM_RTS; + } else { + currentModemState &= ~TIOCM_RTS; + } + if (::ioctl(m_fd, TIOCMSET, ¤tModemState) < 0) { + ::LogError(LOG_HOST, "Cannot restore RTS state for %s", m_port.c_str()); + ::close(m_fd); + m_fd = -1; + return false; + } + ::LogDebug(LOG_HOST, "CTS COR: Restored RTS to %s on %s", savedRtsState ? "HIGH" : "LOW", m_port.c_str()); + } +#endif // defined(_WIN32) + + ::LogInfo(LOG_HOST, "CTS COR Controller opened on %s (RTS preserved)", m_port.c_str()); + m_isOpen = true; + return true; +} + +void CtsCorController::close() +{ + if (!m_isOpen) + return; + +#if defined(_WIN32) + if (m_fd != INVALID_HANDLE_VALUE) { + ::CloseHandle(m_fd); + m_fd = INVALID_HANDLE_VALUE; + } +#else + // Only close the file descriptor if we opened it ourselves + // If we're reusing a descriptor from RTS PTT, don't close it + if (m_fd != -1 && m_ownsFd) { + ::close(m_fd); + m_fd = -1; + } else if (m_fd != -1 && !m_ownsFd) { + m_fd = -1; + } +#endif // defined(_WIN32) + + m_isOpen = false; + ::LogInfo(LOG_HOST, "CTS COR Controller closed"); +} + +bool CtsCorController::isCtsAsserted() +{ + if (!m_isOpen) + return false; + +#if defined(_WIN32) + DWORD modemStat = 0; + if (::GetCommModemStatus(m_fd, &modemStat) == 0) { + ::LogError(LOG_HOST, "Cannot read modem status for %s, err=%04lx", m_port.c_str(), ::GetLastError()); + return false; + } + return (modemStat & MS_CTS_ON) != 0; +#else + int modemState = 0; + if (::ioctl(m_fd, TIOCMGET, &modemState) < 0) { + ::LogError(LOG_HOST, "Cannot get the control attributes for %s", m_port.c_str()); + return false; + } + return (modemState & TIOCM_CTS) != 0; +#endif // defined(_WIN32) +} + +bool CtsCorController::setTermios() +{ +#if !defined(_WIN32) + termios termios; + if (::tcgetattr(m_fd, &termios) < 0) { + ::LogError(LOG_HOST, "Cannot get the attributes for %s", m_port.c_str()); + return false; + } + + termios.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK); + termios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL); + termios.c_iflag &= ~(IXON | IXOFF | IXANY); + termios.c_oflag &= ~(OPOST); + // Important: Disable hardware flow control (CRTSCTS) to avoid affecting RTS + // We only want to read CTS, not control RTS + termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS); + termios.c_cflag |= (CS8 | CLOCAL | CREAD); + termios.c_lflag &= ~(ISIG | ICANON | IEXTEN); + termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + termios.c_cc[VMIN] = 0; + termios.c_cc[VTIME] = 10; + + ::cfsetospeed(&termios, B9600); + ::cfsetispeed(&termios, B9600); + + if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { + ::LogError(LOG_HOST, "Cannot set the attributes for %s", m_port.c_str()); + return false; + } +#endif // !defined(_WIN32) + + return true; +} + + diff --git a/src/bridge/CtsCorController.h b/src/bridge/CtsCorController.h new file mode 100644 index 00000000..58f53806 --- /dev/null +++ b/src/bridge/CtsCorController.h @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Bridge + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Lorenzo L. Romero, K2LLR + */ +/** + * @file CtsCorController.h + * @ingroup bridge + */ +#if !defined(__CTS_COR_CONTROLLER_H__) +#define __CTS_COR_CONTROLLER_H__ + +#include "Defines.h" +#include "common/Log.h" + +#include + +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#include +#include +#include +#endif // defined(_WIN32) + +/** + * @brief This class implements CTS-based COR detection for the bridge. + * @ingroup bridge + */ +class HOST_SW_API CtsCorController { +public: + /** + * @brief Initializes a new instance of the CtsCorController class. + * @param port Serial port device (e.g., /dev/ttyUSB0). + */ + CtsCorController(const std::string& port); + /** + * @brief Finalizes a instance of the CtsCorController class. + */ + ~CtsCorController(); + + /** + * @brief Opens the serial port for CTS readback. + * @param reuseFd Optional file descriptor to reuse (when sharing port with RTS PTT). + * @returns bool True, if port was opened successfully, otherwise false. + */ + bool open(int reuseFd = -1); + /** + * @brief Closes the serial port. + */ + void close(); + + /** + * @brief Reads the current CTS signal state. + * @returns bool True if CTS is asserted (active), otherwise false. + */ + bool isCtsAsserted(); + +private: + std::string m_port; + bool m_isOpen; + bool m_ownsFd; // true if we opened the fd, false if reusing from RTS PTT +#if defined(_WIN32) + HANDLE m_fd; +#else + int m_fd; +#endif // defined(_WIN32) + + /** + * @brief Sets the termios settings on the serial port. + * @returns bool True, if settings are set, otherwise false. + */ + bool setTermios(); +}; + +#endif // __CTS_COR_CONTROLLER_H__ + + diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 006e8703..aaf3742b 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -249,6 +249,15 @@ HostBridge::HostBridge(const std::string& confFile) : m_rtsPttPort(), m_rtsPttController(nullptr), m_rtsPttActive(false), + m_lastAudioOut(), + m_rtsPttHoldoffMs(250U), + m_ctsCorEnable(false), + m_ctsCorPort(), + m_ctsCorController(nullptr), + m_ctsCorActive(false), + m_ctsCorInvert(false), + m_ctsPadTimeout(1000U, 0U, 22U), + m_ctsCorHoldoffMs(250U), m_rtpSeqNo(0U), m_rtpTimestamp(INVALID_TS), m_usrpSeqNo(0U) @@ -411,6 +420,11 @@ int HostBridge::run() if (!ret) return EXIT_FAILURE; + // initialize CTS COR detection + ret = initializeCtsCor(); + if (!ret) + return EXIT_FAILURE; + ma_result result; if (m_localAudio) { // initialize audio devices @@ -950,6 +964,20 @@ bool HostBridge::readParams() m_rtsPttPort = systemConf["rtsPttPort"].as("/dev/ttyUSB0"); m_rtsPttHoldoffMs = (uint32_t)systemConf["rtsPttHoldoffMs"].as(m_rtsPttHoldoffMs); + // CTS COR Configuration + m_ctsCorEnable = systemConf["ctsCorEnable"].as(false); + m_ctsCorPort = systemConf["ctsCorPort"].as("/dev/ttyUSB0"); + m_ctsCorInvert = systemConf["ctsCorInvert"].as(false); + m_ctsCorHoldoffMs = (uint32_t)systemConf["ctsCorHoldoffMs"].as(m_ctsCorHoldoffMs); + + if (m_ctsCorEnable) { + LogInfo("CTS COR Configuration"); + LogInfo(" Enabled: yes"); + LogInfo(" Port: %s", m_ctsCorPort.c_str()); + LogInfo(" Invert: %s (%s triggers)", m_ctsCorInvert ? "yes" : "no", m_ctsCorInvert ? "LOW" : "HIGH"); + LogInfo(" Holdoff: %u ms", m_ctsCorHoldoffMs); + } + std::string txModeStr = "DMR"; if (m_txMode == TX_MODE_P25) txModeStr = "P25"; @@ -3054,7 +3082,23 @@ void* HostBridge::threadAudioProcess(void* arg) { std::lock_guard lock(s_audioMutex); - if (bridge->m_inputAudio.dataSize() >= AUDIO_SAMPLES_LENGTH) { + // When COR is active, we need to send frames continuously when audio data is available + // The audio callback should be continuously feeding data, so we should always have data available + bool hasAudioData = bridge->m_inputAudio.dataSize() >= AUDIO_SAMPLES_LENGTH; + + // Process if we have audio data + // When COR is active: process whenever we have data (which should be continuous) + // When COR is not active: only process when VOX detects audio (normal mode) + bool shouldProcess = false; + if (bridge->m_ctsCorActive && bridge->m_audioDetect) { + // COR is active: process whenever we have audio data (continuous transmission) + shouldProcess = hasAudioData; + } else if (!bridge->m_ctsCorActive && bridge->m_audioDetect) { + // Normal VOX mode: process when we have audio data + shouldProcess = hasAudioData; + } + + if (shouldProcess && hasAudioData) { short samples[AUDIO_SAMPLES_LENGTH]; bridge->m_inputAudio.get(samples, AUDIO_SAMPLES_LENGTH); @@ -3093,74 +3137,114 @@ void* HostBridge::threadAudioProcess(void* arg) bridge->m_detectedSampleCnt++; } - // handle Rx triggered by internal VOX - if (maxSample > sampleLevel) { - bridge->m_audioDetect = true; - if (bridge->m_txStreamId == 0U) { - bridge->m_txStreamId = 1U; // prevent further false starts -- this isn't the right way to handle this... - LogInfoEx(LOG_HOST, "%s, call start, srcId = %u, dstId = %u", trafficType.c_str(), srcId, dstId); - - if (bridge->m_grantDemand) { - switch (bridge->m_txMode) { - case TX_MODE_P25: - { - p25::lc::LC lc = p25::lc::LC(); - lc.setLCO(P25DEF::LCO::GROUP); - lc.setDstId(dstId); - lc.setSrcId(srcId); - - p25::data::LowSpeedData lsd = p25::data::LowSpeedData(); - - uint8_t controlByte = network::NET_CTRL_GRANT_DEMAND; - if (bridge->m_tekAlgoId != P25DEF::ALGO_UNENCRYPT) - controlByte |= network::NET_CTRL_GRANT_ENCRYPT; - bridge->m_network->writeP25TDU(lc, lsd, controlByte); + // handle Rx triggered by internal VOX (unless COR is active, which takes precedence) + if (!bridge->m_ctsCorActive) { + if (maxSample > sampleLevel) { + bridge->m_audioDetect = true; + if (bridge->m_txStreamId == 0U) { + bridge->m_txStreamId = 1U; + LogInfoEx(LOG_HOST, "%s, call start, srcId = %u, dstId = %u", trafficType.c_str(), srcId, dstId); + + if (bridge->m_grantDemand) { + switch (bridge->m_txMode) { + case TX_MODE_P25: + { + p25::lc::LC lc = p25::lc::LC(); + lc.setLCO(P25DEF::LCO::GROUP); + lc.setDstId(dstId); + lc.setSrcId(srcId); + + p25::data::LowSpeedData lsd = p25::data::LowSpeedData(); + + uint8_t controlByte = network::NET_CTRL_GRANT_DEMAND; + if (bridge->m_tekAlgoId != P25DEF::ALGO_UNENCRYPT) + controlByte |= network::NET_CTRL_GRANT_ENCRYPT; + bridge->m_network->writeP25TDU(lc, lsd, controlByte); + } + break; } - break; } } - } - bridge->m_localDropTime.stop(); - } else { - // if we've exceeded the audio drop timeout, then really drop the audio - if (bridge->m_localDropTime.isRunning() && bridge->m_localDropTime.hasExpired()) { - if (bridge->m_audioDetect) { - bridge->callEnd(srcId, dstId); + bridge->m_localDropTime.stop(); + } else { + // if we've exceeded the audio drop timeout, then really drop the audio + if (bridge->m_localDropTime.isRunning() && bridge->m_localDropTime.hasExpired()) { + if (bridge->m_audioDetect) { + bridge->callEnd(srcId, dstId); + } } - } - if (!bridge->m_localDropTime.isRunning()) - bridge->m_localDropTime.start(); + if (!bridge->m_localDropTime.isRunning()) + bridge->m_localDropTime.start(); + } } + // Send audio frames: either from actual audio input OR silence frames when COR is active if (bridge->m_audioDetect && !bridge->m_callInProgress) { - ma_uint32 pcmBytes = AUDIO_SAMPLES_LENGTH * ma_get_bytes_per_frame(bridge->m_maDevice.capture.format, bridge->m_maDevice.capture.channels); - DECLARE_UINT8_ARRAY(pcm, pcmBytes); + // If COR is active, always send the actual audio from the buffer (even if quiet) + // If COR is not active, only send when VOX detects audio + if (bridge->m_ctsCorActive) { + // COR is active: always encode actual audio from buffer + // The buffer should always have data from audio callback, even if it's quiet + ma_uint32 pcmBytes = AUDIO_SAMPLES_LENGTH * ma_get_bytes_per_frame(bridge->m_maDevice.capture.format, bridge->m_maDevice.capture.channels); + DECLARE_UINT8_ARRAY(pcm, pcmBytes); + + // Always encode the actual samples, even if they're quiet + // This ensures real audio is passed through, not just silence + int pcmIdx = 0; + for (uint32_t smpIdx = 0; smpIdx < AUDIO_SAMPLES_LENGTH; smpIdx++) { + pcm[pcmIdx + 0] = (uint8_t)(samples[smpIdx] & 0xFF); + pcm[pcmIdx + 1] = (uint8_t)((samples[smpIdx] >> 8) & 0xFF); + pcmIdx += 2; + } - int pcmIdx = 0; - for (uint32_t smpIdx = 0; smpIdx < AUDIO_SAMPLES_LENGTH; smpIdx++) { - pcm[pcmIdx + 0] = (uint8_t)(samples[smpIdx] & 0xFF); - pcm[pcmIdx + 1] = (uint8_t)((samples[smpIdx] >> 8) & 0xFF); - pcmIdx += 2; - } + switch (bridge->m_txMode) + { + case TX_MODE_DMR: + bridge->encodeDMRAudioFrame(pcm); + break; + case TX_MODE_P25: + bridge->encodeP25AudioFrame(pcm); + break; + case TX_MODE_ANALOG: + bridge->encodeAnalogAudioFrame(pcm); + break; + } + } else { + // COR is not active: normal VOX-based audio processing + if (maxSample > sampleLevel) { + ma_uint32 pcmBytes = AUDIO_SAMPLES_LENGTH * ma_get_bytes_per_frame(bridge->m_maDevice.capture.format, bridge->m_maDevice.capture.channels); + DECLARE_UINT8_ARRAY(pcm, pcmBytes); + + int pcmIdx = 0; + for (uint32_t smpIdx = 0; smpIdx < AUDIO_SAMPLES_LENGTH; smpIdx++) { + pcm[pcmIdx + 0] = (uint8_t)(samples[smpIdx] & 0xFF); + pcm[pcmIdx + 1] = (uint8_t)((samples[smpIdx] >> 8) & 0xFF); + pcmIdx += 2; + } - switch (bridge->m_txMode) - { - case TX_MODE_DMR: - bridge->encodeDMRAudioFrame(pcm); - break; - case TX_MODE_P25: - bridge->encodeP25AudioFrame(pcm); - break; - case TX_MODE_ANALOG: - bridge->encodeAnalogAudioFrame(pcm); - break; + switch (bridge->m_txMode) + { + case TX_MODE_DMR: + bridge->encodeDMRAudioFrame(pcm); + break; + case TX_MODE_P25: + bridge->encodeP25AudioFrame(pcm); + break; + case TX_MODE_ANALOG: + bridge->encodeAnalogAudioFrame(pcm); + break; + } + } } } } } + // When COR is active, we need to process frames continuously + // The audio callback should be feeding data, but if buffer is empty and COR is active, + // we'll send silence frames. Keep minimal sleep to ensure responsive processing. Thread::sleep(1U); } @@ -3171,6 +3255,127 @@ void* HostBridge::threadAudioProcess(void* arg) return nullptr; } +/* Entry point to CTS COR monitor thread. */ + +void* HostBridge::threadCtsCorMonitor(void* arg) +{ + thread_t* th = (thread_t*)arg; + if (th != nullptr) { +#if defined(_WIN32) + ::CloseHandle(th->thread); +#else + ::pthread_detach(th->thread); +#endif // defined(_WIN32) + + std::string threadName("bridge:cts-cor-monitor"); + HostBridge* bridge = static_cast(th->obj); + if (bridge == nullptr) { + g_killed = true; + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } + + if (g_killed) { + delete th; + return nullptr; + } + + LogInfoEx(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + // Initialize lastCts to current state to avoid false trigger on startup + bool lastCts = false; + if (bridge->m_ctsCorEnable && bridge->m_ctsCorController != nullptr) { + bool ctsRawInit = bridge->m_ctsCorController->isCtsAsserted(); + lastCts = bridge->m_ctsCorInvert ? !ctsRawInit : ctsRawInit; + bridge->m_ctsCorActive = lastCts; + LogInfoEx(LOG_HOST, "CTS COR monitor initialized: initial state = %s (raw: %s)", + lastCts ? "TRIGGER" : "IDLE", ctsRawInit ? "HIGH" : "LOW"); + } + uint32_t pollCount = 0U; + + while (!g_killed) { + if (!bridge->m_running) { + Thread::sleep(10U); + continue; + } + + if (!bridge->m_ctsCorEnable) { + LogDebug(LOG_HOST, "CTS COR is disabled, waiting..."); + Thread::sleep(1000U); + continue; + } + + if (bridge->m_ctsCorController == nullptr) { + LogError(LOG_HOST, "CTS COR Controller is null!"); + Thread::sleep(1000U); + continue; + } + + bool ctsRaw = bridge->m_ctsCorController->isCtsAsserted(); + // Apply inversion: if invert is true, LOW triggers (so we invert the raw signal) + bool cts = bridge->m_ctsCorInvert ? !ctsRaw : ctsRaw; + pollCount++; + + if (cts != lastCts) { + LogInfoEx(LOG_HOST, "CTS COR state changed: %s -> %s (raw: %s)", + lastCts ? "TRIGGER" : "IDLE", cts ? "TRIGGER" : "IDLE", ctsRaw ? "HIGH" : "LOW"); + lastCts = cts; + bridge->m_ctsCorActive = cts; + if (cts) { + // Rising edge: force call start, immediately send silence frame, and start padding timer + uint32_t srcId = bridge->m_srcId; + uint32_t dstId = bridge->m_dstId; + if (!bridge->m_audioDetect) { + bridge->m_audioDetect = true; + if (bridge->m_txStreamId == 0U) { + bridge->m_txStreamId = 1U; + LogInfoEx(LOG_HOST, "%s, call start (CTS COR), srcId = %u, dstId = %u", LOCAL_CALL, srcId, dstId); + if (bridge->m_grantDemand) { + switch (bridge->m_txMode) { + case TX_MODE_P25: + { + p25::lc::LC lc = p25::lc::LC(); + lc.setLCO(P25DEF::LCO::GROUP); + lc.setDstId(dstId); + lc.setSrcId(srcId); + + p25::data::LowSpeedData lsd = p25::data::LowSpeedData(); + + uint8_t controlByte = network::NET_CTRL_GRANT_DEMAND; + if (bridge->m_tekAlgoId != P25DEF::ALGO_UNENCRYPT) + controlByte |= network::NET_CTRL_GRANT_ENCRYPT; + bridge->m_network->writeP25TDU(lc, lsd, controlByte); + } + break; + } + } + } + } + // Stop drop timer while COR is activem audio is processing + bridge->m_localDropTime.stop(); + // Don't start padding timer + } + else { + // Falling edge: start hold-off timer before allowing call to end + bridge->m_ctsPadTimeout.stop(); + // Start drop timer with hold-off delay + bridge->m_localDropTime = Timer(1000U, 0U, bridge->m_ctsCorHoldoffMs); + bridge->m_localDropTime.start(); + } + } + + Thread::sleep(5U); + } + + LogInfoEx(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } + + return nullptr; +} + /* Entry point to UDP audio processing thread. */ void* HostBridge::threadUDPAudioProcess(void* arg) @@ -3291,7 +3496,7 @@ void* HostBridge::threadUDPAudioProcess(void* arg) if ((!bridge->m_audioDetect && !bridge->m_callInProgress) || forceCallStart) { bridge->m_audioDetect = true; if (bridge->m_txStreamId == 0U) { - bridge->m_txStreamId = 1U; // prevent further false starts -- this isn't the right way to handle this... + bridge->m_txStreamId = 1U; if (forceCallStart) bridge->m_txStreamId = txStreamId; @@ -3775,6 +3980,9 @@ void* HostBridge::threadCallWatchdog(void* arg) } } + // When CTS COR is active, the audio processing thread handles frame transmission + // We don't use the watchdog thread for padding to avoid conflicts with actual audio frames + std::string trafficType = LOCAL_CALL; if (bridge->m_trafficFromUDP) trafficType = UDP_CALL; @@ -3797,10 +4005,13 @@ void* HostBridge::threadCallWatchdog(void* arg) } } else { - // if we've exceeded the drop timeout, then really drop the audio - if (bridge->m_localDropTime.isRunning() && (bridge->m_localDropTime.getTimer() >= dropTimeout)) { - LogInfoEx(LOG_HOST, "%s, terminating stuck call", trafficType.c_str()); - bridge->callEnd(srcId, dstId); + // Don't end call due to drop timeout if COR is still active + if (!bridge->m_ctsCorActive) { + // if we've exceeded the drop timeout, then really drop the audio + if (bridge->m_localDropTime.isRunning() && (bridge->m_localDropTime.getTimer() >= dropTimeout)) { + LogInfoEx(LOG_HOST, "%s, terminating stuck call", trafficType.c_str()); + bridge->callEnd(srcId, dstId); + } } } @@ -3838,6 +4049,56 @@ bool HostBridge::initializeRtsPtt() return true; } +/* Helper to initialize CTS COR detection. */ + +bool HostBridge::initializeCtsCor() +{ + if (!m_ctsCorEnable) + return true; + + if (m_ctsCorPort.empty()) { + ::LogError(LOG_HOST, "CTS COR port is not specified"); + return false; + } + + m_ctsCorController = new CtsCorController(m_ctsCorPort); + + // If RTS PTT and CTS COR are on the same port, reuse the file descriptor + // to avoid opening the port twice (which would affect RTS) + int reuseFd = -1; + if (m_rtsPttEnable && m_rtsPttController != nullptr && + m_rtsPttPort == m_ctsCorPort && m_rtsPttController->getFd() >= 0) { + reuseFd = m_rtsPttController->getFd(); + ::LogInfo(LOG_HOST, "CTS COR reusing RTS PTT file descriptor for %s (same port)", m_ctsCorPort.c_str()); + } + + if (!m_ctsCorController->open(reuseFd)) { + ::LogError(LOG_HOST, "Failed to open CTS COR port %s", m_ctsCorPort.c_str()); + delete m_ctsCorController; + m_ctsCorController = nullptr; + return false; + } + + // Start monitor thread + thread_t* th = new thread_t(); + th->obj = this; + if (!Thread::runAsThread(this, &HostBridge::threadCtsCorMonitor, th)) { + ::LogError(LOG_HOST, "Failed to start CTS COR monitor thread"); + return false; + } + + ::LogInfo(LOG_HOST, "CTS COR initialized on %s", m_ctsCorPort.c_str()); + + // Test read CTS state to verify it's working + bool ctsRaw = m_ctsCorController->isCtsAsserted(); + bool ctsEffective = m_ctsCorInvert ? !ctsRaw : ctsRaw; + ::LogInfo(LOG_HOST, "CTS COR initial state: raw=%s, effective=%s (%s)", + ctsRaw ? "HIGH" : "LOW", ctsEffective ? "TRIGGER" : "IDLE", + m_ctsCorInvert ? "inverted" : "normal"); + + return true; +} + /* Helper to assert RTS PTT (start transmission). */ void HostBridge::assertRtsPtt() diff --git a/src/bridge/HostBridge.h b/src/bridge/HostBridge.h index 088c30e7..7449aadc 100644 --- a/src/bridge/HostBridge.h +++ b/src/bridge/HostBridge.h @@ -34,6 +34,7 @@ #include "mdc/mdc_decode.h" #include "network/PeerNetwork.h" #include "RtsPttController.h" +#include "CtsCorController.h" #include #include @@ -265,6 +266,15 @@ class HOST_SW_API HostBridge { system_clock::hrc::hrc_t m_lastAudioOut; uint32_t m_rtsPttHoldoffMs; + // CTS COR Detection + bool m_ctsCorEnable; + std::string m_ctsCorPort; + CtsCorController* m_ctsCorController; + bool m_ctsCorActive; + bool m_ctsCorInvert; // if true, COR LOW triggers (instead of HIGH) + Timer m_ctsPadTimeout; // drives silence padding while CTS is active + uint32_t m_ctsCorHoldoffMs; // hold-off time before clearing COR after it deasserts + uint16_t m_rtpSeqNo; uint32_t m_rtpTimestamp; @@ -523,6 +533,11 @@ class HOST_SW_API HostBridge { * @returns bool True, if RTS PTT was initialized successfully, otherwise false. */ bool initializeRtsPtt(); + /** + * @brief Helper to initialize CTS COR detection. + * @returns bool True, if CTS COR was initialized successfully, otherwise false. + */ + bool initializeCtsCor(); /** * @brief Helper to assert RTS PTT (start transmission). */ @@ -573,6 +588,12 @@ class HOST_SW_API HostBridge { * @returns void* (Ignore) */ static void* threadCallWatchdog(void* arg); + /** + * @brief Entry point to CTS COR monitor thread. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) + */ + static void* threadCtsCorMonitor(void* arg); }; #endif // __HOST_BRIDGE_H__ diff --git a/src/bridge/RtsPttController.cpp b/src/bridge/RtsPttController.cpp index e82be9c9..cb0c29d6 100644 --- a/src/bridge/RtsPttController.cpp +++ b/src/bridge/RtsPttController.cpp @@ -211,6 +211,17 @@ bool RtsPttController::clearPTT() return true; } +/* Gets the file descriptor for sharing with CTS COR controller. */ + +int RtsPttController::getFd() const +{ +#if defined(_WIN32) + return (int)(intptr_t)m_fd; +#else + return m_fd; +#endif // defined(_WIN32) +} + // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- diff --git a/src/bridge/RtsPttController.h b/src/bridge/RtsPttController.h index 5cc8860c..f607c40a 100644 --- a/src/bridge/RtsPttController.h +++ b/src/bridge/RtsPttController.h @@ -72,6 +72,12 @@ class HOST_SW_API RtsPttController { */ bool clearPTT(); + /** + * @brief Gets the file descriptor for sharing with CTS COR controller. + * @returns int File descriptor, or -1 if not open. + */ + int getFd() const; + private: std::string m_port; bool m_isOpen; From dc03a84752ba78a8e767745ed3ce116108c98650 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 5 Nov 2025 13:19:06 -0500 Subject: [PATCH 127/200] refactor KMMFrame to properly support message number and add framework to support MAC; refactor all derived KMM classes to properly determine KMM frame length and body offsets; change collision in class naming for KeyItem in the CryptoContainer and KeyItem in the p25::kmm namespace; cleanup magic numbers; add initial code for transmitting a rekey command; add KEK crypto wrapping testcase; --- configs/fne-config.example.yml | 2 +- src/common/p25/P25Defines.h | 11 +- .../p25/kmm/KMMDeregistrationCommand.cpp | 17 +- src/common/p25/kmm/KMMDeregistrationCommand.h | 8 +- .../p25/kmm/KMMDeregistrationResponse.cpp | 13 +- .../p25/kmm/KMMDeregistrationResponse.h | 8 +- src/common/p25/kmm/KMMFactory.cpp | 2 + src/common/p25/kmm/KMMFactory.h | 1 + src/common/p25/kmm/KMMFrame.cpp | 121 +++++++++- src/common/p25/kmm/KMMFrame.h | 37 ++- src/common/p25/kmm/KMMHello.cpp | 13 +- src/common/p25/kmm/KMMHello.h | 8 +- src/common/p25/kmm/KMMInventoryCommand.cpp | 13 +- src/common/p25/kmm/KMMInventoryCommand.h | 8 +- .../p25/kmm/KMMInventoryResponseHeader.cpp | 17 +- .../p25/kmm/KMMInventoryResponseHeader.h | 10 +- .../kmm/KMMInventoryResponseListKeyIDs.cpp | 20 +- .../p25/kmm/KMMInventoryResponseListKeyIDs.h | 13 ++ .../kmm/KMMInventoryResponseListKeysets.cpp | 8 +- src/common/p25/kmm/KMMModifyKey.cpp | 75 +++--- src/common/p25/kmm/KMMModifyKey.h | 4 +- src/common/p25/kmm/KMMNegativeAck.cpp | 22 +- src/common/p25/kmm/KMMNegativeAck.h | 8 +- src/common/p25/kmm/KMMNoService.cpp | 2 +- src/common/p25/kmm/KMMNoService.h | 13 -- src/common/p25/kmm/KMMRegistrationCommand.cpp | 18 +- src/common/p25/kmm/KMMRegistrationCommand.h | 8 +- .../p25/kmm/KMMRegistrationResponse.cpp | 14 +- src/common/p25/kmm/KMMRegistrationResponse.h | 8 +- src/common/p25/kmm/KMMRekeyAck.cpp | 24 +- src/common/p25/kmm/KMMRekeyAck.h | 2 +- src/common/p25/kmm/KMMRekeyCommand.cpp | 85 ++++--- src/common/p25/kmm/KMMRekeyCommand.h | 9 +- src/common/p25/kmm/KMMUnableToDecrypt.cpp | 217 ++++++++++++++++++ src/common/p25/kmm/KMMUnableToDecrypt.h | 142 ++++++++++++ src/common/p25/kmm/KMMZeroize.cpp | 2 +- src/common/p25/kmm/KMMZeroize.h | 13 -- src/common/p25/kmm/KeysetItem.h | 11 +- src/fne/CryptoContainer.cpp | 50 +++- src/fne/CryptoContainer.h | 33 ++- src/fne/network/FNENetwork.cpp | 2 +- src/fne/network/P25OTARService.cpp | 178 +++++++++++--- src/fne/network/P25OTARService.h | 12 + tests/crypto/P25_KEK_Crypto_Test.cpp | 78 +++++++ tests/crypto/RC4_Crypto_Test.cpp | 2 +- 45 files changed, 1135 insertions(+), 227 deletions(-) create mode 100644 src/common/p25/kmm/KMMUnableToDecrypt.cpp create mode 100644 src/common/p25/kmm/KMMUnableToDecrypt.h create mode 100644 tests/crypto/P25_KEK_Crypto_Test.cpp diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 7133e7f3..d6beb41e 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -183,7 +183,7 @@ master: crypto_container: # Flag indicating whether or not crypto services are enabled. enable: false - # Full path to the talkgroup rules file. + # Full path to the KFDtool crypto container file. file: key_container.ekc # Container password. password: "PASSWORD" diff --git a/src/common/p25/P25Defines.h b/src/common/p25/P25Defines.h index e4395ca4..03a86bfd 100644 --- a/src/common/p25/P25Defines.h +++ b/src/common/p25/P25Defines.h @@ -120,6 +120,9 @@ namespace p25 const uint8_t AUTH_KEY_LENGTH_BYTES = 16U; const uint8_t MAX_ENC_KEY_LENGTH_BYTES = 32U; + const uint8_t MAX_WRAPPED_ENC_KEY_LENGTH_BYTES = 40U; + const uint8_t KMM_AES_MAC_LENGTH = 8U; + const uint8_t KMM_DES_MAC_LENGTH = 4U; /* @} */ /** @name Thresholds */ @@ -454,6 +457,8 @@ namespace p25 DEREG_RSP = 0x24U, //!< SU Deregistration Response REG_CMD = 0x25U, //!< SU Registration Command REG_RSP = 0x26U, //!< SU Registration Response + + UNABLE_TO_DECRYPT = 0x27U, //!< Unable to Decrypt Response }; } @@ -527,11 +532,11 @@ namespace p25 /** @brief KMM Decryption Instruction - Message Indicator */ const uint8_t KMM_DECRYPT_INSTRUCT_MI = 0x40U; - /** @brief KMM Key Format TEK */ + /** @brief KMM Body/Key Format - TEK Included */ const uint8_t KEY_FORMAT_TEK = 0x80U; - /** @brief KMM Key Format TEK */ + /** @brief KMM Body/Key Format - KEK Exists */ const uint8_t KEY_FORMAT_KEK_EXISTS = 0x40U; - /** @brief KMM Key Format Delete Key */ + /** @brief KMM Key Format - Delete Key */ const uint8_t KEY_FORMAT_DELETE = 0x20U; /** @brief SNDCP version 1 */ diff --git a/src/common/p25/kmm/KMMDeregistrationCommand.cpp b/src/common/p25/kmm/KMMDeregistrationCommand.cpp index 5353938a..401e06d8 100644 --- a/src/common/p25/kmm/KMMDeregistrationCommand.cpp +++ b/src/common/p25/kmm/KMMDeregistrationCommand.cpp @@ -36,6 +36,13 @@ KMMDeregistrationCommand::KMMDeregistrationCommand() : KMMFrame(), KMMDeregistrationCommand::~KMMDeregistrationCommand() = default; +/* Gets the byte length of this KMMDeregistrationCommand. */ + +uint32_t KMMDeregistrationCommand::length() const +{ + return KMMFrame::length() + KMM_BODY_DEREGISTRATION_CMD_LENGTH; +} + /* Decode a KMM modify key. */ bool KMMDeregistrationCommand::decode(const uint8_t* data) @@ -44,8 +51,8 @@ bool KMMDeregistrationCommand::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_bodyFormat = data[10U]; // Body Format - m_kmfRSI = GET_UINT24(data, 11U); // KMF RSI + m_bodyFormat = data[10U + m_bodyOffset]; // Body Format + m_kmfRSI = GET_UINT24(data, 11U + m_bodyOffset); // KMF RSI return true; } @@ -55,13 +62,13 @@ bool KMMDeregistrationCommand::decode(const uint8_t* data) void KMMDeregistrationCommand::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_DEREGISTRATION_CMD_LENGTH; + m_messageLength = length(); m_bodyFormat = 0U; // reset this to none -- we don't support warm start right now KMMFrame::encodeHeader(data); - data[10U] = m_bodyFormat; // Body Format - SET_UINT24(m_kmfRSI, data, 11U); // KMF RSI + data[10U + m_bodyOffset] = m_bodyFormat; // Body Format + SET_UINT24(m_kmfRSI, data, 11U + m_bodyOffset); // KMF RSI } /* Returns a string that represents the current KMM frame. */ diff --git a/src/common/p25/kmm/KMMDeregistrationCommand.h b/src/common/p25/kmm/KMMDeregistrationCommand.h index 5e27b908..bc8f203b 100644 --- a/src/common/p25/kmm/KMMDeregistrationCommand.h +++ b/src/common/p25/kmm/KMMDeregistrationCommand.h @@ -36,7 +36,7 @@ namespace p25 * @{ */ - const uint32_t KMM_DEREGISTRATION_CMD_LENGTH = KMM_FRAME_LENGTH + 4U; + const uint32_t KMM_BODY_DEREGISTRATION_CMD_LENGTH = 4U; /** @} */ @@ -55,6 +55,12 @@ namespace p25 */ ~KMMDeregistrationCommand(); + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + /** * @brief Decode a KMM deregistration command. * @param[in] data Buffer containing KMM frame data to decode. diff --git a/src/common/p25/kmm/KMMDeregistrationResponse.cpp b/src/common/p25/kmm/KMMDeregistrationResponse.cpp index 788c08cf..bdba65ea 100644 --- a/src/common/p25/kmm/KMMDeregistrationResponse.cpp +++ b/src/common/p25/kmm/KMMDeregistrationResponse.cpp @@ -35,6 +35,13 @@ KMMDeregistrationResponse::KMMDeregistrationResponse() : KMMFrame(), KMMDeregistrationResponse::~KMMDeregistrationResponse() = default; +/* Gets the byte length of this KMMDeregistrationResponse. */ + +uint32_t KMMDeregistrationResponse::length() const +{ + return KMMFrame::length() + KMM_BODY_DEREGISTRATION_RSP_LENGTH; +} + /* Decode a KMM modify key. */ bool KMMDeregistrationResponse::decode(const uint8_t* data) @@ -43,7 +50,7 @@ bool KMMDeregistrationResponse::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_status = data[10U]; // Status + m_status = data[10U + m_bodyOffset]; // Status return true; } @@ -53,11 +60,11 @@ bool KMMDeregistrationResponse::decode(const uint8_t* data) void KMMDeregistrationResponse::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_DEREGISTRATION_RSP_LENGTH; + m_messageLength = length(); KMMFrame::encodeHeader(data); - data[10U] = m_status; // Status + data[10U + m_bodyOffset] = m_status; // Status } /* Returns a string that represents the current KMM frame. */ diff --git a/src/common/p25/kmm/KMMDeregistrationResponse.h b/src/common/p25/kmm/KMMDeregistrationResponse.h index 68d2cd5e..aa0373f2 100644 --- a/src/common/p25/kmm/KMMDeregistrationResponse.h +++ b/src/common/p25/kmm/KMMDeregistrationResponse.h @@ -36,7 +36,7 @@ namespace p25 * @{ */ - const uint32_t KMM_DEREGISTRATION_RSP_LENGTH = KMM_FRAME_LENGTH + 1U; + const uint32_t KMM_BODY_DEREGISTRATION_RSP_LENGTH = 1U; /** @} */ @@ -55,6 +55,12 @@ namespace p25 */ ~KMMDeregistrationResponse(); + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + /** * @brief Decode a KMM deregistration response. * @param[in] data Buffer containing KMM frame data to decode. diff --git a/src/common/p25/kmm/KMMFactory.cpp b/src/common/p25/kmm/KMMFactory.cpp index 8879d1ea..5fb15c0a 100644 --- a/src/common/p25/kmm/KMMFactory.cpp +++ b/src/common/p25/kmm/KMMFactory.cpp @@ -91,6 +91,8 @@ std::unique_ptr KMMFactory::create(const uint8_t* data) return decode(new KMMRekeyAck(), data); case KMM_MessageType::REKEY_CMD: return decode(new KMMRekeyCommand(), data); + case KMM_MessageType::UNABLE_TO_DECRYPT: + return decode(new KMMUnableToDecrypt(), data); default: LogError(LOG_P25, "KMMFactory::create(), unknown KMM message ID value, messageId = $%02X", messageId); break; diff --git a/src/common/p25/kmm/KMMFactory.h b/src/common/p25/kmm/KMMFactory.h index cfdeb1e3..306369d5 100644 --- a/src/common/p25/kmm/KMMFactory.h +++ b/src/common/p25/kmm/KMMFactory.h @@ -35,6 +35,7 @@ #include "common/p25/kmm/KMMRegistrationResponse.h" #include "common/p25/kmm/KMMRekeyAck.h" #include "common/p25/kmm/KMMRekeyCommand.h" +#include "common/p25/kmm/KMMUnableToDecrypt.h" #include "common/p25/kmm/KMMZeroize.h" namespace p25 diff --git a/src/common/p25/kmm/KMMFrame.cpp b/src/common/p25/kmm/KMMFrame.cpp index 73601197..989253b7 100644 --- a/src/common/p25/kmm/KMMFrame.cpp +++ b/src/common/p25/kmm/KMMFrame.cpp @@ -35,16 +35,46 @@ KMMFrame::KMMFrame() : m_messageId(KMM_MessageType::NULL_CMD), m_messageLength(KMM_FRAME_LENGTH), m_respKind(KMM_ResponseKind::NONE), + m_macType(KMM_MAC::NO_MAC), + m_messageNumber(0U), + m_dstLlId(0U), + m_srcLlId(0U), m_complete(true), - m_mfMessageNumber(0U), - m_mfMac(KMM_MAC::NO_MAC) + m_messageFullLength(0U), + m_bodyOffset(0U), + m_macAlgId(ALGO_UNENCRYPT), + m_macKId(0U), + m_mac(nullptr) { - /* stub */ + m_mac = new uint8_t[P25DEF::KMM_AES_MAC_LENGTH]; + ::memset(m_mac, 0x00U, P25DEF::KMM_AES_MAC_LENGTH); } /* Finalizes a instance of the KMMFrame class. */ -KMMFrame::~KMMFrame() = default; +KMMFrame::~KMMFrame() +{ + if (m_mac != nullptr) + delete[] m_mac; +} + +/* Helper to generate a message authentication code for a KMM frame. */ + +void KMMFrame::generateMAC(uint8_t algoId, uint16_t kId) +{ + m_macAlgId = algoId; + m_macKId = kId; + + m_macType = KMM_MAC::ENH_MAC; + m_messageLength += P25DEF::KMM_AES_MAC_LENGTH; + m_messageFullLength = m_messageLength + 3U; + + ::memset(m_mac, 0x00U, P25DEF::KMM_AES_MAC_LENGTH); + + /* + ** TODO - generate actual MAC + */ +} /* Returns a string that represents the current KMM frame. */ @@ -65,10 +95,11 @@ bool KMMFrame::decodeHeader(const uint8_t* data) m_messageId = data[0U]; // Message ID m_messageLength = GET_UINT16(data, 1U); // Message Length + m_messageFullLength = m_messageLength + 3U; // length including ID and length fields m_respKind = (data[3U] >> 6U) & 0x03U; // Response Kind - m_mfMessageNumber = (data[3U] >> 4U) & 0x03U; // Message Number - m_mfMac = (data[3U] >> 2U) & 0x03U; // MAC + bool hasMN = ((data[3U] >> 4U) & 0x03U) == 0x02U; // Message Number Flag + m_macType = (data[3U] >> 2U) & 0x03U; // MAC Type bool done = (data[3U] & 0x01U) == 0x01U; // Done Flag if (!done) @@ -79,6 +110,44 @@ bool KMMFrame::decodeHeader(const uint8_t* data) m_dstLlId = GET_UINT24(data, 4U); // Destination RSI m_srcLlId = GET_UINT24(data, 7U); // Source RSI + if (hasMN) { + m_bodyOffset = 2U; + m_messageNumber = GET_UINT16(data, 10U); // Message Number + } + + switch (m_macType) { + case KMM_MAC::DES_MAC: + { + uint8_t macLength = 4U; + + m_macAlgId = data[m_messageFullLength - 4U]; + m_macKId = GET_UINT16(data, m_messageFullLength - 3U); + + ::memset(m_mac, 0x00U, macLength); + ::memcpy(m_mac, data + m_messageFullLength - (macLength + 5U), macLength); + } + break; + + case KMM_MAC::ENH_MAC: + { + uint8_t macLength = 8U; + + m_macAlgId = data[m_messageFullLength - 4U]; + m_macKId = GET_UINT16(data, m_messageFullLength - 3U); + + ::memset(m_mac, 0x00U, macLength); + ::memcpy(m_mac, data + m_messageFullLength - (macLength + 5U), macLength); + } + break; + + case KMM_MAC::NO_MAC: + break; + + default: + ::LogError(LOG_P25, "KMMFrame::decodeHeader(), unknown KMM MAC inventory type value, macType = $%02X", m_macType); + break; + } + return true; } @@ -90,14 +159,45 @@ void KMMFrame::encodeHeader(uint8_t* data) data[0U] = m_messageId; // Message ID SET_UINT16(m_messageLength, data, 1U); // Message Length + m_messageFullLength = m_messageLength + 3U; data[3U] = ((m_respKind & 0x03U) << 6U) + // Response Kind - ((m_mfMessageNumber & 0x03U) << 4U) + // Message Number - ((m_mfMac & 0x03U) << 2U) + // MAC + ((m_messageNumber > 0U) ? 0x20U : 0x00U) + // Message Number Flag + ((m_macType & 0x03U) << 2U) + // MAC Type ((!m_complete) ? 0x01U : 0x00U); // Done Flag SET_UINT24(m_dstLlId, data, 4U); // Destination RSI SET_UINT24(m_srcLlId, data, 7U); // Source RSI + + if (m_messageNumber > 0U) { + SET_UINT16(m_messageNumber, data, 10U); // Message Number + m_bodyOffset = 2U; + } + + switch (m_macType) { + case KMM_MAC::DES_MAC: + ::LogError(LOG_P25, "KMMFrame::decodeHeader(), DES MAC type is not supported, macType = $%02X", m_macType); + return; + + case KMM_MAC::ENH_MAC: + { + uint8_t macLength = 8U; + + m_macAlgId = data[m_messageFullLength - 4U]; + m_macKId = GET_UINT16(data, m_messageFullLength - 3U); + + ::memset(m_mac, 0x00U, macLength); + ::memcpy(m_mac, data + m_messageFullLength - (macLength + 5U), macLength); + } + break; + + case KMM_MAC::NO_MAC: + break; + + default: + ::LogError(LOG_P25, "KMMFrame::decodeHeader(), unknown KMM MAC inventory type value, macType = $%02X", m_macType); + break; + } } /* Internal helper to copy the the class. */ @@ -106,9 +206,10 @@ void KMMFrame::copy(const KMMFrame& data) { m_messageId = data.m_messageId; m_messageLength = data.m_messageLength; + m_messageFullLength = data.m_messageFullLength; m_respKind = data.m_respKind; m_complete = data.m_complete; - m_mfMessageNumber = data.m_mfMessageNumber; - m_mfMac = data.m_mfMac; + m_messageNumber = data.m_messageNumber; + m_macType = data.m_macType; } diff --git a/src/common/p25/kmm/KMMFrame.h b/src/common/p25/kmm/KMMFrame.h index 389ec522..646386a1 100644 --- a/src/common/p25/kmm/KMMFrame.h +++ b/src/common/p25/kmm/KMMFrame.h @@ -21,6 +21,7 @@ #define __P25_KMM__KMM_FRAME_H__ #include "common/Defines.h" +#include "common/p25/P25Defines.h" #include "common/Utils.h" #include @@ -70,7 +71,16 @@ namespace p25 * @brief Gets the byte length of this KMMFrame. * @return uint32_t Length of KMMFrame. */ - virtual uint32_t length() const { return KMM_FRAME_LENGTH; } + virtual uint32_t length() const + { + uint32_t len = KMM_FRAME_LENGTH; + if (m_messageNumber > 0U) + len += 2U; + if (m_macType == P25DEF::KMM_MAC::ENH_MAC) + len += P25DEF::KMM_AES_MAC_LENGTH; + + return len; + } /** * @brief Decode a KMM frame. @@ -84,6 +94,14 @@ namespace p25 */ virtual void encode(uint8_t* data) = 0; + /** + * @brief Helper to generate a message authentication code for a KMM frame. + * @note DVM only supports generating AES-256 CBC-MAC. + * @param algoId Algorithm ID. + * @param kId Key ID. + */ + void generateMAC(uint8_t algoId, uint16_t kId); + /** * @brief Returns a string that represents the current KMM frame. * @returns std::string String representation of the KMM frame. @@ -106,6 +124,15 @@ namespace p25 */ DECLARE_PROTECTED_PROPERTY(uint8_t, respKind, ResponseKind); + /** + * @brief Message Authentication Type. + */ + DECLARE_PROTECTED_PROPERTY(uint8_t, macType, MACType); + /** + * @brief Message Number. + */ + DECLARE_PROTECTED_PROPERTY(uint16_t, messageNumber, MessageNumber); + /** * @brief Destination Logical link ID. */ @@ -121,8 +148,12 @@ namespace p25 DECLARE_PROTECTED_PROPERTY(bool, complete, Complete); protected: - uint8_t m_mfMessageNumber; - uint8_t m_mfMac; + uint16_t m_messageFullLength; //!< Complete length of entire frame in bytes. + uint8_t m_bodyOffset; //!< Offset to KMM frame body data. + + uint8_t m_macAlgId; + uint16_t m_macKId; + uint8_t* m_mac; /** * @brief Internal helper to decode a KMM header. diff --git a/src/common/p25/kmm/KMMHello.cpp b/src/common/p25/kmm/KMMHello.cpp index 2f8f0440..60e5df17 100644 --- a/src/common/p25/kmm/KMMHello.cpp +++ b/src/common/p25/kmm/KMMHello.cpp @@ -35,6 +35,13 @@ KMMHello::KMMHello() : KMMFrame(), KMMHello::~KMMHello() = default; +/* Gets the byte length of this KMMHello. */ + +uint32_t KMMHello::length() const +{ + return KMMFrame::length() + KMM_BODY_HELLO_LENGTH; +} + /* Decode a KMM modify key. */ bool KMMHello::decode(const uint8_t* data) @@ -43,7 +50,7 @@ bool KMMHello::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_flag = data[10U]; // Hello Flag + m_flag = data[10U + m_bodyOffset]; // Hello Flag return true; } @@ -53,11 +60,11 @@ bool KMMHello::decode(const uint8_t* data) void KMMHello::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_HELLO_LENGTH; + m_messageLength = length(); KMMFrame::encodeHeader(data); - data[10U] = m_flag; // Hello Flag + data[10U + m_bodyOffset] = m_flag; // Hello Flag } /* Returns a string that represents the current KMM frame. */ diff --git a/src/common/p25/kmm/KMMHello.h b/src/common/p25/kmm/KMMHello.h index ab44975d..fbb5a20f 100644 --- a/src/common/p25/kmm/KMMHello.h +++ b/src/common/p25/kmm/KMMHello.h @@ -36,7 +36,7 @@ namespace p25 * @{ */ - const uint32_t KMM_HELLO_LENGTH = KMM_FRAME_LENGTH + 1U; + const uint32_t KMM_BODY_HELLO_LENGTH = 1U; /** @} */ @@ -55,6 +55,12 @@ namespace p25 */ ~KMMHello(); + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + /** * @brief Decode a KMM hello. * @param[in] data Buffer containing KMM frame data to decode. diff --git a/src/common/p25/kmm/KMMInventoryCommand.cpp b/src/common/p25/kmm/KMMInventoryCommand.cpp index e1383bf0..3450de81 100644 --- a/src/common/p25/kmm/KMMInventoryCommand.cpp +++ b/src/common/p25/kmm/KMMInventoryCommand.cpp @@ -35,6 +35,13 @@ KMMInventoryCommand::KMMInventoryCommand() : KMMFrame(), KMMInventoryCommand::~KMMInventoryCommand() = default; +/* Gets the byte length of this KMMInventoryCommand. */ + +uint32_t KMMInventoryCommand::length() const +{ + return KMMFrame::length() + KMM_BODY_INVENTORY_CMD_LENGTH; +} + /* Decode a KMM inventory command. */ bool KMMInventoryCommand::decode(const uint8_t* data) @@ -43,7 +50,7 @@ bool KMMInventoryCommand::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_inventoryType = data[10U]; // Inventory Type + m_inventoryType = data[10U + m_bodyOffset]; // Inventory Type return true; } @@ -53,11 +60,11 @@ bool KMMInventoryCommand::decode(const uint8_t* data) void KMMInventoryCommand::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_INVENTORY_CMD_LENGTH; + m_messageLength = length(); KMMFrame::encodeHeader(data); - data[10U] = m_inventoryType; // Inventory Type + data[10U + m_bodyOffset] = m_inventoryType; // Inventory Type } /* Returns a string that represents the current KMM frame. */ diff --git a/src/common/p25/kmm/KMMInventoryCommand.h b/src/common/p25/kmm/KMMInventoryCommand.h index b3825a11..4b34db43 100644 --- a/src/common/p25/kmm/KMMInventoryCommand.h +++ b/src/common/p25/kmm/KMMInventoryCommand.h @@ -36,7 +36,7 @@ namespace p25 * @{ */ - const uint32_t KMM_INVENTORY_CMD_LENGTH = KMM_FRAME_LENGTH + 1U; + const uint32_t KMM_BODY_INVENTORY_CMD_LENGTH = 1U; /** @} */ @@ -55,6 +55,12 @@ namespace p25 */ ~KMMInventoryCommand(); + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + /** * @brief Decode a KMM inventory command. * @param[in] data Buffer containing KMM frame data to decode. diff --git a/src/common/p25/kmm/KMMInventoryResponseHeader.cpp b/src/common/p25/kmm/KMMInventoryResponseHeader.cpp index c02ac740..ecdb3d68 100644 --- a/src/common/p25/kmm/KMMInventoryResponseHeader.cpp +++ b/src/common/p25/kmm/KMMInventoryResponseHeader.cpp @@ -35,6 +35,13 @@ KMMInventoryResponseHeader::KMMInventoryResponseHeader() : KMMFrame(), KMMInventoryResponseHeader::~KMMInventoryResponseHeader() = default; +/* Gets the byte length of this KMMInventoryResponseHeader. */ + +uint32_t KMMInventoryResponseHeader::length() const +{ + return KMMFrame::length() + KMM_BODY_INV_RSP_HDR_LENGTH; +} + /* Decode a KMM inventory response header. */ bool KMMInventoryResponseHeader::decode(const uint8_t* data) @@ -43,8 +50,8 @@ bool KMMInventoryResponseHeader::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_inventoryType = data[10U]; // Inventory Type - m_numberOfItems = GET_UINT16(data, 11U); // Number of Items + m_inventoryType = data[10U + m_bodyOffset]; // Inventory Type + m_numberOfItems = GET_UINT16(data, 11U + m_bodyOffset); // Number of Items return true; } @@ -54,12 +61,12 @@ bool KMMInventoryResponseHeader::decode(const uint8_t* data) void KMMInventoryResponseHeader::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_INVENTORY_RSP_HDR_LENGTH; + m_messageLength = length(); KMMFrame::encodeHeader(data); - data[10U] = m_inventoryType; // Inventory Type - SET_UINT16(m_numberOfItems, data, 11U); // Number of Items + data[10U + m_bodyOffset] = m_inventoryType; // Inventory Type + SET_UINT16(m_numberOfItems, data, 11U + m_bodyOffset); // Number of Items } /* Returns a string that represents the current KMM frame. */ diff --git a/src/common/p25/kmm/KMMInventoryResponseHeader.h b/src/common/p25/kmm/KMMInventoryResponseHeader.h index 8ff3f43c..7c8c521a 100644 --- a/src/common/p25/kmm/KMMInventoryResponseHeader.h +++ b/src/common/p25/kmm/KMMInventoryResponseHeader.h @@ -36,10 +36,10 @@ namespace p25 * @{ */ - const uint32_t KMM_INVENTORY_RSP_HDR_LENGTH = KMM_FRAME_LENGTH + 3U; + const uint32_t KMM_BODY_INV_RSP_HDR_LENGTH = 3U; /** @} */ - + // --------------------------------------------------------------------------- // Class Declaration // --------------------------------------------------------------------------- @@ -55,6 +55,12 @@ namespace p25 */ ~KMMInventoryResponseHeader(); + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + /** * @brief Decode a KMM inventory response header. * @param[in] data Buffer containing KMM frame data to decode. diff --git a/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.cpp b/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.cpp index e0201d8f..4c283aac 100644 --- a/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.cpp +++ b/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.cpp @@ -48,7 +48,7 @@ KMMInventoryResponseListKeyIDs::~KMMInventoryResponseListKeyIDs() = default; uint32_t KMMInventoryResponseListKeyIDs::length() const { - uint32_t len = KMM_INVENTORY_RSP_HDR_LENGTH + 3U; + uint32_t len = KMMInventoryResponseHeader::length() + KMM_BODY_INV_RSP_LIST_KIDS_LENGTH; len += m_keyIds.size() * 2U; return len; @@ -62,13 +62,13 @@ bool KMMInventoryResponseListKeyIDs::decode(const uint8_t* data) KMMInventoryResponseHeader::decodeHeader(data); - m_keysetId = data[13U]; - m_algId = data[14U]; - m_numberOfKeyIDs = data[15U]; + m_keysetId = data[13U + m_bodyOffset]; + m_algId = data[14U + m_bodyOffset]; + m_numberOfKeyIDs = data[15U + m_bodyOffset]; uint16_t offset = 0U; for (uint16_t i = 0U; i < m_numberOfKeyIDs; i++) { - uint16_t keyId = GET_UINT16(data, 16U + offset); + uint16_t keyId = GET_UINT16(data, 16U + (m_bodyOffset + offset)); m_keyIds.push_back(keyId); offset += 2U; } @@ -81,18 +81,18 @@ bool KMMInventoryResponseListKeyIDs::decode(const uint8_t* data) void KMMInventoryResponseListKeyIDs::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_INVENTORY_RSP_HDR_LENGTH; + m_messageLength = length(); m_numberOfItems = 1U; // this is a naive approach... KMMInventoryResponseHeader::encodeHeader(data); - data[13U] = m_keysetId; - data[14U] = m_algId; - data[15U] = m_numberOfKeyIDs; + data[13U + m_bodyOffset] = m_keysetId; + data[14U + m_bodyOffset] = m_algId; + data[15U + m_bodyOffset] = m_numberOfKeyIDs; uint16_t offset = 0U; for (auto entry : m_keyIds) { - SET_UINT16(entry, data, 16U + offset); + SET_UINT16(entry, data, 16U + (m_bodyOffset + offset)); offset += 2U; } } diff --git a/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.h b/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.h index db012c39..b4466633 100644 --- a/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.h +++ b/src/common/p25/kmm/KMMInventoryResponseListKeyIDs.h @@ -28,6 +28,19 @@ namespace p25 { namespace kmm { + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + + /** + * @addtogroup p25_kmm + * @{ + */ + + const uint32_t KMM_BODY_INV_RSP_LIST_KIDS_LENGTH = 3U; + + /** @} */ + // --------------------------------------------------------------------------- // Class Declaration // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMInventoryResponseListKeysets.cpp b/src/common/p25/kmm/KMMInventoryResponseListKeysets.cpp index aa52c74f..2f6b29d3 100644 --- a/src/common/p25/kmm/KMMInventoryResponseListKeysets.cpp +++ b/src/common/p25/kmm/KMMInventoryResponseListKeysets.cpp @@ -40,7 +40,7 @@ KMMInventoryResponseListKeysets::~KMMInventoryResponseListKeysets() = default; uint32_t KMMInventoryResponseListKeysets::length() const { - uint32_t len = KMM_INVENTORY_RSP_HDR_LENGTH; + uint32_t len = KMMInventoryResponseHeader::length(); len += m_keysetIds.size(); return len; @@ -55,7 +55,7 @@ bool KMMInventoryResponseListKeysets::decode(const uint8_t* data) KMMInventoryResponseHeader::decodeHeader(data); for (uint16_t i = 0U; i < m_numberOfItems; i++) { - uint8_t keysetId = data[13U + i]; + uint8_t keysetId = data[13U + (m_bodyOffset + i)]; m_keysetIds.push_back(keysetId); } @@ -67,14 +67,14 @@ bool KMMInventoryResponseListKeysets::decode(const uint8_t* data) void KMMInventoryResponseListKeysets::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_INVENTORY_RSP_HDR_LENGTH; + m_messageLength = length(); m_numberOfItems = (uint16_t)m_keysetIds.size(); KMMInventoryResponseHeader::encodeHeader(data); uint16_t offset = 0U; for (auto entry : m_keysetIds) { - data[13U + offset] = entry; + data[13U + (m_bodyOffset + offset)] = entry; offset += 1U; } } diff --git a/src/common/p25/kmm/KMMModifyKey.cpp b/src/common/p25/kmm/KMMModifyKey.cpp index a43a2d62..993d140e 100644 --- a/src/common/p25/kmm/KMMModifyKey.cpp +++ b/src/common/p25/kmm/KMMModifyKey.cpp @@ -52,7 +52,7 @@ KMMModifyKey::~KMMModifyKey() uint32_t KMMModifyKey::length() const { - uint32_t len = KMM_MODIFY_KEY_LENGTH; + uint32_t len = KMMFrame::length() + KMM_BODY_MODIFY_KEY_LENGTH; if (m_miSet) len += MI_LENGTH_BYTES; len += m_keysetItem.length(); @@ -68,39 +68,39 @@ bool KMMModifyKey::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_decryptInfoFmt = data[10U]; // Decryption Instruction Format - m_algId = data[11U]; // Algorithm ID - m_kId = GET_UINT16(data, 12U); // Key ID + m_decryptInfoFmt = data[10U + m_bodyOffset]; // Decryption Instruction Format + m_algId = data[11U + m_bodyOffset]; // Algorithm ID + m_kId = GET_UINT16(data, 12U + m_bodyOffset); // Key ID uint16_t offset = 0U; if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); - ::memcpy(m_mi, data + 14U, MI_LENGTH_BYTES); - offset += 9U; + ::memcpy(m_mi, data + (m_bodyOffset + 14U), MI_LENGTH_BYTES); + offset += MI_LENGTH_BYTES; } - m_keysetItem.keysetId(data[14U + offset]); - m_keysetItem.algId(data[15U + offset]); - m_keysetItem.keyLength(data[16U + offset]); + m_keysetItem.keysetId(data[14U + (m_bodyOffset + offset)]); + m_keysetItem.algId(data[15U + (m_bodyOffset + offset)]); + m_keysetItem.keyLength(data[16U + (m_bodyOffset + offset)]); - uint8_t keyCount = data[17U + offset]; + uint8_t keyCount = data[17U + (m_bodyOffset + offset)]; for (uint8_t i = 0U; i < keyCount; i++) { KeyItem key = KeyItem(); DECLARE_UINT8_ARRAY(keyPayload, m_keysetItem.keyLength()); - uint8_t keyFormat = data[18U + offset]; + uint8_t keyFormat = data[18U + (m_bodyOffset + offset)]; uint8_t keyNameLen = keyFormat & 0x1FU; key.keyFormat(keyFormat & 0xE0U); - uint16_t sln = GET_UINT16(data, 19U + offset); + uint16_t sln = GET_UINT16(data, 19U + (m_bodyOffset + offset)); key.sln(sln); - uint16_t kId = GET_UINT16(data, 21U + offset); + uint16_t kId = GET_UINT16(data, 21U + (m_bodyOffset + offset)); key.kId(kId); - ::memcpy(keyPayload, data + (23U + offset), m_keysetItem.keyLength()); + ::memcpy(keyPayload, data + (23U + (m_bodyOffset + offset)), m_keysetItem.keyLength()); key.setKey(keyPayload, m_keysetItem.keyLength()); m_keysetItem.push_back(key); @@ -124,32 +124,32 @@ void KMMModifyKey::encode(uint8_t* data) m_decryptInfoFmt = KMM_DECRYPT_INSTRUCT_NONE; } - data[10U] = m_decryptInfoFmt; // Decryption Instruction Format - data[11U] = m_algId; // Algorithm ID - SET_UINT16(m_kId, data, 12U); // Key ID + data[10U + m_bodyOffset] = m_decryptInfoFmt; // Decryption Instruction Format + data[11U + m_bodyOffset] = m_algId; // Algorithm ID + SET_UINT16(m_kId, data, 12U + m_bodyOffset); // Key ID uint16_t offset = 0U; if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { - ::memcpy(data + 14U, m_mi, MI_LENGTH_BYTES); + ::memcpy(data + (m_bodyOffset + 14U), m_mi, MI_LENGTH_BYTES); offset += 9U; } - data[14U + offset] = m_keysetItem.keysetId(); - data[15U + offset] = m_keysetItem.algId(); - data[16U + offset] = m_keysetItem.keyLength(); + data[14U + (m_bodyOffset + offset)] = m_keysetItem.keysetId(); + data[15U + (m_bodyOffset + offset)] = m_keysetItem.algId(); + data[16U + (m_bodyOffset + offset)] = m_keysetItem.keyLength(); uint8_t keyCount = m_keysetItem.keys().size(); - data[17U + offset] = keyCount; + data[17U + (m_bodyOffset + offset)] = keyCount; for (auto key : m_keysetItem.keys()) { uint8_t keyNameLen = key.keyFormat() & 0x1FU; - data[18U + offset] = key.keyFormat(); - SET_UINT16(key.sln(), data, 19U + offset); - SET_UINT16(key.kId(), data, 21U + offset); + data[18U + (m_bodyOffset + offset)] = key.keyFormat(); + SET_UINT16(key.sln(), data, 19U + (m_bodyOffset + offset)); + SET_UINT16(key.kId(), data, 21U + (m_bodyOffset + offset)); DECLARE_UINT8_ARRAY(keyPayload, m_keysetItem.keyLength()); key.getKey(keyPayload); - ::memcpy(data + (23U + offset), keyPayload, m_keysetItem.keyLength()); + ::memcpy(data + (23U + (m_bodyOffset + offset)), keyPayload, m_keysetItem.keyLength()); offset += 5U + keyNameLen + m_keysetItem.keyLength(); } @@ -162,6 +162,29 @@ std::string KMMModifyKey::toString() return std::string("KMM, MODIFY_KEY_CMD (Modify Key)"); } +/* +** Encryption data +*/ + +/* Sets the encryption message indicator. */ + +void KMMModifyKey::setMI(const uint8_t* mi) +{ + assert(mi != nullptr); + + m_miSet = true; + ::memcpy(m_mi, mi, MI_LENGTH_BYTES); +} + +/* Gets the encryption message indicator. */ + +void KMMModifyKey::getMI(uint8_t* mi) const +{ + assert(mi != nullptr); + + ::memcpy(mi, m_mi, MI_LENGTH_BYTES); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMModifyKey.h b/src/common/p25/kmm/KMMModifyKey.h index 10af6282..fa8e0cac 100644 --- a/src/common/p25/kmm/KMMModifyKey.h +++ b/src/common/p25/kmm/KMMModifyKey.h @@ -37,7 +37,7 @@ namespace p25 * @{ */ - const uint32_t KMM_MODIFY_KEY_LENGTH = KMM_FRAME_LENGTH + 8U; + const uint32_t KMM_BODY_MODIFY_KEY_LENGTH = 8U; /** @} */ @@ -105,7 +105,7 @@ namespace p25 /** * @brief Encryption key ID. */ - DECLARE_PROPERTY(uint32_t, kId, KId); + DECLARE_PROPERTY(uint16_t, kId, KId); /** * @brief diff --git a/src/common/p25/kmm/KMMNegativeAck.cpp b/src/common/p25/kmm/KMMNegativeAck.cpp index 39a8df7b..f80ad309 100644 --- a/src/common/p25/kmm/KMMNegativeAck.cpp +++ b/src/common/p25/kmm/KMMNegativeAck.cpp @@ -37,6 +37,14 @@ KMMNegativeAck::KMMNegativeAck() : KMMFrame(), KMMNegativeAck::~KMMNegativeAck() = default; +/* Gets the byte length of this KMMNegativeAck. */ + +uint32_t KMMNegativeAck::length() const +{ + uint32_t len = KMMFrame::length() + KMM_BODY_NEGATIVE_ACK_LENGTH; + return len; +} + /* Decode a KMM NAK. */ bool KMMNegativeAck::decode(const uint8_t* data) @@ -45,9 +53,9 @@ bool KMMNegativeAck::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_messageId = data[10U]; // Message ID - m_messageNo = GET_UINT16(data, 11U); // Message Number - m_status = data[13U]; // Status + m_messageId = data[10U + m_bodyOffset]; // Message ID + m_messageNo = GET_UINT16(data, 11U + m_bodyOffset); // Message Number + m_status = data[13U + m_bodyOffset]; // Status return true; } @@ -57,13 +65,13 @@ bool KMMNegativeAck::decode(const uint8_t* data) void KMMNegativeAck::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_NEGATIVE_ACK_LENGTH; + m_messageLength = length(); KMMFrame::encodeHeader(data); - data[10U] = m_messageId; // Message ID - SET_UINT16(m_messageNo, data, 11U); // Message Number - data[13U] = m_status; + data[10U + m_bodyOffset] = m_messageId; // Message ID + SET_UINT16(m_messageNo, data, 11U + m_bodyOffset); // Message Number + data[13U + m_bodyOffset] = m_status; // Status } /* Returns a string that represents the current KMM frame. */ diff --git a/src/common/p25/kmm/KMMNegativeAck.h b/src/common/p25/kmm/KMMNegativeAck.h index a4750be8..a8c07035 100644 --- a/src/common/p25/kmm/KMMNegativeAck.h +++ b/src/common/p25/kmm/KMMNegativeAck.h @@ -36,7 +36,7 @@ namespace p25 * @{ */ - const uint32_t KMM_NEGATIVE_ACK_LENGTH = KMM_FRAME_LENGTH + 4U; + const uint32_t KMM_BODY_NEGATIVE_ACK_LENGTH = 4U; /** @} */ @@ -55,6 +55,12 @@ namespace p25 */ ~KMMNegativeAck(); + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + /** * @brief Decode a KMM NAK. * @param[in] data Buffer containing KMM frame data to decode. diff --git a/src/common/p25/kmm/KMMNoService.cpp b/src/common/p25/kmm/KMMNoService.cpp index feca70bf..03c030ec 100644 --- a/src/common/p25/kmm/KMMNoService.cpp +++ b/src/common/p25/kmm/KMMNoService.cpp @@ -50,7 +50,7 @@ bool KMMNoService::decode(const uint8_t* data) void KMMNoService::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_NO_SERVICE_LENGTH; + m_messageLength = length(); KMMFrame::encodeHeader(data); } diff --git a/src/common/p25/kmm/KMMNoService.h b/src/common/p25/kmm/KMMNoService.h index 0637169c..8af8a0db 100644 --- a/src/common/p25/kmm/KMMNoService.h +++ b/src/common/p25/kmm/KMMNoService.h @@ -27,19 +27,6 @@ namespace p25 { namespace kmm { - // --------------------------------------------------------------------------- - // Constants - // --------------------------------------------------------------------------- - - /** - * @addtogroup p25_kmm - * @{ - */ - - const uint32_t KMM_NO_SERVICE_LENGTH = KMM_FRAME_LENGTH; - - /** @} */ - // --------------------------------------------------------------------------- // Class Declaration // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMRegistrationCommand.cpp b/src/common/p25/kmm/KMMRegistrationCommand.cpp index 2eb50c87..33a55455 100644 --- a/src/common/p25/kmm/KMMRegistrationCommand.cpp +++ b/src/common/p25/kmm/KMMRegistrationCommand.cpp @@ -36,6 +36,14 @@ KMMRegistrationCommand::KMMRegistrationCommand() : KMMFrame(), KMMRegistrationCommand::~KMMRegistrationCommand() = default; +/* Gets the byte length of this KMMRegistrationCommand. */ + +uint32_t KMMRegistrationCommand::length() const +{ + uint32_t len = KMMFrame::length() + KMM_BODY_REGISTRATION_CMD_LENGTH; + return len; +} + /* Decode a KMM modify key. */ bool KMMRegistrationCommand::decode(const uint8_t* data) @@ -44,8 +52,8 @@ bool KMMRegistrationCommand::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_bodyFormat = data[10U]; // Body Format - m_kmfRSI = GET_UINT24(data, 11U); // KMF RSI + m_bodyFormat = data[10U + m_bodyOffset]; // Body Format + m_kmfRSI = GET_UINT24(data, 11U + m_bodyOffset); // KMF RSI return true; } @@ -55,13 +63,13 @@ bool KMMRegistrationCommand::decode(const uint8_t* data) void KMMRegistrationCommand::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_REGISTRATION_CMD_LENGTH; + m_messageLength = length(); m_bodyFormat = 0U; // reset this to none -- we don't support warm start right now KMMFrame::encodeHeader(data); - data[10U] = m_bodyFormat; // Body Format - SET_UINT24(m_kmfRSI, data, 11U); // KMF RSI + data[10U + m_bodyOffset] = m_bodyFormat; // Body Format + SET_UINT24(m_kmfRSI, data, 11U + m_bodyOffset); // KMF RSI } /* Returns a string that represents the current KMM frame. */ diff --git a/src/common/p25/kmm/KMMRegistrationCommand.h b/src/common/p25/kmm/KMMRegistrationCommand.h index 85d1fce3..033563d4 100644 --- a/src/common/p25/kmm/KMMRegistrationCommand.h +++ b/src/common/p25/kmm/KMMRegistrationCommand.h @@ -36,7 +36,7 @@ namespace p25 * @{ */ - const uint32_t KMM_REGISTRATION_CMD_LENGTH = KMM_FRAME_LENGTH + 4U; + const uint32_t KMM_BODY_REGISTRATION_CMD_LENGTH = 4U; /** @} */ @@ -55,6 +55,12 @@ namespace p25 */ ~KMMRegistrationCommand(); + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + /** * @brief Decode a KMM deregistration command. * @param[in] data Buffer containing KMM frame data to decode. diff --git a/src/common/p25/kmm/KMMRegistrationResponse.cpp b/src/common/p25/kmm/KMMRegistrationResponse.cpp index 9f735af3..40900cd4 100644 --- a/src/common/p25/kmm/KMMRegistrationResponse.cpp +++ b/src/common/p25/kmm/KMMRegistrationResponse.cpp @@ -35,6 +35,14 @@ KMMRegistrationResponse::KMMRegistrationResponse() : KMMFrame(), KMMRegistrationResponse::~KMMRegistrationResponse() = default; +/* Gets the byte length of this KMMRegistrationResponse. */ + +uint32_t KMMRegistrationResponse::length() const +{ + uint32_t len = KMMFrame::length() + KMM_BODY_REGISTRATION_RSP_LENGTH; + return len; +} + /* Decode a KMM modify key. */ bool KMMRegistrationResponse::decode(const uint8_t* data) @@ -43,7 +51,7 @@ bool KMMRegistrationResponse::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_status = data[10U]; // Status + m_status = data[10U + m_bodyOffset]; // Status return true; } @@ -53,11 +61,11 @@ bool KMMRegistrationResponse::decode(const uint8_t* data) void KMMRegistrationResponse::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_REGISTRATION_RSP_LENGTH; + m_messageLength = length(); KMMFrame::encodeHeader(data); - data[10U] = m_status; // Status + data[10U + m_bodyOffset] = m_status; // Status } /* Returns a string that represents the current KMM frame. */ diff --git a/src/common/p25/kmm/KMMRegistrationResponse.h b/src/common/p25/kmm/KMMRegistrationResponse.h index f70e5ac0..ccd89144 100644 --- a/src/common/p25/kmm/KMMRegistrationResponse.h +++ b/src/common/p25/kmm/KMMRegistrationResponse.h @@ -36,7 +36,7 @@ namespace p25 * @{ */ - const uint32_t KMM_REGISTRATION_RSP_LENGTH = KMM_FRAME_LENGTH + 1U; + const uint32_t KMM_BODY_REGISTRATION_RSP_LENGTH = 1U; /** @} */ @@ -55,6 +55,12 @@ namespace p25 */ ~KMMRegistrationResponse(); + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + /** * @brief Decode a KMM deregistration response. * @param[in] data Buffer containing KMM frame data to decode. diff --git a/src/common/p25/kmm/KMMRekeyAck.cpp b/src/common/p25/kmm/KMMRekeyAck.cpp index c1b0e9df..61a321f5 100644 --- a/src/common/p25/kmm/KMMRekeyAck.cpp +++ b/src/common/p25/kmm/KMMRekeyAck.cpp @@ -29,7 +29,7 @@ KMMRekeyAck::KMMRekeyAck() : KMMFrame(), m_numberOfKeyStatus(0U), m_keystatus() { - m_messageId = KMM_MessageType::NAK; + m_messageId = KMM_MessageType::REKEY_ACK; m_respKind = KMM_ResponseKind::NONE; } @@ -41,7 +41,7 @@ KMMRekeyAck::~KMMRekeyAck() = default; uint32_t KMMRekeyAck::length() const { - uint32_t len = KMM_REKEY_ACK_LENGTH; + uint32_t len = KMMFrame::length() + KMM_BODY_REKEY_ACK_LENGTH; len += m_keystatus.size() * 4U; return len; @@ -55,19 +55,19 @@ bool KMMRekeyAck::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_messageId = data[10U]; // Message ID - m_numberOfKeyStatus = data[11U]; // Number of Key Status + m_messageId = data[10U + m_bodyOffset]; // Message ID + m_numberOfKeyStatus = data[11U + m_bodyOffset]; // Number of Key Status uint16_t offset = 0U; for (uint8_t i = 0U; i < m_numberOfKeyStatus; i++) { KeyStatus keyStatus = KeyStatus(); - keyStatus.algId(data[12U + offset]); + keyStatus.algId(data[12U + (m_bodyOffset + offset)]); - uint16_t kId = GET_UINT16(data, 13U + offset); + uint16_t kId = GET_UINT16(data, 13U + (m_bodyOffset + offset)); keyStatus.kId(kId); - keyStatus.status(data[15U + offset]); + keyStatus.status(data[15U + (m_bodyOffset + offset)]); m_keystatus.push_back(keyStatus); offset += 4U; @@ -85,15 +85,15 @@ void KMMRekeyAck::encode(uint8_t* data) KMMFrame::encodeHeader(data); - data[10U] = m_messageId; // Message ID - data[11U] = m_numberOfKeyStatus; // Number of Key Status + data[10U + m_bodyOffset] = m_messageId; // Message ID + data[11U + m_bodyOffset] = m_numberOfKeyStatus; // Number of Key Status uint16_t offset = 0U; for (auto keyStatus : m_keystatus) { - data[12U + offset] = keyStatus.algId(); - SET_UINT16(keyStatus.kId(), data, 13U + offset); + data[12U + (m_bodyOffset + offset)] = keyStatus.algId(); + SET_UINT16(keyStatus.kId(), data, 13U + (m_bodyOffset + offset)); - data[15U + offset] = keyStatus.status(); + data[15U + (m_bodyOffset + offset)] = keyStatus.status(); offset += 4U; } } diff --git a/src/common/p25/kmm/KMMRekeyAck.h b/src/common/p25/kmm/KMMRekeyAck.h index 0637c084..831442a1 100644 --- a/src/common/p25/kmm/KMMRekeyAck.h +++ b/src/common/p25/kmm/KMMRekeyAck.h @@ -37,7 +37,7 @@ namespace p25 * @{ */ - const uint32_t KMM_REKEY_ACK_LENGTH = KMM_FRAME_LENGTH + 2U; + const uint32_t KMM_BODY_REKEY_ACK_LENGTH = 2U; /** @} */ diff --git a/src/common/p25/kmm/KMMRekeyCommand.cpp b/src/common/p25/kmm/KMMRekeyCommand.cpp index b33373df..d3cbbeef 100644 --- a/src/common/p25/kmm/KMMRekeyCommand.cpp +++ b/src/common/p25/kmm/KMMRekeyCommand.cpp @@ -52,7 +52,7 @@ KMMRekeyCommand::~KMMRekeyCommand() uint32_t KMMRekeyCommand::length() const { - uint32_t len = KMM_REKEY_CMD_LENGTH; + uint32_t len = KMMFrame::length() + KMM_BODY_REKEY_CMD_LENGTH; if (m_miSet) len += MI_LENGTH_BYTES; @@ -70,43 +70,48 @@ bool KMMRekeyCommand::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_decryptInfoFmt = data[10U]; // Decryption Instruction Format - m_algId = data[11U]; // Algorithm ID - m_kId = GET_UINT16(data, 12U); // Key ID + m_containsTeks = false; + + m_decryptInfoFmt = data[10U + m_bodyOffset]; // Decryption Instruction Format + m_algId = data[11U + m_bodyOffset]; // Algorithm ID + m_kId = GET_UINT16(data, 12U + m_bodyOffset); // Key ID uint16_t offset = 0U; if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); - ::memcpy(m_mi, data + 14U, MI_LENGTH_BYTES); + ::memcpy(m_mi, data + (m_bodyOffset + 14U), MI_LENGTH_BYTES); offset += 9U; } - uint8_t keysetCount = data[14U + offset]; + uint8_t keysetCount = data[14U + (m_bodyOffset + offset)]; for (uint8_t n = 0U; n < keysetCount; n++) { KeysetItem keysetItem = KeysetItem(); - keysetItem.keysetId(data[16U + offset]); - keysetItem.algId(data[17U + offset]); - keysetItem.keyLength(data[18U + offset]); + if (!m_containsTeks) + m_containsTeks = data[15U + (m_bodyOffset + offset)] & KEY_FORMAT_TEK; + + keysetItem.keysetId(data[16U + (m_bodyOffset + offset)]); + keysetItem.algId(data[17U + (m_bodyOffset + offset)]); + keysetItem.keyLength(data[18U + (m_bodyOffset + offset)]); - uint8_t keyCount = data[19U + offset]; + uint8_t keyCount = data[19U + (m_bodyOffset + offset)]; for (uint8_t i = 0U; i < keyCount; i++) { KeyItem key = KeyItem(); DECLARE_UINT8_ARRAY(keyPayload, keysetItem.keyLength()); - uint8_t keyFormat = data[20U + offset]; + uint8_t keyFormat = data[20U + (m_bodyOffset + offset)]; uint8_t keyNameLen = keyFormat & 0x1FU; key.keyFormat(keyFormat & 0xE0U); - uint16_t sln = GET_UINT16(data, 21U + offset); + uint16_t sln = GET_UINT16(data, 21U + (m_bodyOffset + offset)); key.sln(sln); - uint16_t kId = GET_UINT16(data, 23U + offset); + uint16_t kId = GET_UINT16(data, 23U + (m_bodyOffset + offset)); key.kId(kId); - ::memcpy(keyPayload, data + (25U + offset), keysetItem.keyLength()); + ::memcpy(keyPayload, data + (25U + (m_bodyOffset + offset)), keysetItem.keyLength()); key.setKey(keyPayload, keysetItem.keyLength()); keysetItem.push_back(key); @@ -134,35 +139,38 @@ void KMMRekeyCommand::encode(uint8_t* data) m_decryptInfoFmt = KMM_DECRYPT_INSTRUCT_NONE; } - data[10U] = m_decryptInfoFmt; // Decryption Instruction Format - data[11U] = m_algId; // Algorithm ID - SET_UINT16(m_kId, data, 12U); // Key ID + data[10U + m_bodyOffset] = m_decryptInfoFmt; // Decryption Instruction Format + data[11U + m_bodyOffset] = m_algId; // Algorithm ID + SET_UINT16(m_kId, data, 12U + m_bodyOffset); // Key ID uint16_t offset = 0U; if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { - ::memcpy(data + 14U, m_mi, MI_LENGTH_BYTES); + ::memcpy(data + (m_bodyOffset + 14U), m_mi, MI_LENGTH_BYTES); offset += 9U; } uint8_t keysetCount = m_keysets.size(); - data[14U + offset] = keysetCount; + data[14U + (m_bodyOffset + offset)] = keysetCount; + for (auto keysetItem : m_keysets) { - data[16U + offset] = keysetItem.keysetId(); - data[17U + offset] = keysetItem.algId(); - data[18U + offset] = keysetItem.keyLength(); + uint8_t keysetFormat = (m_containsTeks) ? KEY_FORMAT_TEK : 0x00U; + data[15U + (m_bodyOffset + offset)] = keysetFormat; + data[16U + (m_bodyOffset + offset)] = keysetItem.keysetId(); + data[17U + (m_bodyOffset + offset)] = keysetItem.algId(); + data[18U + (m_bodyOffset + offset)] = keysetItem.keyLength(); uint8_t keyCount = keysetItem.keys().size(); - data[19U + offset] = keyCount; + data[19U + (m_bodyOffset + offset)] = keyCount; for (auto key : keysetItem.keys()) { uint8_t keyNameLen = key.keyFormat() & 0x1FU; - data[18U + offset] = key.keyFormat(); - SET_UINT16(key.sln(), data, 19U + offset); - SET_UINT16(key.kId(), data, 21U + offset); + data[18U + (m_bodyOffset + offset)] = key.keyFormat(); + SET_UINT16(key.sln(), data, 19U + (m_bodyOffset + offset)); + SET_UINT16(key.kId(), data, 21U + (m_bodyOffset + offset)); DECLARE_UINT8_ARRAY(keyPayload, keysetItem.keyLength()); key.getKey(keyPayload); - ::memcpy(data + (23U + offset), keyPayload, keysetItem.keyLength()); + ::memcpy(data + (23U + (m_bodyOffset + offset)), keyPayload, keysetItem.keyLength()); offset += 5U + keyNameLen + keysetItem.keyLength(); } @@ -178,6 +186,29 @@ std::string KMMRekeyCommand::toString() return std::string("KMM, REKEY_CMD (Rekey Command)"); } +/* +** Encryption data +*/ + +/* Sets the encryption message indicator. */ + +void KMMRekeyCommand::setMI(const uint8_t* mi) +{ + assert(mi != nullptr); + + m_miSet = true; + ::memcpy(m_mi, mi, MI_LENGTH_BYTES); +} + +/* Gets the encryption message indicator. */ + +void KMMRekeyCommand::getMI(uint8_t* mi) const +{ + assert(mi != nullptr); + + ::memcpy(mi, m_mi, MI_LENGTH_BYTES); +} + // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KMMRekeyCommand.h b/src/common/p25/kmm/KMMRekeyCommand.h index 82e0e7e5..55a52c19 100644 --- a/src/common/p25/kmm/KMMRekeyCommand.h +++ b/src/common/p25/kmm/KMMRekeyCommand.h @@ -37,7 +37,7 @@ namespace p25 * @{ */ - const uint32_t KMM_REKEY_CMD_LENGTH = KMM_FRAME_LENGTH + 5U; + const uint32_t KMM_BODY_REKEY_CMD_LENGTH = 5U; /** @} */ @@ -105,7 +105,12 @@ namespace p25 /** * @brief Encryption key ID. */ - DECLARE_PROPERTY(uint32_t, kId, KId); + DECLARE_PROPERTY(uint16_t, kId, KId); + + /** + * @brief Flag indicating whether or not this rekey command contains TEKs or KEKs. + */ + DECLARE_PROPERTY(bool, containsTeks, ContainsTEKs); /** * @brief List of keysets. diff --git a/src/common/p25/kmm/KMMUnableToDecrypt.cpp b/src/common/p25/kmm/KMMUnableToDecrypt.cpp new file mode 100644 index 00000000..9512d2e4 --- /dev/null +++ b/src/common/p25/kmm/KMMUnableToDecrypt.cpp @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "p25/P25Defines.h" +#include "p25/kmm/KMMUnableToDecrypt.h" +#include "Log.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::kmm; + +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the KMMUnableToDecrypt class. */ + +KMMUnableToDecrypt::KMMUnableToDecrypt() : KMMFrame(), + m_bodyFormat(0U), + m_algId(ALGO_UNENCRYPT), + m_kId(0U), + m_status(0U), + m_decryptInfoFmt(KMM_DECRYPT_INSTRUCT_NONE), + m_decryptAlgId(ALGO_UNENCRYPT), + m_decryptKId(0U), + m_key(), + m_miSet(false), + m_mi(nullptr) +{ + m_messageId = KMM_MessageType::UNABLE_TO_DECRYPT; + m_respKind = KMM_ResponseKind::NONE; + + m_mi = new uint8_t[MI_LENGTH_BYTES]; + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); +} + +/* Finalizes a instance of the KMMUnableToDecrypt class. */ + +KMMUnableToDecrypt::~KMMUnableToDecrypt() +{ + if (m_mi != nullptr) { + delete[] m_mi; + m_mi = nullptr; + } +} + +/* Gets the byte length of this KMMUnableToDecrypt. */ + +uint32_t KMMUnableToDecrypt::length() const +{ + uint32_t len = KMMFrame::length() + KMM_BODY_UNABLE_TO_DECRYPT_LENGTH; + if ((m_bodyFormat & KEY_FORMAT_TEK) == KEY_FORMAT_TEK) { + len += 3U; + } + + if (m_miSet) { + len += MI_LENGTH_BYTES; + } + + len += 1U + m_key.getLength(); + + return len; +} + +/* Decode a KMM Unable-To-Decrypt. */ + +bool KMMUnableToDecrypt::decode(const uint8_t* data) +{ + assert(data != nullptr); + + KMMFrame::decodeHeader(data); + + m_bodyFormat = data[10U + m_bodyOffset]; // Body Format + m_algId = data[12U + m_bodyOffset]; // Algorithm ID + m_kId = GET_UINT16(data, 13U + m_bodyOffset); // Key ID + m_status = data[15U + m_bodyOffset]; // Status + + uint8_t offset = 0U; + if ((m_bodyFormat & KEY_FORMAT_TEK) == KEY_FORMAT_TEK) { + m_decryptInfoFmt = data[16U + m_bodyOffset]; // Decrypt Info Format + m_decryptAlgId = data[17U + m_bodyOffset]; // Decrypt Algorithm ID + m_decryptKId = GET_UINT16(data, 18U + m_bodyOffset); // Decrypt Key ID + offset += 4U; + + if ((m_decryptInfoFmt & KMM_DECRYPT_INSTRUCT_MI) == KMM_DECRYPT_INSTRUCT_MI) { + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + ::memcpy(m_mi, data + (m_bodyOffset + 20U), MI_LENGTH_BYTES); + offset += MI_LENGTH_BYTES; + } + } + + uint8_t keyLength = data[16U + (m_bodyOffset + offset)]; + m_key.keyFormat(data[18U + (m_bodyOffset + offset)]); + + uint16_t sln = GET_UINT16(data, 19U + (m_bodyOffset + offset)); + m_key.sln(sln); + + uint16_t kId = GET_UINT16(data, 21U + (m_bodyOffset + offset)); + m_key.kId(kId); + + DECLARE_UINT8_ARRAY(keyPayload, keyLength); + + ::memcpy(keyPayload, data + (23U + (m_bodyOffset + offset)), keyLength); + m_key.setKey(keyPayload, keyLength); + + return true; +} + +/* Encode a KMM Unable-To-Decrypt. */ + +void KMMUnableToDecrypt::encode(uint8_t* data) +{ + assert(data != nullptr); + m_messageLength = length(); + + KMMFrame::encodeHeader(data); + + if (!m_miSet && m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { + m_decryptInfoFmt = KMM_DECRYPT_INSTRUCT_NONE; + } + + data[10U + m_bodyOffset] = m_bodyFormat; // Body Format + data[11U + m_bodyOffset] = m_algId; // Algorithm ID + SET_UINT16(m_kId, data, 13U + m_bodyOffset); // Key ID + data[15U + m_bodyOffset] = m_status; // Status + + uint8_t offset = 0U; + if ((m_bodyFormat & KEY_FORMAT_TEK) == KEY_FORMAT_TEK) { + data[16U + m_bodyOffset] = m_decryptInfoFmt; // Decrypt Info Format + data[17U + m_bodyOffset] = m_decryptAlgId; // Decrypt Algorithm ID + SET_UINT16(m_decryptKId, data, 18U + m_bodyOffset); // Decrypt Key ID + + if (m_miSet) { + ::memcpy(data + (m_bodyOffset + 20U), m_mi, MI_LENGTH_BYTES); + offset += MI_LENGTH_BYTES; + } + } + + data[16U + (m_bodyOffset + offset)] = m_key.getLength(); + + uint16_t sln = m_key.sln(); + SET_UINT16(sln, data, 19U + (m_bodyOffset + offset)); + + uint16_t kId = m_key.kId(); + SET_UINT16(kId, data, 21U + (m_bodyOffset + offset)); + + DECLARE_UINT8_ARRAY(keyPayload, m_key.getLength()); + m_key.getKey(keyPayload); + + ::memcpy(data + (23U + (m_bodyOffset + offset)), keyPayload, m_key.getLength()); +} + +/* Returns a string that represents the current KMM frame. */ + +std::string KMMUnableToDecrypt::toString() +{ + return std::string("KMM, UNABLE_TO_DECRYPT (Unable to Decrypt)"); +} + +/* +** Encryption data +*/ + +/* Sets the encryption message indicator. */ + +void KMMUnableToDecrypt::setMI(const uint8_t* mi) +{ + assert(mi != nullptr); + + m_miSet = true; + ::memcpy(m_mi, mi, MI_LENGTH_BYTES); +} + +/* Gets the encryption message indicator. */ + +void KMMUnableToDecrypt::getMI(uint8_t* mi) const +{ + assert(mi != nullptr); + + ::memcpy(mi, m_mi, MI_LENGTH_BYTES); +} + +// --------------------------------------------------------------------------- +// Protected Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to copy the the class. */ + +void KMMUnableToDecrypt::copy(const KMMUnableToDecrypt& data) +{ + KMMFrame::copy(data); + + m_bodyFormat = data.m_bodyFormat; + m_algId = data.m_algId; + m_kId = data.m_kId; + m_status = data.m_status; + + m_decryptInfoFmt = data.m_decryptInfoFmt; + m_decryptAlgId = data.m_decryptAlgId; + m_decryptKId = data.m_decryptKId; + + m_key = data.m_key; + + if (data.m_mi != nullptr) { + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + ::memcpy(m_mi, data.m_mi, MI_LENGTH_BYTES); + } +} diff --git a/src/common/p25/kmm/KMMUnableToDecrypt.h b/src/common/p25/kmm/KMMUnableToDecrypt.h new file mode 100644 index 00000000..9aa08a81 --- /dev/null +++ b/src/common/p25/kmm/KMMUnableToDecrypt.h @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file KMMUnableToDecrypt.h + * @ingroup p25_kmm + * @file KMMUnableToDecrypt.cpp + * @ingroup p25_kmm + */ +#if !defined(__P25_KMM__KMM_UNABLE_TO_DECRYPT_H__) +#define __P25_KMM__KMM_UNABLE_TO_DECRYPT_H__ + +#include "common/Defines.h" +#include "common/p25/kmm/KMMFrame.h" +#include "common/p25/kmm/KeysetItem.h" +#include "common/Utils.h" + +#include +#include + +namespace p25 +{ + namespace kmm + { + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + + /** + * @addtogroup p25_kmm + * @{ + */ + + const uint32_t KMM_BODY_UNABLE_TO_DECRYPT_LENGTH = 7U; + + /** @} */ + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + class HOST_SW_API KMMUnableToDecrypt : public KMMFrame { + public: + /** + * @brief Initializes a new instance of the KMMUnableToDecrypt class. + */ + KMMUnableToDecrypt(); + /** + * @brief Finalizes a instance of the KMMUnableToDecrypt class. + */ + ~KMMUnableToDecrypt(); + + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + + /** + * @brief Decode a KMM Unable-To-Decrypt. + * @param[in] data Buffer containing KMM frame data to decode. + * @returns bool True, if decoded, otherwise false. + */ + bool decode(const uint8_t* data) override; + /** + * @brief Encode a KMM Unable-To-Decrypt. + * @param[out] data Buffer to encode KMM frame data to. + */ + void encode(uint8_t* data) override; + + /** + * @brief Returns a string that represents the current KMM frame. + * @returns std::string String representation of the KMM frame. + */ + std::string toString() override; + + /** @name Encryption data */ + /** + * @brief Sets the encryption message indicator. + * @param[in] mi Buffer containing the 9-byte Message Indicator. + */ + void setMI(const uint8_t* mi); + /** + * @brief Gets the encryption message indicator. + * @param[out] mi Buffer containing the 9-byte Message Indicator. + */ + void getMI(uint8_t* mi) const; + /** @} */ + + public: + /** + * @brief + */ + DECLARE_PROPERTY(uint8_t, bodyFormat, BodyFormat); + /** + * @brief Encryption algorithm ID. + */ + DECLARE_PROPERTY(uint8_t, algId, AlgId); + /** + * @brief Encryption key ID. + */ + DECLARE_PROPERTY(uint16_t, kId, KId); + /** + * @brief + */ + DECLARE_PROPERTY(uint8_t, status, Status); + + /** + * @brief + */ + DECLARE_PROPERTY(uint8_t, decryptInfoFmt, DecryptInfoFmt); + /** + * @brief Encryption algorithm ID. + */ + DECLARE_PROPERTY(uint8_t, decryptAlgId, DecryptAlgId); + /** + * @brief Encryption key ID. + */ + DECLARE_PROPERTY(uint16_t, decryptKId, DecryptKId); + + /** + * @brief List of keys. + */ + DECLARE_PROPERTY(KeyItem, key, Key); + + DECLARE_COPY(KMMUnableToDecrypt); + + private: + // Encryption data + bool m_miSet; + uint8_t* m_mi; + }; + } // namespace kmm +} // namespace p25 + +#endif // __P25_KMM__KMM_UNABLE_TO_DECRYPT_H__ diff --git a/src/common/p25/kmm/KMMZeroize.cpp b/src/common/p25/kmm/KMMZeroize.cpp index 4d31b236..08711d1e 100644 --- a/src/common/p25/kmm/KMMZeroize.cpp +++ b/src/common/p25/kmm/KMMZeroize.cpp @@ -50,7 +50,7 @@ bool KMMZeroize::decode(const uint8_t* data) void KMMZeroize::encode(uint8_t* data) { assert(data != nullptr); - m_messageLength = KMM_ZEROIZE_LENGTH; + m_messageLength = length(); KMMFrame::encodeHeader(data); } diff --git a/src/common/p25/kmm/KMMZeroize.h b/src/common/p25/kmm/KMMZeroize.h index e4d18d13..a48eb16a 100644 --- a/src/common/p25/kmm/KMMZeroize.h +++ b/src/common/p25/kmm/KMMZeroize.h @@ -27,19 +27,6 @@ namespace p25 { namespace kmm { - // --------------------------------------------------------------------------- - // Constants - // --------------------------------------------------------------------------- - - /** - * @addtogroup p25_kmm - * @{ - */ - - const uint32_t KMM_ZEROIZE_LENGTH = KMM_FRAME_LENGTH; - - /** @} */ - // --------------------------------------------------------------------------- // Class Declaration // --------------------------------------------------------------------------- diff --git a/src/common/p25/kmm/KeysetItem.h b/src/common/p25/kmm/KeysetItem.h index 34cf98fe..9a9287bc 100644 --- a/src/common/p25/kmm/KeysetItem.h +++ b/src/common/p25/kmm/KeysetItem.h @@ -33,7 +33,7 @@ namespace p25 * @{ */ - const uint8_t MAX_ENC_KEY_LENGTH_BYTES = 32U; + const uint8_t MAX_ENC_KEY_LENGTH_BYTES = 32U; /** @} */ @@ -82,6 +82,15 @@ namespace p25 return *this; } + /** + * @brief Returns the length of this key item in bytes. + * @returns uint32_t Length of key item. + */ + uint32_t getLength() const + { + return 5U + m_keyLength; + } + /** * @brief Set the key material. * @param key diff --git a/src/fne/CryptoContainer.cpp b/src/fne/CryptoContainer.cpp index 29db98c0..7566894b 100644 --- a/src/fne/CryptoContainer.cpp +++ b/src/fne/CryptoContainer.cpp @@ -226,18 +226,18 @@ void CryptoContainer::clear() /* Adds a new entry to the lookup table by the specified unique ID. */ -void CryptoContainer::addEntry(KeyItem key) +void CryptoContainer::addEntry(EKCKeyItem key) { if (key.isInvalid()) return; - KeyItem entry = key; + EKCKeyItem entry = key; uint32_t id = entry.id(); uint32_t kId = entry.kId(); std::lock_guard lock(s_mutex); auto it = std::find_if(m_keys.begin(), m_keys.end(), - [&](KeyItem& x) + [&](EKCKeyItem& x) { return x.id() == id && x.kId() == kId; }); @@ -255,7 +255,7 @@ void CryptoContainer::eraseEntry(uint32_t id) { std::lock_guard lock(s_mutex); auto it = std::find_if(m_keys.begin(), m_keys.end(), - [&](KeyItem& x) { + [&](EKCKeyItem& x) { return x.id() == id; }); if (it != m_keys.end()) { @@ -265,24 +265,56 @@ void CryptoContainer::eraseEntry(uint32_t id) /* Finds a table entry in this lookup table. */ -KeyItem CryptoContainer::find(uint32_t kId) +EKCKeyItem CryptoContainer::find(uint32_t kId) { - KeyItem entry; + EKCKeyItem entry; std::lock_guard lock(s_mutex); auto it = std::find_if(m_keys.begin(), m_keys.end(), - [&](KeyItem& x) { + [&](EKCKeyItem& x) { return x.kId() == kId; }); if (it != m_keys.end()) { entry = *it; } else { - entry = KeyItem(); + entry = EKCKeyItem(); } return entry; } +/* Finds a table entry in this lookup table. */ + +EKCKeyItem CryptoContainer::findUKEK(uint32_t rsi) +{ + EKCKeyItem entry; + + std::lock_guard lock(s_mutex); + + /* + ** TODO TODO TODO + */ + entry = EKCKeyItem(); + + return entry; +} + +/* Finds a table entry in this lookup table. */ + +EKCKeyItem CryptoContainer::findLLA(uint32_t rsi) +{ + EKCKeyItem entry; + + std::lock_guard lock(s_mutex); + + /* + ** TODO TODO TODO + */ + entry = EKCKeyItem(); + + return entry; +} + // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- @@ -505,7 +537,7 @@ bool CryptoContainer::load() if (keys != nullptr) { uint32_t i = 0U; for (rapidxml::xml_node<>* keyNode = keys->first_node("KeyItem"); keyNode; keyNode = keyNode->next_sibling()) { - KeyItem key = KeyItem(); + EKCKeyItem key = EKCKeyItem(); key.id(i); // get name diff --git a/src/fne/CryptoContainer.h b/src/fne/CryptoContainer.h index a78866a6..78a3cab7 100644 --- a/src/fne/CryptoContainer.h +++ b/src/fne/CryptoContainer.h @@ -31,15 +31,15 @@ // --------------------------------------------------------------------------- /** - * @brief Represents an key item. + * @brief Represents an EKC key item. * @ingroup fne */ -class HOST_SW_API KeyItem { +class HOST_SW_API EKCKeyItem { public: /** - * @brief Initializes a new instance of the KeyItem class. + * @brief Initializes a new instance of the EKCKeyItem class. */ - KeyItem() : + EKCKeyItem() : m_id(0U), m_name(), m_keysetId(0U), @@ -52,10 +52,10 @@ class HOST_SW_API KeyItem { } /** - * @brief Equals operator. Copies this KeyItem to another KeyItem. - * @param data Instance of KeyItem to copy. + * @brief Equals operator. Copies this EKCKeyItem to another EKCKeyItem. + * @param data Instance of EKCKeyItem to copy. */ - virtual KeyItem& operator= (const KeyItem& data) + virtual EKCKeyItem& operator= (const EKCKeyItem& data) { if (this != &data) { m_id = data.m_id; @@ -207,7 +207,7 @@ class HOST_SW_API CryptoContainer : public Thread { * @brief Adds a new entry to the lookup table. * @param key Key Item. */ - void addEntry(KeyItem key); + void addEntry(EKCKeyItem key); /** * @brief Erases an existing entry from the lookup table by the specified unique ID. * @param id Unique ID to erase. @@ -218,7 +218,20 @@ class HOST_SW_API CryptoContainer : public Thread { * @param kId Unique identifier for table entry. * @returns KeyItem Table entry. */ - virtual KeyItem find(uint32_t kId); + virtual EKCKeyItem find(uint32_t kId); + + /** + * @brief Finds a table entry in this lookup table. + * @param rsi Unique radio set identifier (RID/LLID). + * @returns KeyItem Table entry. + */ + virtual EKCKeyItem findUKEK(uint32_t rsi); + /** + * @brief Finds a table entry in this lookup table. + * @param rsi Unique radio set identifier (RID/LLID). + * @returns KeyItem Table entry. + */ + virtual EKCKeyItem findLLA(uint32_t rsi); /** * @brief Helper to return the flag indicating whether or not the crypto container is enabled. @@ -252,7 +265,7 @@ class HOST_SW_API CryptoContainer : public Thread { /** * @brief List of keys. */ - DECLARE_PROPERTY_PLAIN(std::vector, keys); + DECLARE_PROPERTY_PLAIN(std::vector, keys); }; #endif // __CRYPTO_CONTAINER_H__ diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index d1dfed66..c51962e7 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1504,7 +1504,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) if (modifyKey->getAlgId() > 0U && modifyKey->getKId() > 0U) { LogInfoEx(LOG_MASTER, "PEER %u (%s) requested enc. key, algId = $%02X, kID = $%04X", peerId, connection->identWithQualifier().c_str(), modifyKey->getAlgId(), modifyKey->getKId()); - ::KeyItem keyItem = network->m_cryptoLookup->find(modifyKey->getKId()); + ::EKCKeyItem keyItem = network->m_cryptoLookup->find(modifyKey->getKId()); if (!keyItem.isInvalid()) { uint8_t key[P25DEF::MAX_ENC_KEY_LENGTH_BYTES]; ::memset(key, 0x00U, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); diff --git a/src/fne/network/P25OTARService.cpp b/src/fne/network/P25OTARService.cpp index 17ae4796..62b0bb22 100644 --- a/src/fne/network/P25OTARService.cpp +++ b/src/fne/network/P25OTARService.cpp @@ -57,6 +57,7 @@ P25OTARService::P25OTARService(FNENetwork* network, P25PacketData* packetData, b m_threadPool(MAX_THREAD_CNT, "otar"), m_network(network), m_packetData(packetData), + m_allowNoUKEKRekey(false), m_debug(debug), m_verbose(verbose) { @@ -280,7 +281,7 @@ UInt8Array P25OTARService::cryptKMM(uint8_t algoId, uint16_t kid, uint8_t* mi, c ** because we aren't performing FNE KEY_REQ's to upstream peer'ed FNEs to find the key used to encrypt the KMM. */ - ::KeyItem keyItem = m_network->m_cryptoLookup->find(kid); + ::EKCKeyItem keyItem = m_network->m_cryptoLookup->find(kid); if (!keyItem.isInvalid()) { uint8_t key[P25DEF::MAX_ENC_KEY_LENGTH_BYTES]; ::memset(key, 0x00U, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); @@ -422,6 +423,14 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ } break; + case KMM_MessageType::UNABLE_TO_DECRYPT: + { + KMMUnableToDecrypt* kmm = static_cast(frame.get()); + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, status = $%02X", kmm->toString().c_str(), + llId, kmm->getStatus()); + logResponseStatus(llId, kmm->toString(), kmm->getStatus()); + } + default: break; } // switch (frame->getMessageId()) @@ -429,16 +438,129 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ return nullptr; } -/* Helper used to return a Registration-Command KMM to the calling SU. */ +/* Helper used to return a Rekey-Command KMM to the calling SU. */ -UInt8Array P25OTARService::write_KMM_Reg_Command(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize) +UInt8Array P25OTARService::write_KMM_Rekey_Command(uint32_t llId, uint32_t kmmRSI, uint8_t flags, uint32_t* payloadSize) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + P25Crypto crypto; + crypto.generateMI(); + crypto.getMI(mi); + + KMMRekeyCommand outKmm = KMMRekeyCommand(); + + /* + ** bryanb: Architecturally this is a problem. Because KMF services would essentially be limited to the local FNE + ** because we aren't performing FNE KEY_REQ's to upstream peer'ed FNEs to find the key used to encrypt the KMM. + */ + + uint8_t kekKey[P25DEF::MAX_ENC_KEY_LENGTH_BYTES]; + ::memset(kekKey, 0x00U, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); + + uint8_t kekAlgId = P25DEF::ALGO_UNENCRYPT; + uint16_t kekKId = 0U; + + ::EKCKeyItem keyItem = m_network->m_cryptoLookup->findUKEK(kmmRSI); + if (!keyItem.isInvalid()) { + uint8_t keyLength = keyItem.getKey(kekKey); + + kekAlgId = keyItem.algId(); + kekKId = keyItem.kId(); + + if (m_network->m_debug) { + LogDebugEx(LOG_P25, "P25OTARService::cryptKMM()", "keyLength = %u", keyLength); + Utils::dump(1U, "P25OTARService::cryptKMM(), Key", kekKey, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); + } + } + else { + if (!m_allowNoUKEKRekey) { + LogError(LOG_P25, P25_KMM_STR", %s, aborting rekey, no KEK to keyload with, llId = %u, RSI = %u", outKmm.toString().c_str(), + outKmm.getSrcLLId(), outKmm.getDstLLId()); + return nullptr; + } else { + LogWarning(LOG_P25, P25_KMM_STR", %s, WARNING WARNING WARNING, rekey without KEK enabled, WARNING WARNING WARNING, keys transmitted in the clear, llId = %u, RSI = %u", outKmm.toString().c_str(), + outKmm.getSrcLLId(), outKmm.getDstLLId()); + } + } + + outKmm.setDecryptInfoFmt(KMM_DECRYPT_INSTRUCT_NONE); + outKmm.setSrcLLId(WUID_FNE); + outKmm.setDstLLId(kmmRSI); + + outKmm.setMI(mi); + + outKmm.setAlgId(kekAlgId); + outKmm.setKId(kekKId); + + KeysetItem ks; + ks.keysetId(1U); + ks.algId(ALGO_AES_256); // we currently can only OTAR AES256 keys + if (kekAlgId != P25DEF::ALGO_UNENCRYPT) + ks.keyLength(P25DEF::MAX_WRAPPED_ENC_KEY_LENGTH_BYTES); + else + ks.keyLength(P25DEF::MAX_ENC_KEY_LENGTH_BYTES); + + for (EKCKeyItem keyItem : m_network->m_cryptoLookup->keys()) { + if (keyItem.algId() != ALGO_AES_256) { + LogWarning(LOG_P25, P25_KMM_STR", %s, ignoring kId = %u, is not an AES-256 key, llId = %u, RSI = %u", outKmm.toString().c_str(), + keyItem.kId(), outKmm.getSrcLLId(), outKmm.getDstLLId()); + continue; + } + + uint8_t key[P25DEF::MAX_WRAPPED_ENC_KEY_LENGTH_BYTES]; + ::memset(key, 0x00U, P25DEF::MAX_WRAPPED_ENC_KEY_LENGTH_BYTES); + uint8_t keyLength = keyItem.getKey(key); + + // encrypt key + UInt8Array wrappedKey = nullptr; + if (kekAlgId != P25DEF::ALGO_UNENCRYPT) { + wrappedKey = crypto.cryptAES_TEK(kekKey, key, keyLength); + keyLength = P25DEF::MAX_WRAPPED_ENC_KEY_LENGTH_BYTES; + } else { + wrappedKey = std::make_unique(P25DEF::MAX_WRAPPED_ENC_KEY_LENGTH_BYTES); + ::memcpy(wrappedKey.get(), key, keyLength); + } + + p25::kmm::KeyItem ki = p25::kmm::KeyItem(); + ki.keyFormat(KEY_FORMAT_TEK); + ki.kId((uint16_t)keyItem.kId()); + ki.sln((uint16_t)keyItem.sln()); + ki.setKey(wrappedKey.get(), keyLength); + + ks.push_back(ki); + } + + if (ks.keys().size() == 0U) { + LogWarning(LOG_P25, P25_KMM_STR", %s, aborting rekey, no keys to keyload, llId = %u, RSI = %u", outKmm.toString().c_str(), + outKmm.getSrcLLId(), outKmm.getDstLLId()); + return nullptr; + } + + std::vector keysets; + keysets.push_back(ks); + + outKmm.setKeysets(keysets); + outKmm.generateMAC(kekAlgId, kekKId); // this isn't right... + + if (m_verbose) { + LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, RSI = %u, keyCount = %u", outKmm.toString().c_str(), + outKmm.getSrcLLId(), outKmm.getDstLLId(), ks.keys().size()); + } + if (payloadSize != nullptr) - *payloadSize = KMM_REGISTRATION_CMD_LENGTH; + *payloadSize = outKmm.length(); - UInt8Array kmmFrame = std::make_unique(KMM_REGISTRATION_CMD_LENGTH); + UInt8Array kmmFrame = std::make_unique(outKmm.length()); + outKmm.encode(kmmFrame.get()); + return kmmFrame; +} - uint8_t buffer[KMM_REGISTRATION_CMD_LENGTH]; +/* Helper used to return a Registration-Command KMM to the calling SU. */ + +UInt8Array P25OTARService::write_KMM_Reg_Command(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize) +{ KMMRegistrationCommand outKmm = KMMRegistrationCommand(); outKmm.setSrcLLId(WUID_FNE); outKmm.setDstLLId(kmmRSI); @@ -448,9 +570,11 @@ UInt8Array P25OTARService::write_KMM_Reg_Command(uint32_t llId, uint32_t kmmRSI, outKmm.getSrcLLId(), outKmm.getDstLLId()); } - outKmm.encode(buffer); + if (payloadSize != nullptr) + *payloadSize = outKmm.length(); - ::memcpy(kmmFrame.get(), buffer, KMM_REGISTRATION_CMD_LENGTH); + UInt8Array kmmFrame = std::make_unique(outKmm.length()); + outKmm.encode(kmmFrame.get()); return kmmFrame; } @@ -458,12 +582,6 @@ UInt8Array P25OTARService::write_KMM_Reg_Command(uint32_t llId, uint32_t kmmRSI, UInt8Array P25OTARService::write_KMM_Dereg_Response(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize) { - if (payloadSize != nullptr) - *payloadSize = KMM_DEREGISTRATION_RSP_LENGTH; - - UInt8Array kmmFrame = std::make_unique(KMM_DEREGISTRATION_RSP_LENGTH); - - uint8_t buffer[KMM_DEREGISTRATION_RSP_LENGTH]; KMMDeregistrationResponse outKmm = KMMDeregistrationResponse(); outKmm.setSrcLLId(WUID_FNE); outKmm.setDstLLId(kmmRSI); @@ -474,9 +592,11 @@ UInt8Array P25OTARService::write_KMM_Dereg_Response(uint32_t llId, uint32_t kmmR outKmm.getSrcLLId(), outKmm.getDstLLId()); } - outKmm.encode(buffer); + if (payloadSize != nullptr) + *payloadSize = outKmm.length(); - ::memcpy(kmmFrame.get(), buffer, KMM_DEREGISTRATION_RSP_LENGTH); + UInt8Array kmmFrame = std::make_unique(outKmm.length()); + outKmm.encode(kmmFrame.get()); return kmmFrame; } @@ -484,12 +604,6 @@ UInt8Array P25OTARService::write_KMM_Dereg_Response(uint32_t llId, uint32_t kmmR UInt8Array P25OTARService::write_KMM_NoService(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize) { - if (payloadSize != nullptr) - *payloadSize = KMM_NO_SERVICE_LENGTH; - - UInt8Array kmmFrame = std::make_unique(KMM_NO_SERVICE_LENGTH); - - uint8_t buffer[KMM_NO_SERVICE_LENGTH]; KMMNoService outKmm = KMMNoService(); outKmm.setSrcLLId(WUID_FNE); outKmm.setDstLLId(kmmRSI); @@ -499,9 +613,11 @@ UInt8Array P25OTARService::write_KMM_NoService(uint32_t llId, uint32_t kmmRSI, u outKmm.getSrcLLId(), outKmm.getDstLLId()); } - outKmm.encode(buffer); - - ::memcpy(kmmFrame.get(), buffer, KMM_NO_SERVICE_LENGTH); + if (payloadSize != nullptr) + *payloadSize = outKmm.length(); + + UInt8Array kmmFrame = std::make_unique(outKmm.length()); + outKmm.encode(kmmFrame.get()); return kmmFrame; } @@ -509,12 +625,6 @@ UInt8Array P25OTARService::write_KMM_NoService(uint32_t llId, uint32_t kmmRSI, u UInt8Array P25OTARService::write_KMM_Zeroize(uint32_t llId, uint32_t kmmRSI, uint32_t* payloadSize) { - if (payloadSize != nullptr) - *payloadSize = KMM_ZEROIZE_LENGTH; - - UInt8Array kmmFrame = std::make_unique(KMM_ZEROIZE_LENGTH); - - uint8_t buffer[KMM_ZEROIZE_LENGTH]; KMMZeroize outKmm = KMMZeroize(); outKmm.setSrcLLId(WUID_FNE); outKmm.setDstLLId(kmmRSI); @@ -524,9 +634,11 @@ UInt8Array P25OTARService::write_KMM_Zeroize(uint32_t llId, uint32_t kmmRSI, uin outKmm.getSrcLLId(), outKmm.getDstLLId()); } - outKmm.encode(buffer); + if (payloadSize != nullptr) + *payloadSize = outKmm.length(); - ::memcpy(kmmFrame.get(), buffer, KMM_ZEROIZE_LENGTH); + UInt8Array kmmFrame = std::make_unique(outKmm.length()); + outKmm.encode(kmmFrame.get()); return kmmFrame; } diff --git a/src/fne/network/P25OTARService.h b/src/fne/network/P25OTARService.h index 0697ff15..0a8683c8 100644 --- a/src/fne/network/P25OTARService.h +++ b/src/fne/network/P25OTARService.h @@ -104,6 +104,8 @@ namespace network FNENetwork* m_network; network::callhandler::packetdata::P25PacketData* m_packetData; + bool m_allowNoUKEKRekey; + bool m_debug; bool m_verbose; @@ -136,6 +138,16 @@ namespace network */ UInt8Array processKMM(const uint8_t* data, uint32_t len, uint32_t llId, bool encrypted, uint32_t* payloadSize); + /** + * @brief Helper used to return a Rekey-Command KMM to the calling SU. + * @param llId Logical Link Address. + * @param kmmRSI KMM Radio Set Identifier. + * @param flags Hello KMM flags. + * @param[out] payloadSize Size of the returned KMM payload. + * @returns UInt8Array Buffer containing the processed KMM frame (if any). + */ + UInt8Array write_KMM_Rekey_Command(uint32_t llId, uint32_t kmmRSI, uint8_t flags, uint32_t* payloadSize); + /** * @brief Helper used to return a Registration-Command KMM to the calling SU. * @param llId Logical Link Address. diff --git a/tests/crypto/P25_KEK_Crypto_Test.cpp b/tests/crypto/P25_KEK_Crypto_Test.cpp new file mode 100644 index 00000000..bc8db724 --- /dev/null +++ b/tests/crypto/P25_KEK_Crypto_Test.cpp @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/Crypto.h" +#include "common/Log.h" +#include "common/Utils.h" + +#include +#include +#include + +TEST_CASE("AES", "[KEK Crypto Test]") { + SECTION("P25_AES_KEK_Crypto_Test") { + bool failed = false; + + INFO("P25 AES256 KEK Crypto Test"); + + srand((unsigned int)time(NULL)); + + // Encrypted Key Frame + uint8_t testWrappedKeyFrame[40U] = + { + 0x80, 0x28, 0x9C, 0xF6, 0x35, 0xFB, 0x68, 0xD3, 0x45, 0xD3, 0x4F, 0x62, 0xEF, 0x06, 0x3B, 0xA4, + 0xE0, 0x5C, 0xAE, 0x47, 0x56, 0xE7, 0xD3, 0x04, 0x46, 0xD1, 0xF0, 0x7C, 0x6E, 0xB4, 0xE9, 0xE0, + 0x84, 0x09, 0x45, 0x37, 0x23, 0x72, 0xFB, 0x80 + }; + + // key (K) + uint8_t K[32U] = + { + 0x49, 0x40, 0x02, 0xBF, 0x16, 0x31, 0x32, 0xA4, 0x21, 0xFB, 0xEF, 0x11, 0x7F, 0x98, 0x5A, 0x0C, + 0xAA, 0xDD, 0xC2, 0x50, 0xA4, 0xC2, 0x19, 0x47, 0xD5, 0x93, 0xE6, 0xC0, 0x67, 0xDE, 0x40, 0x2C + }; + + // message + uint8_t message[32U] = + { + 0x2A, 0x19, 0x38, 0xCD, 0x0B, 0x6B, 0x6B, 0xD0, 0xB7, 0x74, 0x56, 0x92, 0xFE, 0x19, 0x14, 0xF0, + 0x38, 0x76, 0x61, 0x2F, 0xC2, 0x9D, 0x57, 0x77, 0x89, 0xA6, 0x2F, 0x65, 0xFA, 0x05, 0xEF, 0x83 + }; + + Utils::dump(2U, "KEK_Crypto_Test, Key", K, 32); + Utils::dump(2U, "KEK_Crypto_Test, Message", message, 32); + + p25::crypto::P25Crypto crypto; + + UInt8Array wrappedKey = crypto.cryptAES_TEK(K, message, 32U); + + Utils::dump(2U, "KEK_Crypto_Test, Wrapped", wrappedKey.get(), 40); + + for (uint32_t i = 0; i < 40U; i++) { + if (wrappedKey[i] != testWrappedKeyFrame[i]) { + ::LogDebug("T", "P25_AES_KEK_Crypto_Test, WRAPPED INVALID AT IDX %d\n", i); + failed = true; + } + } + + UInt8Array unwrappedKey = crypto.decryptAES_TEK(K, wrappedKey.get(), 40U); + + Utils::dump(2U, "KEK_Crypto_Test, Unwrapped", unwrappedKey.get(), 40); + + for (uint32_t i = 0; i < 32U; i++) { + if (unwrappedKey[i] != message[i]) { + ::LogDebug("T", "P25_AES_KEK_Crypto_Test, UNWRAPPED INVALID AT IDX %d\n", i); + failed = true; + } + } + + REQUIRE(failed==false); + } +} diff --git a/tests/crypto/RC4_Crypto_Test.cpp b/tests/crypto/RC4_Crypto_Test.cpp index 2208f5c6..21b64cef 100644 --- a/tests/crypto/RC4_Crypto_Test.cpp +++ b/tests/crypto/RC4_Crypto_Test.cpp @@ -38,7 +38,7 @@ TEST_CASE("RC4", "[Crypto Test]") { { 0x90, 0x56, 0x00, 0x00, 0x2D, 0x75, 0xE6, 0x8D, 0x00, 0x89, 0x69, 0xCF, 0x00, 0xFE, 0x00, 0x04, 0x4F, 0xC7, 0x60, 0xFF, 0x30, 0x3E, 0x2B, 0xAD, 0x00, 0x89, 0x69, 0xCF, 0x00, 0x00, 0x00, 0x08, - 0x52, 0x50, 0x54, 0x4C, 0x00, 0x89, 0x69, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x52, 0x50, 0x54, 0x4C, 0x00, 0x89, 0x69, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // perform crypto From 5c8fa2976c0859c015b674e1eec36d5fd393cf46 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 5 Nov 2025 18:16:41 -0500 Subject: [PATCH 128/200] begin work on generating KMM CMAC for message authentication; --- src/common/p25/Crypto.cpp | 206 +++++++++++++++++++++++++++-- src/common/p25/Crypto.h | 19 +++ src/common/p25/kmm/KMMFrame.cpp | 24 +--- src/common/p25/kmm/KMMFrame.h | 18 ++- src/fne/network/P25OTARService.cpp | 1 - tests/crypto/P25_MAC_CMAC_Test.cpp | 119 +++++++++++++++++ 6 files changed, 344 insertions(+), 43 deletions(-) create mode 100644 tests/crypto/P25_MAC_CMAC_Test.cpp diff --git a/src/common/p25/Crypto.cpp b/src/common/p25/Crypto.cpp index f36b3120..58c450bb 100644 --- a/src/common/p25/Crypto.cpp +++ b/src/common/p25/Crypto.cpp @@ -19,6 +19,9 @@ #if defined(ENABLE_SSL) #include #include +#include +#include +#include #endif // ENABLE_SSL using namespace ::crypto; @@ -32,6 +35,7 @@ using namespace p25::crypto; // Constants // --------------------------------------------------------------------------- +#define TEMP_BUFFER_LEN 1024U #define MAX_ENC_KEY_LENGTH_BYTES 32U // --------------------------------------------------------------------------- @@ -211,27 +215,29 @@ UInt8Array P25Crypto::cryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tek }; int len; - uint8_t tempBuf[1024U]; - ::memset(tempBuf, 0x00U, 1024U); + uint8_t tempBuf[TEMP_BUFFER_LEN]; + ::memset(tempBuf, 0x00U, TEMP_BUFFER_LEN); + + ERR_load_crypto_strings(); EVP_CIPHER_CTX* ctx; // create and initialize a cipher context if (!(ctx = EVP_CIPHER_CTX_new())) { - LogError(LOG_P25, "EVP_CIPHER_CTX_new(), failed to initialize cipher context"); + LogError(LOG_P25, "EVP_CIPHER_CTX_new(), failed to initialize cipher context: %s", ERR_error_string(ERR_get_error(), NULL)); return nullptr; } // initialize the wrapper context with AES-256-WRAP if (EVP_EncryptInit_ex(ctx, EVP_aes_256_wrap(), NULL, kek, iv) != 1) { - LogError(LOG_P25, "EVP_EncryptInit_ex(), failed to initialize cipher wrapping context"); + LogError(LOG_P25, "EVP_EncryptInit_ex(), failed to initialize cipher wrapping context: %s", ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_free(ctx); return nullptr; } // perform the wrapping operation if (EVP_EncryptUpdate(ctx, tempBuf, &len, tek, tekLen) != 1) { - LogError(LOG_P25, "EVP_EncryptUpdate(), failed to wrap TEK"); + LogError(LOG_P25, "EVP_EncryptUpdate(), failed to wrap TEK: %s", ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_free(ctx); return nullptr; } @@ -239,7 +245,7 @@ UInt8Array P25Crypto::cryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tek // finalize the wrapping (no output, just padding) int tempLen; if (EVP_EncryptFinal_ex(ctx, tempBuf + len, &tempLen) != 1) { - LogError(LOG_P25, "EVP_EncryptFinal_ex(), failed to finalize wrapping TEK"); + LogError(LOG_P25, "EVP_EncryptFinal_ex(), failed to finalize wrapping TEK: %s", ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_free(ctx); return nullptr; } @@ -269,27 +275,29 @@ UInt8Array P25Crypto::decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t t }; int len; - uint8_t tempBuf[1024U]; - ::memset(tempBuf, 0x00U, 1024U); + uint8_t tempBuf[TEMP_BUFFER_LEN]; + ::memset(tempBuf, 0x00U, TEMP_BUFFER_LEN); + + ERR_load_crypto_strings(); EVP_CIPHER_CTX* ctx; // create and initialize a cipher context if (!(ctx = EVP_CIPHER_CTX_new())) { - LogError(LOG_P25, "EVP_CIPHER_CTX_new(), failed to initialize cipher context"); + LogError(LOG_P25, "EVP_CIPHER_CTX_new(), failed to initialize cipher context: %s", ERR_error_string(ERR_get_error(), NULL)); return nullptr; } // initialize the wrapper context with AES-256-WRAP if (EVP_DecryptInit_ex(ctx, EVP_aes_256_wrap(), NULL, kek, iv) != 1) { - LogError(LOG_P25, "EVP_DecryptInit_ex(), failed to initialize cipher wrapping context"); + LogError(LOG_P25, "EVP_DecryptInit_ex(), failed to initialize cipher wrapping context: %s", ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_free(ctx); return nullptr; } // perform the wrapping operation if (EVP_DecryptUpdate(ctx, tempBuf, &len, tek, tekLen) != 1) { - LogError(LOG_P25, "EVP_DecryptUpdate(), failed to unwrap TEK"); + LogError(LOG_P25, "EVP_DecryptUpdate(), failed to unwrap TEK: %s", ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_free(ctx); return nullptr; } @@ -297,7 +305,7 @@ UInt8Array P25Crypto::decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t t // finalize the wrapping (no output, just padding) int tempLen; if (EVP_DecryptFinal_ex(ctx, tempBuf + len, &tempLen) != 1) { - LogError(LOG_P25, "EVP_DecryptFinal_ex(), failed to finalize unwrapping TEK"); + LogError(LOG_P25, "EVP_DecryptFinal_ex(), failed to finalize unwrapping TEK: %s", ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_free(ctx); return nullptr; } @@ -316,6 +324,180 @@ UInt8Array P25Crypto::decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t t #endif // ENABLE_SSL } +/* Helper to generate a P25 KMM CMAC MAC key with the given AES-256 KEK. */ + +UInt8Array P25Crypto::cryptAES_KMM_CMAC_KDF(const uint8_t* kek, const uint8_t* msg, uint8_t msgLen, bool hasMN) +{ +#if defined(ENABLE_SSL) + // O T A R M A C + uint8_t label[8U] = { 0x4FU, 0x54U, 0x41U, 0x52U, 0x20U, 0x4DU, 0x41U, 0x43U }; + + uint8_t context[12U]; + ::memset(context, 0x00U, 12U); + + uint8_t contextLen = 0U; + if (hasMN) { + ::memcpy(context, msg, 12U); + contextLen = 12U; + } else { + ::memcpy(context, msg, 10U); + contextLen = 10U; + } + + /** DEBUG REMOVEME */ + Utils::dump(2U, "KEK", kek, MAX_ENC_KEY_LENGTH_BYTES); + Utils::dump(2U, "Label", label, 8U); + Utils::dump(2U, "Context", context, contextLen); + /** DEBUG REMOVEME */ + + size_t len; + uint8_t tempBuf[TEMP_BUFFER_LEN]; + ::memset(tempBuf, 0x00U, TEMP_BUFFER_LEN); + + ERR_load_crypto_strings(); + + // create a library context (required for OpenSSL 3.0+) + OSSL_LIB_CTX* libCtx = OSSL_LIB_CTX_new(); + if (!libCtx) { + LogError(LOG_P25, "OSSL_LIB_CTX_new(), failed to finalize OpenSSL: %s", ERR_error_string(ERR_get_error(), NULL)); + return nullptr; + } + + // load the KDF algorithm + EVP_KDF* kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL); + if (!kdf) { + LogError(LOG_P25, "EVP_KDF_fetch(), failed to load OpenSSL KDF algorithm: %s", ERR_error_string(ERR_get_error(), NULL)); + OSSL_LIB_CTX_free(libCtx); + return nullptr; + } + + // create a context for the MAC operation + EVP_KDF_CTX* ctx = EVP_KDF_CTX_new(kdf); + if (!ctx) { + LogError(LOG_P25, "EVP_KDF_CTX_new(), failed to create a OpenSSL KDF context: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_KDF_free(kdf); + OSSL_LIB_CTX_free(libCtx); + return nullptr; + } + + // set the cipher to AES-256-CBC and initialize the MAC operation + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC, "HMAC", 0), + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, "SHA-256", 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void*)kek, MAX_ENC_KEY_LENGTH_BYTES), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void*)label, 8U), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void*)context, contextLen), + OSSL_PARAM_END + }; + + uint8_t macKey[MAX_ENC_KEY_LENGTH_BYTES]; + ::memset(macKey, 0x00U, MAX_ENC_KEY_LENGTH_BYTES); + + // derive MAC key + if (EVP_KDF_derive(ctx, macKey, MAX_ENC_KEY_LENGTH_BYTES, params) <= 0) { + LogError(LOG_P25, "EVP_KDF_derive(), failed to derive MAC key: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_KDF_CTX_free(ctx); + EVP_KDF_free(kdf); + OSSL_LIB_CTX_free(libCtx); + return nullptr; + } + + /** DEBUG REMOVEME */ + Utils::dump(2U, "macKey", macKey, MAX_ENC_KEY_LENGTH_BYTES); + /** DEBUG REMOVEME */ + + UInt8Array wrappedKey = std::unique_ptr(new uint8_t[MAX_ENC_KEY_LENGTH_BYTES]); + ::memset(wrappedKey.get(), 0x00U, MAX_ENC_KEY_LENGTH_BYTES); + ::memcpy(wrappedKey.get(), macKey, MAX_ENC_KEY_LENGTH_BYTES); + + return wrappedKey; +#else + LogError(LOG_P25, "No OpenSSL, TEK encryption is not supported!"); + return nullptr; +#endif // ENABLE_SSL +} + +/* Helper to generate a P25 KMM CMAC with the given AES-256 CMAC key. */ + +UInt8Array P25Crypto::cryptAES_KMM_CMAC(const uint8_t* macKey, const uint8_t* msg, uint8_t msgLen) +{ +#if defined(ENABLE_SSL) + size_t len; + uint8_t tempBuf[TEMP_BUFFER_LEN]; + ::memset(tempBuf, 0x00U, TEMP_BUFFER_LEN); + + // create a library context (required for OpenSSL 3.0+) + OSSL_LIB_CTX* libCtx = OSSL_LIB_CTX_new(); + if (!libCtx) { + LogError(LOG_P25, "OSSL_LIB_CTX_new(), failed to finalize OpenSSL: %s", ERR_error_string(ERR_get_error(), NULL)); + return nullptr; + } + + // fetch the CMAC implementation + EVP_MAC* hmac = EVP_MAC_fetch(libCtx, "CMAC", NULL); + if (!hmac) { + LogError(LOG_P25, "EVP_MAC_fetch(), failed to fetch OpenSSL CMAC: %s", ERR_error_string(ERR_get_error(), NULL)); + OSSL_LIB_CTX_free(libCtx); + return nullptr; + } + + // create a context for the MAC operation + EVP_MAC_CTX* ctx = EVP_MAC_CTX_new(hmac); + if (!ctx) { + LogError(LOG_P25, "EVP_MAC_CTX_new(), failed to create a OpenSSL CMAC context: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MAC_free(hmac); + OSSL_LIB_CTX_free(libCtx); + return nullptr; + } + + // set the cipher to AES-256-CBC and initialize the MAC operation + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, "AES-256-CBC", 0), + OSSL_PARAM_END + }; + + // initialize the MAC operation + if (!EVP_MAC_init(ctx, macKey, MAX_ENC_KEY_LENGTH_BYTES, params)) { + LogError(LOG_P25, "EVP_MAC_init(), failed to initialize the AES-256-CBC MAC operation: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MAC_CTX_free(ctx); + EVP_MAC_free(hmac); + OSSL_LIB_CTX_free(libCtx); + return nullptr; + } + + // provide the message data to be authenticated + if (!EVP_MAC_update(ctx, msg, msgLen)) { + LogError(LOG_P25, "EVP_MAC_init(), failed to set message data to authenticate: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MAC_CTX_free(ctx); + EVP_MAC_free(hmac); + OSSL_LIB_CTX_free(libCtx); + return nullptr; + } + + // finalize the MAC operation and get the MAC value + if (!EVP_MAC_final(ctx, tempBuf, &len, TEMP_BUFFER_LEN)) { + LogError(LOG_P25, "EVP_MAC_init(), failed to generate MAC: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MAC_CTX_free(ctx); + EVP_MAC_free(hmac); + OSSL_LIB_CTX_free(libCtx); + return nullptr; + } + + /** DEBUG REMOVEME */ + Utils::dump(2U, "tempBuf", tempBuf, len); + /** DEBUG REMOVEME */ + + UInt8Array wrappedKey = std::unique_ptr(new uint8_t[len]); + ::memset(wrappedKey.get(), 0x00U, len); + ::memcpy(wrappedKey.get(), tempBuf, len); + + return wrappedKey; +#else + LogError(LOG_P25, "No OpenSSL, TEK encryption is not supported!"); + return nullptr; +#endif // ENABLE_SSL +} + /* Helper to crypt IMBE audio using AES-256. */ void P25Crypto::cryptAES_IMBE(uint8_t* imbe, DUID::E duid) diff --git a/src/common/p25/Crypto.h b/src/common/p25/Crypto.h index 1f9a69b3..14ec82fa 100644 --- a/src/common/p25/Crypto.h +++ b/src/common/p25/Crypto.h @@ -91,6 +91,25 @@ namespace p25 * @returns UInt8Array Buffer containing the decrypted TEK. */ UInt8Array decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tekLen); + /** + * @brief Helper to generate a P25 KMM CMAC MAC key with the given AES-256 KEK. + * @note This assumes AES-256 KEK and will not work with other key types. + * @param kek Key Encryption Key + * @param msg KMM Message + * @param msgLen Message Length + * @param hasMN Message has a message number. + * @returns UInt8Array Buffer containing the derived MAC key. + */ + UInt8Array cryptAES_KMM_CMAC_KDF(const uint8_t* kek, const uint8_t* msg, uint8_t msgLen, bool hasMN); + /** + * @brief Helper to generate a P25 KMM CMAC with the given AES-256 CMAC key. + * @note This assumes AES-256 KEK and will not work with other key types. + * @param macKey CMAC Derived Key + * @param msg KMM Message + * @param msgLen Message Length + * @returns UInt8Array Buffer containing the derived MAC key. + */ + UInt8Array cryptAES_KMM_CMAC(const uint8_t* macKey, const uint8_t* msg, uint8_t msgLen); /** * @brief Helper to crypt P25 IMBE audio using AES-256. diff --git a/src/common/p25/kmm/KMMFrame.cpp b/src/common/p25/kmm/KMMFrame.cpp index 989253b7..e00165fe 100644 --- a/src/common/p25/kmm/KMMFrame.cpp +++ b/src/common/p25/kmm/KMMFrame.cpp @@ -36,14 +36,14 @@ KMMFrame::KMMFrame() : m_messageLength(KMM_FRAME_LENGTH), m_respKind(KMM_ResponseKind::NONE), m_macType(KMM_MAC::NO_MAC), + m_macAlgId(ALGO_UNENCRYPT), + m_macKId(0U), m_messageNumber(0U), m_dstLlId(0U), m_srcLlId(0U), m_complete(true), m_messageFullLength(0U), m_bodyOffset(0U), - m_macAlgId(ALGO_UNENCRYPT), - m_macKId(0U), m_mac(nullptr) { m_mac = new uint8_t[P25DEF::KMM_AES_MAC_LENGTH]; @@ -58,24 +58,6 @@ KMMFrame::~KMMFrame() delete[] m_mac; } -/* Helper to generate a message authentication code for a KMM frame. */ - -void KMMFrame::generateMAC(uint8_t algoId, uint16_t kId) -{ - m_macAlgId = algoId; - m_macKId = kId; - - m_macType = KMM_MAC::ENH_MAC; - m_messageLength += P25DEF::KMM_AES_MAC_LENGTH; - m_messageFullLength = m_messageLength + 3U; - - ::memset(m_mac, 0x00U, P25DEF::KMM_AES_MAC_LENGTH); - - /* - ** TODO - generate actual MAC - */ -} - /* Returns a string that represents the current KMM frame. */ std::string KMMFrame::toString() @@ -211,5 +193,7 @@ void KMMFrame::copy(const KMMFrame& data) m_complete = data.m_complete; m_messageNumber = data.m_messageNumber; + m_macAlgId = data.m_macAlgId; + m_macKId = data.m_macKId; m_macType = data.m_macType; } diff --git a/src/common/p25/kmm/KMMFrame.h b/src/common/p25/kmm/KMMFrame.h index 646386a1..612a71aa 100644 --- a/src/common/p25/kmm/KMMFrame.h +++ b/src/common/p25/kmm/KMMFrame.h @@ -94,14 +94,6 @@ namespace p25 */ virtual void encode(uint8_t* data) = 0; - /** - * @brief Helper to generate a message authentication code for a KMM frame. - * @note DVM only supports generating AES-256 CBC-MAC. - * @param algoId Algorithm ID. - * @param kId Key ID. - */ - void generateMAC(uint8_t algoId, uint16_t kId); - /** * @brief Returns a string that represents the current KMM frame. * @returns std::string String representation of the KMM frame. @@ -128,6 +120,14 @@ namespace p25 * @brief Message Authentication Type. */ DECLARE_PROTECTED_PROPERTY(uint8_t, macType, MACType); + /** + * @brief Message Authentication Algorithm ID. + */ + DECLARE_PROTECTED_PROPERTY(uint8_t, macAlgId, MACAlgId); + /** + * @brief Message Authentication Key ID. + */ + DECLARE_PROTECTED_PROPERTY(uint16_t, macKId, MACKId); /** * @brief Message Number. */ @@ -151,8 +151,6 @@ namespace p25 uint16_t m_messageFullLength; //!< Complete length of entire frame in bytes. uint8_t m_bodyOffset; //!< Offset to KMM frame body data. - uint8_t m_macAlgId; - uint16_t m_macKId; uint8_t* m_mac; /** diff --git a/src/fne/network/P25OTARService.cpp b/src/fne/network/P25OTARService.cpp index 62b0bb22..8a8921de 100644 --- a/src/fne/network/P25OTARService.cpp +++ b/src/fne/network/P25OTARService.cpp @@ -542,7 +542,6 @@ UInt8Array P25OTARService::write_KMM_Rekey_Command(uint32_t llId, uint32_t kmmRS keysets.push_back(ks); outKmm.setKeysets(keysets); - outKmm.generateMAC(kekAlgId, kekKId); // this isn't right... if (m_verbose) { LogInfoEx(LOG_P25, P25_KMM_STR ", %s, llId = %u, RSI = %u, keyCount = %u", outKmm.toString().c_str(), diff --git a/tests/crypto/P25_MAC_CMAC_Test.cpp b/tests/crypto/P25_MAC_CMAC_Test.cpp new file mode 100644 index 00000000..d8a11312 --- /dev/null +++ b/tests/crypto/P25_MAC_CMAC_Test.cpp @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/Crypto.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25::defines; + +#include +#include +#include + +TEST_CASE("AES", "[AES256 MAC CMAC Test]") { + SECTION("P25_MAC_CMAC_Crypto_Test") { + bool failed = false; + + INFO("P25 AES256 MAC CMAC Test"); + + srand((unsigned int)time(NULL)); + + // MAC TEK + uint8_t macTek[] = + { + 0x16, 0x85, 0x62, 0x45, 0x3B, 0x3E, 0x7F, 0x61, 0x8D, 0x68, 0xB3, 0x87, 0xE0, 0xB9, 0x97, 0xE1, + 0xFB, 0x0F, 0x26, 0x4F, 0xA8, 0x3B, 0x74, 0xE4, 0x3B, 0x17, 0x29, 0x17, 0xBD, 0x39, 0x33, 0x9F + }; + + // expected CMAC key + uint8_t expectedCMAC[] = + { + 0x5F, 0xB2, 0x91, 0xD0, 0x9E, 0xE3, 0x99, 0x1E, 0x13, 0x1A, 0x04, 0xB0, 0xE3, 0xA0, 0xBF, 0x58, + 0xB4, 0xA1, 0xCE, 0x46, 0x10, 0x48, 0xEB, 0x14, 0xB4, 0x97, 0xAE, 0x95, 0x22, 0xD0, 0x0D, 0x31 + }; + + // data block + uint8_t dataBlock[] = + { + 0x1E, 0x00, 0x4D, 0xA8, 0x64, 0x3B, 0xA8, 0x71, 0x2B, 0x1D, 0x17, 0x72, 0x00, 0x84, 0x50, 0xBC, + 0x01, 0x00, 0x01, 0x84, 0x28, 0x01, 0x00, 0x00, 0x00, 0x49, 0x83, 0x80, 0x28, 0x9C, 0xF6, 0x35, + 0xFB, 0x68, 0xD3, 0x45, 0xD3, 0x4F, 0x62, 0xEF, 0x06, 0x3B, 0xA4, 0xE0, 0x5C, 0xAE, 0x47, 0x56, + 0xE7, 0xD3, 0x04, 0x46, 0xD1, 0xF0, 0x7C, 0x6E, 0xB4, 0xE9, 0xE0, 0x84, 0x09, 0x45, 0x37, 0x23, + 0x72, 0xFB, 0x80, 0x21, 0x85, 0x22, 0x33, 0x41, 0xD9, 0x8A, 0x97, 0x08, 0x84, 0x2F, 0x62, 0x41 + }; + + Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, DataBlock", dataBlock, 80U); + + uint16_t fullLength = 0U; + uint16_t messageLength = GET_UINT16(dataBlock, 1U); + fullLength = messageLength + 3U; + bool hasMN = ((dataBlock[3U] >> 4U) & 0x03U) == 0x02U; + uint8_t macType = (dataBlock[3U] >> 2U) & 0x03U; + + ::LogInfoEx("T", "P25_MAC_CMAC_Crypto_Test, messageLength = %u, hasMN = %u, macType = $%02X", messageLength, hasMN, macType); + + switch (macType) { + case KMM_MAC::DES_MAC: + { + uint8_t macLength = 4U; + uint8_t mac[macLength]; + ::memset(mac, 0x00U, macLength); + + uint8_t macAlgId = dataBlock[fullLength - 4U]; + uint16_t macKId = GET_UINT16(dataBlock, fullLength - 3U); + + ::memcpy(mac, dataBlock + fullLength - (macLength + 5U), macLength); + + ::LogInfoEx("T", "P25_MAC_CMAC_Crypto_Test, macAlgId = $%02X, macKId = $%04X", macAlgId, macKId); + Utils::dump(2U, "MAC", mac, macLength); + } + break; + + case KMM_MAC::ENH_MAC: + { + uint8_t macLength = 8U; + uint8_t mac[macLength]; + ::memset(mac, 0x00U, macLength); + + uint8_t macAlgId = dataBlock[fullLength - 4U]; + uint16_t macKId = GET_UINT16(dataBlock, fullLength - 3U); + + ::memcpy(mac, dataBlock + fullLength - (macLength + 5U), macLength); + + ::LogInfoEx("T", "P25_MAC_CMAC_Crypto_Test, macAlgId = $%02X, macKId = $%04X", macAlgId, macKId); + Utils::dump(2U, "MAC", mac, macLength); + } + break; + + case KMM_MAC::NO_MAC: + break; + + default: + ::LogError(LOG_P25, "P25_MAC_CMAC_Crypto_Test, unknown KMM MAC inventory type value, macType = $%02X", macType); + break; + } + + p25::crypto::P25Crypto crypto; + + UInt8Array macKey = crypto.cryptAES_KMM_CMAC_KDF(macTek, dataBlock, fullLength, true); + Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, CMAC MAC Key", macKey.get(), 32U); + + for (uint32_t i = 0; i < 32U; i++) { + if (macKey[i] != expectedCMAC[i]) { + ::LogDebug("T", "P25_MAC_CMAC_Crypto_Test, INVALID AT IDX %d\n", i); + failed = true; + } + } + + REQUIRE(failed==false); + } +} From a7942467b18803792b86bb786720c3517a0d726d Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 5 Nov 2025 18:21:44 -0500 Subject: [PATCH 129/200] document sections of doc where a test originates test data; --- tests/crypto/P25_KEK_Crypto_Test.cpp | 2 ++ tests/crypto/P25_MAC_CMAC_Test.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/crypto/P25_KEK_Crypto_Test.cpp b/tests/crypto/P25_KEK_Crypto_Test.cpp index bc8db724..e1bc5734 100644 --- a/tests/crypto/P25_KEK_Crypto_Test.cpp +++ b/tests/crypto/P25_KEK_Crypto_Test.cpp @@ -24,6 +24,8 @@ TEST_CASE("AES", "[KEK Crypto Test]") { srand((unsigned int)time(NULL)); + // example data taken from TIA-102.AACA-C-2023 Section 14.3.3 + // Encrypted Key Frame uint8_t testWrappedKeyFrame[40U] = { diff --git a/tests/crypto/P25_MAC_CMAC_Test.cpp b/tests/crypto/P25_MAC_CMAC_Test.cpp index d8a11312..33e5f63d 100644 --- a/tests/crypto/P25_MAC_CMAC_Test.cpp +++ b/tests/crypto/P25_MAC_CMAC_Test.cpp @@ -27,6 +27,8 @@ TEST_CASE("AES", "[AES256 MAC CMAC Test]") { srand((unsigned int)time(NULL)); + // example data taken from TIA-102.AACA-C-2023 Section 14.3.5.1 + // MAC TEK uint8_t macTek[] = { From 945a0348403284c5808628d9f6454b8f45d39bb1 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 5 Nov 2025 23:22:58 -0500 Subject: [PATCH 130/200] begin work on generating CBC-MAC for message authentication; --- src/common/p25/Crypto.cpp | 123 ++++++++++++++++++++++- src/common/p25/Crypto.h | 23 ++++- src/common/p25/P25Defines.h | 5 + src/common/p25/kmm/KMMFrame.cpp | 19 ++-- src/common/p25/kmm/KMMFrame.h | 17 +++- src/common/p25/kmm/KMMRekeyCommand.cpp | 11 +- src/common/p25/kmm/KMMRekeyCommand.h | 2 +- src/common/p25/kmm/KeysetItem.h | 2 +- tests/crypto/AES_Crypto_Test.cpp | 2 +- tests/crypto/AES_LLA_AM1_Test.cpp | 4 +- tests/crypto/AES_LLA_AM2_Test.cpp | 4 +- tests/crypto/AES_LLA_AM3_Test.cpp | 4 +- tests/crypto/AES_LLA_AM4_Test.cpp | 4 +- tests/crypto/P25_KEK_Crypto_Test.cpp | 6 +- tests/crypto/P25_MAC_CBC_Test.cpp | 134 +++++++++++++++++++++++++ tests/crypto/P25_MAC_CMAC_Test.cpp | 15 +-- tests/crypto/RC4_Crypto_Test.cpp | 2 +- tests/edac/CRC_12_Test.cpp | 6 +- tests/edac/CRC_15_Test.cpp | 6 +- tests/edac/CRC_16_Test.cpp | 6 +- tests/edac/CRC_32_Test.cpp | 6 +- tests/edac/CRC_6_Test.cpp | 6 +- tests/edac/CRC_8_Test.cpp | 6 +- tests/edac/CRC_9_Test.cpp | 4 +- tests/edac/CRC_CCITT_161_Test.cpp | 6 +- tests/edac/CRC_CCITT_162_Test.cpp | 6 +- tests/p25/HDU_RS_Test.cpp | 6 +- tests/p25/KMM_Rekey_CBC_Test.cpp | 100 ++++++++++++++++++ tests/p25/KMM_Rekey_CMAC_Test.cpp | 100 ++++++++++++++++++ tests/p25/LDU1_RS_Test.cpp | 6 +- tests/p25/LDU2_RS_Test.cpp | 6 +- 31 files changed, 572 insertions(+), 75 deletions(-) create mode 100644 tests/crypto/P25_MAC_CBC_Test.cpp create mode 100644 tests/p25/KMM_Rekey_CBC_Test.cpp create mode 100644 tests/p25/KMM_Rekey_CMAC_Test.cpp diff --git a/src/common/p25/Crypto.cpp b/src/common/p25/Crypto.cpp index 58c450bb..446b41a3 100644 --- a/src/common/p25/Crypto.cpp +++ b/src/common/p25/Crypto.cpp @@ -211,7 +211,7 @@ UInt8Array P25Crypto::cryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tek #if defined(ENABLE_SSL) // static IV with $A6 pattern defined in TIA-102.AACA-C-2023 13.3 uint8_t iv[] = { - 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U + 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U }; int len; @@ -271,7 +271,7 @@ UInt8Array P25Crypto::decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t t #if defined(ENABLE_SSL) // static IV with $A6 pattern defined in TIA-102.AACA-C-2023 13.3 uint8_t iv[] = { - 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U + 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U }; int len; @@ -324,9 +324,124 @@ UInt8Array P25Crypto::decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t t #endif // ENABLE_SSL } +/* Helper to generate a P25 KMM CBC MAC key with the given AES-256 KEK. */ + +UInt8Array P25Crypto::cryptAES_KMM_CBC_KDF(const uint8_t* kek, const uint8_t* msg, uint16_t msgLen) +{ +#if defined(ENABLE_SSL) + uint8_t iv[] = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U + }; + + uint16_t authLen = msgLen - KMM_AES_MAC_LENGTH; + SET_UINT16(authLen, iv, 14U); + + /** DEBUG REMOVEME */ + Utils::dump(2U, "KEK", kek, MAX_ENC_KEY_LENGTH_BYTES); + Utils::dump(2U, "IV", iv, 16U); + /** DEBUG REMOVEME */ + + int len; + uint8_t tempBuf[TEMP_BUFFER_LEN]; + ::memset(tempBuf, 0x00U, TEMP_BUFFER_LEN); + + ERR_load_crypto_strings(); + + EVP_CIPHER_CTX* ctx; + + // create and initialize a cipher context + if (!(ctx = EVP_CIPHER_CTX_new())) { + LogError(LOG_P25, "EVP_CIPHER_CTX_new(), failed to initialize cipher context: %s", ERR_error_string(ERR_get_error(), NULL)); + return nullptr; + } + + // initialize the wrapper context with AES-256-WRAP + if (EVP_EncryptInit_ex(ctx, EVP_aes_256_wrap(), NULL, kek, iv) != 1) { + LogError(LOG_P25, "EVP_EncryptInit_ex(), failed to initialize cipher wrapping context: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_free(ctx); + return nullptr; + } + + // perform the wrapping operation + if (EVP_EncryptUpdate(ctx, tempBuf, &len, kek, MAX_ENC_KEY_LENGTH_BYTES) != 1) { + LogError(LOG_P25, "EVP_EncryptUpdate(), failed to wrap TEK: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_free(ctx); + return nullptr; + } + + // finalize the wrapping (no output, just padding) + int tempLen; + if (EVP_EncryptFinal_ex(ctx, tempBuf + len, &tempLen) != 1) { + LogError(LOG_P25, "EVP_EncryptFinal_ex(), failed to finalize wrapping TEK: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_free(ctx); + return nullptr; + } + len += tempLen; + + EVP_CIPHER_CTX_free(ctx); + + /** DEBUG REMOVEME */ + LogInfoEx("T", "len = %u", len); + Utils::dump(2U, "macKey", tempBuf, 128U); + /** DEBUG REMOVEME */ + + UInt8Array wrappedKey = std::unique_ptr(new uint8_t[MAX_ENC_KEY_LENGTH_BYTES]); + ::memset(wrappedKey.get(), 0x00U, MAX_ENC_KEY_LENGTH_BYTES); + ::memcpy(wrappedKey.get(), tempBuf + 8U, MAX_ENC_KEY_LENGTH_BYTES); + + return wrappedKey; +#else + LogError(LOG_P25, "No OpenSSL, TEK encryption is not supported!"); + return nullptr; +#endif // ENABLE_SSL +} + +/* Helper to generate a P25 KMM CBC-MAC with the given AES-256 CBC-MAC key. */ + +UInt8Array P25Crypto::cryptAES_KMM_CBC(const uint8_t* macKey, const uint8_t* msg, uint16_t msgLen) +{ +#if defined(ENABLE_SSL) + uint8_t iv[] = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U + }; + + size_t len; + uint8_t tempBuf[TEMP_BUFFER_LEN]; + ::memset(tempBuf, 0x00U, TEMP_BUFFER_LEN); + + AES_KEY aesKey; + + // set the encryption key + if (AES_set_encrypt_key(macKey, 256U, &aesKey) < 0) { + LogError(LOG_P25, "AES_set_encrypt_key(), failed to set AES-256 key: %s", ERR_error_string(ERR_get_error(), NULL)); + return nullptr; + } + + // pad the message as necessary + size_t paddedLen = msgLen + (AES_BLOCK_SIZE - (msgLen % AES_BLOCK_SIZE)); + uint8_t paddedMessage[TEMP_BUFFER_LEN]; + ::memset(paddedMessage, 0x00U, TEMP_BUFFER_LEN); + + ::memcpy(paddedMessage, msg, msgLen - KMM_AES_MAC_LENGTH - 5U); + ::memcpy(paddedMessage + msgLen - KMM_AES_MAC_LENGTH - 5U, msg + msgLen - 5U, 5U); + + // perform AES-CBC encryption + AES_cbc_encrypt(paddedMessage, tempBuf, paddedLen, &aesKey, iv, AES_ENCRYPT); + + UInt8Array wrappedKey = std::unique_ptr(new uint8_t[8U]); + ::memset(wrappedKey.get(), 0x00U, 8U); + ::memcpy(wrappedKey.get(), tempBuf + (msgLen - AES_BLOCK_SIZE), 8U); + + return wrappedKey; +#else + LogError(LOG_P25, "No OpenSSL, TEK encryption is not supported!"); + return nullptr; +#endif // ENABLE_SSL +} + /* Helper to generate a P25 KMM CMAC MAC key with the given AES-256 KEK. */ -UInt8Array P25Crypto::cryptAES_KMM_CMAC_KDF(const uint8_t* kek, const uint8_t* msg, uint8_t msgLen, bool hasMN) +UInt8Array P25Crypto::cryptAES_KMM_CMAC_KDF(const uint8_t* kek, const uint8_t* msg, uint16_t msgLen, bool hasMN) { #if defined(ENABLE_SSL) // O T A R M A C @@ -419,7 +534,7 @@ UInt8Array P25Crypto::cryptAES_KMM_CMAC_KDF(const uint8_t* kek, const uint8_t* m /* Helper to generate a P25 KMM CMAC with the given AES-256 CMAC key. */ -UInt8Array P25Crypto::cryptAES_KMM_CMAC(const uint8_t* macKey, const uint8_t* msg, uint8_t msgLen) +UInt8Array P25Crypto::cryptAES_KMM_CMAC(const uint8_t* macKey, const uint8_t* msg, uint16_t msgLen) { #if defined(ENABLE_SSL) size_t len; diff --git a/src/common/p25/Crypto.h b/src/common/p25/Crypto.h index 14ec82fa..733dcd23 100644 --- a/src/common/p25/Crypto.h +++ b/src/common/p25/Crypto.h @@ -91,6 +91,25 @@ namespace p25 * @returns UInt8Array Buffer containing the decrypted TEK. */ UInt8Array decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tekLen); + /** + * @brief Helper to generate a P25 KMM CBC MAC key with the given AES-256 KEK. + * @note This assumes AES-256 KEK and will not work with other key types. + * @param kek Key Encryption Key + * @param msg KMM Message + * @param msgLen Message Length + * @returns UInt8Array Buffer containing the derived MAC key. + */ + UInt8Array cryptAES_KMM_CBC_KDF(const uint8_t* kek, const uint8_t* msg, uint16_t msgLen); + /** + * @brief Helper to generate a P25 KMM CBC-MAC with the given AES-256 CBC-MAC key. + * @note This assumes AES-256 KEK and will not work with other key types. + * @param macKey CBC-MAC Derived Key + * @param msg KMM Message + * @param msgLen Message Length + * @returns UInt8Array Buffer containing the derived MAC key. + */ + UInt8Array cryptAES_KMM_CBC(const uint8_t* macKey, const uint8_t* msg, uint16_t msgLen); + /** * @brief Helper to generate a P25 KMM CMAC MAC key with the given AES-256 KEK. * @note This assumes AES-256 KEK and will not work with other key types. @@ -100,7 +119,7 @@ namespace p25 * @param hasMN Message has a message number. * @returns UInt8Array Buffer containing the derived MAC key. */ - UInt8Array cryptAES_KMM_CMAC_KDF(const uint8_t* kek, const uint8_t* msg, uint8_t msgLen, bool hasMN); + UInt8Array cryptAES_KMM_CMAC_KDF(const uint8_t* kek, const uint8_t* msg, uint16_t msgLen, bool hasMN); /** * @brief Helper to generate a P25 KMM CMAC with the given AES-256 CMAC key. * @note This assumes AES-256 KEK and will not work with other key types. @@ -109,7 +128,7 @@ namespace p25 * @param msgLen Message Length * @returns UInt8Array Buffer containing the derived MAC key. */ - UInt8Array cryptAES_KMM_CMAC(const uint8_t* macKey, const uint8_t* msg, uint8_t msgLen); + UInt8Array cryptAES_KMM_CMAC(const uint8_t* macKey, const uint8_t* msg, uint16_t msgLen); /** * @brief Helper to crypt P25 IMBE audio using AES-256. diff --git a/src/common/p25/P25Defines.h b/src/common/p25/P25Defines.h index 03a86bfd..f852c765 100644 --- a/src/common/p25/P25Defines.h +++ b/src/common/p25/P25Defines.h @@ -532,6 +532,11 @@ namespace p25 /** @brief KMM Decryption Instruction - Message Indicator */ const uint8_t KMM_DECRYPT_INSTRUCT_MI = 0x40U; + /** @brief KMM MAC Format - CBC-MAC */ + const uint8_t KMM_MAC_FORMAT_CBC = 0x40U; + /** @brief KMM MAC Format - CMAC */ + const uint8_t KMM_MAC_FORMAT_CMAC = 0x41U; + /** @brief KMM Body/Key Format - TEK Included */ const uint8_t KEY_FORMAT_TEK = 0x80U; /** @brief KMM Body/Key Format - KEK Exists */ diff --git a/src/common/p25/kmm/KMMFrame.cpp b/src/common/p25/kmm/KMMFrame.cpp index e00165fe..a9271e7d 100644 --- a/src/common/p25/kmm/KMMFrame.cpp +++ b/src/common/p25/kmm/KMMFrame.cpp @@ -38,6 +38,7 @@ KMMFrame::KMMFrame() : m_macType(KMM_MAC::NO_MAC), m_macAlgId(ALGO_UNENCRYPT), m_macKId(0U), + m_macFormat(0U), m_messageNumber(0U), m_dstLlId(0U), m_srcLlId(0U), @@ -100,8 +101,8 @@ bool KMMFrame::decodeHeader(const uint8_t* data) switch (m_macType) { case KMM_MAC::DES_MAC: { - uint8_t macLength = 4U; - + uint8_t macLength = P25DEF::KMM_DES_MAC_LENGTH; + m_macAlgId = data[m_messageFullLength - 4U]; m_macKId = GET_UINT16(data, m_messageFullLength - 3U); @@ -112,10 +113,11 @@ bool KMMFrame::decodeHeader(const uint8_t* data) case KMM_MAC::ENH_MAC: { - uint8_t macLength = 8U; + uint8_t macLength = P25DEF::KMM_AES_MAC_LENGTH; m_macAlgId = data[m_messageFullLength - 4U]; m_macKId = GET_UINT16(data, m_messageFullLength - 3U); + m_macFormat = data[m_messageFullLength - 1U]; ::memset(m_mac, 0x00U, macLength); ::memcpy(m_mac, data + m_messageFullLength - (macLength + 5U), macLength); @@ -163,13 +165,14 @@ void KMMFrame::encodeHeader(uint8_t* data) case KMM_MAC::ENH_MAC: { - uint8_t macLength = 8U; + uint8_t macLength = P25DEF::KMM_AES_MAC_LENGTH; - m_macAlgId = data[m_messageFullLength - 4U]; - m_macKId = GET_UINT16(data, m_messageFullLength - 3U); + data[m_messageFullLength - 5U] = macLength; + data[m_messageFullLength - 4U] = m_macAlgId; + SET_UINT16(m_macKId, data, m_messageFullLength - 3U); + data[m_messageFullLength - 1U] = m_macFormat; - ::memset(m_mac, 0x00U, macLength); - ::memcpy(m_mac, data + m_messageFullLength - (macLength + 5U), macLength); + ::memcpy(data + m_messageFullLength - (macLength + 5U), m_mac, macLength); } break; diff --git a/src/common/p25/kmm/KMMFrame.h b/src/common/p25/kmm/KMMFrame.h index 612a71aa..96b60171 100644 --- a/src/common/p25/kmm/KMMFrame.h +++ b/src/common/p25/kmm/KMMFrame.h @@ -77,11 +77,22 @@ namespace p25 if (m_messageNumber > 0U) len += 2U; if (m_macType == P25DEF::KMM_MAC::ENH_MAC) - len += P25DEF::KMM_AES_MAC_LENGTH; + len += P25DEF::KMM_AES_MAC_LENGTH + 5U; return len; } + /** + * @brief Gets the full byte length of this KMMFrame. + * @return uint32_t Full Length of KMMFrame. + */ + uint32_t fullLength() + { + m_messageLength = length(); + m_messageFullLength = m_messageLength + 3U; + return m_messageFullLength; + } + /** * @brief Decode a KMM frame. * @param[in] data Buffer containing KMM frame data to decode. @@ -128,6 +139,10 @@ namespace p25 * @brief Message Authentication Key ID. */ DECLARE_PROTECTED_PROPERTY(uint16_t, macKId, MACKId); + /** + * @brief Message Authentication Format. + */ + DECLARE_PROTECTED_PROPERTY(uint16_t, macFormat, MACFormat); /** * @brief Message Number. */ diff --git a/src/common/p25/kmm/KMMRekeyCommand.cpp b/src/common/p25/kmm/KMMRekeyCommand.cpp index d3cbbeef..58fccb81 100644 --- a/src/common/p25/kmm/KMMRekeyCommand.cpp +++ b/src/common/p25/kmm/KMMRekeyCommand.cpp @@ -33,6 +33,7 @@ KMMRekeyCommand::KMMRekeyCommand() : KMMFrame(), m_mi(nullptr) { m_messageId = KMM_MessageType::REKEY_CMD; + m_respKind = KMM_ResponseKind::IMMEDIATE; m_mi = new uint8_t[MI_LENGTH_BYTES]; ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); @@ -163,14 +164,16 @@ void KMMRekeyCommand::encode(uint8_t* data) data[19U + (m_bodyOffset + offset)] = keyCount; for (auto key : keysetItem.keys()) { uint8_t keyNameLen = key.keyFormat() & 0x1FU; - data[18U + (m_bodyOffset + offset)] = key.keyFormat(); - SET_UINT16(key.sln(), data, 19U + (m_bodyOffset + offset)); - SET_UINT16(key.kId(), data, 21U + (m_bodyOffset + offset)); + data[20U + (m_bodyOffset + offset)] = key.keyFormat(); + SET_UINT16(key.sln(), data, 21U + (m_bodyOffset + offset)); + SET_UINT16(key.kId(), data, 23U + (m_bodyOffset + offset)); DECLARE_UINT8_ARRAY(keyPayload, keysetItem.keyLength()); key.getKey(keyPayload); - ::memcpy(data + (23U + (m_bodyOffset + offset)), keyPayload, keysetItem.keyLength()); + Utils::dump(2U, "keyPayload", keyPayload, keysetItem.keyLength()); + + ::memcpy(data + (25U + (m_bodyOffset + offset)), keyPayload, keysetItem.keyLength()); offset += 5U + keyNameLen + keysetItem.keyLength(); } diff --git a/src/common/p25/kmm/KMMRekeyCommand.h b/src/common/p25/kmm/KMMRekeyCommand.h index 55a52c19..49f97a7c 100644 --- a/src/common/p25/kmm/KMMRekeyCommand.h +++ b/src/common/p25/kmm/KMMRekeyCommand.h @@ -37,7 +37,7 @@ namespace p25 * @{ */ - const uint32_t KMM_BODY_REKEY_CMD_LENGTH = 5U; + const uint32_t KMM_BODY_REKEY_CMD_LENGTH = 4U; /** @} */ diff --git a/src/common/p25/kmm/KeysetItem.h b/src/common/p25/kmm/KeysetItem.h index 9a9287bc..7c0d6ca8 100644 --- a/src/common/p25/kmm/KeysetItem.h +++ b/src/common/p25/kmm/KeysetItem.h @@ -33,7 +33,7 @@ namespace p25 * @{ */ - const uint8_t MAX_ENC_KEY_LENGTH_BYTES = 32U; + const uint8_t MAX_ENC_KEY_LENGTH_BYTES = 40U; /** @} */ diff --git a/tests/crypto/AES_Crypto_Test.cpp b/tests/crypto/AES_Crypto_Test.cpp index 4a5a8393..b02c45a1 100644 --- a/tests/crypto/AES_Crypto_Test.cpp +++ b/tests/crypto/AES_Crypto_Test.cpp @@ -56,7 +56,7 @@ TEST_CASE("AES", "[Crypto Test]") { for (uint32_t i = 0; i < 48U; i++) { if (decrypted[i] != message[i]) { - ::LogDebug("T", "AES_Crypto_Test, INVALID AT IDX %d\n", i); + ::LogError("T", "AES_Crypto_Test, INVALID AT IDX %d", i); failed = true; } } diff --git a/tests/crypto/AES_LLA_AM1_Test.cpp b/tests/crypto/AES_LLA_AM1_Test.cpp index 4195ca3f..83c9de08 100644 --- a/tests/crypto/AES_LLA_AM1_Test.cpp +++ b/tests/crypto/AES_LLA_AM1_Test.cpp @@ -18,7 +18,7 @@ using namespace crypto; #include #include -TEST_CASE("AES", "[LLA AM1 Test]") { +TEST_CASE("AES_LLA", "[LLA AM1 Test]") { SECTION("LLA_AM1_Test") { bool failed = false; @@ -70,7 +70,7 @@ TEST_CASE("AES", "[LLA AM1 Test]") { for (uint32_t i = 0; i < 16U; i++) { if (KS[i] != resultKS[i]) { - ::LogDebug("T", "LLA_AM1_Test, INVALID AT IDX %d\n", i); + ::LogError("T", "LLA_AM1_Test, INVALID AT IDX %d", i); failed = true; } } diff --git a/tests/crypto/AES_LLA_AM2_Test.cpp b/tests/crypto/AES_LLA_AM2_Test.cpp index 45c810bf..e336e151 100644 --- a/tests/crypto/AES_LLA_AM2_Test.cpp +++ b/tests/crypto/AES_LLA_AM2_Test.cpp @@ -18,7 +18,7 @@ using namespace crypto; #include #include -TEST_CASE("AES", "[LLA AM2 Test]") { +TEST_CASE("AES_LLA", "[LLA AM2 Test]") { SECTION("LLA_AM2_Test") { bool failed = false; @@ -72,7 +72,7 @@ TEST_CASE("AES", "[LLA AM2 Test]") { for (uint32_t i = 0; i < 4U; i++) { if (RES1[i] != resultRES1[i]) { - ::LogDebug("T", "LLA_AM2_Test, INVALID AT IDX %d\n", i); + ::LogError("T", "LLA_AM2_Test, INVALID AT IDX %d", i); failed = true; } } diff --git a/tests/crypto/AES_LLA_AM3_Test.cpp b/tests/crypto/AES_LLA_AM3_Test.cpp index c6e465b4..15aae7ca 100644 --- a/tests/crypto/AES_LLA_AM3_Test.cpp +++ b/tests/crypto/AES_LLA_AM3_Test.cpp @@ -18,7 +18,7 @@ using namespace crypto; #include #include -TEST_CASE("AES", "[LLA AM3 Test]") { +TEST_CASE("AES_LLA", "[LLA AM3 Test]") { SECTION("LLA_AM3_Test") { bool failed = false; @@ -77,7 +77,7 @@ TEST_CASE("AES", "[LLA AM3 Test]") { for (uint32_t i = 0; i < 16U; i++) { if (KS[i] != resultKS[i]) { - ::LogDebug("T", "LLA_AM3_Test, INVALID AT IDX %d\n", i); + ::LogError("T", "LLA_AM3_Test, INVALID AT IDX %d", i); failed = true; } } diff --git a/tests/crypto/AES_LLA_AM4_Test.cpp b/tests/crypto/AES_LLA_AM4_Test.cpp index 0428a2fc..17b45a81 100644 --- a/tests/crypto/AES_LLA_AM4_Test.cpp +++ b/tests/crypto/AES_LLA_AM4_Test.cpp @@ -18,7 +18,7 @@ using namespace crypto; #include #include -TEST_CASE("AES", "[LLA AM4 Test]") { +TEST_CASE("AES_LLA", "[LLA AM4 Test]") { SECTION("LLA_AM4_Test") { bool failed = false; @@ -72,7 +72,7 @@ TEST_CASE("AES", "[LLA AM4 Test]") { for (uint32_t i = 0; i < 4U; i++) { if (RES2[i] != resultRES2[i]) { - ::LogDebug("T", "LLA_AM4_Test, INVALID AT IDX %d\n", i); + ::LogError("T", "LLA_AM4_Test, INVALID AT IDX %d", i); failed = true; } } diff --git a/tests/crypto/P25_KEK_Crypto_Test.cpp b/tests/crypto/P25_KEK_Crypto_Test.cpp index e1bc5734..40e42a79 100644 --- a/tests/crypto/P25_KEK_Crypto_Test.cpp +++ b/tests/crypto/P25_KEK_Crypto_Test.cpp @@ -16,7 +16,7 @@ #include #include -TEST_CASE("AES", "[KEK Crypto Test]") { +TEST_CASE("AES_KEK", "[KEK Crypto Test]") { SECTION("P25_AES_KEK_Crypto_Test") { bool failed = false; @@ -59,7 +59,7 @@ TEST_CASE("AES", "[KEK Crypto Test]") { for (uint32_t i = 0; i < 40U; i++) { if (wrappedKey[i] != testWrappedKeyFrame[i]) { - ::LogDebug("T", "P25_AES_KEK_Crypto_Test, WRAPPED INVALID AT IDX %d\n", i); + ::LogDebug("T", "P25_AES_KEK_Crypto_Test, WRAPPED INVALID AT IDX %d", i); failed = true; } } @@ -70,7 +70,7 @@ TEST_CASE("AES", "[KEK Crypto Test]") { for (uint32_t i = 0; i < 32U; i++) { if (unwrappedKey[i] != message[i]) { - ::LogDebug("T", "P25_AES_KEK_Crypto_Test, UNWRAPPED INVALID AT IDX %d\n", i); + ::LogError("T", "P25_AES_KEK_Crypto_Test, UNWRAPPED INVALID AT IDX %d", i); failed = true; } } diff --git a/tests/crypto/P25_MAC_CBC_Test.cpp b/tests/crypto/P25_MAC_CBC_Test.cpp new file mode 100644 index 00000000..fffee5bf --- /dev/null +++ b/tests/crypto/P25_MAC_CBC_Test.cpp @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/Crypto.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25::defines; + +#include +#include +#include + +TEST_CASE("AES_MAC_CBC", "[AES256 MAC CBC-MAC Test]") { + SECTION("P25_MAC_CBC_Crypto_Test") { + bool failed = false; + + INFO("P25 AES256 MAC CBC-MAC Test"); + + srand((unsigned int)time(NULL)); + + // example data taken from TIA-102.AACA-C-2023 Section 14.3.4 + + // MAC TEK + uint8_t macTek[] = + { + 0x16, 0x85, 0x62, 0x45, 0x3B, 0x3E, 0x7F, 0x61, 0x8D, 0x68, 0xB3, 0x87, 0xE0, 0xB9, 0x97, 0xE1, + 0xFB, 0x0F, 0x26, 0x4F, 0xA8, 0x3B, 0x74, 0xE4, 0x3B, 0x17, 0x29, 0x17, 0xBD, 0x39, 0x33, 0x9F + }; + + // expected CBC key + uint8_t expectedCBC[] = + { + 0x09, 0xE7, 0x11, 0x7B, 0x4E, 0x42, 0x06, 0xDE, 0xD3, 0x66, 0xEA, 0x5D, 0x69, 0x33, 0x01, 0xCA, + 0x83, 0x21, 0xBC, 0xC2, 0x0F, 0xA5, 0x05, 0xDF, 0x12, 0x67, 0xDC, 0x2A, 0xE4, 0x58, 0xA0, 0x57 + }; + + // data block + uint8_t dataBlock[] = + { + 0x1E, 0x00, 0x4D, 0xA8, 0x64, 0x3B, 0xA8, 0x71, 0x2B, 0x1D, 0x17, 0x72, 0x00, 0x84, 0x50, 0xBC, + 0x01, 0x00, 0x01, 0x84, 0x28, 0x01, 0x00, 0x00, 0x00, 0x49, 0x83, 0x80, 0x28, 0x9C, 0xF6, 0x35, + 0xFB, 0x68, 0xD3, 0x45, 0xD3, 0x4F, 0x62, 0xEF, 0x06, 0x3B, 0xA4, 0xE0, 0x5C, 0xAE, 0x47, 0x56, + 0xE7, 0xD3, 0x04, 0x46, 0xD1, 0xF0, 0x7C, 0x6E, 0xB4, 0xE9, 0xE0, 0x84, 0x09, 0x45, 0x37, 0x23, + 0x72, 0xFB, 0x80, 0x42, 0xA0, 0x91, 0x56, 0xF0, 0xD4, 0x72, 0x1C, 0x08, 0x84, 0x2F, 0x62, 0x40 + }; + + uint8_t expectedMAC[8U]; + + Utils::dump(2U, "P25_MAC_CBC_Crypto_Test, DataBlock", dataBlock, 80U); + Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, Expected CBC-MAC Key", expectedCBC, 32U); + + uint16_t fullLength = 0U; + uint16_t messageLength = GET_UINT16(dataBlock, 1U); + fullLength = messageLength + 3U; + bool hasMN = ((dataBlock[3U] >> 4U) & 0x03U) == 0x02U; + uint8_t macType = (dataBlock[3U] >> 2U) & 0x03U; + + ::LogInfoEx("T", "P25_MAC_CBC_Crypto_Test, messageLength = %u, hasMN = %u, macType = $%02X", messageLength, hasMN, macType); + + switch (macType) { + case KMM_MAC::DES_MAC: + { + uint8_t macLength = 4U; + ::memset(expectedMAC, 0x00U, macLength); + + uint8_t macAlgId = dataBlock[fullLength - 4U]; + uint16_t macKId = GET_UINT16(dataBlock, fullLength - 3U); + uint8_t macFormat = dataBlock[fullLength - 1U]; + + ::memcpy(expectedMAC, dataBlock + fullLength - (macLength + 5U), macLength); + + ::LogInfoEx("T", "P25_MAC_CBC_Crypto_Test, macAlgId = $%02X, macKId = $%04X, macFormat = $%02X", macAlgId, macKId, macFormat); + Utils::dump(2U, "P25_MAC_CBC_Crypto_Test, Expected MAC", expectedMAC, macLength); + } + break; + + case KMM_MAC::ENH_MAC: + { + uint8_t macLength = 8U; + ::memset(expectedMAC, 0x00U, macLength); + + uint8_t macAlgId = dataBlock[fullLength - 4U]; + uint16_t macKId = GET_UINT16(dataBlock, fullLength - 3U); + uint8_t macFormat = dataBlock[fullLength - 1U]; + + ::memcpy(expectedMAC, dataBlock + fullLength - (macLength + 5U), macLength); + + ::LogInfoEx("T", "P25_MAC_CBC_Crypto_Test, macAlgId = $%02X, macKId = $%04X, macFormat = $%02X", macAlgId, macKId, macFormat); + Utils::dump(2U, "P25_MAC_CBC_Crypto_Test, Expected MAC", expectedMAC, macLength); + } + break; + + case KMM_MAC::NO_MAC: + break; + + default: + ::LogError(LOG_P25, "P25_MAC_CBC_Crypto_Test, unknown KMM MAC inventory type value, macType = $%02X", macType); + break; + } + + p25::crypto::P25Crypto crypto; + + UInt8Array macKey = crypto.cryptAES_KMM_CBC_KDF(macTek, dataBlock, fullLength); + Utils::dump(2U, "P25_MAC_CBC_Crypto_Test, CBC MAC Key", macKey.get(), 32U); + + for (uint32_t i = 0; i < 32U; i++) { + if (macKey[i] != expectedCBC[i]) { + ::LogError("T", "P25_MAC_CBC_Crypto_Test, INVALID AT IDX %d", i); + failed = true; + } + } + + UInt8Array mac = crypto.cryptAES_KMM_CBC(expectedCBC, dataBlock, fullLength); + Utils::dump(2U, "P25_MAC_CBC_Crypto_Test, MAC", mac.get(), 8U); + + for (uint32_t i = 0; i < 8U; i++) { + if (mac[i] != expectedMAC[i]) { + ::LogError("T", "P25_MAC_CBC_Crypto_Test, INVALID AT IDX %d", i); + failed = true; + } + } + + REQUIRE(failed==false); + } +} diff --git a/tests/crypto/P25_MAC_CMAC_Test.cpp b/tests/crypto/P25_MAC_CMAC_Test.cpp index 33e5f63d..5bf53262 100644 --- a/tests/crypto/P25_MAC_CMAC_Test.cpp +++ b/tests/crypto/P25_MAC_CMAC_Test.cpp @@ -19,7 +19,7 @@ using namespace p25::defines; #include #include -TEST_CASE("AES", "[AES256 MAC CMAC Test]") { +TEST_CASE("AES_MAC_CMAC", "[AES256 MAC CMAC Test]") { SECTION("P25_MAC_CMAC_Crypto_Test") { bool failed = false; @@ -54,6 +54,7 @@ TEST_CASE("AES", "[AES256 MAC CMAC Test]") { }; Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, DataBlock", dataBlock, 80U); + Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, Expected CMAC Key", expectedCMAC, 32U); uint16_t fullLength = 0U; uint16_t messageLength = GET_UINT16(dataBlock, 1U); @@ -72,11 +73,12 @@ TEST_CASE("AES", "[AES256 MAC CMAC Test]") { uint8_t macAlgId = dataBlock[fullLength - 4U]; uint16_t macKId = GET_UINT16(dataBlock, fullLength - 3U); + uint8_t macFormat = dataBlock[fullLength - 1U]; ::memcpy(mac, dataBlock + fullLength - (macLength + 5U), macLength); - ::LogInfoEx("T", "P25_MAC_CMAC_Crypto_Test, macAlgId = $%02X, macKId = $%04X", macAlgId, macKId); - Utils::dump(2U, "MAC", mac, macLength); + ::LogInfoEx("T", "P25_MAC_CMAC_Crypto_Test, macAlgId = $%02X, macKId = $%04X, macFormat = $%02X", macAlgId, macKId, macFormat); + Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, Expected MAC", mac, macLength); } break; @@ -88,11 +90,12 @@ TEST_CASE("AES", "[AES256 MAC CMAC Test]") { uint8_t macAlgId = dataBlock[fullLength - 4U]; uint16_t macKId = GET_UINT16(dataBlock, fullLength - 3U); + uint8_t macFormat = dataBlock[fullLength - 1U]; ::memcpy(mac, dataBlock + fullLength - (macLength + 5U), macLength); - ::LogInfoEx("T", "P25_MAC_CMAC_Crypto_Test, macAlgId = $%02X, macKId = $%04X", macAlgId, macKId); - Utils::dump(2U, "MAC", mac, macLength); + ::LogInfoEx("T", "P25_MAC_CMAC_Crypto_Test, macAlgId = $%02X, macKId = $%04X, macFormat = $%02X", macAlgId, macKId, macFormat); + Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, Expected MAC", mac, macLength); } break; @@ -111,7 +114,7 @@ TEST_CASE("AES", "[AES256 MAC CMAC Test]") { for (uint32_t i = 0; i < 32U; i++) { if (macKey[i] != expectedCMAC[i]) { - ::LogDebug("T", "P25_MAC_CMAC_Crypto_Test, INVALID AT IDX %d\n", i); + ::LogError("T", "P25_MAC_CMAC_Crypto_Test, INVALID AT IDX %d", i); failed = true; } } diff --git a/tests/crypto/RC4_Crypto_Test.cpp b/tests/crypto/RC4_Crypto_Test.cpp index 21b64cef..2df39bee 100644 --- a/tests/crypto/RC4_Crypto_Test.cpp +++ b/tests/crypto/RC4_Crypto_Test.cpp @@ -54,7 +54,7 @@ TEST_CASE("RC4", "[Crypto Test]") { for (uint32_t i = 0; i < 48U; i++) { if (decrypted[i] != message[i]) { - ::LogDebug("T", "RC4_Crypto_Test, INVALID AT IDX %d\n", i); + ::LogError("T", "RC4_Crypto_Test, INVALID AT IDX %d", i); failed = true; } } diff --git a/tests/edac/CRC_12_Test.cpp b/tests/edac/CRC_12_Test.cpp index c1c2fe7b..91c59526 100644 --- a/tests/edac/CRC_12_Test.cpp +++ b/tests/edac/CRC_12_Test.cpp @@ -37,13 +37,13 @@ TEST_CASE("CRC", "[12-bit Test]") { CRC::addCRC12(random, lenBits); uint16_t inCrc = (random[len - 2U] << 8) | (random[len - 1U] << 0); - ::LogDebug("T", "CRC::checkCRC12(), crc = $%04X", inCrc); + ::LogInfoEx("T", "CRC::checkCRC12(), crc = $%04X", inCrc); Utils::dump(2U, "12_Sanity_Test CRC", random, len); bool ret = CRC::checkCRC12(random, lenBits); if (!ret) { - ::LogDebug("T", "12_Sanity_Test, failed CRC12 check"); + ::LogError("T", "12_Sanity_Test, failed CRC12 check"); failed = true; goto cleanup; } @@ -53,7 +53,7 @@ TEST_CASE("CRC", "[12-bit Test]") { ret = CRC::checkCRC12(random, lenBits); if (ret) { - ::LogDebug("T", "12_Sanity_Test, failed CRC12 error check"); + ::LogError("T", "12_Sanity_Test, failed CRC12 error check"); failed = true; goto cleanup; } diff --git a/tests/edac/CRC_15_Test.cpp b/tests/edac/CRC_15_Test.cpp index 03ed8699..9920896c 100644 --- a/tests/edac/CRC_15_Test.cpp +++ b/tests/edac/CRC_15_Test.cpp @@ -37,13 +37,13 @@ TEST_CASE("CRC", "[15-bit Test]") { CRC::addCRC15(random, lenBits); uint16_t inCrc = (random[len - 2U] << 8) | (random[len - 1U] << 0); - ::LogDebug("T", "CRC::checkCRC15(), crc = $%04X", inCrc); + ::LogInfoEx("T", "CRC::checkCRC15(), crc = $%04X", inCrc); Utils::dump(2U, "15_Sanity_Test CRC", random, len); bool ret = CRC::checkCRC15(random, lenBits); if (!ret) { - ::LogDebug("T", "15_Sanity_Test, failed CRC15 check"); + ::LogError("T", "15_Sanity_Test, failed CRC15 check"); failed = true; goto cleanup; } @@ -53,7 +53,7 @@ TEST_CASE("CRC", "[15-bit Test]") { ret = CRC::checkCRC15(random, lenBits); if (ret) { - ::LogDebug("T", "15_Sanity_Test, failed CRC15 error check"); + ::LogError("T", "15_Sanity_Test, failed CRC15 error check"); failed = true; goto cleanup; } diff --git a/tests/edac/CRC_16_Test.cpp b/tests/edac/CRC_16_Test.cpp index 33014d3c..9da1e5e5 100644 --- a/tests/edac/CRC_16_Test.cpp +++ b/tests/edac/CRC_16_Test.cpp @@ -37,13 +37,13 @@ TEST_CASE("CRC", "[16-bit Test]") { CRC::addCRC16(random, lenBits); uint16_t inCrc = (random[len - 2U] << 8) | (random[len - 1U] << 0); - ::LogDebug("T", "CRC::checkCRC16(), crc = $%04X", inCrc); + ::LogInfoEx("T", "CRC::checkCRC16(), crc = $%04X", inCrc); Utils::dump(2U, "16_Sanity_Test CRC", random, len); bool ret = CRC::checkCRC16(random, lenBits); if (!ret) { - ::LogDebug("T", "16_Sanity_Test, failed CRC16 check"); + ::LogError("T", "16_Sanity_Test, failed CRC16 check"); failed = true; goto cleanup; } @@ -53,7 +53,7 @@ TEST_CASE("CRC", "[16-bit Test]") { ret = CRC::checkCRC16(random, lenBits); if (ret) { - ::LogDebug("T", "16_Sanity_Test, failed CRC16 error check"); + ::LogError("T", "16_Sanity_Test, failed CRC16 error check"); failed = true; goto cleanup; } diff --git a/tests/edac/CRC_32_Test.cpp b/tests/edac/CRC_32_Test.cpp index 32520a7f..0e8321c6 100644 --- a/tests/edac/CRC_32_Test.cpp +++ b/tests/edac/CRC_32_Test.cpp @@ -36,13 +36,13 @@ TEST_CASE("CRC", "[32-bit Test]") { CRC::addCRC32(random, len); uint32_t inCrc = (random[len - 4U] << 24) | (random[len - 3U] << 16) | (random[len - 2U] << 8) | (random[len - 1U] << 0); - ::LogDebug("T", "CRC::checkCRC32(), crc = $%08X", inCrc); + ::LogInfoEx("T", "CRC::checkCRC32(), crc = $%08X", inCrc); Utils::dump(2U, "32_Sanity_Test CRC", random, len); bool ret = CRC::checkCRC32(random, len); if (!ret) { - ::LogDebug("T", "32_Sanity_Test, failed CRC32 check"); + ::LogError("T", "32_Sanity_Test, failed CRC32 check"); failed = true; goto cleanup; } @@ -52,7 +52,7 @@ TEST_CASE("CRC", "[32-bit Test]") { ret = CRC::checkCRC32(random, len); if (ret) { - ::LogDebug("T", "32_Sanity_Test, failed CRC32 error check"); + ::LogError("T", "32_Sanity_Test, failed CRC32 error check"); failed = true; goto cleanup; } diff --git a/tests/edac/CRC_6_Test.cpp b/tests/edac/CRC_6_Test.cpp index 5462a22e..89571bef 100644 --- a/tests/edac/CRC_6_Test.cpp +++ b/tests/edac/CRC_6_Test.cpp @@ -37,13 +37,13 @@ TEST_CASE("CRC", "[6-bit Test]") { CRC::addCRC6(random, lenBits); uint32_t inCrc = (random[len - 1U] << 0); - ::LogDebug("T", "CRC::checkCRC6(), crc = $%02X", inCrc); + ::LogInfoEx("T", "CRC::checkCRC6(), crc = $%02X", inCrc); Utils::dump(2U, "6_Sanity_Test CRC", random, len); bool ret = CRC::checkCRC6(random, lenBits); if (!ret) { - ::LogDebug("T", "6_Sanity_Test, failed CRC6 check"); + ::LogError("T", "6_Sanity_Test, failed CRC6 check"); failed = true; goto cleanup; } @@ -53,7 +53,7 @@ TEST_CASE("CRC", "[6-bit Test]") { ret = CRC::checkCRC6(random, lenBits); if (ret) { - ::LogDebug("T", "6_Sanity_Test, failed CRC6 error check"); + ::LogError("T", "6_Sanity_Test, failed CRC6 error check"); failed = true; goto cleanup; } diff --git a/tests/edac/CRC_8_Test.cpp b/tests/edac/CRC_8_Test.cpp index 2a387be7..56561f65 100644 --- a/tests/edac/CRC_8_Test.cpp +++ b/tests/edac/CRC_8_Test.cpp @@ -34,7 +34,7 @@ TEST_CASE("CRC", "[8-bit Test]") { } uint8_t crc = CRC::crc8(random, len); - ::LogDebug("T", "crc = %02X", crc); + ::LogInfoEx("T", "crc = %02X", crc); Utils::dump(2U, "8_Sanity_Test CRC", random, len); @@ -42,9 +42,9 @@ TEST_CASE("CRC", "[8-bit Test]") { random[11U] >>= 8; uint8_t calc = CRC::crc8(random, len); - ::LogDebug("T", "calc = %02X", calc); + ::LogInfoEx("T", "calc = %02X", calc); if (crc == calc) { - ::LogDebug("T", "8_Sanity_Test, failed CRC8 error check"); + ::LogError("T", "8_Sanity_Test, failed CRC8 error check"); failed = true; goto cleanup; } diff --git a/tests/edac/CRC_9_Test.cpp b/tests/edac/CRC_9_Test.cpp index 55b4fbe7..2fa872d9 100644 --- a/tests/edac/CRC_9_Test.cpp +++ b/tests/edac/CRC_9_Test.cpp @@ -37,7 +37,7 @@ TEST_CASE("CRC", "[9-bit Test]") { random[1U] = 0; uint16_t crc = edac::CRC::createCRC9(random, 144U); - ::LogDebug("T", "crc = %04X", crc); + ::LogInfoEx("T", "crc = %04X", crc); random[0U] = random[0U] + ((crc >> 8) & 0x01U); random[1U] = (crc & 0xFFU); @@ -49,7 +49,7 @@ TEST_CASE("CRC", "[9-bit Test]") { uint16_t calculated = edac::CRC::createCRC9(random, 144U); if (((crc ^ calculated) == 0)/*|| ((crc ^ calculated) == 0x1FFU)*/) { - ::LogDebug("T", "9_Sanity_Test, failed CRC9 error check"); + ::LogError("T", "9_Sanity_Test, failed CRC9 error check"); failed = true; goto cleanup; } diff --git a/tests/edac/CRC_CCITT_161_Test.cpp b/tests/edac/CRC_CCITT_161_Test.cpp index f49d4207..e63081c1 100644 --- a/tests/edac/CRC_CCITT_161_Test.cpp +++ b/tests/edac/CRC_CCITT_161_Test.cpp @@ -36,13 +36,13 @@ TEST_CASE("CRC", "[16-bit CCITT-161 Test]") { CRC::addCCITT161(random, len); uint16_t inCrc = (random[len - 2U] << 8) | (random[len - 1U] << 0); - ::LogDebug("T", "CRC::checkCCITT161(), crc = $%04X", inCrc); + ::LogInfoEx("T", "CRC::checkCCITT161(), crc = $%04X", inCrc); Utils::dump(2U, "CCITT-161_Sanity_Test CRC", random, len); bool ret = CRC::checkCCITT161(random, len); if (!ret) { - ::LogDebug("T", "CCITT-161_Sanity_Test, failed CRC CCITT-162 check"); + ::LogError("T", "CCITT-161_Sanity_Test, failed CRC CCITT-162 check"); failed = true; goto cleanup; } @@ -52,7 +52,7 @@ TEST_CASE("CRC", "[16-bit CCITT-161 Test]") { ret = CRC::checkCCITT161(random, len); if (ret) { - ::LogDebug("T", "CCITT-161_Sanity_Test, failed CRC CCITT-162 error check"); + ::LogError("T", "CCITT-161_Sanity_Test, failed CRC CCITT-162 error check"); failed = true; goto cleanup; } diff --git a/tests/edac/CRC_CCITT_162_Test.cpp b/tests/edac/CRC_CCITT_162_Test.cpp index 83fe65bd..88e47445 100644 --- a/tests/edac/CRC_CCITT_162_Test.cpp +++ b/tests/edac/CRC_CCITT_162_Test.cpp @@ -36,13 +36,13 @@ TEST_CASE("CRC", "[16-bit CCITT-162 Test]") { CRC::addCCITT162(random, len); uint16_t inCrc = (random[len - 2U] << 8) | (random[len - 1U] << 0); - ::LogDebug("T", "CRC::checkCCITT162(), crc = $%04X", inCrc); + ::LogInfoEx("T", "CRC::checkCCITT162(), crc = $%04X", inCrc); Utils::dump(2U, "CCITT-162_Sanity_Test CRC", random, len); bool ret = CRC::checkCCITT162(random, len); if (!ret) { - ::LogDebug("T", "CCITT-162_Sanity_Test, failed CRC CCITT-162 check"); + ::LogError("T", "CCITT-162_Sanity_Test, failed CRC CCITT-162 check"); failed = true; goto cleanup; } @@ -52,7 +52,7 @@ TEST_CASE("CRC", "[16-bit CCITT-162 Test]") { ret = CRC::checkCCITT162(random, len); if (ret) { - ::LogDebug("T", "CCITT-162_Sanity_Test, failed CRC CCITT-162 error check"); + ::LogError("T", "CCITT-162_Sanity_Test, failed CRC CCITT-162 error check"); failed = true; goto cleanup; } diff --git a/tests/p25/HDU_RS_Test.cpp b/tests/p25/HDU_RS_Test.cpp index 372ef45f..cfb218ac 100644 --- a/tests/p25/HDU_RS_Test.cpp +++ b/tests/p25/HDU_RS_Test.cpp @@ -64,7 +64,7 @@ TEST_CASE("HDU", "[Reed-Soloman 36,20,17 Test]") { try { bool ret = m_rs.decode362017(rs); if (!ret) { - ::LogDebug("T", "LC::decodeHDU(), failed to decode RS (36,20,17) FEC\n"); + ::LogError("T", "LC::decodeHDU(), failed to decode RS (36,20,17) FEC"); failed = true; goto cleanup; } @@ -80,13 +80,13 @@ TEST_CASE("HDU", "[Reed-Soloman 36,20,17 Test]") { for (uint32_t i = 0; i < 15U; i++) { if (i == 14U) { if (rs[i] != 0xF0U) { - ::LogDebug("T", "LC::decodeHDU(), UNCORRECTABLE AT IDX %d\n", i); + ::LogError("T", "LC::decodeHDU(), UNCORRECTABLE AT IDX %d", i); failed = true; } } else { if (rs[i] != random[i]) { - ::LogDebug("T", "LC::decodeHDU(), UNCORRECTABLE AT IDX %d\n", i); + ::LogError("T", "LC::decodeHDU(), UNCORRECTABLE AT IDX %d", i); failed = true; } } diff --git a/tests/p25/KMM_Rekey_CBC_Test.cpp b/tests/p25/KMM_Rekey_CBC_Test.cpp new file mode 100644 index 00000000..395da345 --- /dev/null +++ b/tests/p25/KMM_Rekey_CBC_Test.cpp @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/kmm/KMMRekeyCommand.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::kmm; + +#include +#include +#include + +TEST_CASE("KMM_ReKey", "[P25 KMM Rekey Command CBC Test]") { + SECTION("P25_KMM_ReKey_CBC_Test") { + bool failed = false; + + INFO("P25 KMM ReKey Test"); + + srand((unsigned int)time(NULL)); + + // data block + uint8_t dataBlock[] = + { + 0x1E, 0x00, 0x4D, 0xA8, 0x64, 0x3B, 0xA8, 0x71, 0x2B, 0x1D, 0x17, 0x72, 0x00, 0x84, 0x50, 0xBC, + 0x01, 0x00, 0x01, 0x84, 0x28, 0x01, 0x00, 0x00, 0x00, 0x49, 0x83, 0x80, 0x28, 0x9C, 0xF6, 0x35, + 0xFB, 0x68, 0xD3, 0x45, 0xD3, 0x4F, 0x62, 0xEF, 0x06, 0x3B, 0xA4, 0xE0, 0x5C, 0xAE, 0x47, 0x56, + 0xE7, 0xD3, 0x04, 0x46, 0xD1, 0xF0, 0x7C, 0x6E, 0xB4, 0xE9, 0xE0, 0x84, 0x09, 0x45, 0x37, 0x23, + 0x72, 0xFB, 0x80, 0x42, 0xA0, 0x91, 0x56, 0xF0, 0xD4, 0x72, 0x1C, 0x08, 0x84, 0x2F, 0x62, 0x40 + }; + + // Encrypted Key Frame + uint8_t testWrappedKeyFrame[40U] = + { + 0x80, 0x28, 0x9C, 0xF6, 0x35, 0xFB, 0x68, 0xD3, 0x45, 0xD3, 0x4F, 0x62, 0xEF, 0x06, 0x3B, 0xA4, + 0xE0, 0x5C, 0xAE, 0x47, 0x56, 0xE7, 0xD3, 0x04, 0x46, 0xD1, 0xF0, 0x7C, 0x6E, 0xB4, 0xE9, 0xE0, + 0x84, 0x09, 0x45, 0x37, 0x23, 0x72, 0xFB, 0x80 + }; + + Utils::dump(2U, "P25_KMM_ReKey_CBC_Test, DataBlock", dataBlock, 80U); + + KMMRekeyCommand outKmm = KMMRekeyCommand(); + + outKmm.setDecryptInfoFmt(KMM_DECRYPT_INSTRUCT_NONE); + outKmm.setSrcLLId(0x712B1DU); + outKmm.setDstLLId(0x643BA8U); + + outKmm.setMACType(KMM_MAC::ENH_MAC); + outKmm.setMACAlgId(ALGO_AES_256); + outKmm.setMACKId(0x2F62U); + outKmm.setMACFormat(KMM_MAC_FORMAT_CBC); + + outKmm.setMessageNumber(0x1772U); + + outKmm.setAlgId(ALGO_AES_256); + outKmm.setKId(0x50BCU); + + KeysetItem ks; + ks.keysetId(1U); + ks.algId(ALGO_AES_256); // we currently can only OTAR AES256 keys + ks.keyLength(P25DEF::MAX_WRAPPED_ENC_KEY_LENGTH_BYTES); + + p25::kmm::KeyItem ki = p25::kmm::KeyItem(); + ki.keyFormat(0U); + ki.sln(0U); + ki.kId(0x4983U); + + ki.setKey(testWrappedKeyFrame, 40U); + ks.push_back(ki); + + std::vector keysets; + keysets.push_back(ks); + + outKmm.setKeysets(keysets); + + UInt8Array kmmFrame = std::make_unique(outKmm.fullLength()); + outKmm.encode(kmmFrame.get()); + + Utils::dump(2U, "P25_KMM_ReKey_CBC_Test, GeneratedDataBlock", kmmFrame.get(), outKmm.fullLength()); + + for (uint32_t i = 0; i < outKmm.fullLength(); i++) { + if (kmmFrame.get()[i] != dataBlock[i]) { + ::LogError("T", "P25_KMM_ReKey_CBC_Test, INVALID AT IDX %d", i); + failed = true; + } + } + + REQUIRE(failed==false); + } +} diff --git a/tests/p25/KMM_Rekey_CMAC_Test.cpp b/tests/p25/KMM_Rekey_CMAC_Test.cpp new file mode 100644 index 00000000..4027c2a4 --- /dev/null +++ b/tests/p25/KMM_Rekey_CMAC_Test.cpp @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/kmm/KMMRekeyCommand.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::kmm; + +#include +#include +#include + +TEST_CASE("KMM_ReKey", "[P25 KMM Rekey Command CMAC Test]") { + SECTION("P25_KMM_ReKey_CMAC_Test") { + bool failed = false; + + INFO("P25 KMM ReKey Test"); + + srand((unsigned int)time(NULL)); + + // data block + uint8_t dataBlock[] = + { + 0x1E, 0x00, 0x4D, 0xA8, 0x64, 0x3B, 0xA8, 0x71, 0x2B, 0x1D, 0x17, 0x72, 0x00, 0x84, 0x50, 0xBC, + 0x01, 0x00, 0x01, 0x84, 0x28, 0x01, 0x00, 0x00, 0x00, 0x49, 0x83, 0x80, 0x28, 0x9C, 0xF6, 0x35, + 0xFB, 0x68, 0xD3, 0x45, 0xD3, 0x4F, 0x62, 0xEF, 0x06, 0x3B, 0xA4, 0xE0, 0x5C, 0xAE, 0x47, 0x56, + 0xE7, 0xD3, 0x04, 0x46, 0xD1, 0xF0, 0x7C, 0x6E, 0xB4, 0xE9, 0xE0, 0x84, 0x09, 0x45, 0x37, 0x23, + 0x72, 0xFB, 0x80, 0x21, 0x85, 0x22, 0x33, 0x41, 0xD9, 0x8A, 0x97, 0x08, 0x84, 0x2F, 0x62, 0x41 + }; + + // Encrypted Key Frame + uint8_t testWrappedKeyFrame[40U] = + { + 0x80, 0x28, 0x9C, 0xF6, 0x35, 0xFB, 0x68, 0xD3, 0x45, 0xD3, 0x4F, 0x62, 0xEF, 0x06, 0x3B, 0xA4, + 0xE0, 0x5C, 0xAE, 0x47, 0x56, 0xE7, 0xD3, 0x04, 0x46, 0xD1, 0xF0, 0x7C, 0x6E, 0xB4, 0xE9, 0xE0, + 0x84, 0x09, 0x45, 0x37, 0x23, 0x72, 0xFB, 0x80 + }; + + Utils::dump(2U, "P25_KMM_ReKey_CMAC_Test, DataBlock", dataBlock, 80U); + + KMMRekeyCommand outKmm = KMMRekeyCommand(); + + outKmm.setDecryptInfoFmt(KMM_DECRYPT_INSTRUCT_NONE); + outKmm.setSrcLLId(0x712B1DU); + outKmm.setDstLLId(0x643BA8U); + + outKmm.setMACType(KMM_MAC::ENH_MAC); + outKmm.setMACAlgId(ALGO_AES_256); + outKmm.setMACKId(0x2F62U); + outKmm.setMACFormat(KMM_MAC_FORMAT_CMAC); + + outKmm.setMessageNumber(0x1772U); + + outKmm.setAlgId(ALGO_AES_256); + outKmm.setKId(0x50BCU); + + KeysetItem ks; + ks.keysetId(1U); + ks.algId(ALGO_AES_256); // we currently can only OTAR AES256 keys + ks.keyLength(P25DEF::MAX_WRAPPED_ENC_KEY_LENGTH_BYTES); + + p25::kmm::KeyItem ki = p25::kmm::KeyItem(); + ki.keyFormat(0U); + ki.sln(0U); + ki.kId(0x4983U); + + ki.setKey(testWrappedKeyFrame, 40U); + ks.push_back(ki); + + std::vector keysets; + keysets.push_back(ks); + + outKmm.setKeysets(keysets); + + UInt8Array kmmFrame = std::make_unique(outKmm.fullLength()); + outKmm.encode(kmmFrame.get()); + + Utils::dump(2U, "P25_KMM_ReKey_CMAC_Test, GeneratedDataBlock", kmmFrame.get(), outKmm.fullLength()); + + for (uint32_t i = 0; i < outKmm.fullLength(); i++) { + if (kmmFrame.get()[i] != dataBlock[i]) { + ::LogError("T", "P25_KMM_ReKey_CMAC_Test, INVALID AT IDX %d", i); + failed = true; + } + } + + REQUIRE(failed==false); + } +} diff --git a/tests/p25/LDU1_RS_Test.cpp b/tests/p25/LDU1_RS_Test.cpp index dcede1ae..86ef8e53 100644 --- a/tests/p25/LDU1_RS_Test.cpp +++ b/tests/p25/LDU1_RS_Test.cpp @@ -62,7 +62,7 @@ TEST_CASE("LDU1", "[Reed-Soloman 24,12,13 Test]") { try { bool ret = m_rs.decode241213(rs); if (!ret) { - ::LogDebug("T", "LC::decodeLDU1(), failed to decode RS (24,12,13) FEC\n"); + ::LogError("T", "LC::decodeLDU1(), failed to decode RS (24,12,13) FEC"); failed = true; goto cleanup; } @@ -78,13 +78,13 @@ TEST_CASE("LDU1", "[Reed-Soloman 24,12,13 Test]") { for (uint32_t i = 0; i < 9U; i++) { if (i == 8U) { if (rs[i] != 0xF0U) { - ::LogDebug("T", "LC::decodeLDU1(), UNCORRECTABLE AT IDX %d\n", i); + ::LogError("T", "LC::decodeLDU1(), UNCORRECTABLE AT IDX %d", i); failed = true; } } else { if (rs[i] != random[i]) { - ::LogDebug("T", "LC::decodeLDU1(), UNCORRECTABLE AT IDX %d\n", i); + ::LogError("T", "LC::decodeLDU1(), UNCORRECTABLE AT IDX %d", i); failed = true; } } diff --git a/tests/p25/LDU2_RS_Test.cpp b/tests/p25/LDU2_RS_Test.cpp index 9251107a..9d1b8879 100644 --- a/tests/p25/LDU2_RS_Test.cpp +++ b/tests/p25/LDU2_RS_Test.cpp @@ -61,7 +61,7 @@ TEST_CASE("LDU2", "[Reed-Soloman 24,16,9 Test]") { try { bool ret = m_rs.decode24169(rs); if (!ret) { - ::LogDebug("T", "LC::decodeLDU2(), failed to decode RS (24,16,9) FEC\n"); + ::LogError("T", "LC::decodeLDU2(), failed to decode RS (24,16,9) FEC"); failed = true; goto cleanup; } @@ -77,13 +77,13 @@ TEST_CASE("LDU2", "[Reed-Soloman 24,16,9 Test]") { for (uint32_t i = 0; i < 12U; i++) { if (i == 11U) { if (rs[i] != 0xF0U) { - ::LogDebug("T", "LC::decodeLDU2(), UNCORRECTABLE AT IDX %d\n", i); + ::LogError("T", "LC::decodeLDU2(), UNCORRECTABLE AT IDX %d", i); failed = true; } } else { if (rs[i] != random[i]) { - ::LogDebug("T", "LC::decodeLDU2(), UNCORRECTABLE AT IDX %d\n", i); + ::LogError("T", "LC::decodeLDU2(), UNCORRECTABLE AT IDX %d", i); failed = true; } } From 36bac19c221a1bc625ccf705e3a02c6c4b92fed4 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 5 Nov 2025 23:42:14 -0500 Subject: [PATCH 131/200] minor cleanups; --- src/common/p25/Crypto.cpp | 21 +++++++++------------ tests/crypto/P25_MAC_CBC_Test.cpp | 1 + tests/crypto/P25_MAC_CMAC_Test.cpp | 27 +++++++++++++++++++-------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/common/p25/Crypto.cpp b/src/common/p25/Crypto.cpp index 446b41a3..f4f6d4b3 100644 --- a/src/common/p25/Crypto.cpp +++ b/src/common/p25/Crypto.cpp @@ -364,7 +364,7 @@ UInt8Array P25Crypto::cryptAES_KMM_CBC_KDF(const uint8_t* kek, const uint8_t* ms // perform the wrapping operation if (EVP_EncryptUpdate(ctx, tempBuf, &len, kek, MAX_ENC_KEY_LENGTH_BYTES) != 1) { - LogError(LOG_P25, "EVP_EncryptUpdate(), failed to wrap TEK: %s", ERR_error_string(ERR_get_error(), NULL)); + LogError(LOG_P25, "EVP_EncryptUpdate(), failed to wrap KEK: %s", ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_free(ctx); return nullptr; } @@ -372,7 +372,7 @@ UInt8Array P25Crypto::cryptAES_KMM_CBC_KDF(const uint8_t* kek, const uint8_t* ms // finalize the wrapping (no output, just padding) int tempLen; if (EVP_EncryptFinal_ex(ctx, tempBuf + len, &tempLen) != 1) { - LogError(LOG_P25, "EVP_EncryptFinal_ex(), failed to finalize wrapping TEK: %s", ERR_error_string(ERR_get_error(), NULL)); + LogError(LOG_P25, "EVP_EncryptFinal_ex(), failed to finalize wrapping KEK: %s", ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_free(ctx); return nullptr; } @@ -391,7 +391,7 @@ UInt8Array P25Crypto::cryptAES_KMM_CBC_KDF(const uint8_t* kek, const uint8_t* ms return wrappedKey; #else - LogError(LOG_P25, "No OpenSSL, TEK encryption is not supported!"); + LogError(LOG_P25, "No OpenSSL, CBC-MAC generation is not supported!"); return nullptr; #endif // ENABLE_SSL } @@ -434,7 +434,7 @@ UInt8Array P25Crypto::cryptAES_KMM_CBC(const uint8_t* macKey, const uint8_t* msg return wrappedKey; #else - LogError(LOG_P25, "No OpenSSL, TEK encryption is not supported!"); + LogError(LOG_P25, "No OpenSSL, CBC-MAC generation is not supported!"); return nullptr; #endif // ENABLE_SSL } @@ -505,11 +505,8 @@ UInt8Array P25Crypto::cryptAES_KMM_CMAC_KDF(const uint8_t* kek, const uint8_t* m OSSL_PARAM_END }; - uint8_t macKey[MAX_ENC_KEY_LENGTH_BYTES]; - ::memset(macKey, 0x00U, MAX_ENC_KEY_LENGTH_BYTES); - // derive MAC key - if (EVP_KDF_derive(ctx, macKey, MAX_ENC_KEY_LENGTH_BYTES, params) <= 0) { + if (EVP_KDF_derive(ctx, tempBuf, MAX_ENC_KEY_LENGTH_BYTES, params) <= 0) { LogError(LOG_P25, "EVP_KDF_derive(), failed to derive MAC key: %s", ERR_error_string(ERR_get_error(), NULL)); EVP_KDF_CTX_free(ctx); EVP_KDF_free(kdf); @@ -518,16 +515,16 @@ UInt8Array P25Crypto::cryptAES_KMM_CMAC_KDF(const uint8_t* kek, const uint8_t* m } /** DEBUG REMOVEME */ - Utils::dump(2U, "macKey", macKey, MAX_ENC_KEY_LENGTH_BYTES); + Utils::dump(2U, "tempBuf", tempBuf, 128U); /** DEBUG REMOVEME */ UInt8Array wrappedKey = std::unique_ptr(new uint8_t[MAX_ENC_KEY_LENGTH_BYTES]); ::memset(wrappedKey.get(), 0x00U, MAX_ENC_KEY_LENGTH_BYTES); - ::memcpy(wrappedKey.get(), macKey, MAX_ENC_KEY_LENGTH_BYTES); + ::memcpy(wrappedKey.get(), tempBuf, MAX_ENC_KEY_LENGTH_BYTES); return wrappedKey; #else - LogError(LOG_P25, "No OpenSSL, TEK encryption is not supported!"); + LogError(LOG_P25, "No OpenSSL, CMAC generation is not supported!"); return nullptr; #endif // ENABLE_SSL } @@ -608,7 +605,7 @@ UInt8Array P25Crypto::cryptAES_KMM_CMAC(const uint8_t* macKey, const uint8_t* ms return wrappedKey; #else - LogError(LOG_P25, "No OpenSSL, TEK encryption is not supported!"); + LogError(LOG_P25, "No OpenSSL, CMAC generation is not supported!"); return nullptr; #endif // ENABLE_SSL } diff --git a/tests/crypto/P25_MAC_CBC_Test.cpp b/tests/crypto/P25_MAC_CBC_Test.cpp index fffee5bf..3da80fe6 100644 --- a/tests/crypto/P25_MAC_CBC_Test.cpp +++ b/tests/crypto/P25_MAC_CBC_Test.cpp @@ -55,6 +55,7 @@ TEST_CASE("AES_MAC_CBC", "[AES256 MAC CBC-MAC Test]") { uint8_t expectedMAC[8U]; + Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, TEK", macTek, 32U); Utils::dump(2U, "P25_MAC_CBC_Crypto_Test, DataBlock", dataBlock, 80U); Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, Expected CBC-MAC Key", expectedCBC, 32U); diff --git a/tests/crypto/P25_MAC_CMAC_Test.cpp b/tests/crypto/P25_MAC_CMAC_Test.cpp index 5bf53262..1f0add9e 100644 --- a/tests/crypto/P25_MAC_CMAC_Test.cpp +++ b/tests/crypto/P25_MAC_CMAC_Test.cpp @@ -53,6 +53,9 @@ TEST_CASE("AES_MAC_CMAC", "[AES256 MAC CMAC Test]") { 0x72, 0xFB, 0x80, 0x21, 0x85, 0x22, 0x33, 0x41, 0xD9, 0x8A, 0x97, 0x08, 0x84, 0x2F, 0x62, 0x41 }; + uint8_t expectedMAC[8U]; + + Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, TEK", macTek, 32U); Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, DataBlock", dataBlock, 80U); Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, Expected CMAC Key", expectedCMAC, 32U); @@ -68,34 +71,32 @@ TEST_CASE("AES_MAC_CMAC", "[AES256 MAC CMAC Test]") { case KMM_MAC::DES_MAC: { uint8_t macLength = 4U; - uint8_t mac[macLength]; - ::memset(mac, 0x00U, macLength); + ::memset(expectedMAC, 0x00U, macLength); uint8_t macAlgId = dataBlock[fullLength - 4U]; uint16_t macKId = GET_UINT16(dataBlock, fullLength - 3U); uint8_t macFormat = dataBlock[fullLength - 1U]; - ::memcpy(mac, dataBlock + fullLength - (macLength + 5U), macLength); + ::memcpy(expectedMAC, dataBlock + fullLength - (macLength + 5U), macLength); ::LogInfoEx("T", "P25_MAC_CMAC_Crypto_Test, macAlgId = $%02X, macKId = $%04X, macFormat = $%02X", macAlgId, macKId, macFormat); - Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, Expected MAC", mac, macLength); + Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, Expected MAC", expectedMAC, macLength); } break; case KMM_MAC::ENH_MAC: { uint8_t macLength = 8U; - uint8_t mac[macLength]; - ::memset(mac, 0x00U, macLength); + ::memset(expectedMAC, 0x00U, macLength); uint8_t macAlgId = dataBlock[fullLength - 4U]; uint16_t macKId = GET_UINT16(dataBlock, fullLength - 3U); uint8_t macFormat = dataBlock[fullLength - 1U]; - ::memcpy(mac, dataBlock + fullLength - (macLength + 5U), macLength); + ::memcpy(expectedMAC, dataBlock + fullLength - (macLength + 5U), macLength); ::LogInfoEx("T", "P25_MAC_CMAC_Crypto_Test, macAlgId = $%02X, macKId = $%04X, macFormat = $%02X", macAlgId, macKId, macFormat); - Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, Expected MAC", mac, macLength); + Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, Expected MAC", expectedMAC, macLength); } break; @@ -119,6 +120,16 @@ TEST_CASE("AES_MAC_CMAC", "[AES256 MAC CMAC Test]") { } } + UInt8Array mac = crypto.cryptAES_KMM_CMAC(expectedCMAC, dataBlock, fullLength); + Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, MAC", mac.get(), 8U); + + for (uint32_t i = 0; i < 8U; i++) { + if (mac[i] != expectedMAC[i]) { + ::LogError("T", "P25_MAC_CMAC_Crypto_Test, INVALID AT IDX %d", i); + failed = true; + } + } + REQUIRE(failed==false); } } From 489a1c43fd09e9e2e902f23adf5ee7eeb96ed097 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 6 Nov 2025 00:31:31 -0500 Subject: [PATCH 132/200] test should be using generated key; --- tests/crypto/P25_MAC_CBC_Test.cpp | 6 +++--- tests/crypto/P25_MAC_CMAC_Test.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/crypto/P25_MAC_CBC_Test.cpp b/tests/crypto/P25_MAC_CBC_Test.cpp index 3da80fe6..66ba252e 100644 --- a/tests/crypto/P25_MAC_CBC_Test.cpp +++ b/tests/crypto/P25_MAC_CBC_Test.cpp @@ -55,9 +55,9 @@ TEST_CASE("AES_MAC_CBC", "[AES256 MAC CBC-MAC Test]") { uint8_t expectedMAC[8U]; - Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, TEK", macTek, 32U); + Utils::dump(2U, "P25_MAC_CBC_Crypto_Test, TEK", macTek, 32U); Utils::dump(2U, "P25_MAC_CBC_Crypto_Test, DataBlock", dataBlock, 80U); - Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, Expected CBC-MAC Key", expectedCBC, 32U); + Utils::dump(2U, "P25_MAC_CBC_Crypto_Test, Expected CBC-MAC Key", expectedCBC, 32U); uint16_t fullLength = 0U; uint16_t messageLength = GET_UINT16(dataBlock, 1U); @@ -120,7 +120,7 @@ TEST_CASE("AES_MAC_CBC", "[AES256 MAC CBC-MAC Test]") { } } - UInt8Array mac = crypto.cryptAES_KMM_CBC(expectedCBC, dataBlock, fullLength); + UInt8Array mac = crypto.cryptAES_KMM_CBC(macKey.get(), dataBlock, fullLength); Utils::dump(2U, "P25_MAC_CBC_Crypto_Test, MAC", mac.get(), 8U); for (uint32_t i = 0; i < 8U; i++) { diff --git a/tests/crypto/P25_MAC_CMAC_Test.cpp b/tests/crypto/P25_MAC_CMAC_Test.cpp index 1f0add9e..f75b8202 100644 --- a/tests/crypto/P25_MAC_CMAC_Test.cpp +++ b/tests/crypto/P25_MAC_CMAC_Test.cpp @@ -120,7 +120,7 @@ TEST_CASE("AES_MAC_CMAC", "[AES256 MAC CMAC Test]") { } } - UInt8Array mac = crypto.cryptAES_KMM_CMAC(expectedCMAC, dataBlock, fullLength); + UInt8Array mac = crypto.cryptAES_KMM_CMAC(macKey.get(), dataBlock, fullLength); Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, MAC", mac.get(), 8U); for (uint32_t i = 0; i < 8U; i++) { From 96caec673381b1c785659934ffbe22f58c10d4d3 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 6 Nov 2025 01:03:36 -0500 Subject: [PATCH 133/200] CBC-MAC now works properly; CMAC MAC generation works (just gotta fix CMAC MAC key generation); --- src/common/p25/Crypto.cpp | 53 ++++++++++++++++++++---------- tests/crypto/P25_MAC_CMAC_Test.cpp | 2 +- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/common/p25/Crypto.cpp b/src/common/p25/Crypto.cpp index f4f6d4b3..e7853eec 100644 --- a/src/common/p25/Crypto.cpp +++ b/src/common/p25/Crypto.cpp @@ -210,8 +210,8 @@ UInt8Array P25Crypto::cryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t tek { #if defined(ENABLE_SSL) // static IV with $A6 pattern defined in TIA-102.AACA-C-2023 13.3 - uint8_t iv[] = { - 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U + uint8_t iv[AES_BLOCK_SIZE / 2] = { + 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U }; int len; @@ -270,8 +270,8 @@ UInt8Array P25Crypto::decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t t { #if defined(ENABLE_SSL) // static IV with $A6 pattern defined in TIA-102.AACA-C-2023 13.3 - uint8_t iv[] = { - 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U + uint8_t iv[AES_BLOCK_SIZE / 2] = { + 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U, 0xA6U }; int len; @@ -329,16 +329,16 @@ UInt8Array P25Crypto::decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t t UInt8Array P25Crypto::cryptAES_KMM_CBC_KDF(const uint8_t* kek, const uint8_t* msg, uint16_t msgLen) { #if defined(ENABLE_SSL) - uint8_t iv[] = { - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U + uint8_t iv[AES_BLOCK_SIZE / 2] = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U }; uint16_t authLen = msgLen - KMM_AES_MAC_LENGTH; - SET_UINT16(authLen, iv, 14U); + SET_UINT16(authLen, iv, 6U); /** DEBUG REMOVEME */ Utils::dump(2U, "KEK", kek, MAX_ENC_KEY_LENGTH_BYTES); - Utils::dump(2U, "IV", iv, 16U); + Utils::dump(2U, "IV", iv, AES_BLOCK_SIZE / 2); /** DEBUG REMOVEME */ int len; @@ -401,7 +401,7 @@ UInt8Array P25Crypto::cryptAES_KMM_CBC_KDF(const uint8_t* kek, const uint8_t* ms UInt8Array P25Crypto::cryptAES_KMM_CBC(const uint8_t* macKey, const uint8_t* msg, uint16_t msgLen) { #if defined(ENABLE_SSL) - uint8_t iv[] = { + uint8_t iv[AES_BLOCK_SIZE] = { 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U }; @@ -514,6 +514,10 @@ UInt8Array P25Crypto::cryptAES_KMM_CMAC_KDF(const uint8_t* kek, const uint8_t* m return nullptr; } + EVP_KDF_CTX_free(ctx); + EVP_KDF_free(kdf); + OSSL_LIB_CTX_free(libCtx); + /** DEBUG REMOVEME */ Utils::dump(2U, "tempBuf", tempBuf, 128U); /** DEBUG REMOVEME */ @@ -538,6 +542,12 @@ UInt8Array P25Crypto::cryptAES_KMM_CMAC(const uint8_t* macKey, const uint8_t* ms uint8_t tempBuf[TEMP_BUFFER_LEN]; ::memset(tempBuf, 0x00U, TEMP_BUFFER_LEN); + uint8_t paddedMessage[TEMP_BUFFER_LEN]; + ::memset(paddedMessage, 0x00U, TEMP_BUFFER_LEN); + + ::memcpy(paddedMessage, msg, msgLen - KMM_AES_MAC_LENGTH - 5U); + ::memcpy(paddedMessage + msgLen - KMM_AES_MAC_LENGTH - 5U, msg + msgLen - 5U, 5U); + // create a library context (required for OpenSSL 3.0+) OSSL_LIB_CTX* libCtx = OSSL_LIB_CTX_new(); if (!libCtx) { @@ -578,26 +588,35 @@ UInt8Array P25Crypto::cryptAES_KMM_CMAC(const uint8_t* macKey, const uint8_t* ms } // provide the message data to be authenticated - if (!EVP_MAC_update(ctx, msg, msgLen)) { - LogError(LOG_P25, "EVP_MAC_init(), failed to set message data to authenticate: %s", ERR_error_string(ERR_get_error(), NULL)); + if (!EVP_MAC_update(ctx, paddedMessage, msgLen - KMM_AES_MAC_LENGTH)) { + LogError(LOG_P25, "EVP_MAC_update(), failed to set message data to authenticate: %s", ERR_error_string(ERR_get_error(), NULL)); EVP_MAC_CTX_free(ctx); EVP_MAC_free(hmac); OSSL_LIB_CTX_free(libCtx); return nullptr; } - // finalize the MAC operation and get the MAC value - if (!EVP_MAC_final(ctx, tempBuf, &len, TEMP_BUFFER_LEN)) { - LogError(LOG_P25, "EVP_MAC_init(), failed to generate MAC: %s", ERR_error_string(ERR_get_error(), NULL)); + // get the length of the MAC + if (!EVP_MAC_final(ctx, NULL, &len, 0)) { + LogError(LOG_P25, "EVP_MAC_final(), failed to get MAC length: %s", ERR_error_string(ERR_get_error(), NULL)); EVP_MAC_CTX_free(ctx); EVP_MAC_free(hmac); OSSL_LIB_CTX_free(libCtx); return nullptr; } - /** DEBUG REMOVEME */ - Utils::dump(2U, "tempBuf", tempBuf, len); - /** DEBUG REMOVEME */ + // generate the MAC + if (!EVP_MAC_final(ctx, tempBuf, &len, len)) { + LogError(LOG_P25, "EVP_MAC_final(), failed to get MAC length: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MAC_CTX_free(ctx); + EVP_MAC_free(hmac); + OSSL_LIB_CTX_free(libCtx); + return nullptr; + } + + EVP_MAC_CTX_free(ctx); + EVP_MAC_free(hmac); + OSSL_LIB_CTX_free(libCtx); UInt8Array wrappedKey = std::unique_ptr(new uint8_t[len]); ::memset(wrappedKey.get(), 0x00U, len); diff --git a/tests/crypto/P25_MAC_CMAC_Test.cpp b/tests/crypto/P25_MAC_CMAC_Test.cpp index f75b8202..5173251c 100644 --- a/tests/crypto/P25_MAC_CMAC_Test.cpp +++ b/tests/crypto/P25_MAC_CMAC_Test.cpp @@ -120,7 +120,7 @@ TEST_CASE("AES_MAC_CMAC", "[AES256 MAC CMAC Test]") { } } - UInt8Array mac = crypto.cryptAES_KMM_CMAC(macKey.get(), dataBlock, fullLength); + UInt8Array mac = crypto.cryptAES_KMM_CMAC(expectedCMAC/* macKey.get()*/, dataBlock, fullLength); Utils::dump(2U, "P25_MAC_CMAC_Crypto_Test, MAC", mac.get(), 8U); for (uint32_t i = 0; i < 8U; i++) { From 54b5f8f9fdb1d408d504f887550522580201b273 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 6 Nov 2025 11:05:36 -0500 Subject: [PATCH 134/200] complete CBC-MAC implemenation, at least for the provided samples from TIA-102.AACA-C this is now passing for the rekey example provided in section 14.3.4; anything compiling against libcommon.a while OpenSSL is enabled will require OpenSSL -lssl; fix delete vs free in various tests; --- src/common/p25/Crypto.cpp | 5 ++ src/common/p25/kmm/KMMFrame.cpp | 77 +++++++++++++++++++++++++++++++ src/common/p25/kmm/KMMFrame.h | 7 +++ tests/edac/CRC_12_Test.cpp | 2 +- tests/edac/CRC_15_Test.cpp | 2 +- tests/edac/CRC_16_Test.cpp | 2 +- tests/edac/CRC_32_Test.cpp | 2 +- tests/edac/CRC_6_Test.cpp | 2 +- tests/edac/CRC_8_Test.cpp | 2 +- tests/edac/CRC_9_Test.cpp | 2 +- tests/edac/CRC_CCITT_161_Test.cpp | 2 +- tests/edac/CRC_CCITT_162_Test.cpp | 2 +- tests/p25/HDU_RS_Test.cpp | 2 +- tests/p25/KMM_Rekey_CBC_Test.cpp | 8 ++++ tests/p25/KMM_Rekey_CMAC_Test.cpp | 8 ++++ tests/p25/LDU1_RS_Test.cpp | 2 +- tests/p25/LDU2_RS_Test.cpp | 2 +- 17 files changed, 117 insertions(+), 12 deletions(-) diff --git a/src/common/p25/Crypto.cpp b/src/common/p25/Crypto.cpp index e7853eec..de888815 100644 --- a/src/common/p25/Crypto.cpp +++ b/src/common/p25/Crypto.cpp @@ -329,6 +329,11 @@ UInt8Array P25Crypto::decryptAES_TEK(const uint8_t* kek, uint8_t* tek, uint8_t t UInt8Array P25Crypto::cryptAES_KMM_CBC_KDF(const uint8_t* kek, const uint8_t* msg, uint16_t msgLen) { #if defined(ENABLE_SSL) + + /* + ** bryanb: some bizarre bullshit requiring a 8-byte IV -- thanks Ilya for helping look at this + */ + uint8_t iv[AES_BLOCK_SIZE / 2] = { 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U }; diff --git a/src/common/p25/kmm/KMMFrame.cpp b/src/common/p25/kmm/KMMFrame.cpp index a9271e7d..af307612 100644 --- a/src/common/p25/kmm/KMMFrame.cpp +++ b/src/common/p25/kmm/KMMFrame.cpp @@ -9,10 +9,12 @@ */ #include "Defines.h" #include "p25/P25Defines.h" +#include "p25/Crypto.h" #include "p25/kmm/KMMFrame.h" #include "Log.h" using namespace p25; +using namespace p25::crypto; using namespace p25::defines; using namespace p25::kmm; @@ -59,6 +61,80 @@ KMMFrame::~KMMFrame() delete[] m_mac; } +/* Generate a MAC code for the given KMM frame. */ + +void KMMFrame::generateMAC(uint8_t* kek, uint8_t* data) +{ + assert(data != nullptr); + + if (m_macType == KMM_MAC::NO_MAC) { + ::LogError(LOG_P25, "KMMFrame::generateMAC(), MAC type is set to no MAC, aborting MAC signing"); + return; + } + + if (m_macAlgId == ALGO_UNENCRYPT) { + ::LogError(LOG_P25, "KMMFrame::generateMAC(), MAC algorithm is not set, aborting MAC signing"); + return; + } + + if (m_macKId == 0U) { + ::LogError(LOG_P25, "KMMFrame::generateMAC(), MAC key ID is not set, aborting MAC signing"); + return; + } + + switch (m_macType) { + case KMM_MAC::DES_MAC: + ::LogError(LOG_P25, "KMMFrame::generateMAC(), DES MAC type is not supported, macType = $%02X", m_macType); + return; + + case KMM_MAC::ENH_MAC: + { + uint8_t macLength = P25DEF::KMM_AES_MAC_LENGTH; + P25Crypto crypto; + + switch (m_macFormat) { + case KMM_MAC_FORMAT_CBC: + { + // generate intermediate derived key + UInt8Array macKey = crypto.cryptAES_KMM_CBC_KDF(kek, data, m_messageFullLength); + + // generate MAC + UInt8Array mac = crypto.cryptAES_KMM_CBC(macKey.get(), data, m_messageFullLength); + + ::memset(data + m_messageFullLength - (macLength + 5U), 0x00U, macLength); + ::memcpy(data + m_messageFullLength - (macLength + 5U), mac.get(), macLength); + } + break; + + case KMM_MAC_FORMAT_CMAC: + { + // generate intermediate derived key + UInt8Array macKey = crypto.cryptAES_KMM_CMAC_KDF(kek, data, m_messageFullLength, m_messageNumber > 0U); + + // generate MAC + UInt8Array mac = crypto.cryptAES_KMM_CMAC(macKey.get(), data, m_messageFullLength); + + ::memset(data + m_messageFullLength - (macLength + 5U), 0x00U, macLength); + ::memcpy(data + m_messageFullLength - (macLength + 5U), mac.get(), macLength); + } + break; + + default: + ::LogError(LOG_P25, "KMMFrame::generateMAC(), unknown KMM MAC format type value, macType = $%02X", m_macType); + break; + } + } + break; + + case KMM_MAC::NO_MAC: + break; + + default: + ::LogError(LOG_P25, "KMMFrame::generateMAC(), unknown KMM MAC inventory type value, macType = $%02X", m_macType); + break; + } +} + /* Returns a string that represents the current KMM frame. */ std::string KMMFrame::toString() @@ -105,6 +181,7 @@ bool KMMFrame::decodeHeader(const uint8_t* data) m_macAlgId = data[m_messageFullLength - 4U]; m_macKId = GET_UINT16(data, m_messageFullLength - 3U); + m_macFormat = data[m_messageFullLength - 1U]; ::memset(m_mac, 0x00U, macLength); ::memcpy(m_mac, data + m_messageFullLength - (macLength + 5U), macLength); diff --git a/src/common/p25/kmm/KMMFrame.h b/src/common/p25/kmm/KMMFrame.h index 96b60171..ee391463 100644 --- a/src/common/p25/kmm/KMMFrame.h +++ b/src/common/p25/kmm/KMMFrame.h @@ -105,6 +105,13 @@ namespace p25 */ virtual void encode(uint8_t* data) = 0; + /** + * @brief Generate a MAC code for the given KMM frame. + * @param kek Key Encryption Key + * @param[out] data Buffer to encode KMM MAC to. + */ + void generateMAC(uint8_t* kek, uint8_t* data); + /** * @brief Returns a string that represents the current KMM frame. * @returns std::string String representation of the KMM frame. diff --git a/tests/edac/CRC_12_Test.cpp b/tests/edac/CRC_12_Test.cpp index 91c59526..8458d279 100644 --- a/tests/edac/CRC_12_Test.cpp +++ b/tests/edac/CRC_12_Test.cpp @@ -59,7 +59,7 @@ TEST_CASE("CRC", "[12-bit Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } diff --git a/tests/edac/CRC_15_Test.cpp b/tests/edac/CRC_15_Test.cpp index 9920896c..cf48095d 100644 --- a/tests/edac/CRC_15_Test.cpp +++ b/tests/edac/CRC_15_Test.cpp @@ -59,7 +59,7 @@ TEST_CASE("CRC", "[15-bit Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } diff --git a/tests/edac/CRC_16_Test.cpp b/tests/edac/CRC_16_Test.cpp index 9da1e5e5..0503d91d 100644 --- a/tests/edac/CRC_16_Test.cpp +++ b/tests/edac/CRC_16_Test.cpp @@ -59,7 +59,7 @@ TEST_CASE("CRC", "[16-bit Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } diff --git a/tests/edac/CRC_32_Test.cpp b/tests/edac/CRC_32_Test.cpp index 0e8321c6..6411e0ab 100644 --- a/tests/edac/CRC_32_Test.cpp +++ b/tests/edac/CRC_32_Test.cpp @@ -58,7 +58,7 @@ TEST_CASE("CRC", "[32-bit Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } diff --git a/tests/edac/CRC_6_Test.cpp b/tests/edac/CRC_6_Test.cpp index 89571bef..f31d05a4 100644 --- a/tests/edac/CRC_6_Test.cpp +++ b/tests/edac/CRC_6_Test.cpp @@ -59,7 +59,7 @@ TEST_CASE("CRC", "[6-bit Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } diff --git a/tests/edac/CRC_8_Test.cpp b/tests/edac/CRC_8_Test.cpp index 56561f65..fd72c7b7 100644 --- a/tests/edac/CRC_8_Test.cpp +++ b/tests/edac/CRC_8_Test.cpp @@ -50,7 +50,7 @@ TEST_CASE("CRC", "[8-bit Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } diff --git a/tests/edac/CRC_9_Test.cpp b/tests/edac/CRC_9_Test.cpp index 2fa872d9..975a1637 100644 --- a/tests/edac/CRC_9_Test.cpp +++ b/tests/edac/CRC_9_Test.cpp @@ -55,7 +55,7 @@ TEST_CASE("CRC", "[9-bit Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } diff --git a/tests/edac/CRC_CCITT_161_Test.cpp b/tests/edac/CRC_CCITT_161_Test.cpp index e63081c1..b320ca65 100644 --- a/tests/edac/CRC_CCITT_161_Test.cpp +++ b/tests/edac/CRC_CCITT_161_Test.cpp @@ -58,7 +58,7 @@ TEST_CASE("CRC", "[16-bit CCITT-161 Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } diff --git a/tests/edac/CRC_CCITT_162_Test.cpp b/tests/edac/CRC_CCITT_162_Test.cpp index 88e47445..402b8587 100644 --- a/tests/edac/CRC_CCITT_162_Test.cpp +++ b/tests/edac/CRC_CCITT_162_Test.cpp @@ -58,7 +58,7 @@ TEST_CASE("CRC", "[16-bit CCITT-162 Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } diff --git a/tests/p25/HDU_RS_Test.cpp b/tests/p25/HDU_RS_Test.cpp index cfb218ac..78d8604b 100644 --- a/tests/p25/HDU_RS_Test.cpp +++ b/tests/p25/HDU_RS_Test.cpp @@ -93,7 +93,7 @@ TEST_CASE("HDU", "[Reed-Soloman 36,20,17 Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } diff --git a/tests/p25/KMM_Rekey_CBC_Test.cpp b/tests/p25/KMM_Rekey_CBC_Test.cpp index 395da345..2d3edf9d 100644 --- a/tests/p25/KMM_Rekey_CBC_Test.cpp +++ b/tests/p25/KMM_Rekey_CBC_Test.cpp @@ -29,6 +29,13 @@ TEST_CASE("KMM_ReKey", "[P25 KMM Rekey Command CBC Test]") { srand((unsigned int)time(NULL)); + // MAC TEK + uint8_t macTek[] = + { + 0x16, 0x85, 0x62, 0x45, 0x3B, 0x3E, 0x7F, 0x61, 0x8D, 0x68, 0xB3, 0x87, 0xE0, 0xB9, 0x97, 0xE1, + 0xFB, 0x0F, 0x26, 0x4F, 0xA8, 0x3B, 0x74, 0xE4, 0x3B, 0x17, 0x29, 0x17, 0xBD, 0x39, 0x33, 0x9F + }; + // data block uint8_t dataBlock[] = { @@ -85,6 +92,7 @@ TEST_CASE("KMM_ReKey", "[P25 KMM Rekey Command CBC Test]") { UInt8Array kmmFrame = std::make_unique(outKmm.fullLength()); outKmm.encode(kmmFrame.get()); + outKmm.generateMAC(macTek, kmmFrame.get()); Utils::dump(2U, "P25_KMM_ReKey_CBC_Test, GeneratedDataBlock", kmmFrame.get(), outKmm.fullLength()); diff --git a/tests/p25/KMM_Rekey_CMAC_Test.cpp b/tests/p25/KMM_Rekey_CMAC_Test.cpp index 4027c2a4..396c326d 100644 --- a/tests/p25/KMM_Rekey_CMAC_Test.cpp +++ b/tests/p25/KMM_Rekey_CMAC_Test.cpp @@ -29,6 +29,13 @@ TEST_CASE("KMM_ReKey", "[P25 KMM Rekey Command CMAC Test]") { srand((unsigned int)time(NULL)); + // MAC TEK + uint8_t macTek[] = + { + 0x16, 0x85, 0x62, 0x45, 0x3B, 0x3E, 0x7F, 0x61, 0x8D, 0x68, 0xB3, 0x87, 0xE0, 0xB9, 0x97, 0xE1, + 0xFB, 0x0F, 0x26, 0x4F, 0xA8, 0x3B, 0x74, 0xE4, 0x3B, 0x17, 0x29, 0x17, 0xBD, 0x39, 0x33, 0x9F + }; + // data block uint8_t dataBlock[] = { @@ -85,6 +92,7 @@ TEST_CASE("KMM_ReKey", "[P25 KMM Rekey Command CMAC Test]") { UInt8Array kmmFrame = std::make_unique(outKmm.fullLength()); outKmm.encode(kmmFrame.get()); + outKmm.generateMAC(macTek, kmmFrame.get()); Utils::dump(2U, "P25_KMM_ReKey_CMAC_Test, GeneratedDataBlock", kmmFrame.get(), outKmm.fullLength()); diff --git a/tests/p25/LDU1_RS_Test.cpp b/tests/p25/LDU1_RS_Test.cpp index 86ef8e53..58fc7c45 100644 --- a/tests/p25/LDU1_RS_Test.cpp +++ b/tests/p25/LDU1_RS_Test.cpp @@ -91,7 +91,7 @@ TEST_CASE("LDU1", "[Reed-Soloman 24,12,13 Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } diff --git a/tests/p25/LDU2_RS_Test.cpp b/tests/p25/LDU2_RS_Test.cpp index 9d1b8879..9a0de303 100644 --- a/tests/p25/LDU2_RS_Test.cpp +++ b/tests/p25/LDU2_RS_Test.cpp @@ -90,7 +90,7 @@ TEST_CASE("LDU2", "[Reed-Soloman 24,16,9 Test]") { } cleanup: - delete random; + free(random); REQUIRE(failed==false); } } From c5a298f4a1400dc0bec8159d812f18f0e27d87a0 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 6 Nov 2025 11:08:55 -0500 Subject: [PATCH 135/200] typo --- src/common/p25/Crypto.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/p25/Crypto.cpp b/src/common/p25/Crypto.cpp index de888815..4dbe96be 100644 --- a/src/common/p25/Crypto.cpp +++ b/src/common/p25/Crypto.cpp @@ -331,7 +331,7 @@ UInt8Array P25Crypto::cryptAES_KMM_CBC_KDF(const uint8_t* kek, const uint8_t* ms #if defined(ENABLE_SSL) /* - ** bryanb: some bizarre bullshit requiring a 8-byte IV -- thanks Ilya for helping look at this + ** bryanb: some bizarre bullshit requiring a 8-byte IV -- thanks Ilya (https://github.com/ilyacodes) for helping look at this */ uint8_t iv[AES_BLOCK_SIZE / 2] = { From 661ebe6824b72f42ded496b04bb55d01f7f1026e Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 6 Nov 2025 13:25:08 -0500 Subject: [PATCH 136/200] implement OFB data encryption per TIA-102.AAAD-B; test proper final encrypted output for CBC-MAC check; implement use of cryptAES_PDU() in P25OTARService; --- src/common/p25/Crypto.cpp | 18 ++++++++++++ src/common/p25/Crypto.h | 7 +++++ src/common/p25/P25Defines.h | 3 ++ src/common/p25/kmm/KMMRekeyCommand.cpp | 9 +----- src/common/p25/kmm/KMMRekeyCommand.h | 5 ---- src/fne/network/P25OTARService.cpp | 27 ++++++++++++++++-- src/fne/network/P25OTARService.h | 3 ++ tests/p25/KMM_Rekey_CBC_Test.cpp | 39 +++++++++++++++++++++++++- tests/p25/KMM_Rekey_CMAC_Test.cpp | 2 +- 9 files changed, 96 insertions(+), 17 deletions(-) diff --git a/src/common/p25/Crypto.cpp b/src/common/p25/Crypto.cpp index 4dbe96be..7ff7873e 100644 --- a/src/common/p25/Crypto.cpp +++ b/src/common/p25/Crypto.cpp @@ -634,6 +634,24 @@ UInt8Array P25Crypto::cryptAES_KMM_CMAC(const uint8_t* macKey, const uint8_t* ms #endif // ENABLE_SSL } +/* Helper to crypt a P25 PDU frame using AES-256. */ + +void P25Crypto::cryptAES_PDU(uint8_t* frame, uint8_t frameLen) +{ + if (m_keystream == nullptr) + return; + + uint32_t offset = 16U; + for (uint8_t i = 0U; i < frameLen; i++) { + if (offset > 240U) { + offset = 16U; + } + + frame[i] ^= m_keystream[offset]; + offset++; + } +} + /* Helper to crypt IMBE audio using AES-256. */ void P25Crypto::cryptAES_IMBE(uint8_t* imbe, DUID::E duid) diff --git a/src/common/p25/Crypto.h b/src/common/p25/Crypto.h index 733dcd23..ed47ab6c 100644 --- a/src/common/p25/Crypto.h +++ b/src/common/p25/Crypto.h @@ -130,6 +130,13 @@ namespace p25 */ UInt8Array cryptAES_KMM_CMAC(const uint8_t* macKey, const uint8_t* msg, uint16_t msgLen); + /** + * @brief Helper to crypt a P25 PDU frame using AES-256. + * @param frame Data frame + * @param frameLen Data frame Length + */ + void cryptAES_PDU(uint8_t* frame, uint8_t frameLen); + /** * @brief Helper to crypt P25 IMBE audio using AES-256. * @param imbe Buffer containing IMBE to crypt. diff --git a/src/common/p25/P25Defines.h b/src/common/p25/P25Defines.h index f852c765..68b17157 100644 --- a/src/common/p25/P25Defines.h +++ b/src/common/p25/P25Defines.h @@ -537,6 +537,9 @@ namespace p25 /** @brief KMM MAC Format - CMAC */ const uint8_t KMM_MAC_FORMAT_CMAC = 0x41U; + /** @brief KMM Keyset Format - KEKs */ + const uint8_t KEYSET_FORMAT_KEK = 0x80U; + /** @brief KMM Body/Key Format - TEK Included */ const uint8_t KEY_FORMAT_TEK = 0x80U; /** @brief KMM Body/Key Format - KEK Exists */ diff --git a/src/common/p25/kmm/KMMRekeyCommand.cpp b/src/common/p25/kmm/KMMRekeyCommand.cpp index 58fccb81..d463a3ae 100644 --- a/src/common/p25/kmm/KMMRekeyCommand.cpp +++ b/src/common/p25/kmm/KMMRekeyCommand.cpp @@ -71,8 +71,6 @@ bool KMMRekeyCommand::decode(const uint8_t* data) KMMFrame::decodeHeader(data); - m_containsTeks = false; - m_decryptInfoFmt = data[10U + m_bodyOffset]; // Decryption Instruction Format m_algId = data[11U + m_bodyOffset]; // Algorithm ID m_kId = GET_UINT16(data, 12U + m_bodyOffset); // Key ID @@ -87,10 +85,6 @@ bool KMMRekeyCommand::decode(const uint8_t* data) uint8_t keysetCount = data[14U + (m_bodyOffset + offset)]; for (uint8_t n = 0U; n < keysetCount; n++) { KeysetItem keysetItem = KeysetItem(); - - if (!m_containsTeks) - m_containsTeks = data[15U + (m_bodyOffset + offset)] & KEY_FORMAT_TEK; - keysetItem.keysetId(data[16U + (m_bodyOffset + offset)]); keysetItem.algId(data[17U + (m_bodyOffset + offset)]); keysetItem.keyLength(data[18U + (m_bodyOffset + offset)]); @@ -154,8 +148,7 @@ void KMMRekeyCommand::encode(uint8_t* data) data[14U + (m_bodyOffset + offset)] = keysetCount; for (auto keysetItem : m_keysets) { - uint8_t keysetFormat = (m_containsTeks) ? KEY_FORMAT_TEK : 0x00U; - data[15U + (m_bodyOffset + offset)] = keysetFormat; + data[15U + (m_bodyOffset + offset)] = 0U; // currently we won't send KEKs data[16U + (m_bodyOffset + offset)] = keysetItem.keysetId(); data[17U + (m_bodyOffset + offset)] = keysetItem.algId(); data[18U + (m_bodyOffset + offset)] = keysetItem.keyLength(); diff --git a/src/common/p25/kmm/KMMRekeyCommand.h b/src/common/p25/kmm/KMMRekeyCommand.h index 49f97a7c..1879f292 100644 --- a/src/common/p25/kmm/KMMRekeyCommand.h +++ b/src/common/p25/kmm/KMMRekeyCommand.h @@ -107,11 +107,6 @@ namespace p25 */ DECLARE_PROPERTY(uint16_t, kId, KId); - /** - * @brief Flag indicating whether or not this rekey command contains TEKs or KEKs. - */ - DECLARE_PROPERTY(bool, containsTeks, ContainsTEKs); - /** * @brief List of keysets. */ diff --git a/src/fne/network/P25OTARService.cpp b/src/fne/network/P25OTARService.cpp index 8a8921de..3c957f67 100644 --- a/src/fne/network/P25OTARService.cpp +++ b/src/fne/network/P25OTARService.cpp @@ -57,6 +57,7 @@ P25OTARService::P25OTARService(FNENetwork* network, P25PacketData* packetData, b m_threadPool(MAX_THREAD_CNT, "otar"), m_network(network), m_packetData(packetData), + m_rsiMessageNumber(), m_allowNoUKEKRekey(false), m_debug(debug), m_verbose(verbose) @@ -298,7 +299,7 @@ UInt8Array P25OTARService::cryptKMM(uint8_t algoId, uint16_t kid, uint8_t* mi, c switch (algoId) { case P25DEF::ALGO_AES_256: - // TODO: implement handling AES-256 KMM encryption/decryption for data blocks + crypto.cryptAES_PDU(outBuffer.get(), len); return outBuffer; default: LogError(LOG_P25, "unsupported KEK algorithm, algoId = $%02X", algoId); @@ -353,6 +354,17 @@ UInt8Array P25OTARService::processKMM(const uint8_t* data, uint32_t len, uint32_ llId = frame->getSrcLLId(); } + // does this llId have a RSI message number setup? + if (m_rsiMessageNumber.find(llId) == m_rsiMessageNumber.end()) { + m_rsiMessageNumber[llId] = 0U; + } else { + // roll message number + uint16_t mn = m_rsiMessageNumber[llId]; + mn++; + + m_rsiMessageNumber[llId] = mn; + } + switch (frame->getMessageId()) { case KMM_MessageType::HELLO: { @@ -445,6 +457,11 @@ UInt8Array P25OTARService::write_KMM_Rekey_Command(uint32_t llId, uint32_t kmmRS uint8_t mi[MI_LENGTH_BYTES]; ::memset(mi, 0x00U, MI_LENGTH_BYTES); + uint16_t mn = 0U; + if (m_rsiMessageNumber.find(llId) != m_rsiMessageNumber.end()) { + mn = m_rsiMessageNumber[llId]; + } + P25Crypto crypto; crypto.generateMI(); crypto.getMI(mi); @@ -489,7 +506,12 @@ UInt8Array P25OTARService::write_KMM_Rekey_Command(uint32_t llId, uint32_t kmmRS outKmm.setSrcLLId(WUID_FNE); outKmm.setDstLLId(kmmRSI); - outKmm.setMI(mi); + outKmm.setMACType(KMM_MAC::ENH_MAC); + outKmm.setMACAlgId(kekAlgId); + outKmm.setMACKId(kekKId); + outKmm.setMACFormat(KMM_MAC_FORMAT_CBC); + + outKmm.setMessageNumber(mn); outKmm.setAlgId(kekAlgId); outKmm.setKId(kekKId); @@ -553,6 +575,7 @@ UInt8Array P25OTARService::write_KMM_Rekey_Command(uint32_t llId, uint32_t kmmRS UInt8Array kmmFrame = std::make_unique(outKmm.length()); outKmm.encode(kmmFrame.get()); + outKmm.generateMAC(kekKey, kmmFrame.get()); return kmmFrame; } diff --git a/src/fne/network/P25OTARService.h b/src/fne/network/P25OTARService.h index 0a8683c8..2d8c53e6 100644 --- a/src/fne/network/P25OTARService.h +++ b/src/fne/network/P25OTARService.h @@ -17,6 +17,7 @@ #define __P25_OTAR_SERVICE_H__ #include "fne/Defines.h" +#include "common/concurrent/unordered_map.h" #include "common/p25/P25Defines.h" #include "common/p25/Crypto.h" #include "common/network/udp/Socket.h" @@ -104,6 +105,8 @@ namespace network FNENetwork* m_network; network::callhandler::packetdata::P25PacketData* m_packetData; + concurrent::unordered_map m_rsiMessageNumber; + bool m_allowNoUKEKRekey; bool m_debug; diff --git a/tests/p25/KMM_Rekey_CBC_Test.cpp b/tests/p25/KMM_Rekey_CBC_Test.cpp index 2d3edf9d..a8b3ae6c 100644 --- a/tests/p25/KMM_Rekey_CBC_Test.cpp +++ b/tests/p25/KMM_Rekey_CBC_Test.cpp @@ -9,11 +9,13 @@ */ #include "host/Defines.h" #include "common/p25/P25Defines.h" +#include "common/p25/Crypto.h" #include "common/p25/kmm/KMMRekeyCommand.h" #include "common/Log.h" #include "common/Utils.h" using namespace p25; +using namespace p25::crypto; using namespace p25::defines; using namespace p25::kmm; @@ -21,7 +23,7 @@ using namespace p25::kmm; #include #include -TEST_CASE("KMM_ReKey", "[P25 KMM Rekey Command CBC Test]") { +TEST_CASE("KMM_ReKey_CBC", "[P25 KMM Rekey Command CBC Test]") { SECTION("P25_KMM_ReKey_CBC_Test") { bool failed = false; @@ -54,7 +56,25 @@ TEST_CASE("KMM_ReKey", "[P25 KMM Rekey Command CBC Test]") { 0x84, 0x09, 0x45, 0x37, 0x23, 0x72, 0xFB, 0x80 }; + uint8_t encryptMI[] = + { + 0x70, 0x30, 0xF1, 0xF7, 0x65, 0x69, 0x26, 0x67 + }; + + // final encrypted block + uint8_t encryptedBlock[] = + { + 0x67, 0x75, 0xB1, 0xD1, 0x8A, 0xBD, 0xCF, 0x86, 0x08, 0x54, 0xDF, 0x09, 0x8E, 0xA3, 0x41, 0x29, + 0x13, 0x2A, 0x0E, 0x48, 0x4C, 0xCC, 0x5C, 0xAE, 0x80, 0x08, 0x0B, 0x19, 0xF7, 0x08, 0xAE, 0x8F, + 0xB8, 0x40, 0xAA, 0x2E, 0x3E, 0x5E, 0xCD, 0x03, 0x73, 0x52, 0x75, 0xFE, 0xE2, 0x88, 0x0E, 0x6D, + 0xDD, 0x00, 0xC1, 0x11, 0x42, 0x8F, 0xEE, 0x39, 0xC6, 0x2B, 0xF3, 0xC1, 0xD2, 0xEE, 0x3B, 0xEB, + 0xBB, 0x7C, 0x44, 0xA5, 0xE3, 0xC9, 0x30, 0x8C, 0x5D, 0xE9, 0x17, 0x84, 0x7C, 0x17, 0xAF, 0x23 + }; + + Utils::dump(2U, "P25_KMM_ReKey_CBC_Test, MAC TEK", macTek, 32U); + Utils::dump(2U, "P25_KMM_ReKey_CBC_Test, OFB MI", encryptMI, 8U); Utils::dump(2U, "P25_KMM_ReKey_CBC_Test, DataBlock", dataBlock, 80U); + Utils::dump(2U, "P25_KMM_ReKey_CBC_Test, EncryptedBlock", encryptedBlock, 80U); KMMRekeyCommand outKmm = KMMRekeyCommand(); @@ -103,6 +123,23 @@ TEST_CASE("KMM_ReKey", "[P25 KMM Rekey Command CBC Test]") { } } + P25Crypto crypto; + crypto.setMI(encryptMI); + crypto.setTEKAlgoId(ALGO_AES_256); + crypto.setKey(macTek, 32U); + crypto.generateKeystream(); + + crypto.cryptAES_PDU(kmmFrame.get(), outKmm.fullLength()); + + Utils::dump(2U, "P25_KMM_ReKey_CBC_Test, EncryptedDataBlock", kmmFrame.get(), outKmm.fullLength()); + + for (uint32_t i = 0; i < outKmm.fullLength(); i++) { + if (kmmFrame.get()[i] != encryptedBlock[i]) { + ::LogError("T", "P25_KMM_ReKey_CBC_Test, INVALID AT IDX %d", i); + failed = true; + } + } + REQUIRE(failed==false); } } diff --git a/tests/p25/KMM_Rekey_CMAC_Test.cpp b/tests/p25/KMM_Rekey_CMAC_Test.cpp index 396c326d..2a54e9a4 100644 --- a/tests/p25/KMM_Rekey_CMAC_Test.cpp +++ b/tests/p25/KMM_Rekey_CMAC_Test.cpp @@ -21,7 +21,7 @@ using namespace p25::kmm; #include #include -TEST_CASE("KMM_ReKey", "[P25 KMM Rekey Command CMAC Test]") { +TEST_CASE("KMM_ReKey_CMAC", "[P25 KMM Rekey Command CMAC Test]") { SECTION("P25_KMM_ReKey_CMAC_Test") { bool failed = false; From c3f14f94e58f622b35e3f35bb210b809f7f24cde Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 6 Nov 2025 14:29:17 -0500 Subject: [PATCH 137/200] add experimental DES crypto; --- src/common/DESCrypto.cpp | 362 ++++++++++++++++++++++++++++++++++++++ src/common/DESCrypto.h | 71 ++++++++ src/common/p25/Crypto.cpp | 50 ++++++ src/common/p25/Crypto.h | 6 + 4 files changed, 489 insertions(+) create mode 100644 src/common/DESCrypto.cpp create mode 100644 src/common/DESCrypto.h diff --git a/src/common/DESCrypto.cpp b/src/common/DESCrypto.cpp new file mode 100644 index 00000000..83c96f0f --- /dev/null +++ b/src/common/DESCrypto.cpp @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: MIT +/* + * Digital Voice Modem - Common Library + * MIT Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "DESCrypto.h" +#include "Log.h" +#include "Utils.h" + +using namespace crypto; + +#include +#include +#include + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +#define LB32_MASK 0x00000001 +#define LB64_MASK 0x0000000000000001 +#define L64_MASK 0x00000000ffffffff +#define H64_MASK 0xffffffff00000000 + +// Permuted Choice 1 Table [7*8] +static const char PC1_TABLE[] = { + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4 +}; + +// Permuted Choice 2 Table [6*8] +static const char PC2_TABLE[] = { + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 +}; + +// Iteration Shift Array +static const char ITERATION_SHIFT[] = { +// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +// Initial Permutation Table [8*8] +static const char IP[] = { + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7 +}; + +// Inverse Initial Permutation Table [8*8] +static const char FP[] = { + 40, 8, 48, 16, 56, 24, 64, 32, + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25 +}; + +// Expansion Table [6*8] +static const char EXPANSION[] = { + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1 +}; + +// Post S-Box Permutation [4*8] +static const char PBOX[] = { + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25 +}; + +// The S-Box Tables [8*16*4] +static const char SBOX[8][64] = { + { // S1 + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 }, + { // S2 + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 }, + { // S3 + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 }, + { // S4 + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 }, + { // S5 + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 }, + { // S6 + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 }, + { // S7 + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 }, + { // S8 + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 } +}; + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the DES class. */ + +DES::DES() = default; + +/* Encrypt input block with given key. */ + +uint8_t* DES::encryptBlock(const uint8_t block[], const uint8_t key[]) +{ + ulong64_t keyValue = toValue(key); + ulong64_t blockValue = toValue(block); + + generateSubkeys(keyValue); + ulong64_t out = des(blockValue, false); + + return fromValue(out); +} + +/* Decrypt input block with given key. */ + +uint8_t* DES::decryptBlock(const uint8_t block[], const uint8_t key[]) +{ + ulong64_t keyValue = toValue(key); + ulong64_t blockValue = toValue(block); + + generateSubkeys(keyValue); + ulong64_t out = des(blockValue, true); + + return fromValue(out); +} + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to convert payload bytes to a 64-bit long value. */ + +ulong64_t DES::toValue(const uint8_t* payload) +{ + assert(payload != nullptr); + + ulong64_t value = 0U; + + // combine bytes into ulong64_t (8 byte) value + value = payload[0U]; + value = (value << 8) + payload[1U]; + value = (value << 8) + payload[2U]; + value = (value << 8) + payload[3U]; + value = (value << 8) + payload[4U]; + value = (value << 8) + payload[5U]; + value = (value << 8) + payload[6U]; + value = (value << 8) + payload[7U]; + + return value; +} + +/* Internal helper to convert a 64-bit long value to payload bytes. */ + +uint8_t* DES::fromValue(const ulong64_t value) +{ + uint8_t* payload = new uint8_t[8U]; + ::memset(payload, 0x00U, 8U); + + // split ulong64_t (8 byte) value into bytes + payload[0U] = (uint8_t)((value >> 56) & 0xFFU); + payload[1U] = (uint8_t)((value >> 48) & 0xFFU); + payload[2U] = (uint8_t)((value >> 40) & 0xFFU); + payload[3U] = (uint8_t)((value >> 32) & 0xFFU); + payload[4U] = (uint8_t)((value >> 24) & 0xFFU); + payload[5U] = (uint8_t)((value >> 16) & 0xFFU); + payload[6U] = (uint8_t)((value >> 8) & 0xFFU); + payload[7U] = (uint8_t)((value >> 0) & 0xFFU); + + return payload; +} + +/* */ + +void DES::generateSubkeys(uint64_t key) +{ + // initial key schedule calculation + uint64_t PC1 = 0; // 56 bits + for (uint8_t i = 0; i < 56; i++) { + PC1 <<= 1; + PC1 |= (key >> (64 - PC1_TABLE[i])) & LB64_MASK; + } + + // 28 bits + uint32_t C = (uint32_t)((PC1 >> 28) & 0x000000000fffffff); + uint32_t D = (uint32_t)(PC1 & 0x000000000fffffff); + + // calculation of the 16 keys + for (uint8_t i = 0; i < 16; i++) { + // key schedule, shifting Ci and Di + for (uint8_t j = 0; j < ITERATION_SHIFT[i]; j++) { + C = (0x0fffffff & (C << 1)) | (0x00000001 & (C >> 27)); + D = (0x0fffffff & (D << 1)) | (0x00000001 & (D >> 27)); + } + + uint64_t PC2 = (((uint64_t)C) << 28) | (uint64_t)D; + + sub_key[i] = 0; // 48 bits (2*24) + for (uint8_t j = 0; j < 48; j++) { + sub_key[i] <<= 1; + sub_key[i] |= (PC2 >> (56 - PC2_TABLE[j])) & LB64_MASK; + } + } +} + +/* */ + +ulong64_t DES::des(ulong64_t block, bool decrypt) +{ + // applying initial permutation + block = intialPermutation(block); + + // dividing T' into two 32-bit parts + uint32_t L = (uint32_t)(block >> 32) & L64_MASK; + uint32_t R = (uint32_t)(block & L64_MASK); + + // 16 rounds + for (uint8_t i = 0; i < 16; i++) { + uint32_t F = decrypt ? f(R, sub_key[15 - i]) : f(R, sub_key[i]); + feistel(L, R, F); + } + + // swapping the two parts + block = (((uint64_t)R) << 32) | (uint64_t)L; + + // applying final permutation + return finalPermutation(block); +} + +/* */ + +ulong64_t DES::intialPermutation(ulong64_t block) +{ + // initial permutation + uint64_t result = 0; + for (uint8_t i = 0; i < 64; i++) { + result <<= 1; + result |= (block >> (64 - IP[i])) & LB64_MASK; + } + + return result; +} + +/* */ + +ulong64_t DES::finalPermutation(ulong64_t block) +{ + // inverse initial permutation + uint64_t result = 0; + for (uint8_t i = 0; i < 64; i++) { + result <<= 1; + result |= (block >> (64 - FP[i])) & LB64_MASK; + } + + return result; +} + +/* */ + +void DES::feistel(uint32_t& L, uint32_t& R, uint32_t F) +{ + uint32_t temp = R; + R = L ^ F; + L = temp; +} + +/* */ + +uint32_t DES::f(uint32_t R, ulong64_t k) +{ +// applying expansion permutation and returning 48-bit data + ulong64_t input = 0; + for (uint8_t i = 0; i < 48; i++) { + input <<= 1; + input |= (ulong64_t)((R >> (32 - EXPANSION[i])) & LB32_MASK); + } + + // XORing expanded Ri with Ki, the round key + input = input ^ k; + + // applying S-Boxes function and returning 32-bit data + uint32_t output = 0; + for (uint8_t i = 0; i < 8; i++) { + // Outer bits + char row = (char)((input & (0x0000840000000000 >> 6 * i)) >> (42 - 6 * i)); + row = (row >> 4) | (row & 0x01); + + // Middle 4 bits of input + char column = (char)((input & (0x0000780000000000 >> 6 * i)) >> (43 - 6 * i)); + + output <<= 4; + output |= (uint32_t)(SBOX[i][16 * row + column] & 0x0f); + } + + // applying the round permutation + uint32_t result = 0; + for (uint8_t i = 0; i < 32; i++) { + result <<= 1; + result |= (output >> (32 - PBOX[i])) & LB32_MASK; + } + + return result; +} diff --git a/src/common/DESCrypto.h b/src/common/DESCrypto.h new file mode 100644 index 00000000..d437d614 --- /dev/null +++ b/src/common/DESCrypto.h @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +/* + * Digital Voice Modem - Common Library + * MIT Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file DESCrypto.h + * @ingroup crypto + * @file DESCrypto.cpp + * @ingroup crypto + */ +#if !defined(__DES_CRYPTO_H__) +#define __DES_CRYPTO_H__ + +#include "common/Defines.h" + +namespace crypto +{ + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Data Encryption Standard Algorithm. + * @ingroup crypto + */ + class HOST_SW_API DES { + public: + /** + * @brief Initializes a new instance of the DES class. + */ + explicit DES(); + + /** + * @brief Encrypt input block with given key. + * @param in Input buffer with block to encrypt. + * @param key Encryption key. + * @returns uint8_t* Encrypted input buffer. + */ + uint8_t* encryptBlock(const uint8_t block[], const uint8_t key[]); + /** + * @brief Decrypt input block with given key. + * @param block Input buffer with block to encrypt. + * @param key Encryption key. + * @returns uint8_t* Encrypted input buffer. + */ + uint8_t* decryptBlock(const uint8_t block[], const uint8_t key[]); + + private: + uint64_t sub_key[16]; // 48 bits each + + static ulong64_t toValue(const uint8_t* payload); + static uint8_t* fromValue(const ulong64_t value); + + void generateSubkeys(uint64_t key); + + ulong64_t des(ulong64_t block, bool decrypt); + + ulong64_t intialPermutation(ulong64_t block); + ulong64_t finalPermutation(ulong64_t block); + + void feistel(uint32_t& L, uint32_t& R, uint32_t F); + uint32_t f(uint32_t R, ulong64_t k); + }; +} // namespace crypto + +#endif // __DES_CRYPTO_H__ diff --git a/src/common/p25/Crypto.cpp b/src/common/p25/Crypto.cpp index 7ff7873e..b14236c3 100644 --- a/src/common/p25/Crypto.cpp +++ b/src/common/p25/Crypto.cpp @@ -12,6 +12,7 @@ #include "p25/P25Defines.h" #include "p25/Crypto.h" #include "AESCrypto.h" +#include "DESCrypto.h" #include "RC4Crypto.h" #include "Log.h" #include "Utils.h" @@ -138,6 +139,34 @@ void P25Crypto::generateKeystream() // generate keystream switch (m_tekAlgoId) { + case ALGO_DES: + { + if (m_keystream == nullptr) + m_keystream = new uint8_t[224U]; + ::memset(m_keystream, 0x00U, 224U); + + uint8_t desKey[8U]; + ::memset(desKey, 0x00U, 8U); + uint8_t padLen = (uint8_t)::fmax(8 - m_tekLength, 0); + for (uint8_t i = 0U; i < padLen; i++) + desKey[i] = 0U; + for (uint8_t i = padLen; i < 8U; i++) + desKey[i] = m_tek[i - padLen]; + + DES des = DES(); + + uint8_t input[8U]; + ::memset(input, 0x00U, 8U); + ::memcpy(input, m_mi, 8U); + + for (uint32_t i = 0U; i < (224U / 8U); i++) { + uint8_t* output = des.encryptBlock(input, desKey); + ::memcpy(m_keystream + (i * 8U), output, 8U); + ::memcpy(input, output, 8U); + delete[] output; + } + } + break; case ALGO_AES_256: { if (m_keystream == nullptr) @@ -156,6 +185,7 @@ void P25Crypto::generateKeystream() uint8_t* output = aes.encryptECB(input, 16U, m_tek.get()); ::memcpy(m_keystream + (i * 16U), output, 16U); ::memcpy(input, output, 16U); + delete[] output; } delete[] iv; @@ -652,6 +682,26 @@ void P25Crypto::cryptAES_PDU(uint8_t* frame, uint8_t frameLen) } } +/* Helper to crypt IMBE audio using DES. */ + +void P25Crypto::cryptDES_IMBE(uint8_t* imbe, DUID::E duid) +{ + if (m_keystream == nullptr) + return; + + uint32_t offset = 8U; + if (duid == DUID::LDU2) { + offset += 101U; + } + + offset += (m_keystreamPos * RAW_IMBE_LENGTH_BYTES) + RAW_IMBE_LENGTH_BYTES + ((m_keystreamPos < 8U) ? 0U : 2U); + m_keystreamPos = (m_keystreamPos + 1U) % 9U; + + for (uint8_t i = 0U; i < RAW_IMBE_LENGTH_BYTES; i++) { + imbe[i] ^= m_keystream[offset + i]; + } +} + /* Helper to crypt IMBE audio using AES-256. */ void P25Crypto::cryptAES_IMBE(uint8_t* imbe, DUID::E duid) diff --git a/src/common/p25/Crypto.h b/src/common/p25/Crypto.h index ed47ab6c..b267adac 100644 --- a/src/common/p25/Crypto.h +++ b/src/common/p25/Crypto.h @@ -137,6 +137,12 @@ namespace p25 */ void cryptAES_PDU(uint8_t* frame, uint8_t frameLen); + /** + * @brief Helper to crypt P25 IMBE audio using DES. + * @param imbe Buffer containing IMBE to crypt. + * @param duid P25 DUID. + */ + void cryptDES_IMBE(uint8_t* imbe, P25DEF::DUID::E duid); /** * @brief Helper to crypt P25 IMBE audio using AES-256. * @param imbe Buffer containing IMBE to crypt. From 0b03962ed4894773c504cc7cfd420fc82819ec04 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 6 Nov 2025 14:39:00 -0500 Subject: [PATCH 138/200] move Git hash global defines out of Defines.h and into GitHash.h; --- src/bridge/Defines.h | 1 + src/common/Defines.h | 9 +-------- src/common/GitHash.h | 37 +++++++++++++++++++++++++++++++++++++ src/fne/Defines.h | 1 + src/host/Defines.h | 1 + src/monitor/Defines.h | 1 + src/patch/Defines.h | 1 + src/peered/Defines.h | 1 + src/remote/Defines.h | 1 + src/sysview/Defines.h | 1 + src/tged/Defines.h | 1 + 11 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 src/common/GitHash.h diff --git a/src/bridge/Defines.h b/src/bridge/Defines.h index c7c383e1..0785ffc8 100644 --- a/src/bridge/Defines.h +++ b/src/bridge/Defines.h @@ -20,6 +20,7 @@ #define __DEFINES_H__ #include "common/Defines.h" +#include "common/GitHash.h" // --------------------------------------------------------------------------- // Constants diff --git a/src/common/Defines.h b/src/common/Defines.h index 7f90228b..4b167cea 100644 --- a/src/common/Defines.h +++ b/src/common/Defines.h @@ -106,13 +106,6 @@ typedef unsigned long long ulong64_t; // Constants // --------------------------------------------------------------------------- -#ifndef __GIT_VER__ -#define __GIT_VER__ "00000000" -#endif -#ifndef __GIT_VER_HASH__ -#define __GIT_VER_HASH__ "00000000" -#endif - #define __PROG_NAME__ "" #define __EXE_NAME__ "" @@ -121,7 +114,7 @@ typedef unsigned long long ulong64_t; #define VERSION_REV "A" #define __NETVER__ "DVM_R" VERSION_MAJOR VERSION_REV VERSION_MINOR -#define __VER__ VERSION_MAJOR "." VERSION_MINOR VERSION_REV " (R" VERSION_MAJOR VERSION_REV VERSION_MINOR " " __GIT_VER__ ")" +#define __VER__ VERSION_MAJOR "." VERSION_MINOR VERSION_REV " (R" VERSION_MAJOR VERSION_REV VERSION_MINOR ")" #define __BUILD__ __DATE__ " " __TIME__ #if !defined(NDEBUG) diff --git a/src/common/GitHash.h b/src/common/GitHash.h new file mode 100644 index 00000000..58bc0d7c --- /dev/null +++ b/src/common/GitHash.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX + * Copyright (C) 2018-2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file GitHash.h + * @ingroup common + */ +#pragma once +#if !defined(__GIT_HASH_H__) +#define __GIT_HASH_H__ + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +#ifndef __GIT_VER__ +#define __GIT_VER__ "00000000" +#endif +#ifndef __GIT_VER_HASH__ +#define __GIT_VER_HASH__ "00000000" +#endif + +#ifdef __VER__ +#undef __VER__ +#define __VER__ VERSION_MAJOR "." VERSION_MINOR VERSION_REV " (R" VERSION_MAJOR VERSION_REV VERSION_MINOR " " __GIT_VER__ ")" +#endif + +/** @} */ + +#endif // __GIT_HASH_H__ diff --git a/src/fne/Defines.h b/src/fne/Defines.h index 19217d06..56ec425e 100644 --- a/src/fne/Defines.h +++ b/src/fne/Defines.h @@ -20,6 +20,7 @@ #define __DEFINES_H__ #include "common/Defines.h" +#include "common/GitHash.h" // --------------------------------------------------------------------------- // Constants diff --git a/src/host/Defines.h b/src/host/Defines.h index 33ecb9dd..6190291e 100644 --- a/src/host/Defines.h +++ b/src/host/Defines.h @@ -20,6 +20,7 @@ #define __DEFINES_H__ #include "common/Defines.h" +#include "common/GitHash.h" // --------------------------------------------------------------------------- // Constants diff --git a/src/monitor/Defines.h b/src/monitor/Defines.h index 44e3f9ab..46f2d81b 100644 --- a/src/monitor/Defines.h +++ b/src/monitor/Defines.h @@ -20,6 +20,7 @@ #define __DEFINES_H__ #include "common/Defines.h" +#include "common/GitHash.h" // --------------------------------------------------------------------------- // Constants diff --git a/src/patch/Defines.h b/src/patch/Defines.h index 394c328c..35ac458a 100644 --- a/src/patch/Defines.h +++ b/src/patch/Defines.h @@ -20,6 +20,7 @@ #define __DEFINES_H__ #include "common/Defines.h" +#include "common/GitHash.h" // --------------------------------------------------------------------------- // Constants diff --git a/src/peered/Defines.h b/src/peered/Defines.h index 7ec07b5e..9dbb20f5 100644 --- a/src/peered/Defines.h +++ b/src/peered/Defines.h @@ -20,6 +20,7 @@ #define __DEFINES_H__ #include "common/Defines.h" +#include "common/GitHash.h" // --------------------------------------------------------------------------- // Constants diff --git a/src/remote/Defines.h b/src/remote/Defines.h index 57959ab1..002e7f7e 100644 --- a/src/remote/Defines.h +++ b/src/remote/Defines.h @@ -20,6 +20,7 @@ #define __DEFINES_H__ #include "common/Defines.h" +#include "common/GitHash.h" // --------------------------------------------------------------------------- // Constants diff --git a/src/sysview/Defines.h b/src/sysview/Defines.h index 2aaff965..0ed1a7d0 100644 --- a/src/sysview/Defines.h +++ b/src/sysview/Defines.h @@ -20,6 +20,7 @@ #define __DEFINES_H__ #include "common/Defines.h" +#include "common/GitHash.h" // --------------------------------------------------------------------------- // Constants diff --git a/src/tged/Defines.h b/src/tged/Defines.h index cc780a48..0d02f58f 100644 --- a/src/tged/Defines.h +++ b/src/tged/Defines.h @@ -20,6 +20,7 @@ #define __DEFINES_H__ #include "common/Defines.h" +#include "common/GitHash.h" // --------------------------------------------------------------------------- // Constants From ecb9341914a63e014c02de4f35540ec25baefa26 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 6 Nov 2025 14:42:02 -0500 Subject: [PATCH 139/200] fix copyrights; --- src/common/GitHash.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/common/GitHash.h b/src/common/GitHash.h index 58bc0d7c..a1077126 100644 --- a/src/common/GitHash.h +++ b/src/common/GitHash.h @@ -4,8 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX - * Copyright (C) 2018-2025 Bryan Biedenkapp, N2PLL + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL * */ /** From 19f47a33bddcc4d4d98aa8cf3f6aea6c5c31dcfb Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 6 Nov 2025 15:29:10 -0500 Subject: [PATCH 140/200] cleanup file headers containing lingering old comments; update fw/modem submodule; update fw/hotspot submodule; --- src/common/dmr/lc/CSBK.h | 4 ---- src/common/dmr/lc/FullLC.h | 4 ---- src/common/dmr/lc/PrivacyLC.cpp | 3 --- src/common/dmr/lc/PrivacyLC.h | 3 --- src/common/dmr/lc/csbk/CSBKFactory.h | 3 --- src/common/dmr/lc/csbk/CSBK_MAINT.h | 3 --- src/common/lookups/ChannelLookup.h | 3 --- src/common/network/json/json.h | 4 ---- src/common/network/rest/http/SecureClientConnection.h | 3 --- src/common/network/rest/http/SecureHTTPClient.h | 3 --- src/common/network/tcp/SecureTcpListener.h | 3 --- src/common/nxdn/acl/AccessControl.cpp | 4 ---- src/common/p25/lc/tsbk/OSP_SCCB_EXP.h | 3 --- src/common/p25/lc/tsbk/OSP_SNDCP_CH_ANN.h | 3 --- src/common/p25/lc/tsbk/OSP_U_DEREG_ACK.h | 3 --- src/common/p25/lc/tsbk/OSP_U_REG_CMD.cpp | 3 --- src/common/p25/lc/tsbk/OSP_U_REG_CMD.h | 3 --- src/fw/hotspot | 2 +- src/fw/modem | 2 +- src/host/Host.Config.cpp | 3 --- src/host/Host.DMR.cpp | 3 --- src/host/Host.NXDN.cpp | 3 --- src/host/Host.P25.cpp | 3 --- src/host/HostMain.h | 4 ---- src/host/modem/port/ModemNullPort.cpp | 4 ---- src/host/modem/port/specialized/V24UDPPort.h | 4 ---- src/host/p25/lc/tsbk/OSP_DVM_GIT_HASH.h | 3 --- src/host/p25/packet/Voice.h | 4 ---- 28 files changed, 2 insertions(+), 88 deletions(-) diff --git a/src/common/dmr/lc/CSBK.h b/src/common/dmr/lc/CSBK.h index 7f4c7e2b..04f2eabc 100644 --- a/src/common/dmr/lc/CSBK.h +++ b/src/common/dmr/lc/CSBK.h @@ -4,10 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Common Library -* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost) -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2015,2016 Jonathan Naylor, G4KLX * Copyright (C) 2019-2024 Bryan Biedenkapp, N2PLL * diff --git a/src/common/dmr/lc/FullLC.h b/src/common/dmr/lc/FullLC.h index 61e56515..13274e8f 100644 --- a/src/common/dmr/lc/FullLC.h +++ b/src/common/dmr/lc/FullLC.h @@ -4,10 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Common Library -* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost) -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2015,2016 Jonathan Naylor, G4KLX * Copyright (C) 2021,2024 Bryan Biedenkapp, N2PLL * diff --git a/src/common/dmr/lc/PrivacyLC.cpp b/src/common/dmr/lc/PrivacyLC.cpp index 80be911b..5e995725 100644 --- a/src/common/dmr/lc/PrivacyLC.cpp +++ b/src/common/dmr/lc/PrivacyLC.cpp @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Common Library - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2021,2024,2025 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/dmr/lc/PrivacyLC.h b/src/common/dmr/lc/PrivacyLC.h index 9935c51a..12bd0dc6 100644 --- a/src/common/dmr/lc/PrivacyLC.h +++ b/src/common/dmr/lc/PrivacyLC.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Common Library -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2021,2025 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/dmr/lc/csbk/CSBKFactory.h b/src/common/dmr/lc/csbk/CSBKFactory.h index 571cb59c..1d07fb4c 100644 --- a/src/common/dmr/lc/csbk/CSBKFactory.h +++ b/src/common/dmr/lc/csbk/CSBKFactory.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Common Library -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/dmr/lc/csbk/CSBK_MAINT.h b/src/common/dmr/lc/csbk/CSBK_MAINT.h index 6b3f2a74..c5f3300a 100644 --- a/src/common/dmr/lc/csbk/CSBK_MAINT.h +++ b/src/common/dmr/lc/csbk/CSBK_MAINT.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Common Library -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2023 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/lookups/ChannelLookup.h b/src/common/lookups/ChannelLookup.h index 545fe151..2c442093 100644 --- a/src/common/lookups/ChannelLookup.h +++ b/src/common/lookups/ChannelLookup.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Common Library -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/network/json/json.h b/src/common/network/json/json.h index 7a9a2f64..86b0b67b 100644 --- a/src/common/network/json/json.h +++ b/src/common/network/json/json.h @@ -4,10 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Common Library -* @derivedfrom picojson (https://github.com/kazuho/picojson) -* @license BSD-2-Clause License (https://opensource.org/licenses/BSD-2-Clause) -* * Copyright 2009-2010 Cybozu Labs, Inc. * Copyright 2011-2014 Kazuho Oku., All rights reserved. * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL diff --git a/src/common/network/rest/http/SecureClientConnection.h b/src/common/network/rest/http/SecureClientConnection.h index 3ce83a85..b6f7f199 100644 --- a/src/common/network/rest/http/SecureClientConnection.h +++ b/src/common/network/rest/http/SecureClientConnection.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Common Library - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2024 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/network/rest/http/SecureHTTPClient.h b/src/common/network/rest/http/SecureHTTPClient.h index 009e78f9..3e6366a3 100644 --- a/src/common/network/rest/http/SecureHTTPClient.h +++ b/src/common/network/rest/http/SecureHTTPClient.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Common Library -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2024 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/network/tcp/SecureTcpListener.h b/src/common/network/tcp/SecureTcpListener.h index 978c4bcd..6d7bdab7 100644 --- a/src/common/network/tcp/SecureTcpListener.h +++ b/src/common/network/tcp/SecureTcpListener.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Common Library -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/nxdn/acl/AccessControl.cpp b/src/common/nxdn/acl/AccessControl.cpp index 6eaccc71..49544164 100644 --- a/src/common/nxdn/acl/AccessControl.cpp +++ b/src/common/nxdn/acl/AccessControl.cpp @@ -4,10 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Common Library - * @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost) - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2016 Simon Rune, G7RZU * Copyright (C) 2016,2017 Jonathan Naylor, G4KLX * Copyright (C) 2017,2019,2024 Bryan Biedenkapp, N2PLL diff --git a/src/common/p25/lc/tsbk/OSP_SCCB_EXP.h b/src/common/p25/lc/tsbk/OSP_SCCB_EXP.h index 39301ae0..53fe2003 100644 --- a/src/common/p25/lc/tsbk/OSP_SCCB_EXP.h +++ b/src/common/p25/lc/tsbk/OSP_SCCB_EXP.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Common Library - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2022 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/p25/lc/tsbk/OSP_SNDCP_CH_ANN.h b/src/common/p25/lc/tsbk/OSP_SNDCP_CH_ANN.h index c3a2a111..bc3a17c4 100644 --- a/src/common/p25/lc/tsbk/OSP_SNDCP_CH_ANN.h +++ b/src/common/p25/lc/tsbk/OSP_SNDCP_CH_ANN.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Common Library - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/p25/lc/tsbk/OSP_U_DEREG_ACK.h b/src/common/p25/lc/tsbk/OSP_U_DEREG_ACK.h index 7b8b0096..3bdb6b87 100644 --- a/src/common/p25/lc/tsbk/OSP_U_DEREG_ACK.h +++ b/src/common/p25/lc/tsbk/OSP_U_DEREG_ACK.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Common Library - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2022 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/p25/lc/tsbk/OSP_U_REG_CMD.cpp b/src/common/p25/lc/tsbk/OSP_U_REG_CMD.cpp index 803f1cb8..bdd7b27a 100644 --- a/src/common/p25/lc/tsbk/OSP_U_REG_CMD.cpp +++ b/src/common/p25/lc/tsbk/OSP_U_REG_CMD.cpp @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Common Library - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL * */ diff --git a/src/common/p25/lc/tsbk/OSP_U_REG_CMD.h b/src/common/p25/lc/tsbk/OSP_U_REG_CMD.h index b82f0d6a..d474e7fc 100644 --- a/src/common/p25/lc/tsbk/OSP_U_REG_CMD.h +++ b/src/common/p25/lc/tsbk/OSP_U_REG_CMD.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Common Library - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2022 Bryan Biedenkapp, N2PLL * */ diff --git a/src/fw/hotspot b/src/fw/hotspot index 718093ae..e50f1ec9 160000 --- a/src/fw/hotspot +++ b/src/fw/hotspot @@ -1 +1 @@ -Subproject commit 718093aea3a5a6c80f7fceb1bbbc940168b8d98c +Subproject commit e50f1ec9027d3c6d54f95452aadb8dd4b0f113ec diff --git a/src/fw/modem b/src/fw/modem index b124551a..a65c44a8 160000 --- a/src/fw/modem +++ b/src/fw/modem @@ -1 +1 @@ -Subproject commit b124551ad8bf56692b82da80325a7508a30a3a0b +Subproject commit a65c44a8df1867517f9a30346577d22ba2797b19 diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp index c0aee3c7..23d4a91c 100644 --- a/src/host/Host.Config.cpp +++ b/src/host/Host.Config.cpp @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Modem Host Software -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ diff --git a/src/host/Host.DMR.cpp b/src/host/Host.DMR.cpp index 1e3d3a9a..da87c784 100644 --- a/src/host/Host.DMR.cpp +++ b/src/host/Host.DMR.cpp @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Modem Host Software -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL * */ diff --git a/src/host/Host.NXDN.cpp b/src/host/Host.NXDN.cpp index a99153cd..d478bd5f 100644 --- a/src/host/Host.NXDN.cpp +++ b/src/host/Host.NXDN.cpp @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Modem Host Software -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL * */ diff --git a/src/host/Host.P25.cpp b/src/host/Host.P25.cpp index f8eb602f..eb0c7ff9 100644 --- a/src/host/Host.P25.cpp +++ b/src/host/Host.P25.cpp @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Modem Host Software -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL * */ diff --git a/src/host/HostMain.h b/src/host/HostMain.h index 00b1bbe2..83f12dc1 100644 --- a/src/host/HostMain.h +++ b/src/host/HostMain.h @@ -4,10 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Modem Host Software -* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost) -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX * Copyright (C) 2020,2022 Bryan Biedenkapp, N2PLL * diff --git a/src/host/modem/port/ModemNullPort.cpp b/src/host/modem/port/ModemNullPort.cpp index a3068d56..91af7f45 100644 --- a/src/host/modem/port/ModemNullPort.cpp +++ b/src/host/modem/port/ModemNullPort.cpp @@ -4,10 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Modem Host Software - * @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost) - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2021 Jonathan Naylor, G4KLX * Copyright (C) 2021,2024 Bryan Biedenkapp, N2PLL * diff --git a/src/host/modem/port/specialized/V24UDPPort.h b/src/host/modem/port/specialized/V24UDPPort.h index 6b326b58..e671b5d7 100644 --- a/src/host/modem/port/specialized/V24UDPPort.h +++ b/src/host/modem/port/specialized/V24UDPPort.h @@ -4,10 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Modem Host Software - * @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost) - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ diff --git a/src/host/p25/lc/tsbk/OSP_DVM_GIT_HASH.h b/src/host/p25/lc/tsbk/OSP_DVM_GIT_HASH.h index 8062920c..f4e8308e 100644 --- a/src/host/p25/lc/tsbk/OSP_DVM_GIT_HASH.h +++ b/src/host/p25/lc/tsbk/OSP_DVM_GIT_HASH.h @@ -4,9 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * @package DVM / Modem Host Software - * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) - * * Copyright (C) 2022 Bryan Biedenkapp, N2PLL * */ diff --git a/src/host/p25/packet/Voice.h b/src/host/p25/packet/Voice.h index 2f03c898..a5bb238f 100644 --- a/src/host/p25/packet/Voice.h +++ b/src/host/p25/packet/Voice.h @@ -4,10 +4,6 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -* @package DVM / Modem Host Software -* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost) -* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) -* * Copyright (C) 2016,2017 Jonathan Naylor, G4KLX * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * From cd37a9d7c6daf0dc96ba2c4e92c491fe5824d305 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 6 Nov 2025 15:36:07 -0500 Subject: [PATCH 141/200] remove debug trace not needed anymore; --- src/common/DESCrypto.cpp | 2 +- src/common/p25/Crypto.cpp | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/common/DESCrypto.cpp b/src/common/DESCrypto.cpp index 83c96f0f..7871ca88 100644 --- a/src/common/DESCrypto.cpp +++ b/src/common/DESCrypto.cpp @@ -327,7 +327,7 @@ void DES::feistel(uint32_t& L, uint32_t& R, uint32_t F) uint32_t DES::f(uint32_t R, ulong64_t k) { -// applying expansion permutation and returning 48-bit data + // applying expansion permutation and returning 48-bit data ulong64_t input = 0; for (uint8_t i = 0; i < 48; i++) { input <<= 1; diff --git a/src/common/p25/Crypto.cpp b/src/common/p25/Crypto.cpp index b14236c3..f800c7b6 100644 --- a/src/common/p25/Crypto.cpp +++ b/src/common/p25/Crypto.cpp @@ -371,11 +371,6 @@ UInt8Array P25Crypto::cryptAES_KMM_CBC_KDF(const uint8_t* kek, const uint8_t* ms uint16_t authLen = msgLen - KMM_AES_MAC_LENGTH; SET_UINT16(authLen, iv, 6U); - /** DEBUG REMOVEME */ - Utils::dump(2U, "KEK", kek, MAX_ENC_KEY_LENGTH_BYTES); - Utils::dump(2U, "IV", iv, AES_BLOCK_SIZE / 2); - /** DEBUG REMOVEME */ - int len; uint8_t tempBuf[TEMP_BUFFER_LEN]; ::memset(tempBuf, 0x00U, TEMP_BUFFER_LEN); @@ -415,11 +410,6 @@ UInt8Array P25Crypto::cryptAES_KMM_CBC_KDF(const uint8_t* kek, const uint8_t* ms EVP_CIPHER_CTX_free(ctx); - /** DEBUG REMOVEME */ - LogInfoEx("T", "len = %u", len); - Utils::dump(2U, "macKey", tempBuf, 128U); - /** DEBUG REMOVEME */ - UInt8Array wrappedKey = std::unique_ptr(new uint8_t[MAX_ENC_KEY_LENGTH_BYTES]); ::memset(wrappedKey.get(), 0x00U, MAX_ENC_KEY_LENGTH_BYTES); ::memcpy(wrappedKey.get(), tempBuf + 8U, MAX_ENC_KEY_LENGTH_BYTES); From 96e68b44fdc6eeb315fffd6c97a2f3cd565e4540 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 7 Nov 2025 10:43:40 -0500 Subject: [PATCH 142/200] add support for PDU auxiliary ES headers; --- src/common/p25/data/DataHeader.cpp | 216 ++++++++- src/common/p25/data/DataHeader.h | 50 +- .../callhandler/packetdata/P25PacketData.cpp | 161 ++++++- .../callhandler/packetdata/P25PacketData.h | 8 +- src/host/p25/packet/ControlSignaling.cpp | 2 +- src/host/p25/packet/Data.cpp | 436 ++++++++++++++++-- src/host/p25/packet/Data.h | 10 +- 7 files changed, 831 insertions(+), 52 deletions(-) diff --git a/src/common/p25/data/DataHeader.cpp b/src/common/p25/data/DataHeader.cpp index ecdf6c79..81c0c95c 100644 --- a/src/common/p25/data/DataHeader.cpp +++ b/src/common/p25/data/DataHeader.cpp @@ -61,14 +61,23 @@ DataHeader::DataHeader() : m_ambtOpcode(0U), m_ambtField8(0U), m_ambtField9(0U), + m_algId(ALGO_UNENCRYPT), + m_kId(0U), m_trellis(), m_data(nullptr), - m_extAddrData(nullptr) + m_extAddrData(nullptr), + m_auxESData(nullptr), + m_mi(nullptr) { m_data = new uint8_t[P25_PDU_HEADER_LENGTH_BYTES]; ::memset(m_data, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); m_extAddrData = new uint8_t[P25_PDU_HEADER_LENGTH_BYTES]; ::memset(m_extAddrData, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); + m_auxESData = new uint8_t[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES]; + ::memset(m_auxESData, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + + m_mi = new uint8_t[MI_LENGTH_BYTES]; + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); } /* Finalizes a instance of the DataHeader class. */ @@ -77,6 +86,12 @@ DataHeader::~DataHeader() { delete[] m_data; delete[] m_extAddrData; + delete[] m_auxESData; + + if (m_mi != nullptr) { + delete[] m_mi; + m_mi = nullptr; + } } /* Decodes P25 PDU data header. */ @@ -346,7 +361,7 @@ void DataHeader::encodeExtAddr(uint8_t* data, bool noTrellis) header[2U] = (m_srcLlId >> 0) & 0xFFU; #if DEBUG_P25_PDU_DATA - Utils::dump(1U, "P25, DataHeader::encodeExtAddr(), PDU Header Data", header, P25_PDU_HEADER_LENGTH_BYTES); + Utils::dump(1U, "P25, DataHeader::encodeExtAddr(), PDU Extended Address Data", header, P25_PDU_HEADER_LENGTH_BYTES); #endif ::memcpy(data, header, 4U); // only copy the 4 bytes of header data for confirmed @@ -366,7 +381,154 @@ void DataHeader::encodeExtAddr(uint8_t* data, bool noTrellis) edac::CRC::addCCITT162(header, P25_PDU_HEADER_LENGTH_BYTES); #if DEBUG_P25_PDU_DATA - Utils::dump(1U, "P25, DataHeader::encodeExtAddr(), PDU Header Data", header, P25_PDU_HEADER_LENGTH_BYTES); + Utils::dump(1U, "P25, DataHeader::encodeExtAddr(), PDU Extended Address Data", header, P25_PDU_HEADER_LENGTH_BYTES); +#endif + + if (!noTrellis) { + // encode 1/2 rate Trellis + m_trellis.encode12(header, data); + } else { + ::memcpy(data, header, P25_PDU_HEADER_LENGTH_BYTES); + } + } +} + +/* Decodes P25 PDU auxiliary ES header. */ + +bool DataHeader::decodeAuxES(const uint8_t* data, bool noTrellis) +{ + assert(data != nullptr); + + ::memset(m_auxESData, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + + if (m_sap != PDUSAP::ENC_USER_DATA && m_sap != PDUSAP::ENC_KMM) + return false; + + if (m_fmt == PDUFormatType::CONFIRMED) { + ::memcpy(m_auxESData, data, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); +#if DEBUG_P25_PDU_DATA + Utils::dump(1U, "P25, DataHeader::decodeAuxES(), PDU Auxiliary ES Data", m_extAddrData, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); +#endif + + m_algId = m_auxESData[9U]; // Algorithm ID + if (m_algId != ALGO_UNENCRYPT) { + if (m_mi != nullptr) + delete[] m_mi; + m_mi = new uint8_t[MI_LENGTH_BYTES]; + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + ::memcpy(m_mi, m_auxESData, MI_LENGTH_BYTES); // Message Indicator + + m_kId = (m_auxESData[10U] << 8) + m_auxESData[11U]; // Key ID + } + else { + if (m_mi != nullptr) + delete[] m_mi; + m_mi = new uint8_t[MI_LENGTH_BYTES]; + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + + m_kId = 0x0000U; + } + + m_exSap = m_auxESData[12U] & 0x3FU; // Service Access Point + } else if (m_fmt == PDUFormatType::UNCONFIRMED) { + // decode 1/2 rate Trellis & check CRC-CCITT 16 + bool valid = true; + if (noTrellis) { + ::memcpy(m_auxESData, data, P25_PDU_HEADER_LENGTH_BYTES); + } + else { + valid = m_trellis.decode12(data, m_auxESData); + } + + if (valid) { + valid = edac::CRC::checkCCITT162(m_auxESData, P25_PDU_HEADER_LENGTH_BYTES); + if (!valid) { + if (s_warnCRC) { + // if we're already warning instead of erroring CRC, don't announce invalid CRC in the + // case where no CRC is defined + if ((m_auxESData[P25_PDU_HEADER_LENGTH_BYTES - 2U] != 0x00U) && (m_auxESData[P25_PDU_HEADER_LENGTH_BYTES - 1U] != 0x00U)) { + LogWarning(LOG_P25, "DataHeader::decodeExtAddr(), failed CRC CCITT-162 check"); + } + + valid = true; // ignore CRC error + } + else { + LogError(LOG_P25, "DataHeader::decodeAuxES(), failed CRC CCITT-162 check"); + } + } + } + + if (!valid) { + return false; + } + +#if DEBUG_P25_PDU_DATA + Utils::dump(1U, "P25, DataHeader::decodeAuxES(), PDU Auxiliary ES Data", m_extAddrData, P25_PDU_HEADER_LENGTH_BYTES); +#endif + + m_algId = m_auxESData[9U]; // Algorithm ID + if (m_algId != ALGO_UNENCRYPT) { + if (m_mi != nullptr) + delete[] m_mi; + m_mi = new uint8_t[MI_LENGTH_BYTES]; + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + ::memcpy(m_mi, m_auxESData, MI_LENGTH_BYTES); // Message Indicator + + m_kId = (m_auxESData[10U] << 8) + m_auxESData[11U]; // Key ID + } + else { + if (m_mi != nullptr) + delete[] m_mi; + m_mi = new uint8_t[MI_LENGTH_BYTES]; + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + + m_kId = 0x0000U; + } + } + + return true; +} + +/* Encodes P25 PDU auxiliary ES header. */ + +void DataHeader::encodeAuxES(uint8_t* data, bool noTrellis) +{ + assert(data != nullptr); + + if (m_sap != PDUSAP::ENC_USER_DATA && m_sap != PDUSAP::ENC_KMM) + return; + + uint8_t header[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES]; + ::memset(header, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + + if (m_fmt == PDUFormatType::CONFIRMED) { + for (uint32_t i = 0; i < MI_LENGTH_BYTES; i++) + header[i + 2U] = m_mi[i]; // Message Indicator + + header[11U] = m_algId; // Algorithm ID + header[12U] = (m_kId >> 8) & 0xFFU; // Key ID + header[13U] = (m_kId >> 0) & 0xFFU; // ... + + header[14U] = m_exSap & 0x3FU; // Service Access Point + +#if DEBUG_P25_PDU_DATA + Utils::dump(1U, "P25, DataHeader::encodeExtAddr(), PDU Auxiliary ES Data", header, P25_PDU_HEADER_LENGTH_BYTES); +#endif + + ::memcpy(data, header, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + } else if (m_fmt == PDUFormatType::UNCONFIRMED) { + for (uint32_t i = 0; i < MI_LENGTH_BYTES; i++) + header[i] = m_mi[i]; // Message Indicator + + header[9U] = m_algId; // Algorithm ID + header[10U] = (m_kId >> 8) & 0xFFU; // Key ID + header[11U] = (m_kId >> 0) & 0xFFU; // ... + + // compute CRC-CCITT 16 + edac::CRC::addCCITT162(header, P25_PDU_HEADER_LENGTH_BYTES); + +#if DEBUG_P25_PDU_DATA + Utils::dump(1U, "P25, DataHeader::encodeAuxES(), PDU Auxiliary ES Data", header, P25_PDU_HEADER_LENGTH_BYTES); #endif if (!noTrellis) { @@ -414,7 +576,17 @@ void DataHeader::reset() m_ambtField8 = 0U; m_ambtField9 = 0U; + m_algId = ALGO_UNENCRYPT; + m_kId = 0U; + ::memset(m_data, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); + + if (m_mi != nullptr) { + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + } else { + m_mi = new uint8_t[MI_LENGTH_BYTES]; + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + } } /* Gets the total length in bytes of enclosed packet data. */ @@ -471,6 +643,22 @@ uint32_t DataHeader::getExtAddrData(uint8_t* buffer) const return P25_PDU_HEADER_LENGTH_BYTES; } +/* Gets the raw auxiliary ES header data. */ + +uint32_t DataHeader::getAuxiliaryESData(uint8_t* buffer) const +{ + assert(buffer != nullptr); + assert(m_auxESData != nullptr); + + if (m_fmt == PDUFormatType::CONFIRMED) { + ::memcpy(buffer, m_auxESData, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + return P25_PDU_CONFIRMED_DATA_LENGTH_BYTES; + } else { + ::memcpy(buffer, m_auxESData, P25_PDU_HEADER_LENGTH_BYTES); + return P25_PDU_HEADER_LENGTH_BYTES; + } +} + /* Helper to calculate the number of blocks to follow and padding length for a PDU. */ void DataHeader::calculateLength(uint32_t packetLength) @@ -507,3 +695,25 @@ uint32_t DataHeader::calculatePadLength(uint8_t fmt, uint32_t packetLength) return P25_PDU_UNCONFIRMED_LENGTH_BYTES - (len % P25_PDU_UNCONFIRMED_LENGTH_BYTES); } } + +/* +** Encryption data +*/ + +/* Sets the encryption message indicator. */ + +void DataHeader::setMI(const uint8_t* mi) +{ + assert(mi != nullptr); + + ::memcpy(m_mi, mi, MI_LENGTH_BYTES); +} + +/* Gets the encryption message indicator. */ + +void DataHeader::getMI(uint8_t* mi) const +{ + assert(mi != nullptr); + + ::memcpy(mi, m_mi, MI_LENGTH_BYTES); +} diff --git a/src/common/p25/data/DataHeader.h b/src/common/p25/data/DataHeader.h index 26433a45..3f4bc50b 100644 --- a/src/common/p25/data/DataHeader.h +++ b/src/common/p25/data/DataHeader.h @@ -76,6 +76,20 @@ namespace p25 */ void encodeExtAddr(uint8_t* data, bool noTrellis = false); + /** + * @brief Decodes P25 PDU auxiliary ES header. + * @param[in] data Buffer containing a PDU data header to decode. + * @param noTrellis Flag indicating not to perform Trellis encoding. + * @returns bool True, if PDU data header decoded, otherwise false. + */ + bool decodeAuxES(const uint8_t* data, bool noTrellis = false); + /** + * @brief Encodes P25 PDU auxiliary ES header. + * @param noTrellis Flag indicating not to perform Trellis encoding. + * @param[out] data Buffer to encode a PDU data header. + */ + void encodeAuxES(uint8_t* data, bool noTrellis = false); + /** * @brief Helper to reset data values to defaults. */ @@ -104,6 +118,12 @@ namespace p25 * @returns uint32_t Length of data copied. */ uint32_t getExtAddrData(uint8_t* buffer) const; + /** + * @brief Gets the raw auxiliary ES header data. + * @param[out] buffer Buffer to copy raw header data to. + * @returns uint32_t Length of data copied. + */ + uint32_t getAuxiliaryESData(uint8_t* buffer) const; /** * @brief Helper to calculate the number of blocks to follow and padding length for a PDU. @@ -125,6 +145,19 @@ namespace p25 */ static uint32_t calculatePadLength(uint8_t fmt, uint32_t packetLength); + /** @name Encryption data */ + /** + * @brief Sets the encryption message indicator. + * @param[in] mi Buffer containing the 9-byte Message Indicator. + */ + void setMI(const uint8_t* mi); + /** + * @brief Gets the encryption message indicator. + * @param[out] mi Buffer containing the 9-byte Message Indicator. + */ + void getMI(uint8_t* mi) const; + /** @} */ + public: /** * @brief Flag indicating if acknowledgement is needed. @@ -226,12 +259,27 @@ namespace p25 DECLARE_PROPERTY(uint8_t, ambtField9, AMBTField9); /** @} */ + /** @name Encryption data */ + /** + * @brief Encryption algorithm ID. + */ + DECLARE_PROPERTY(uint8_t, algId, AlgId); + /** + * @brief Encryption key ID. + */ + DECLARE_PROPERTY(uint32_t, kId, KId); + /** @} */ + private: edac::Trellis m_trellis; uint8_t* m_data; uint8_t* m_extAddrData; - + uint8_t* m_auxESData; + + // Encryption data + uint8_t* m_mi; + static bool s_warnCRC; }; } // namespace data diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index 033cbc06..d58bca44 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -231,6 +231,47 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee status->pduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; } + // process second header if we're using auxiliary ES + if ((status->header.getSAP() == PDUSAP::ENC_USER_DATA || status->header.getSAP() == PDUSAP::ENC_KMM) && + status->header.getFormat() == PDUFormatType::UNCONFIRMED) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + ::memcpy(buffer, status->netPDU, P25_PDU_FEC_LENGTH_BYTES); + + bool ret = status->header.decodeAuxES(buffer); + if (!ret) { + LogWarning(LOG_P25, P25_PDU_STR ", unfixable RF 1/2 rate second header data"); + Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_HEADER_LENGTH_BYTES); + + delete status; + m_status.erase(peerId); + + return false; + } + + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, auxiliary ES, algoId = $%02X, kId = $%04X", + status->header.getAlgId(), status->header.getKId()); + + if (status->header.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + status->header.getMI(mi); + + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + + status->auxiliaryES = true; + + offset += P25_PDU_FEC_LENGTH_BYTES; + blocksToFollow--; + + // if we are using a secondary header place it in the PDU user data buffer + status->header.getAuxiliaryESData(status->pduUserData + dataOffset); + dataOffset += P25_PDU_HEADER_LENGTH_BYTES; + status->pduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; + } + // decode data blocks for (uint32_t i = 0U; i < blocksToFollow; i++) { ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); @@ -261,12 +302,44 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee status->extendedAddress = true; } else { - LogInfoEx(LOG_P25, P25_PDU_STR ", peerId = %u, block %u, fmt = $%02X, lastBlock = %u", - peerId, (status->header.getFormat() == PDUFormatType::CONFIRMED) ? status->blockData[i].getSerialNo() : status->dataBlockCnt, status->blockData[i].getFormat(), - status->blockData[i].getLastBlock()); + // are we processing auxiliary ES data from the first block? + if ((status->header.getSAP() == PDUSAP::ENC_USER_DATA || status->header.getSAP() == PDUSAP::ENC_KMM) && + status->header.getFormat() == PDUFormatType::CONFIRMED && status->blockData[i].getSerialNo() == 0U) { + uint8_t secondHeader[P25_PDU_HEADER_LENGTH_BYTES]; + ::memset(secondHeader, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); + status->blockData[i].getData(secondHeader); + + status->header.decodeAuxES(secondHeader); + LogInfoEx(LOG_NET, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, algoId = $%02X, kId = $%04X", + status->blockData[i].getSerialNo(), status->blockData[i].getFormat(), status->blockData[i].getLastBlock(), status->header.getEXSAP(), + status->header.getAlgId(), status->header.getKId()); + + if (status->header.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + status->header.getMI(mi); + + LogInfoEx(LOG_NET, P25_PDU_STR ", ISP, Enc Sync, block %u, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + status->blockData[i].getSerialNo(), mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + + status->auxiliaryES = true; + } else { + LogInfoEx(LOG_P25, P25_PDU_STR ", peerId = %u, block %u, fmt = $%02X, lastBlock = %u", + peerId, (status->header.getFormat() == PDUFormatType::CONFIRMED) ? status->blockData[i].getSerialNo() : status->dataBlockCnt, status->blockData[i].getFormat(), + status->blockData[i].getLastBlock()); + } } status->blockData[i].getData(status->pduUserData + dataOffset); + + // is this the first unconfirmed data block after a auxiliary ES header? + if (i == 0U && status->header.getFormat() == PDUFormatType::UNCONFIRMED && status->auxiliaryES) { + uint8_t exSAP = status->pduUserData[0U]; // first byte of the first data block after an aux ES header is the extended SAP + status->header.setEXSAP(exSAP); + } + dataOffset += (status->header.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; status->pduUserDataLength = dataOffset; @@ -422,7 +495,7 @@ void P25PacketData::write_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, ui rspHeader.setBlocksToFollow(0U); - dispatchUserFrameToFNE(rspHeader, srcLlId > 0U, nullptr); + dispatchUserFrameToFNE(rspHeader, srcLlId > 0U, false, nullptr); } /* Helper used to return a KMM to the calling SU. */ @@ -445,7 +518,7 @@ void P25PacketData::write_PDU_KMM(const uint8_t* data, uint32_t len, uint32_t ll DECLARE_UINT8_ARRAY(pduUserData, pduLength); ::memcpy(pduUserData, data, len); - dispatchUserFrameToFNE(dataHeader, false, pduUserData); + dispatchUserFrameToFNE(dataHeader, false, false, pduUserData); } /* Updates the timer by the passed number of milliseconds. */ @@ -512,7 +585,7 @@ void P25PacketData::clock(uint32_t ms) } m_readyForNextPkt[frame->llId] = false; - dispatchUserFrameToFNE(*frame->header, false, frame->userData); + dispatchUserFrameToFNE(*frame->header, false, false, frame->userData); } } @@ -611,6 +684,8 @@ void P25PacketData::dispatch(uint32_t peerId) } uint8_t sap = (status->extendedAddress) ? status->header.getEXSAP() : status->header.getSAP(); + if (status->auxiliaryES) + sap = status->header.getEXSAP(); // handle standard P25 service access points switch (sap) { @@ -700,7 +775,7 @@ void P25PacketData::dispatch(uint32_t peerId) LogInfoEx(LOG_P25, "PDU -> VTUN, IP Data, repeated to CAI, broadcast packet, dstIp = %s (%u)", dstIp, status->header.getLLId()); - dispatchUserFrameToFNE(status->header, status->extendedAddress, status->pduUserData); + dispatchUserFrameToFNE(status->header, status->extendedAddress, status->auxiliaryES, status->pduUserData); handled = true; // is the source SU one we have proper ARP entries for? @@ -718,7 +793,7 @@ void P25PacketData::dispatch(uint32_t peerId) LogInfoEx(LOG_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); + dispatchUserFrameToFNE(status->header, status->extendedAddress, status->auxiliaryES, status->pduUserData); handled = true; // is the source SU one we have proper ARP entries for? @@ -806,7 +881,8 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) if (m_network->m_peers.size() > 0U) { for (auto peer : m_network->m_peers) { if (peerId != peer.first) { - write_PDU_User(peer.first, peerId, nullptr, status->header, status->extendedAddress, status->pduUserData); + write_PDU_User(peer.first, peerId, nullptr, status->header, status->extendedAddress, + status->auxiliaryES, status->pduUserData); if (m_network->m_debug) { LogDebug(LOG_P25, "srcPeer = %u, dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", peerId, peer.first, DUID::PDU, srcId, dstId); @@ -832,7 +908,8 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) continue; } - write_PDU_User(dstPeerId, peerId, peer.second, status->header, status->extendedAddress, status->pduUserData); + write_PDU_User(dstPeerId, peerId, peer.second, status->header, status->extendedAddress, + status->auxiliaryES, status->pduUserData); if (m_network->m_debug) { LogDebug(LOG_P25, "srcPeer = %u, dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", peerId, dstPeerId, DUID::PDU, srcId, dstId); @@ -844,7 +921,7 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) /* Helper to dispatch PDU user data back to the local FNE network. (Will not transmit to neighbor FNE peers.) */ -void P25PacketData::dispatchUserFrameToFNE(p25::data::DataHeader& dataHeader, bool extendedAddress, uint8_t* pduUserData) +void P25PacketData::dispatchUserFrameToFNE(p25::data::DataHeader& dataHeader, bool extendedAddress, bool auxiliaryES, uint8_t* pduUserData) { uint32_t srcId = (extendedAddress) ? dataHeader.getSrcLLId() : dataHeader.getLLId(); uint32_t dstId = dataHeader.getLLId(); @@ -866,7 +943,7 @@ void P25PacketData::dispatchUserFrameToFNE(p25::data::DataHeader& dataHeader, bo // repeat traffic to the connected peers if (m_network->m_peers.size() > 0U) { for (auto peer : m_network->m_peers) { - write_PDU_User(peer.first, m_network->m_peerId, nullptr, dataHeader, extendedAddress, pduUserData); + write_PDU_User(peer.first, m_network->m_peerId, nullptr, dataHeader, extendedAddress, auxiliaryES, pduUserData); if (m_network->m_debug) { LogDebug(LOG_P25, "dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", peer.first, DUID::PDU, srcId, dstId); @@ -999,7 +1076,7 @@ void P25PacketData::write_PDU_ARP(uint32_t addr) DECLARE_UINT8_ARRAY(pduUserData, pduLength); ::memcpy(pduUserData + P25_PDU_HEADER_LENGTH_BYTES, arpPacket, P25_PDU_ARP_PCKT_LENGTH); - dispatchUserFrameToFNE(rspHeader, true, pduUserData); + dispatchUserFrameToFNE(rspHeader, true, false, pduUserData); #endif // !defined(_WIN32) } @@ -1055,13 +1132,13 @@ void P25PacketData::write_PDU_ARP_Reply(uint32_t targetAddr, uint32_t requestorL DECLARE_UINT8_ARRAY(pduUserData, pduLength); ::memcpy(pduUserData + P25_PDU_HEADER_LENGTH_BYTES, arpPacket, P25_PDU_ARP_PCKT_LENGTH); - dispatchUserFrameToFNE(rspHeader, true, pduUserData); + dispatchUserFrameToFNE(rspHeader, true, false, pduUserData); } /* Helper to write user data as a P25 PDU packet. */ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network::PeerNetwork* peerNet, data::DataHeader& dataHeader, - bool extendedAddress, uint8_t* pduUserData) + bool extendedAddress, bool auxiliaryES, uint8_t* pduUserData) { uint32_t streamId = m_network->createStreamId(); uint16_t pktSeq = 0U; @@ -1094,7 +1171,7 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: uint32_t dataOffset = 0U; uint32_t networkBlock = 1U; - // generate the second PDU header + // if using extended addressing, generate the second PDU header if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { dataHeader.encodeExtAddr(pduUserData, true); @@ -1113,6 +1190,37 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } + // if using auxiliary ES, generate the second PDU header + if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && + auxiliaryES) { + dataHeader.encodeAuxES(pduUserData, true); + + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + dataHeader.encodeAuxES(buffer); + writeNetwork(peerId, srcPeerId, peerNet, dataHeader, 1U, buffer, P25_PDU_FEC_LENGTH_BYTES, pktSeq, streamId); + ++pktSeq; + + dataOffset += P25_PDU_HEADER_LENGTH_BYTES; + + blocksToFollow--; + networkBlock++; + + if (m_network->m_verbosePacketData) { + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", + dataHeader.getAlgId(), dataHeader.getKId()); + + if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + dataHeader.getMI(mi); + + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + } + // are we processing extended address data from the first block? if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { dataHeader.encodeExtAddr(pduUserData); @@ -1122,6 +1230,27 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } + // are we processing auxiliary ES data from the first block? + if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && + auxiliaryES) { + dataHeader.encodeAuxES(pduUserData); + + if (m_network->m_verbosePacketData) { + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", + dataHeader.getAlgId(), dataHeader.getKId()); + + if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + dataHeader.getMI(mi); + + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + } + if (dataHeader.getFormat() != PDUFormatType::AMBT) { edac::CRC::addCRC32(pduUserData, packetLength); } diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index 452f99d1..751f520b 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -140,6 +140,7 @@ namespace network p25::data::DataHeader header; bool hasRxHeader; bool extendedAddress; + bool auxiliaryES; uint32_t dataOffset; uint8_t dataBlockCnt; uint8_t* netPDU; @@ -159,6 +160,7 @@ namespace network header(), hasRxHeader(false), extendedAddress(false), + auxiliaryES(false), dataOffset(0U), dataBlockCnt(0U), netPDU(nullptr), @@ -212,9 +214,10 @@ namespace network * @brief Helper to dispatch PDU user data back to the local FNE network. (Will not transmit to neighbor FNE peers.) * @param dataHeader Instance of a PDU data header. * @param extendedAddress Flag indicating whether or not to extended addressing is in use. + * @param auxiliaryES Flag indicating whether or not an auxiliary ES is included. * @param pduUserData Buffer containing user data to transmit. */ - void dispatchUserFrameToFNE(p25::data::DataHeader& dataHeader, bool extendedAddress, uint8_t* pduUserData); + void dispatchUserFrameToFNE(p25::data::DataHeader& dataHeader, bool extendedAddress, bool auxiliaryES, uint8_t* pduUserData); /** * @brief Helper used to process conventional data registration from PDU data. @@ -250,10 +253,11 @@ namespace network * @param peerNet Instance of PeerNetwork to use to send traffic. * @param dataHeader Instance of a PDU data header. * @param extendedAddress Flag indicating whether or not to extended addressing is in use. + * @param auxiliaryES Flag indicating whether or not an auxiliary ES is included. * @param pduUserData Buffer containing user data to transmit. */ void write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network::PeerNetwork* peerNet, p25::data::DataHeader& dataHeader, - bool extendedAddress, uint8_t* pduUserData); + bool extendedAddress, bool auxiliaryES, uint8_t* pduUserData); /** * @brief Write data processed to the network. diff --git a/src/host/p25/packet/ControlSignaling.cpp b/src/host/p25/packet/ControlSignaling.cpp index 5a4a9a70..39ea922b 100644 --- a/src/host/p25/packet/ControlSignaling.cpp +++ b/src/host/p25/packet/ControlSignaling.cpp @@ -1699,7 +1699,7 @@ void ControlSignaling::writeRF_TSDU_AMBT(lc::AMBT* ambt, bool imm) Utils::dump(1U, "!!! *PDU (AMBT) TSBK Block Data", pduUserData, P25_PDU_UNCONFIRMED_LENGTH_BYTES * header.getBlocksToFollow()); } - m_p25->m_data->writeRF_PDU_User(header, false, pduUserData, imm); + m_p25->m_data->writeRF_PDU_User(header, false, false, pduUserData, imm); } /* diff --git a/src/host/p25/packet/Data.cpp b/src/host/p25/packet/Data.cpp index c8c60fa7..8095f2e2 100644 --- a/src/host/p25/packet/Data.cpp +++ b/src/host/p25/packet/Data.cpp @@ -84,6 +84,7 @@ bool Data::process(uint8_t* data, uint32_t len) if (m_p25->m_rfState != RS_RF_DATA) { m_rfDataHeader.reset(); m_rfExtendedAddress = false; + m_rfAuxiliaryES = false; m_rfDataBlockCnt = 0U; m_rfPDUCount = 0U; m_rfPDUBits = 0U; @@ -117,6 +118,7 @@ bool Data::process(uint8_t* data, uint32_t len) m_rfDataHeader.reset(); m_rfExtendedAddress = false; + m_rfAuxiliaryES = false; m_rfPDUCount = 0U; m_rfPDUBits = 0U; m_p25->m_rfState = m_prevRfState; @@ -136,6 +138,7 @@ bool Data::process(uint8_t* data, uint32_t len) m_rfDataHeader.reset(); m_rfExtendedAddress = false; + m_rfAuxiliaryES = false; m_rfPDUCount = 0U; m_rfPDUBits = 0U; m_p25->m_rfState = m_prevRfState; @@ -152,6 +155,7 @@ bool Data::process(uint8_t* data, uint32_t len) m_rfDataHeader.reset(); m_rfExtendedAddress = false; + m_rfAuxiliaryES = false; m_rfPDUCount = 0U; m_rfPDUBits = 0U; m_p25->m_rfState = m_prevRfState; @@ -197,6 +201,50 @@ bool Data::process(uint8_t* data, uint32_t len) m_rfPduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; } + // process second header if we're using auxiliary ES + if ((m_rfDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_rfDataHeader.getSAP() == PDUSAP::ENC_KMM) && + m_rfDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(m_rfPDU, buffer, offset, P25_PDU_FEC_LENGTH_BITS); + bool ret = m_rfDataHeader.decodeAuxES(buffer); + if (!ret) { + LogWarning(LOG_RF, P25_PDU_STR ", unfixable RF 1/2 rate second header data"); + Utils::dump(1U, "P25, Unfixable PDU Data", m_rfPDU + offset, P25_PDU_HEADER_LENGTH_BYTES); + + m_rfDataHeader.reset(); + m_rfPDUCount = 0U; + m_rfPDUBits = 0U; + m_p25->m_rfState = m_prevRfState; + return false; + } + + if (m_verbose) { + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, auxiliary ES, algoId = $%02X, kId = $%04X", + m_rfDataHeader.getAlgId(), m_rfDataHeader.getKId()); + + if (m_rfDataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + m_rfDataHeader.getMI(mi); + + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + + m_rfAuxiliaryES = true; + + offset += P25_PDU_FEC_LENGTH_BITS; + m_rfPDUCount++; + blocksToFollow--; + + // if we are using a secondary header place it in the PDU user data buffer + m_rfDataHeader.getAuxiliaryESData(m_rfPduUserData + dataOffset); + dataOffset += P25_PDU_HEADER_LENGTH_BYTES; + m_rfPduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; + } + uint32_t srcId = (m_rfExtendedAddress) ? m_rfDataHeader.getSrcLLId() : m_rfDataHeader.getLLId(); uint32_t dstId = m_rfDataHeader.getLLId(); @@ -244,14 +292,49 @@ bool Data::process(uint8_t* data, uint32_t len) m_rfExtendedAddress = true; } else { - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u", - (m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_rfData[i].getSerialNo() : m_rfDataBlockCnt, m_rfData[i].getFormat(), - m_rfData[i].getLastBlock()); + // are we processing auxiliary ES data from the first block? + if ((m_rfDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_rfDataHeader.getSAP() == PDUSAP::ENC_KMM) && + m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED && m_rfData[i].getSerialNo() == 0U) { + uint8_t secondHeader[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES]; + ::memset(secondHeader, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + m_rfData[i].getData(secondHeader); + + m_rfDataHeader.decodeAuxES(secondHeader); + if (m_verbose) { + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, algoId = $%02X, kId = $%04X", + m_rfData[i].getSerialNo(), m_rfData[i].getFormat(), m_rfData[i].getLastBlock(), m_rfDataHeader.getEXSAP(), + m_rfDataHeader.getAlgId(), m_rfDataHeader.getKId()); + + if (m_rfDataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + m_rfDataHeader.getMI(mi); + + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, Enc Sync, block %u, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + m_rfData[i].getSerialNo(), mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + + m_rfAuxiliaryES = true; + } + else { + if (m_verbose) { + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u", + (m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_rfData[i].getSerialNo() : m_rfDataBlockCnt, m_rfData[i].getFormat(), + m_rfData[i].getLastBlock()); + } } } m_rfData[i].getData(m_rfPduUserData + dataOffset); + + // is this the first unconfirmed data block after a auxiliary ES header? + if (i == 0U && m_rfDataHeader.getFormat() == PDUFormatType::UNCONFIRMED && m_rfAuxiliaryES) { + uint8_t exSAP = m_rfPduUserData[0U]; // first byte of the first data block after an aux ES header is the extended SAP + m_rfDataHeader.setEXSAP(exSAP); + } + dataOffset += (m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; m_rfPduUserDataLength = dataOffset; m_rfDataBlockCnt++; @@ -386,6 +469,8 @@ bool Data::process(uint8_t* data, uint32_t len) } else { uint8_t sap = (m_rfExtendedAddress) ? m_rfDataHeader.getEXSAP() : m_rfDataHeader.getSAP(); + if (m_rfAuxiliaryES) + sap = m_rfDataHeader.getEXSAP(); // handle standard P25 service access points switch (sap) { @@ -410,7 +495,7 @@ bool Data::process(uint8_t* data, uint32_t len) } } - writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfPduUserData); + writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfAuxiliaryES, m_rfPduUserData); writeRF_PDU_Buffered(); // re-generate buffered PDU and send it on } break; @@ -422,7 +507,7 @@ bool Data::process(uint8_t* data, uint32_t len) } processSNDCPControl(m_rfPduUserData); - writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfPduUserData); + writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfAuxiliaryES, m_rfPduUserData); } break; case PDUSAP::CONV_DATA_REG: @@ -433,7 +518,7 @@ bool Data::process(uint8_t* data, uint32_t len) } processConvDataReg(m_rfPduUserData); - writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfPduUserData); + writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfAuxiliaryES, m_rfPduUserData); } break; case PDUSAP::UNENC_KMM: @@ -444,7 +529,7 @@ bool Data::process(uint8_t* data, uint32_t len) m_rfDataHeader.getBlocksToFollow()); } - writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfPduUserData); + writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfAuxiliaryES, m_rfPduUserData); } break; case PDUSAP::TRUNK_CTRL: @@ -458,7 +543,7 @@ bool Data::process(uint8_t* data, uint32_t len) } break; default: - writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfPduUserData); + writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfAuxiliaryES, m_rfPduUserData); // only repeat the PDU locally if the packet isn't for the FNE if (m_repeatPDU && m_rfDataHeader.getLLId() != WUID_FNE) { @@ -478,6 +563,7 @@ bool Data::process(uint8_t* data, uint32_t len) m_rfDataHeader.reset(); m_rfExtendedAddress = false; + m_rfAuxiliaryES = false; m_rfDataBlockCnt = 0U; m_rfPDUCount = 0U; m_rfPDUBits = 0U; @@ -608,6 +694,7 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint8_t currentBlock, uin m_netDataHeader.reset(); m_netExtendedAddress = false; + m_netAuxiliaryES = false; m_netDataOffset = 0U; m_netDataBlockCnt = 0U; m_netPDUCount = 0U; @@ -670,6 +757,50 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint8_t currentBlock, uin m_netPduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; } + // process second header if we're using auxiliary ES + if ((m_netDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_netDataHeader.getSAP() == PDUSAP::ENC_KMM) && + m_netDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + ::memcpy(buffer, m_netPDU, P25_PDU_FEC_LENGTH_BYTES); + + bool ret = m_netDataHeader.decodeAuxES(buffer); + if (!ret) { + LogWarning(LOG_NET, P25_PDU_STR ", unfixable RF 1/2 rate second header data"); + Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_HEADER_LENGTH_BYTES); + + m_netDataHeader.reset(); + m_netDataBlockCnt = 0U; + m_netPDUCount = 0U; + m_p25->m_netState = RS_NET_IDLE; + return false; + } + + if (m_verbose) { + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, auxiliary ES, algoId = $%02X, kId = $%04X", + m_netDataHeader.getAlgId(), m_netDataHeader.getKId()); + + if (m_netDataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + m_netDataHeader.getMI(mi); + + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + + m_netAuxiliaryES = true; + + offset += P25_PDU_FEC_LENGTH_BYTES; + blocksToFollow--; + + // if we are using a secondary header place it in the PDU user data buffer + m_netDataHeader.getExtAddrData(m_netPduUserData + dataOffset); + dataOffset += P25_PDU_HEADER_LENGTH_BYTES; + m_netPduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; + } + m_netDataBlockCnt = 0U; // decode data blocks @@ -703,12 +834,49 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint8_t currentBlock, uin m_netExtendedAddress = true; } else { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u", - (m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_netData[i].getSerialNo() : m_netDataBlockCnt, m_netData[i].getFormat(), - m_netData[i].getLastBlock()); + // are we processing auxiliary ES data from the first block? + if ((m_netDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_netDataHeader.getSAP() == PDUSAP::ENC_KMM) && + m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED && m_netData[i].getSerialNo() == 0U) { + uint8_t secondHeader[P25_PDU_HEADER_LENGTH_BYTES]; + ::memset(secondHeader, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); + m_netData[i].getData(secondHeader); + + m_netDataHeader.decodeAuxES(secondHeader); + if (m_verbose) { + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, algoId = $%02X, kId = $%04X", + m_netData[i].getSerialNo(), m_netData[i].getFormat(), m_netData[i].getLastBlock(), m_netDataHeader.getEXSAP(), + m_netDataHeader.getAlgId(), m_netDataHeader.getKId()); + + if (m_netDataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + m_netDataHeader.getMI(mi); + + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, Enc Sync, block %u, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + m_netData[i].getSerialNo(), mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + + m_netAuxiliaryES = true; + } + else { + if (m_verbose) { + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u", + (m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_netData[i].getSerialNo() : m_netDataBlockCnt, m_netData[i].getFormat(), + m_netData[i].getLastBlock()); + } + } } m_netData[i].getData(m_netPduUserData + dataOffset); + + // is this the first unconfirmed data block after a auxiliary ES header? + if (i == 0U && m_netDataHeader.getFormat() == PDUFormatType::UNCONFIRMED && m_netAuxiliaryES) { + uint8_t exSAP = m_netPduUserData[0U]; // first byte of the first data block after an aux ES header is the extended SAP + m_netDataHeader.setEXSAP(exSAP); + } + dataOffset += (m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; m_netPduUserDataLength = dataOffset; @@ -747,6 +915,8 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint8_t currentBlock, uin uint32_t dstId = m_netDataHeader.getLLId(); uint8_t sap = (m_netExtendedAddress) ? m_netDataHeader.getEXSAP() : m_netDataHeader.getSAP(); + if (m_netAuxiliaryES) + sap = m_netDataHeader.getEXSAP(); // handle standard P25 service access points switch (sap) { @@ -790,6 +960,7 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint8_t currentBlock, uin m_netDataHeader.reset(); m_netExtendedAddress = false; + m_netAuxiliaryES = false; m_netDataOffset = 0U; m_netDataBlockCnt = 0U; m_netPDUCount = 0U; @@ -823,7 +994,7 @@ bool Data::hasLLIdFNEReg(uint32_t llId) const /* Helper to write user data as a P25 PDU packet. */ -void Data::writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, uint8_t* pduUserData, bool imm) +void Data::writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, bool auxiliaryES, uint8_t* pduUserData, bool imm) { assert(pduUserData != nullptr); @@ -858,7 +1029,7 @@ void Data::writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, uint32_t dataOffset = 0U; uint32_t packetLength = dataHeader.getPDULength(); - // generate the second PDU header + // if using extended addressing, generate the second PDU header if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { dataHeader.encodeExtAddr(pduUserData, true); @@ -870,7 +1041,7 @@ void Data::writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, offset += P25_PDU_FEC_LENGTH_BITS; dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - + blocksToFollow--; if (m_verbose) { @@ -879,16 +1050,69 @@ void Data::writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, } } + // if using auxiliary ES, generate the second PDU header + if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && + auxiliaryES) { + dataHeader.encodeAuxES(pduUserData, true); + + ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + dataHeader.encodeAuxES(block); + writeNetwork(1U, block, P25_PDU_FEC_LENGTH_BYTES, false); + + bitLength += P25_PDU_FEC_LENGTH_BITS; + + offset += P25_PDU_FEC_LENGTH_BITS; + dataOffset += P25_PDU_HEADER_LENGTH_BYTES; + + blocksToFollow--; + + if (m_verbose) { + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", + dataHeader.getAlgId(), dataHeader.getKId()); + + if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + dataHeader.getMI(mi); + + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + } + // are we processing extended address data from the first block? if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { dataHeader.encodeExtAddr(pduUserData); if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } } + // are we processing auxiliary ES data from the first block? + if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && + auxiliaryES) { + dataHeader.encodeAuxES(pduUserData); + + if (m_verbose) { + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", + dataHeader.getAlgId(), dataHeader.getKId()); + + if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + dataHeader.getMI(mi); + + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + } + if (dataHeader.getFormat() != PDUFormatType::AMBT) { edac::CRC::addCRC32(pduUserData, packetLength); } @@ -921,7 +1145,7 @@ void Data::writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, /* Helper to write user data as a P25 PDU packet. */ -void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, uint8_t* pduUserData) +void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, bool auxiliaryES, uint8_t* pduUserData) { assert(pduUserData != nullptr); @@ -950,7 +1174,7 @@ void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, uint32_t packetLength = dataHeader.getPDULength(); uint32_t netDataBlockCnt = 1U; - // generate the second PDU header + // if using extended addressing, generate the second PDU header if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { dataHeader.encodeExtAddr(pduUserData, true); @@ -971,16 +1195,69 @@ void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, } } + // if using auxiliary ES, generate the second PDU header + if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && + auxiliaryES) { + dataHeader.encodeAuxES(pduUserData, true); + + ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + dataHeader.encodeAuxES(block); + writeNetwork(1U, block, P25_PDU_FEC_LENGTH_BYTES, false); + netDataBlockCnt++; + + bitLength += P25_PDU_FEC_LENGTH_BITS; + + dataOffset += P25_PDU_HEADER_LENGTH_BYTES; + + blocksToFollow--; + + if (m_verbose) { + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, auxiliary ES, algId = $%02X, kId = $%04X", + dataHeader.getAlgId(), dataHeader.getKId()); + + if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + dataHeader.getMI(mi); + + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + } + // are we processing extended address data from the first block? if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { dataHeader.encodeExtAddr(pduUserData); if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, sap = $%02X, srcLlId = %u", + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, extended address, sap = $%02X, srcLlId = %u", dataHeader.getEXSAP(), dataHeader.getSrcLLId()); } } + // are we processing auxiliary ES data from the first block? + if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && + auxiliaryES) { + dataHeader.encodeAuxES(pduUserData); + + if (m_verbose) { + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, auxiliary ES, algId = $%02X, kId = $%04X", + dataHeader.getAlgId(), dataHeader.getKId()); + + if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + dataHeader.getMI(mi); + + LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + } + if (dataHeader.getFormat() != PDUFormatType::AMBT) { edac::CRC::addCRC32(pduUserData, packetLength); } @@ -1164,6 +1441,7 @@ Data::Data(Control* p25, bool dumpPDUData, bool repeatPDU, bool debug, bool verb m_rfData(nullptr), m_rfDataHeader(), m_rfExtendedAddress(false), + m_rfAuxiliaryES(false), m_rfDataBlockCnt(0U), m_rfPDU(nullptr), m_rfPDUCount(0U), @@ -1171,6 +1449,7 @@ Data::Data(Control* p25, bool dumpPDUData, bool repeatPDU, bool debug, bool verb m_netData(nullptr), m_netDataHeader(), m_netExtendedAddress(false), + m_netAuxiliaryES(false), m_netDataOffset(0U), m_netDataBlockCnt(0U), m_netPDU(nullptr), @@ -1342,7 +1621,7 @@ bool Data::processSNDCPControl(const uint8_t* pduUserData) osp->encode(txPduUserData); rspHeader.calculateLength(2U); - writeRF_PDU_User(rspHeader, false, txPduUserData); + writeRF_PDU_User(rspHeader, false, false, txPduUserData); return true; } @@ -1357,7 +1636,7 @@ bool Data::processSNDCPControl(const uint8_t* pduUserData) osp->encode(txPduUserData); rspHeader.calculateLength(2U); - writeRF_PDU_User(rspHeader, false, txPduUserData); + writeRF_PDU_User(rspHeader, false, false, txPduUserData); sndcpReset(llId, true); } @@ -1372,7 +1651,7 @@ bool Data::processSNDCPControl(const uint8_t* pduUserData) osp->encode(txPduUserData); rspHeader.calculateLength(2U); - writeRF_PDU_User(rspHeader, false, txPduUserData); + writeRF_PDU_User(rspHeader, false, false, txPduUserData); sndcpReset(llId, true); @@ -1408,7 +1687,7 @@ bool Data::processSNDCPControl(const uint8_t* pduUserData) osp->encode(txPduUserData); rspHeader.calculateLength(2U); - writeRF_PDU_User(rspHeader, false, txPduUserData); + writeRF_PDU_User(rspHeader, false, false, txPduUserData); sndcpReset(llId, true); } @@ -1549,7 +1828,7 @@ void Data::writeNet_PDU_Buffered() if (blocksToFollow > 0U) { uint32_t dataOffset = 0U; - // generate the second PDU header + // if using extended addressing, generate the second PDU header if ((m_netDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (m_netDataHeader.getSAP() == PDUSAP::EXT_ADDR) && m_netExtendedAddress) { m_netDataHeader.encodeExtAddr(m_netPduUserData, true); @@ -1569,6 +1848,38 @@ void Data::writeNet_PDU_Buffered() } } + // if using auxiliary ES, generate the second PDU header + if ((m_netDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (m_netDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_netDataHeader.getSAP() == PDUSAP::ENC_KMM) && + m_netAuxiliaryES) { + m_netDataHeader.encodeAuxES(m_netPduUserData, true); + + ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + m_netDataHeader.encodeAuxES(block); + writeNetwork(1U, block, P25_PDU_FEC_LENGTH_BYTES, false); + + bitLength += P25_PDU_FEC_LENGTH_BITS; + + offset += P25_PDU_FEC_LENGTH_BITS; + dataOffset += P25_PDU_HEADER_LENGTH_BYTES; + + blocksToFollow--; + + if (m_verbose) { + LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", + m_netDataHeader.getAlgId(), m_netDataHeader.getKId()); + + if (m_netDataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + m_netDataHeader.getMI(mi); + + LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + } + // are we processing extended address data from the first block? if ((m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) && (m_netDataHeader.getSAP() == PDUSAP::EXT_ADDR) && m_netExtendedAddress) { m_netDataHeader.encodeExtAddr(m_netPduUserData); @@ -1579,6 +1890,27 @@ void Data::writeNet_PDU_Buffered() } } + // are we processing auxiliary ES data from the first block? + if ((m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) && (m_netDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_netDataHeader.getSAP() == PDUSAP::ENC_KMM) && + m_netAuxiliaryES) { + m_netDataHeader.encodeAuxES(m_netPduUserData); + + if (m_verbose) { + LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", + m_netDataHeader.getAlgId(), m_netDataHeader.getKId()); + + if (m_netDataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + m_netDataHeader.getMI(mi); + + LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + } + edac::CRC::addCRC32(m_netPduUserData, m_netPduUserDataLength); if (m_dumpPDUData) { @@ -1641,7 +1973,7 @@ void Data::writeRF_PDU_Buffered() if (blocksToFollow > 0U) { uint32_t dataOffset = 0U; - // generate the second PDU header + // if using extended addressing, generate the second PDU header if ((m_rfDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (m_rfDataHeader.getSAP() == PDUSAP::EXT_ADDR) && m_rfExtendedAddress) { m_rfDataHeader.encodeExtAddr(m_rfPduUserData, true); @@ -1661,6 +1993,37 @@ void Data::writeRF_PDU_Buffered() } } + // if using auxiliary ES, generate the second PDU header + if ((m_rfDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (m_rfDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_rfDataHeader.getSAP() == PDUSAP::ENC_KMM) && + m_rfAuxiliaryES) { + m_rfDataHeader.encodeAuxES(m_rfPduUserData, true); + + ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + m_rfDataHeader.encodeAuxES(block); + writeNetwork(1U, block, P25_PDU_FEC_LENGTH_BYTES, false); + + bitLength += P25_PDU_FEC_LENGTH_BITS; + offset += P25_PDU_FEC_LENGTH_BITS; + dataOffset += P25_PDU_HEADER_LENGTH_BYTES; + + blocksToFollow--; + + if (m_verbose) { + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", + m_rfDataHeader.getAlgId(), m_rfDataHeader.getKId()); + + if (m_rfDataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + m_rfDataHeader.getMI(mi); + + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + } + // are we processing extended address data from the first block? if ((m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) && (m_rfDataHeader.getSAP() == PDUSAP::EXT_ADDR) && m_rfExtendedAddress) { m_rfDataHeader.encodeExtAddr(m_rfPduUserData); @@ -1671,6 +2034,27 @@ void Data::writeRF_PDU_Buffered() } } + // are we processing auxiliary ES data from the first block? + if ((m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) && (m_rfDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_rfDataHeader.getSAP() == PDUSAP::ENC_KMM) && + m_netAuxiliaryES) { + m_rfDataHeader.encodeAuxES(m_rfPduUserData); + + if (m_verbose) { + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", + m_rfDataHeader.getAlgId(), m_rfDataHeader.getKId()); + + if (m_rfDataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + m_rfDataHeader.getMI(mi); + + LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + } + edac::CRC::addCRC32(m_rfPduUserData, m_rfPduUserDataLength); if (m_dumpPDUData) { @@ -1736,7 +2120,7 @@ void Data::writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, uint32_t ipA Utils::dump(1U, "Data::writeRF_PDU_Reg_Response() pduUserData", pduUserData, 12U); rspHeader.calculateLength(12U); - writeRF_PDU_User(rspHeader, false, pduUserData); + writeRF_PDU_User(rspHeader, false, false, pduUserData); } /* Helper to write a PDU acknowledge response. */ diff --git a/src/host/p25/packet/Data.h b/src/host/p25/packet/Data.h index 884e2a35..a14451b5 100644 --- a/src/host/p25/packet/Data.h +++ b/src/host/p25/packet/Data.h @@ -84,18 +84,20 @@ namespace p25 /** * @brief Helper to write user data as a P25 PDU packet. * @param dataHeader Instance of a PDU data header. - * @param extendedAddress Flag indicating whether or not to extended addressing is in use. + * @param extendedAddress Flag indicating whether or not extended addressing is in use. + * @param auxiliaryES Flag indicating whether or not an auxiliary ES is included. * @param pduUserData Buffer containing user data to transmit. * @param imm Flag indicating the PDU should be written to the immediate queue. */ - void writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, uint8_t* pduUserData, bool imm = false); + void writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, bool auxiliaryES, uint8_t* pduUserData, bool imm = false); /** * @brief Helper to write user data as a P25 PDU packet. * @param dataHeader Instance of a PDU data header. * @param extendedAddress Flag indicating whether or not to extended addressing is in use. + * @param auxiliaryES Flag indicating whether or not an auxiliary ES is included. * @param pduUserData Buffer containing user data to transmit. */ - void writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, uint8_t* pduUserData); + void writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, bool auxiliaryES, uint8_t* pduUserData); /** * @brief Updates the processor by the passed number of milliseconds. @@ -131,6 +133,7 @@ namespace p25 data::DataBlock* m_rfData; data::DataHeader m_rfDataHeader; bool m_rfExtendedAddress; + bool m_rfAuxiliaryES; uint8_t m_rfDataBlockCnt; uint8_t* m_rfPDU; uint32_t m_rfPDUCount; @@ -139,6 +142,7 @@ namespace p25 data::DataBlock* m_netData; data::DataHeader m_netDataHeader; bool m_netExtendedAddress; + bool m_netAuxiliaryES; uint32_t m_netDataOffset; uint8_t m_netDataBlockCnt; uint8_t* m_netPDU; From 5d411b2af5ce3374d2828dd443a6c8ef37804913 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 8 Nov 2025 11:47:09 -0500 Subject: [PATCH 143/200] complete refactor of how P25 PDUs are assembled from user data and disassembled to user data; various corrections for data path nullptr reference issues; implement several tests for testing the P25 PDU assembler; --- src/CMakeLists.txt | 4 +- src/common/dmr/data/DataBlock.cpp | 31 +- src/common/dmr/data/DataBlock.h | 10 + src/common/dmr/data/DataHeader.cpp | 57 +- src/common/dmr/data/DataHeader.h | 12 +- src/common/dmr/data/EmbeddedData.cpp | 11 +- src/common/dmr/data/NetData.cpp | 5 +- src/common/network/Network.cpp | 10 +- src/common/p25/data/Assembler.cpp | 520 ++++++ src/common/p25/data/Assembler.h | 169 ++ src/common/p25/data/DataBlock.cpp | 33 +- src/common/p25/data/DataBlock.h | 10 + src/common/p25/data/DataHeader.cpp | 202 ++- src/common/p25/data/DataHeader.h | 22 +- src/fne/network/FNENetwork.cpp | 4 + .../callhandler/packetdata/DMRPacketData.cpp | 82 +- .../callhandler/packetdata/DMRPacketData.h | 10 + .../callhandler/packetdata/P25PacketData.cpp | 684 +++---- .../callhandler/packetdata/P25PacketData.h | 55 +- src/host/p25/packet/Data.cpp | 1583 +++-------------- src/host/p25/packet/Data.h | 17 +- tests/p25/PDU_Confirmed_AuxES_Test.cpp | 129 ++ tests/p25/PDU_Confirmed_ConvReg_Test.cpp | 184 ++ tests/p25/PDU_Confirmed_ExtAddr_Test.cpp | 121 ++ tests/p25/PDU_Confirmed_Large_Test.cpp | 124 ++ tests/p25/PDU_Confirmed_Small_Test.cpp | 118 ++ tests/p25/PDU_Unconfirmed_AuxES_Test.cpp | 129 ++ tests/p25/PDU_Unconfirmed_ExtAddr_Test.cpp | 121 ++ tests/p25/PDU_Unconfirmed_Test.cpp | 121 ++ 29 files changed, 2679 insertions(+), 1899 deletions(-) create mode 100644 src/common/p25/data/Assembler.cpp create mode 100644 src/common/p25/data/Assembler.h create mode 100644 tests/p25/PDU_Confirmed_AuxES_Test.cpp create mode 100644 tests/p25/PDU_Confirmed_ConvReg_Test.cpp create mode 100644 tests/p25/PDU_Confirmed_ExtAddr_Test.cpp create mode 100644 tests/p25/PDU_Confirmed_Large_Test.cpp create mode 100644 tests/p25/PDU_Confirmed_Small_Test.cpp create mode 100644 tests/p25/PDU_Unconfirmed_AuxES_Test.cpp create mode 100644 tests/p25/PDU_Unconfirmed_ExtAddr_Test.cpp create mode 100644 tests/p25/PDU_Unconfirmed_Test.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1b143996..2e748572 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,9 +17,9 @@ add_library(common STATIC ${common_SRC} ${common_INCLUDE}) if (COMPILE_WIN32) target_link_libraries(common PRIVATE asio::asio Threads::Threads) else () - target_link_libraries(common PRIVATE asio::asio Threads::Threads util) + target_link_libraries(common PRIVATE ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio Threads::Threads util) endif (COMPILE_WIN32) -target_include_directories(common PRIVATE src src/common) +target_include_directories(common PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} src src/common) # ## vocoder diff --git a/src/common/dmr/data/DataBlock.cpp b/src/common/dmr/data/DataBlock.cpp index 4d7c6292..9321cf12 100644 --- a/src/common/dmr/data/DataBlock.cpp +++ b/src/common/dmr/data/DataBlock.cpp @@ -25,6 +25,13 @@ using namespace dmr::data; // Public Class Members // --------------------------------------------------------------------------- +/* Initializes a copy instance of the DataBlock class. */ + +DataBlock::DataBlock(const DataBlock& data) : DataBlock() +{ + copy(data); +} + /* Initializes a new instance of the DataBlock class. */ DataBlock::DataBlock() : @@ -43,7 +50,10 @@ DataBlock::DataBlock() : DataBlock::~DataBlock() { - delete[] m_data; + if (m_data != nullptr) { + delete[] m_data; + m_data = nullptr; + } } /* Decodes DMR PDU data block. */ @@ -439,3 +449,22 @@ uint32_t DataBlock::getData(uint8_t* buffer) const } } } + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to copy the the class. */ + +void DataBlock::copy(const DataBlock& data) +{ + m_serialNo = data.m_serialNo; + m_lastBlock = data.m_lastBlock; + + m_dataType = data.m_dataType; + m_DPF = data.m_DPF; + + if (m_data != nullptr && data.m_data != nullptr) { + ::memcpy(m_data, data.m_data, DMR_PDU_UNCODED_LENGTH_BYTES); + } +} diff --git a/src/common/dmr/data/DataBlock.h b/src/common/dmr/data/DataBlock.h index 240051f0..93dc3c42 100644 --- a/src/common/dmr/data/DataBlock.h +++ b/src/common/dmr/data/DataBlock.h @@ -38,6 +38,11 @@ namespace dmr */ class HOST_SW_API DataBlock { public: + /** + * @brief Initializes a copy instance of the DataBlock class. + * @param data Instance of DataBlock class to copy from. + */ + DataBlock(const DataBlock& data); /** * @brief Initializes a new instance of the DataBlock class. */ @@ -117,6 +122,11 @@ namespace dmr defines::DPF::E m_DPF; uint8_t* m_data; + + /** + * @brief Internal helper to copy the class. + */ + void copy(const DataBlock& data); }; } // namespace data } // namespace dmr diff --git a/src/common/dmr/data/DataHeader.cpp b/src/common/dmr/data/DataHeader.cpp index e7e2028d..77a9a2ab 100644 --- a/src/common/dmr/data/DataHeader.cpp +++ b/src/common/dmr/data/DataHeader.cpp @@ -33,6 +33,13 @@ const uint8_t UDTF_NMEA = 0x05U; // Public Class Members // --------------------------------------------------------------------------- +/* Initializes a copy instance of the DataHeader class. */ + +DataHeader::DataHeader(const DataHeader& data) : DataHeader() +{ + copy(data); +} + /* Initializes a new instance of the DataHeader class. */ DataHeader::DataHeader() : @@ -68,7 +75,10 @@ DataHeader::DataHeader() : DataHeader::~DataHeader() { - delete[] m_data; + if (m_data != nullptr) { + delete[] m_data; + m_data = nullptr; + } } /* Equals operator. */ @@ -543,3 +553,48 @@ uint32_t DataHeader::calculatePadLength(DPF::E dpf, DataType::E dataType, uint32 } } } + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to copy the the class. */ + +void DataHeader::copy(const DataHeader& data) +{ + m_GI = data.m_GI; + m_A = data.m_A; + + m_DPF = data.m_DPF; + + m_sap = data.m_sap; + + m_fsn = data.m_fsn; + m_Ns = data.m_Ns; + + m_blocksToFollow = data.m_blocksToFollow; + m_padLength = data.m_padLength; + + m_F = data.m_F; + m_S = data.m_S; + + m_dataFormat = data.m_dataFormat; + + m_srcId = data.m_srcId; + m_dstId = data.m_dstId; + + m_rspClass = data.m_rspClass; + m_rspType = data.m_rspType; + m_rspStatus = data.m_rspStatus; + + m_srcPort = data.m_srcPort; + m_dstPort = data.m_dstPort; + + m_SF = data.m_SF; + m_PF = data.m_PF; + m_UDTO = data.m_UDTO; + + if (m_data != nullptr && data.m_data != nullptr) { + ::memcpy(m_data, data.m_data, DMR_LC_HEADER_LENGTH_BYTES); + } +} diff --git a/src/common/dmr/data/DataHeader.h b/src/common/dmr/data/DataHeader.h index 52d10861..fb778fd4 100644 --- a/src/common/dmr/data/DataHeader.h +++ b/src/common/dmr/data/DataHeader.h @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX - * Copyright (C) 2021,2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2021,2024,2025 Bryan Biedenkapp, N2PLL * */ /** @@ -38,6 +38,11 @@ namespace dmr */ class HOST_SW_API DataHeader { public: + /** + * @brief Initializes a copy instance of the DataHeader class. + * @param data Instance of DataHeader class to copy from. + */ + DataHeader(const DataHeader& data); /** * @brief Initializes a new instance of the DataHeader class. */ @@ -194,6 +199,11 @@ namespace dmr bool m_SF; bool m_PF; uint8_t m_UDTO; + + /** + * @brief Internal helper to copy the class. + */ + void copy(const DataHeader& data); }; } // namespace data } // namespace dmr diff --git a/src/common/dmr/data/EmbeddedData.cpp b/src/common/dmr/data/EmbeddedData.cpp index bf9bad19..8aaedec1 100644 --- a/src/common/dmr/data/EmbeddedData.cpp +++ b/src/common/dmr/data/EmbeddedData.cpp @@ -44,8 +44,15 @@ EmbeddedData::EmbeddedData() : EmbeddedData::~EmbeddedData() { - delete[] m_raw; - delete[] m_data; + if (m_raw != nullptr) { + delete[] m_raw; + m_raw = nullptr; + } + + if (m_data != nullptr) { + delete[] m_data; + m_data = nullptr; + } } /* Add LC data (which may consist of 4 blocks) to the data store. */ diff --git a/src/common/dmr/data/NetData.cpp b/src/common/dmr/data/NetData.cpp index 3514c53b..bae110c6 100644 --- a/src/common/dmr/data/NetData.cpp +++ b/src/common/dmr/data/NetData.cpp @@ -64,7 +64,10 @@ NetData::NetData() : NetData::~NetData() { - delete[] m_data; + if (m_data != nullptr) { + delete[] m_data; + m_data = nullptr; + } } /* Equals operator. */ diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index 2b99e729..72b4901a 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -1195,16 +1195,16 @@ bool Network::open() m_status = NET_STAT_WAITING_CONNECT; // are we rotating IPs for HA reconnect? - if (m_haIPs.size() > 0 && m_retryCount > 0U && !m_flaggedDuplicateConn && + if ((m_haIPs.size() - 1) > 1 && m_retryCount > 0U && !m_flaggedDuplicateConn && m_maxRetryCount == MAX_RETRY_HA_RECONNECT) { - PeerHAIPEntry entry = m_haIPs[m_currentHAIP]; - m_currentHAIP++; - - if (m_currentHAIP > m_haIPs.size()) { + if (m_currentHAIP > (m_haIPs.size() - 1)) { m_currentHAIP = 0U; } + PeerHAIPEntry entry = m_haIPs[m_currentHAIP]; + m_currentHAIP++; + LogInfoEx(LOG_NET, "PEER %u connection to the master has timed out, %s:%u is non-responsive, trying next HA %s:%u", m_peerId, m_address.c_str(), m_port, entry.masterAddress.c_str(), entry.masterPort); m_address = entry.masterAddress; diff --git a/src/common/p25/data/Assembler.cpp b/src/common/p25/data/Assembler.cpp new file mode 100644 index 00000000..d6edb5fb --- /dev/null +++ b/src/common/p25/data/Assembler.cpp @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "p25/P25Defines.h" +#include "p25/data/Assembler.h" +#include "edac/CRC.h" +#include "Log.h" +#include "Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::data; + +#include +#include + +// --------------------------------------------------------------------------- +// Static Class Members +// --------------------------------------------------------------------------- + +bool Assembler::s_dumpPDUData = false; +bool Assembler::s_verbose = false; + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the Assembler class. */ + +Assembler::Assembler() : + dataBlocks(nullptr), + dataHeader(), + m_extendedAddress(false), + m_auxiliaryES(false), + m_dataBlockCnt(0U), + m_undecodableBlockCnt(0U), + m_packetCRCFailed(false), + m_complete(false), + m_pduUserData(nullptr), + m_pduUserDataLength(0U), + m_blockCount(0U), + m_dataOffset(0U), + m_usingCustomWriter(false), + m_blockWriter(nullptr) +{ + dataBlocks = new data::DataBlock[P25_MAX_PDU_BLOCKS]; + + m_pduUserData = new uint8_t[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + ::memset(m_pduUserData, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); + + m_rawPDU = new uint8_t[P25_PDU_FRAME_LENGTH_BYTES + 2U]; + ::memset(m_rawPDU, 0x00U, P25_PDU_FRAME_LENGTH_BYTES + 2U); +} + +/* Finalizes a instance of the Assembler class. */ + +Assembler::~Assembler() +{ + if (dataBlocks != nullptr) + delete[] dataBlocks; + if (m_pduUserData != nullptr) + delete[] m_pduUserData; +} + +/* Helper to disassemble a P25 PDU frame into user data. */ + +bool Assembler::disassemble(const uint8_t* pduBlock, uint32_t blockLength, bool resetState) +{ + assert(pduBlock != nullptr); + + if (resetState) { + resetDisassemblyState(); + } + +#if DEBUG_P25_PDU_DATA + Utils::dump(1U, "P25, PDU Disassembler Block", pduBlock, blockLength); +#endif + + UInt8Array dataArray = std::make_unique(P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); + uint8_t* data = dataArray.get(); + ::memset(data, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); + + if (m_blockCount == 0U) { + bool ret = dataHeader.decode(pduBlock); + if (!ret) { + LogWarning(LOG_P25, P25_PDU_STR ", unfixable RF 1/2 rate header data"); + Utils::dump(1U, "P25, Unfixable PDU Data", pduBlock, P25_PDU_FEC_LENGTH_BYTES); + + resetDisassemblyState(); + return false; + } + + if (s_verbose) { + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", + dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), + dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(), dataHeader.getSynchronize(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), + dataHeader.getHeaderOffset(), dataHeader.getLLId()); + } + + // make sure we don't get a PDU with more blocks then we support + if (dataHeader.getBlocksToFollow() >= P25_MAX_PDU_BLOCKS) { + LogError(LOG_P25, P25_PDU_STR ", ISP, too many PDU blocks to process, %u > %u", dataHeader.getBlocksToFollow(), P25_MAX_PDU_BLOCKS); + + resetDisassemblyState(); + return false; + } + + m_blockCount++; + m_complete = false; + return true; + } + else { + ::memcpy(m_rawPDU + ((m_blockCount - 1U) * blockLength), pduBlock, blockLength); + m_dataOffset += blockLength; + m_blockCount++; + + if (m_blockCount - 1U >= dataHeader.getBlocksToFollow()) { +#if DEBUG_P25_PDU_DATA + LogDebugEx(LOG_P25, "Assembler::disassemble()", "complete PDU, blocksToFollow = %u, blockCount = %u", dataHeader.getBlocksToFollow(), m_blockCount); + Utils::dump(1U, "Assembler::disassemble() rawPDU", m_rawPDU, m_dataOffset); +#endif + uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); + uint32_t offset = 0U; + uint32_t dataOffset = 0U; + + uint8_t buffer[P25_PDU_FEC_LENGTH_BYTES]; + + m_dataBlockCnt = 0U; + + // process all blocks in the data stream + // if the primary header has a header offset ensure data if offset by that amount + if (dataHeader.getHeaderOffset() > 0U) { + offset += dataHeader.getHeaderOffset(); + } + + uint32_t packetLength = dataHeader.getPacketLength(); + uint32_t padLength = dataHeader.getPadLength(); + uint32_t secondHeaderOffset = 0U; + + // decode data blocks + for (uint32_t i = 0U; i < blocksToFollow; i++) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + ::memcpy(buffer, m_rawPDU + offset, P25_PDU_FEC_LENGTH_BYTES); + + bool ret = dataBlocks[i].decode(buffer, dataHeader); + if (ret) { + // if we are getting unconfirmed or confirmed blocks, and if we've reached the total number of blocks + // set this block as the last block for full packet CRC + if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) || (dataHeader.getFormat() == PDUFormatType::UNCONFIRMED)) { + if ((m_dataBlockCnt + 1U) == blocksToFollow) { + dataBlocks[i].setLastBlock(true); + } + } + + // fake data block serial number for unconfirmed mode + if (dataHeader.getFormat() == PDUFormatType::UNCONFIRMED && dataBlocks[i].getSerialNo() == 0U) + dataBlocks[i].setSerialNo(i); + + // are we processing extended address data from the first block? + if (dataHeader.getSAP() == PDUSAP::EXT_ADDR && dataBlocks[i].getSerialNo() == 0U) { + uint8_t secondHeader[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES]; + ::memset(secondHeader, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + dataBlocks[i].getData(secondHeader); + + dataHeader.decodeExtAddr(secondHeader); + if (s_verbose) { + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", + dataBlocks[i].getSerialNo(), dataBlocks[i].getFormat(), dataBlocks[i].getLastBlock(), dataHeader.getEXSAP(), dataHeader.getSrcLLId()); + } + + m_extendedAddress = true; + if (dataHeader.getFormat() == PDUFormatType::CONFIRMED) + secondHeaderOffset += 4U; + else + secondHeaderOffset += P25_PDU_HEADER_LENGTH_BYTES; + } + else { + // are we processing auxiliary ES data from the first block? + if ((dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && + dataBlocks[i].getSerialNo() == 0U) { + uint8_t secondHeader[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES]; + ::memset(secondHeader, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + dataBlocks[i].getData(secondHeader); + + dataHeader.decodeAuxES(secondHeader); + if (s_verbose) { + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, algoId = $%02X, kId = $%04X", + dataBlocks[i].getSerialNo(), dataBlocks[i].getFormat(), dataBlocks[i].getLastBlock(), dataHeader.getEXSAP(), + dataHeader.getAlgId(), dataHeader.getKId()); + + if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + dataHeader.getMI(mi); + + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, Enc Sync, block %u, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + dataBlocks[i].getSerialNo(), mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + + m_auxiliaryES = true; + if (dataHeader.getFormat() == PDUFormatType::CONFIRMED) + secondHeaderOffset += 13U; + else + secondHeaderOffset += P25_PDU_HEADER_LENGTH_BYTES + 1U; + } + else { + if (s_verbose) { + LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u", + (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? dataBlocks[i].getSerialNo() : m_dataBlockCnt, dataBlocks[i].getFormat(), + dataBlocks[i].getLastBlock()); + } + } + } + + dataBlocks[i].getData(m_pduUserData + dataOffset); + + // is this the first unconfirmed data block after a auxiliary ES header? + if (i == 0U && dataHeader.getFormat() == PDUFormatType::UNCONFIRMED && m_auxiliaryES) { + uint8_t exSAP = m_pduUserData[0U]; // first byte of the first data block after an aux ES header is the extended SAP + dataHeader.setEXSAP(exSAP); + } + + dataOffset += (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; + m_dataBlockCnt++; + } + else { + if (dataBlocks[i].getFormat() == PDUFormatType::CONFIRMED) { + LogWarning(LOG_P25, P25_PDU_STR ", unfixable PDU data (3/4 rate or CRC), block %u", i); + + // to prevent data block offset errors fill the bad block with 0's + uint8_t blankBuf[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES]; + ::memset(blankBuf, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + ::memcpy(m_pduUserData + dataOffset, blankBuf, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + + dataOffset += P25_PDU_CONFIRMED_DATA_LENGTH_BYTES; + m_dataBlockCnt++; + m_undecodableBlockCnt++; + } + else { + LogWarning(LOG_P25, P25_PDU_STR ", unfixable PDU data (1/2 rate or CRC), block %u", i); + + // to prevent data block offset errors fill the bad block with 0's + uint8_t blankBuf[P25_PDU_UNCONFIRMED_LENGTH_BYTES]; + ::memset(blankBuf, 0x00U, P25_PDU_UNCONFIRMED_LENGTH_BYTES); + ::memcpy(m_pduUserData + dataOffset, blankBuf, P25_PDU_UNCONFIRMED_LENGTH_BYTES); + + dataOffset += P25_PDU_UNCONFIRMED_LENGTH_BYTES; + m_dataBlockCnt++; + m_undecodableBlockCnt++; + } + + if (s_dumpPDUData) { + Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); + } + } + + offset += P25_PDU_FEC_LENGTH_BYTES; + } + +#if DEBUG_P25_PDU_DATA + LogDebugEx(LOG_P25, "Assembler::disassemble()", "packetLength = %u, secondHeaderOffset = %u, padLength = %u, pduLength = %u", packetLength, secondHeaderOffset, padLength, dataHeader.getPDULength()); +#endif + if (dataHeader.getBlocksToFollow() > 0U) { + if (padLength > 0U) { + // move CRC-32 properly before padding to check CRC of user data + uint8_t crcBytes[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + ::memset(crcBytes, 0x00U, packetLength); + ::memcpy(crcBytes, m_pduUserData, packetLength); + ::memcpy(crcBytes + packetLength, m_pduUserData + packetLength + padLength, 4U); + + bool crcRet = edac::CRC::checkCRC32(crcBytes, packetLength + 4U); + if (!crcRet) { + LogWarning(LOG_P25, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", dataHeader.getBlocksToFollow(), m_pduUserDataLength); + m_packetCRCFailed = true; + } + } else { + bool crcRet = edac::CRC::checkCRC32(m_pduUserData, packetLength + 4U); + if (!crcRet) { + LogWarning(LOG_P25, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", dataHeader.getBlocksToFollow(), m_pduUserDataLength); + m_packetCRCFailed = true; + } + } + } + + // reorganize PDU buffer for second header offsetting + if (secondHeaderOffset > 0U) { + uint8_t tempBuf[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + ::memcpy(tempBuf, m_pduUserData + secondHeaderOffset, packetLength - 4U); + ::memset(m_pduUserData, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); + ::memcpy(m_pduUserData, tempBuf, packetLength - secondHeaderOffset); + } + + if (s_dumpPDUData && m_dataBlockCnt > 0U) { + Utils::dump(1U, "P25, PDU Packet", m_pduUserData, packetLength - secondHeaderOffset); + } + + if (m_dataBlockCnt < blocksToFollow) { + LogWarning(LOG_P25, P25_PDU_STR ", incomplete PDU (%d / %d blocks)", m_dataBlockCnt, blocksToFollow); + } + + m_pduUserDataLength = packetLength - secondHeaderOffset; + + m_blockCount = 0U; + m_complete = true; + return true; + } else { + return true; + } + } + + return false; +} + +/* Helper to assemble user data as a P25 PDU packet. */ + +UInt8Array Assembler::assemble(data::DataHeader& dataHeader, bool extendedAddress, bool auxiliaryES, + const uint8_t* pduUserData, uint32_t* assembledBitLength, void* userContext) +{ + assert(pduUserData != nullptr); + + if (assembledBitLength != nullptr) + *assembledBitLength = 0U; + + uint32_t bitLength = ((dataHeader.getBlocksToFollow() + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; + if (dataHeader.getPadLength() > 0U) + bitLength += (dataHeader.getPadLength() * 8U); + + uint32_t offset = P25_PREAMBLE_LENGTH_BITS; + + UInt8Array dataArray = std::make_unique((bitLength / 8U) + 1U); + uint8_t* data = dataArray.get(); + ::memset(data, 0x00U, (bitLength / 8U) + 1U); + + uint8_t block[P25_PDU_FEC_LENGTH_BYTES]; + ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + + uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); + + if (s_verbose) { + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", + dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), + dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(), dataHeader.getSynchronize(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), + dataHeader.getHeaderOffset(), bitLength, dataHeader.getLLId()); + } + + // generate the PDU header and 1/2 rate Trellis + dataHeader.encode(block); + +#if DEBUG_P25_PDU_DATA + Utils::dump(1U, "P25, PDU Assembler Block", block, P25_PDU_FEC_LENGTH_BYTES); +#endif + + if (m_usingCustomWriter && m_blockWriter != nullptr) + m_blockWriter(userContext, 0U, block, P25_PDU_FEC_LENGTH_BYTES, false); + else + Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); + offset += P25_PDU_FEC_LENGTH_BITS; + + if (pduUserData != nullptr && blocksToFollow > 0U) { + uint32_t dataOffset = 0U; + uint32_t pduLength = dataHeader.getPDULength() + dataHeader.getPadLength(); + uint32_t dataBlockCnt = 1U; + uint32_t secondHeaderOffset = 0U; + + // we pad 20 bytes of extra space -- confirmed data will use various extra space in the PDU + DECLARE_UINT8_ARRAY(packetData, pduLength + 20U); + + // are we processing extended address data from the first block? + if (dataHeader.getSAP() == PDUSAP::EXT_ADDR && extendedAddress) { + if (dataHeader.getFormat() == PDUFormatType::CONFIRMED) + secondHeaderOffset += 4U; + else + secondHeaderOffset += P25_PDU_HEADER_LENGTH_BYTES; + dataHeader.encodeExtAddr(packetData); + + if (s_verbose) { + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", + dataHeader.getEXSAP(), dataHeader.getSrcLLId()); + } + } + + // are we processing auxiliary ES data from the first block? + if ((dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && auxiliaryES) { + if (dataHeader.getFormat() == PDUFormatType::CONFIRMED) + secondHeaderOffset += 13U; + else + secondHeaderOffset += P25_PDU_HEADER_LENGTH_BYTES + 1U; + dataHeader.encodeAuxES(packetData); + + if (s_verbose) { + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", + dataHeader.getAlgId(), dataHeader.getKId()); + + if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + + dataHeader.getMI(mi); + + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", + mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); + } + } + } + + uint32_t packetLength = dataHeader.getPacketLength(); + uint32_t padLength = dataHeader.getPadLength(); +#if DEBUG_P25_PDU_DATA + LogDebugEx(LOG_P25, "Assembler::assemble()", "packetLength = %u, secondHeaderOffset = %u, padLength = %u, pduLength = %u", packetLength, secondHeaderOffset, padLength, pduLength); +#endif + ::memcpy(packetData + secondHeaderOffset, pduUserData, packetLength); + + if (dataHeader.getFormat() != PDUFormatType::AMBT) { + edac::CRC::addCRC32(packetData, packetLength + 4U); + + Utils::dump(2U, "packetData", packetData, packetLength + 4U); + + if (padLength > 0U) { + // move the CRC-32 to the end of the packet data after the padding + uint8_t crcBytes[4U]; + ::memcpy(crcBytes, packetData + packetLength, 4U); + ::memset(packetData + packetLength, 0x00U, 4U); + ::memcpy(packetData + (packetLength + padLength), crcBytes, 4U); + } + } + +#if DEBUG_P25_PDU_DATA + Utils::dump(1U, "P25, Assembled PDU User Data", packetData, packetLength + padLength + 4U); +#endif + + // generate the PDU data + for (uint32_t i = 0U; i < blocksToFollow; i++) { + DataBlock dataBlock = DataBlock(); + dataBlock.setFormat(dataHeader); + dataBlock.setSerialNo(i); + dataBlock.setData(packetData + dataOffset); + dataBlock.setLastBlock((i + 1U) == blocksToFollow); + + if (s_verbose) { + LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u", + (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? dataBlock.getSerialNo() : i, dataBlock.getFormat(), + dataBlock.getLastBlock()); + } + + ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + dataBlock.encode(block); + +#if DEBUG_P25_PDU_DATA + Utils::dump(1U, "P25, PDU Assembler Block", block, P25_PDU_FEC_LENGTH_BYTES); +#endif + + if (m_usingCustomWriter && m_blockWriter != nullptr) + m_blockWriter(userContext, dataBlockCnt, block, P25_PDU_FEC_LENGTH_BYTES, dataBlock.getLastBlock()); + else + Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); + + offset += P25_PDU_FEC_LENGTH_BITS; + dataOffset += (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; + dataBlockCnt++; + } + } + + if (assembledBitLength != nullptr) + *assembledBitLength = bitLength; + + if (m_usingCustomWriter) { + return nullptr; + } + return dataArray; +} + +/* Gets the raw user user data stored. */ + +uint32_t Assembler::getUserData(uint8_t* buffer) const +{ + assert(buffer != nullptr); + assert(m_pduUserData != nullptr); + + if (m_complete) { + ::memcpy(buffer, m_pduUserData, m_pduUserDataLength); + return m_pduUserDataLength; + } else { + return 0U; + } +} + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to reset the disassembly state. */ + +void Assembler::resetDisassemblyState() +{ + dataHeader.reset(); + + m_extendedAddress = false; + m_auxiliaryES = false; + + m_dataBlockCnt = 0U; + m_undecodableBlockCnt = 0U; + + m_blockCount = 0U; + m_dataOffset = 0U; + + ::memset(m_pduUserData, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); + ::memset(m_rawPDU, 0x00U, P25_PDU_FRAME_LENGTH_BYTES + 2U); + + m_packetCRCFailed = false; + m_complete = false; +} diff --git a/src/common/p25/data/Assembler.h b/src/common/p25/data/Assembler.h new file mode 100644 index 00000000..85cbfef8 --- /dev/null +++ b/src/common/p25/data/Assembler.h @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file Assembler.h + * @ingroup p25_pdu + * @file Assembler.cpp + * @ingroup p25_pdu + */ +#if !defined(__P25_DATA__ASSEMBLER_H__) +#define __P25_DATA__ASSEMBLER_H__ + +#include "common/Defines.h" +#include "common/p25/data/DataBlock.h" +#include "common/p25/data/DataHeader.h" + +#include +#include + +namespace p25 +{ + namespace data + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Implements a packet assembler for P25 PDU packet streams. + * @ingroup p25_pdu + */ + class HOST_SW_API Assembler { + public: + /** + * @brief Initializes a new instance of the Assembler class. + */ + Assembler(); + /** + * @brief Finalizes a instance of the Assembler class. + */ + ~Assembler(); + + /** + * @brief Helper to disassemble a P25 PDU frame into user data. + * @param[in] pduBlock Buffer containing a PDU block to disassemble. + * @param blockLength Length of PDU block buffer. + * @param resetState Flag indicating the current disassembly state should be reset. + * @returns bool True, if entire P25 PDU packet is disassembled, otherwise false. + */ + bool disassemble(const uint8_t* pduBlock, uint32_t blockLength, bool resetState = false); + /** + * @brief Helper to assemble user data as a P25 PDU packet. + * @note When using a custom block writer, this will return null. + * @param dataHeader Instance of a PDU data header. + * @param extendedAddress Flag indicating whether or not extended addressing is in use. + * @param auxiliaryES Flag indicating whether or not an auxiliary ES is included. + * @param[in] pduUserData Buffer containing user data to assemble. + * @param[out] assembledBitLength Length of assembled packet in bits. + * @param[in] userContext User supplied context data to pass to custom block writer. + * @returns UInt8Array Assembled PDU buffer. + */ + UInt8Array assemble(data::DataHeader& dataHeader, bool extendedAddress, bool auxiliaryES, const uint8_t* pduUserData, + uint32_t* assembledBitLength, void* userContext = nullptr); + + /** + * @brief Helper to set the custom block writer callback. + * @param callback + */ + void setBlockWriter(std::function&& callback) + { + m_blockWriter = callback; + if (callback == nullptr) + m_usingCustomWriter = false; + else + m_usingCustomWriter = true; + } + + /** + * @brief Gets the raw user user data stored. + * @param[out] buffer Buffer to copy bytes in data block to. + * @returns uint32_t Number of bytes copied. + */ + uint32_t getUserData(uint8_t* buffer) const; + /** + * @brief Gets the length of the raw user data stored. (This will include the 4 bytes for the CRC-32). + * @returns uint32_t Length of raw user data stored. + */ + uint32_t getUserDataLength() const { return m_pduUserDataLength; } + + /** + * @brief Sets the flag indicating whether or not the assembler will dump PDU data. + * @param dumpPDUData Flag indicating PDU log dumping. + */ + static void setDumpPDUData(bool dumpPDUData) { s_dumpPDUData = dumpPDUData; } + /** + * @brief Sets the flag indicating verbose log output. + * @param verbose Flag indicating verbose log output. + */ + static void setVerbose(bool verbose) { s_verbose = verbose; } + + public: + /** + * @brief Data Blocks in disassmbled packet. + */ + data::DataBlock* dataBlocks; + /** + * @brief Data Header from disassembled packet. + */ + data::DataHeader dataHeader; + + /** + * @brief Flag indicating the disassembled packet contains extended addressing. + */ + DECLARE_PROPERTY(bool, extendedAddress, ExtendedAddress); + /** + * @brief Flag indicating the disassembled packet contains an auxiliary ES. + */ + DECLARE_PROPERTY(bool, auxiliaryES, AuxiliaryES); + /** + * @brief Count of data blocks in disassmebled packet. + */ + DECLARE_PROPERTY(uint8_t, dataBlockCnt, DataBlockCount); + /** + * @brief Count of data blocks in disassmebled packet that were undecodable. + */ + DECLARE_PROPERTY(uint8_t, undecodableBlockCnt, UndecodableBlockCount); + + /** + * @brief Flag indicating resulting user data failed the CRC-32 check. + */ + DECLARE_PROPERTY(bool, packetCRCFailed, PacketCRCFailed); + /** + * @brief Flag indicating disassembly is complete and user data is available. + */ + DECLARE_PROPERTY(bool, complete, Complete); + + private: + uint8_t* m_pduUserData; + uint32_t m_pduUserDataLength; + + uint8_t* m_rawPDU; + + uint32_t m_blockCount; + uint32_t m_dataOffset; + + static bool s_dumpPDUData; + static bool s_verbose; + bool m_usingCustomWriter; + + /** + * @brief Custom block writing callback. + */ + std::function m_blockWriter; + + /** + * @brief Internal helper to reset the disassembly state. + */ + void resetDisassemblyState(); + }; + } // namespace data +} // namespace p25 + +#endif // __P25_DATA__ASSEMBLER_H__ diff --git a/src/common/p25/data/DataBlock.cpp b/src/common/p25/data/DataBlock.cpp index ae518184..63e346d5 100644 --- a/src/common/p25/data/DataBlock.cpp +++ b/src/common/p25/data/DataBlock.cpp @@ -25,6 +25,13 @@ using namespace p25::data; // Public Class Members // --------------------------------------------------------------------------- +/* Initializes a copy instance of the DataBlock class. */ + +DataBlock::DataBlock(const DataBlock& data) : DataBlock() +{ + copy(data); +} + /* Initializes a new instance of the DataBlock class. */ DataBlock::DataBlock() : @@ -42,8 +49,10 @@ DataBlock::DataBlock() : DataBlock::~DataBlock() { - if (m_data != nullptr) + if (m_data != nullptr) { delete[] m_data; + m_data = nullptr; + } } /* Decodes P25 PDU data block. */ @@ -106,7 +115,8 @@ bool DataBlock::decode(const uint8_t* data, const DataHeader& header, bool noTre // compute CRC-9 for the packet uint16_t calculated = edac::CRC::createCRC9(crcBuffer, 135U); if ((crc ^ calculated) != 0) { - LogWarning(LOG_P25, "PDU, fmt = $%02X, invalid crc = $%04X != $%04X (computed)", m_fmt, crc, calculated); + LogError(LOG_P25, "PDU, fmt = $%02X, invalid crc = $%04X != $%04X (computed)", m_fmt, crc, calculated); + return false; } #if DEBUG_P25_PDU_DATA @@ -274,3 +284,22 @@ uint32_t DataBlock::getData(uint8_t* buffer) const return 0U; } } + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to copy the the class. */ + +void DataBlock::copy(const DataBlock& data) +{ + m_serialNo = data.m_serialNo; + m_lastBlock = data.m_lastBlock; + + m_fmt = data.m_fmt; + m_headerSap = data.m_headerSap; + + if (m_data != nullptr && data.m_data != nullptr) { + ::memcpy(m_data, data.m_data, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + } +} diff --git a/src/common/p25/data/DataBlock.h b/src/common/p25/data/DataBlock.h index 31589779..4bf7b9fa 100644 --- a/src/common/p25/data/DataBlock.h +++ b/src/common/p25/data/DataBlock.h @@ -36,6 +36,11 @@ namespace p25 */ class HOST_SW_API DataBlock { public: + /** + * @brief Initializes a copy instance of the DataBlock class. + * @param data Instance of DataBlock class to copy from. + */ + DataBlock(const DataBlock& data); /** * @brief Initializes a new instance of the DataBlock class. */ @@ -106,6 +111,11 @@ namespace p25 uint8_t m_headerSap; uint8_t* m_data; + + /** + * @brief Internal helper to copy the class. + */ + void copy(const DataBlock& data); }; } // namespace data } // namespace p25 diff --git a/src/common/p25/data/DataHeader.cpp b/src/common/p25/data/DataHeader.cpp index 81c0c95c..ee6d3f17 100644 --- a/src/common/p25/data/DataHeader.cpp +++ b/src/common/p25/data/DataHeader.cpp @@ -36,6 +36,13 @@ bool DataHeader::s_warnCRC = false; // Public Class Members // --------------------------------------------------------------------------- +/* Initializes a copy instance of the DataHeader class. */ + +DataHeader::DataHeader(const DataHeader& data) : DataHeader() +{ + copy(data); +} + /* Initializes a new instance of the DataHeader class. */ DataHeader::DataHeader() : @@ -84,9 +91,20 @@ DataHeader::DataHeader() : DataHeader::~DataHeader() { - delete[] m_data; - delete[] m_extAddrData; - delete[] m_auxESData; + if (m_data != nullptr) { + delete[] m_data; + m_data = nullptr; + } + + if (m_extAddrData != nullptr) { + delete[] m_extAddrData; + m_extAddrData = nullptr; + } + + if (m_auxESData != nullptr) { + delete[] m_auxESData; + m_auxESData = nullptr; + } if (m_mi != nullptr) { delete[] m_mi; @@ -279,7 +297,7 @@ void DataHeader::encode(uint8_t* data, bool noTrellis) /* Decodes P25 PDU extended addressing header. */ -bool DataHeader::decodeExtAddr(const uint8_t* data, bool noTrellis) +bool DataHeader::decodeExtAddr(const uint8_t* data) { assert(data != nullptr); @@ -297,30 +315,22 @@ bool DataHeader::decodeExtAddr(const uint8_t* data, bool noTrellis) m_srcLlId = (m_extAddrData[0U] << 16) + (m_extAddrData[1U] << 8) + // Source Logical Link ID m_extAddrData[2U]; } else if (m_fmt == PDUFormatType::UNCONFIRMED) { - // decode 1/2 rate Trellis & check CRC-CCITT 16 - bool valid = true; - if (noTrellis) { - ::memcpy(m_extAddrData, data, P25_PDU_HEADER_LENGTH_BYTES); - } - else { - valid = m_trellis.decode12(data, m_extAddrData); - } + ::memcpy(m_extAddrData, data, P25_PDU_HEADER_LENGTH_BYTES); - if (valid) { - valid = edac::CRC::checkCCITT162(m_extAddrData, P25_PDU_HEADER_LENGTH_BYTES); - if (!valid) { - if (s_warnCRC) { - // if we're already warning instead of erroring CRC, don't announce invalid CRC in the - // case where no CRC is defined - if ((m_extAddrData[P25_PDU_HEADER_LENGTH_BYTES - 2U] != 0x00U) && (m_extAddrData[P25_PDU_HEADER_LENGTH_BYTES - 1U] != 0x00U)) { - LogWarning(LOG_P25, "DataHeader::decodeExtAddr(), failed CRC CCITT-162 check"); - } - - valid = true; // ignore CRC error - } - else { - LogError(LOG_P25, "DataHeader::decodeExtAddr(), failed CRC CCITT-162 check"); + // check CRC-CCITT 16 + bool valid = edac::CRC::checkCCITT162(m_extAddrData, P25_PDU_HEADER_LENGTH_BYTES); + if (!valid) { + if (s_warnCRC) { + // if we're already warning instead of erroring CRC, don't announce invalid CRC in the + // case where no CRC is defined + if ((m_extAddrData[P25_PDU_HEADER_LENGTH_BYTES - 2U] != 0x00U) && (m_extAddrData[P25_PDU_HEADER_LENGTH_BYTES - 1U] != 0x00U)) { + LogWarning(LOG_P25, "DataHeader::decodeExtAddr(), failed CRC CCITT-162 check"); } + + valid = true; // ignore CRC error + } + else { + LogError(LOG_P25, "DataHeader::decodeExtAddr(), failed CRC CCITT-162 check"); } } @@ -342,7 +352,7 @@ bool DataHeader::decodeExtAddr(const uint8_t* data, bool noTrellis) /* Encodes P25 PDU extended addressing header. */ -void DataHeader::encodeExtAddr(uint8_t* data, bool noTrellis) +void DataHeader::encodeExtAddr(uint8_t* data) { assert(data != nullptr); @@ -383,19 +393,13 @@ void DataHeader::encodeExtAddr(uint8_t* data, bool noTrellis) #if DEBUG_P25_PDU_DATA Utils::dump(1U, "P25, DataHeader::encodeExtAddr(), PDU Extended Address Data", header, P25_PDU_HEADER_LENGTH_BYTES); #endif - - if (!noTrellis) { - // encode 1/2 rate Trellis - m_trellis.encode12(header, data); - } else { - ::memcpy(data, header, P25_PDU_HEADER_LENGTH_BYTES); - } + ::memcpy(data, header, P25_PDU_HEADER_LENGTH_BYTES); } } /* Decodes P25 PDU auxiliary ES header. */ -bool DataHeader::decodeAuxES(const uint8_t* data, bool noTrellis) +bool DataHeader::decodeAuxES(const uint8_t* data) { assert(data != nullptr); @@ -407,7 +411,7 @@ bool DataHeader::decodeAuxES(const uint8_t* data, bool noTrellis) if (m_fmt == PDUFormatType::CONFIRMED) { ::memcpy(m_auxESData, data, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); #if DEBUG_P25_PDU_DATA - Utils::dump(1U, "P25, DataHeader::decodeAuxES(), PDU Auxiliary ES Data", m_extAddrData, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + Utils::dump(1U, "P25, DataHeader::decodeAuxES(), PDU Auxiliary ES Data", m_auxESData, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); #endif m_algId = m_auxESData[9U]; // Algorithm ID @@ -431,39 +435,10 @@ bool DataHeader::decodeAuxES(const uint8_t* data, bool noTrellis) m_exSap = m_auxESData[12U] & 0x3FU; // Service Access Point } else if (m_fmt == PDUFormatType::UNCONFIRMED) { - // decode 1/2 rate Trellis & check CRC-CCITT 16 - bool valid = true; - if (noTrellis) { - ::memcpy(m_auxESData, data, P25_PDU_HEADER_LENGTH_BYTES); - } - else { - valid = m_trellis.decode12(data, m_auxESData); - } - - if (valid) { - valid = edac::CRC::checkCCITT162(m_auxESData, P25_PDU_HEADER_LENGTH_BYTES); - if (!valid) { - if (s_warnCRC) { - // if we're already warning instead of erroring CRC, don't announce invalid CRC in the - // case where no CRC is defined - if ((m_auxESData[P25_PDU_HEADER_LENGTH_BYTES - 2U] != 0x00U) && (m_auxESData[P25_PDU_HEADER_LENGTH_BYTES - 1U] != 0x00U)) { - LogWarning(LOG_P25, "DataHeader::decodeExtAddr(), failed CRC CCITT-162 check"); - } - - valid = true; // ignore CRC error - } - else { - LogError(LOG_P25, "DataHeader::decodeAuxES(), failed CRC CCITT-162 check"); - } - } - } - - if (!valid) { - return false; - } + ::memcpy(m_auxESData, data, P25_PDU_HEADER_LENGTH_BYTES); #if DEBUG_P25_PDU_DATA - Utils::dump(1U, "P25, DataHeader::decodeAuxES(), PDU Auxiliary ES Data", m_extAddrData, P25_PDU_HEADER_LENGTH_BYTES); + Utils::dump(1U, "P25, DataHeader::decodeAuxES(), PDU Auxiliary ES Data", m_auxESData, P25_PDU_HEADER_LENGTH_BYTES); #endif m_algId = m_auxESData[9U]; // Algorithm ID @@ -491,7 +466,7 @@ bool DataHeader::decodeAuxES(const uint8_t* data, bool noTrellis) /* Encodes P25 PDU auxiliary ES header. */ -void DataHeader::encodeAuxES(uint8_t* data, bool noTrellis) +void DataHeader::encodeAuxES(uint8_t* data) { assert(data != nullptr); @@ -503,16 +478,16 @@ void DataHeader::encodeAuxES(uint8_t* data, bool noTrellis) if (m_fmt == PDUFormatType::CONFIRMED) { for (uint32_t i = 0; i < MI_LENGTH_BYTES; i++) - header[i + 2U] = m_mi[i]; // Message Indicator + header[i] = m_mi[i]; // Message Indicator - header[11U] = m_algId; // Algorithm ID - header[12U] = (m_kId >> 8) & 0xFFU; // Key ID - header[13U] = (m_kId >> 0) & 0xFFU; // ... + header[9U] = m_algId; // Algorithm ID + header[10U] = (m_kId >> 8) & 0xFFU; // Key ID + header[11U] = (m_kId >> 0) & 0xFFU; // ... - header[14U] = m_exSap & 0x3FU; // Service Access Point + header[12U] = m_exSap & 0x3FU; // Service Access Point #if DEBUG_P25_PDU_DATA - Utils::dump(1U, "P25, DataHeader::encodeExtAddr(), PDU Auxiliary ES Data", header, P25_PDU_HEADER_LENGTH_BYTES); + Utils::dump(1U, "P25, DataHeader::encodeExtAddr(), PDU Auxiliary ES Data", header, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); #endif ::memcpy(data, header, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); @@ -524,19 +499,12 @@ void DataHeader::encodeAuxES(uint8_t* data, bool noTrellis) header[10U] = (m_kId >> 8) & 0xFFU; // Key ID header[11U] = (m_kId >> 0) & 0xFFU; // ... - // compute CRC-CCITT 16 - edac::CRC::addCCITT162(header, P25_PDU_HEADER_LENGTH_BYTES); + header[12U] = m_exSap & 0x3FU; // Service Access Point #if DEBUG_P25_PDU_DATA Utils::dump(1U, "P25, DataHeader::encodeAuxES(), PDU Auxiliary ES Data", header, P25_PDU_HEADER_LENGTH_BYTES); #endif - - if (!noTrellis) { - // encode 1/2 rate Trellis - m_trellis.encode12(header, data); - } else { - ::memcpy(data, header, P25_PDU_HEADER_LENGTH_BYTES); - } + ::memcpy(data, header, P25_PDU_HEADER_LENGTH_BYTES + 1U); } } @@ -544,6 +512,9 @@ void DataHeader::encodeAuxES(uint8_t* data, bool noTrellis) void DataHeader::reset() { + if (m_data == nullptr) + return; // bail bail bail + m_ackNeeded = false; m_outbound = false; @@ -579,7 +550,8 @@ void DataHeader::reset() m_algId = ALGO_UNENCRYPT; m_kId = 0U; - ::memset(m_data, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); + if (m_data != nullptr) + ::memset(m_data, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); if (m_mi != nullptr) { ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); @@ -672,6 +644,10 @@ void DataHeader::calculateLength(uint32_t packetLength) len += 4U; } + if ((m_sap == PDUSAP::ENC_USER_DATA || m_sap == PDUSAP::ENC_KMM)) { + len += 13U; + } + uint32_t blockLen = (m_fmt == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; if (len > blockLen) { @@ -717,3 +693,61 @@ void DataHeader::getMI(uint8_t* mi) const ::memcpy(mi, m_mi, MI_LENGTH_BYTES); } + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to copy the the class. */ + +void DataHeader::copy(const DataHeader& data) +{ + m_ackNeeded = data.m_ackNeeded; + m_outbound = data.m_outbound; + + m_fmt = data.m_fmt; + m_sap = data.m_sap; + + m_mfId = data.m_mfId; + m_llId = data.m_llId; + + m_blocksToFollow = data.m_blocksToFollow; + m_padLength = data.m_padLength; + + m_F = data.m_F; + m_S = data.m_S; + m_fsn = data.m_fsn; + m_Ns = data.m_Ns; + m_lastFragment = data.m_lastFragment; + m_headerOffset = data.m_headerOffset; + + m_exSap = data.m_exSap; + m_srcLlId = data.m_srcLlId; + + m_rspClass = data.m_rspClass; + m_rspType = data.m_rspType; + m_rspStatus = data.m_rspStatus; + + m_ambtOpcode = data.m_ambtOpcode; + m_ambtField8 = data.m_ambtField8; + m_ambtField9 = data.m_ambtField9; + + m_algId = data.m_algId; + m_kId = data.m_kId; + + if (m_data != nullptr && data.m_data != nullptr) { + ::memcpy(m_data, data.m_data, P25_PDU_HEADER_LENGTH_BYTES); + } + + if (m_extAddrData != nullptr && data.m_extAddrData != nullptr) { + ::memcpy(m_extAddrData, data.m_extAddrData, P25_PDU_HEADER_LENGTH_BYTES); + } + + if (m_auxESData != nullptr && data.m_auxESData != nullptr) { + ::memcpy(m_auxESData, data.m_auxESData, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); + } + + if (m_mi != nullptr && data.m_mi != nullptr) { + ::memcpy(m_mi, data.m_mi, MI_LENGTH_BYTES); + } +} diff --git a/src/common/p25/data/DataHeader.h b/src/common/p25/data/DataHeader.h index 3f4bc50b..93b5fa47 100644 --- a/src/common/p25/data/DataHeader.h +++ b/src/common/p25/data/DataHeader.h @@ -39,6 +39,11 @@ namespace p25 */ class HOST_SW_API DataHeader { public: + /** + * @brief Initializes a copy instance of the DataHeader class. + * @param data Instance of DataHeader class to copy from. + */ + DataHeader(const DataHeader& data); /** * @brief Initializes a new instance of the DataHeader class. */ @@ -65,30 +70,26 @@ namespace p25 /** * @brief Decodes P25 PDU extended addressing header. * @param[in] data Buffer containing a PDU data header to decode. - * @param noTrellis Flag indicating not to perform Trellis encoding. * @returns bool True, if PDU data header decoded, otherwise false. */ - bool decodeExtAddr(const uint8_t* data, bool noTrellis = false); + bool decodeExtAddr(const uint8_t* data); /** * @brief Encodes P25 PDU extended addressing header. * @param[out] data Buffer to encode a PDU data header. - * @param noTrellis Flag indicating not to perform Trellis encoding. */ - void encodeExtAddr(uint8_t* data, bool noTrellis = false); + void encodeExtAddr(uint8_t* data); /** * @brief Decodes P25 PDU auxiliary ES header. * @param[in] data Buffer containing a PDU data header to decode. - * @param noTrellis Flag indicating not to perform Trellis encoding. * @returns bool True, if PDU data header decoded, otherwise false. */ - bool decodeAuxES(const uint8_t* data, bool noTrellis = false); + bool decodeAuxES(const uint8_t* data); /** * @brief Encodes P25 PDU auxiliary ES header. - * @param noTrellis Flag indicating not to perform Trellis encoding. * @param[out] data Buffer to encode a PDU data header. */ - void encodeAuxES(uint8_t* data, bool noTrellis = false); + void encodeAuxES(uint8_t* data); /** * @brief Helper to reset data values to defaults. @@ -281,6 +282,11 @@ namespace p25 uint8_t* m_mi; static bool s_warnCRC; + + /** + * @brief Internal helper to copy the class. + */ + void copy(const DataHeader& data); }; } // namespace data } // namespace p25 diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index c51962e7..22320129 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -539,6 +539,10 @@ void FNENetwork::clock(uint32_t ms) } } + // cleanup possibly stale data calls + m_tagDMR->packetData()->cleanupStale(); + m_tagP25->packetData()->cleanupStale(); + m_maintainenceTimer.start(); } diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp index dee48cd3..7079b7a4 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp @@ -109,7 +109,38 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } RxStatus* status = m_status[peerId]; - status->streamId = streamId; + if ((status->streamId != 0U && streamId != status->streamId) || status->callBusy) { + if (m_network->m_callCollisionTimeout > 0U) { + uint64_t lastPktDuration = hrc::diff(hrc::now(), status->lastPacket); + if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); + + m_status.lock(false); + status->streamId = streamId; + status->callBusy = false; + m_status.unlock(); + } + else { + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call Collision, peer = %u, slot = %u, streamId = %u, rxPeer = %u, rxStreamId = %u, fromUpstream = %u", + peerId, slotNo, streamId, status->peerId, status->streamId, fromUpstream); + return false; + } + } else { + m_status.lock(false); + status->streamId = streamId; + m_status.unlock(); + } + } + + if (status->callBusy) { + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Data Call Lockout, cannot process data packets while data call in progress, peer = %u, slot = %u, streamId = %u, fromUpstream = %u", + peerId, slotNo, streamId, fromUpstream); + return false; + } + + m_status.lock(false); + status->lastPacket = hrc::now(); + m_status.unlock(); if (dataType == DataType::DATA_HEADER) { bool ret = status->header.decode(frame); @@ -117,8 +148,7 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee LogError(LOG_DMR, "DMR Slot %u, DataType::DATA_HEADER, unable to decode the network data header", status->slotNo); Utils::dump(1U, "DMR, Unfixable PDU Data", frame, DMR_FRAME_LENGTH_BYTES); - delete status; - m_status.erase(peerId); + status->streamId = 0U; return false; } @@ -137,6 +167,7 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // make sure we don't get a PDU with more blocks then we support if (status->header.getBlocksToFollow() >= MAX_PDU_COUNT) { LogError(LOG_DMR, DMR_DT_DATA_HEADER ", too many PDU blocks to process, %u > %u", status->header.getBlocksToFollow(), MAX_PDU_COUNT); + status->streamId = 0U; return false; } @@ -146,8 +177,7 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee // a PDU header only with no blocks to follow is usually a response header if (status->header.getBlocksToFollow() == 0U) { - delete status; - m_status.erase(peerId); + status->streamId = 0U; return true; } @@ -194,12 +224,15 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee .requestAsync(m_network->m_influxServer); } - delete status; m_status.erase(peerId); + delete status; + status = nullptr; return false; } } + status->callBusy = true; + dispatch(peerId, dmrData, data, len); uint64_t duration = hrc::diff(pktTime, status->callStartTime); @@ -224,14 +257,49 @@ bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee .requestAsync(m_network->m_influxServer); } - delete status; m_status.erase(peerId); + delete status; + status = nullptr; + } else { + status->callBusy = false; } } return true; } +/* Helper to cleanup any call's left in a dangling state without any further updates. */ + +void DMRPacketData::cleanupStale() +{ + // check to see if any peers have been quiet (no ping) longer than allowed + std::vector peersToRemove = std::vector(); + m_status.lock(false); + for (auto peerStatus : m_status) { + uint32_t id = peerStatus.first; + RxStatus* status = peerStatus.second; + if (status != nullptr) { + uint64_t lastPktDuration = hrc::diff(hrc::now(), status->lastPacket); + if ((lastPktDuration / 1000) > 10U) { + LogWarning(LOG_DMR, "DMR, Data Call Timeout, lasted more then %us with no further updates", 10U); + status->callBusy = true; // force flag the call busy + peersToRemove.push_back(id); + } + } + } + m_status.unlock(); + + // remove any peers + for (uint32_t peerId : peersToRemove) { + RxStatus* status = m_status[peerId]; + if (status != nullptr) { + m_status.erase(peerId); + delete status; + status = nullptr; + } + } +} + // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.h b/src/fne/network/callhandler/packetdata/DMRPacketData.h index 1100a88a..a484fe7a 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.h +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.h @@ -68,6 +68,11 @@ namespace network */ bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool fromUpstream = false); + /** + * @brief Helper to cleanup any call's left in a dangling state without any further updates. + */ + void cleanupStale(); + private: FNENetwork* m_network; TagDMRData *m_tag; @@ -78,6 +83,7 @@ namespace network class RxStatus { public: system_clock::hrc::hrc_t callStartTime; + system_clock::hrc::hrc_t lastPacket; uint32_t srcId; uint32_t dstId; uint8_t slotNo; @@ -89,6 +95,8 @@ namespace network uint8_t dataBlockCnt; uint8_t frames; + bool callBusy; + uint8_t* pduUserData; uint32_t pduDataOffset; @@ -104,6 +112,8 @@ namespace network header(), hasRxHeader(false), dataBlockCnt(0U), + frames(0U), + callBusy(false), pduUserData(nullptr), pduDataOffset(0U) { diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index d58bca44..c8663c44 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -57,6 +57,7 @@ const uint32_t SUBSCRIBER_READY_RETRY_MS = 1000U; // milliseconds P25PacketData::P25PacketData(FNENetwork* network, TagP25Data* tag, bool debug) : m_network(network), m_tag(tag), + m_assembler(nullptr), m_queuedFrames(), m_status(), m_arpTable(), @@ -66,11 +67,34 @@ P25PacketData::P25PacketData(FNENetwork* network, TagP25Data* tag, bool debug) : { assert(network != nullptr); assert(tag != nullptr); + + data::Assembler::setVerbose(network->m_verbose); + data::Assembler::setDumpPDUData(network->m_dumpPacketData); + + m_assembler = new data::Assembler(); + m_assembler->setBlockWriter([](const void* userContext, const uint8_t currentBlock, const uint8_t *data, uint32_t len, bool lastBlock) { + const UserContext* context = static_cast(userContext); + if (context == nullptr) { + return; + } + + P25PacketData* packetData = static_cast(context->obj); + if (packetData == nullptr) { + return; + } + + packetData->writeNetwork(context->peerId, context->srcPeerId, context->peerNet, *(context->header), + currentBlock, data, len, context->pktSeq, context->streamId); + }); } /* Finalizes a instance of the P25PacketData class. */ -P25PacketData::~P25PacketData() = default; +P25PacketData::~P25PacketData() +{ + if (m_assembler != nullptr) + delete m_assembler; +} /* Process a data frame from the network. */ @@ -103,54 +127,75 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee } RxStatus* status = m_status[peerId]; - status->streamId = streamId; + if ((status->streamId != 0U && streamId != status->streamId) || status->callBusy) { + LogDebugEx(LOG_NET, "P25PacketData::processFrame()", "streamId = %u, status->streamId = %u, status->callBusy = %u", streamId, status->streamId, status->callBusy); + if (m_network->m_callCollisionTimeout > 0U) { + uint64_t lastPktDuration = hrc::diff(hrc::now(), status->lastPacket); + if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Data Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); + + m_status.lock(false); + status->streamId = streamId; + status->callBusy = false; + m_status.unlock(); + } + else { + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Data Call Collision, peer = %u, streamId = %u, rxPeer = %u, rxStreamId = %u, fromUpstream = %u", + peerId, streamId, status->peerId, status->streamId, fromUpstream); + return false; + } + } else { + m_status.lock(false); + status->streamId = streamId; + m_status.unlock(); + } + } + + if (status->callBusy) { + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Data Call Lockout, cannot process data packets while data call in progress, peer = %u, streamId = %u, fromUpstream = %u", + peerId, streamId, fromUpstream); + return false; + } + + m_status.lock(false); + status->lastPacket = hrc::now(); + m_status.unlock(); // make sure we don't get a PDU with more blocks then we support if (currentBlock >= P25_MAX_PDU_BLOCKS) { LogError(LOG_P25, P25_PDU_STR ", too many PDU blocks to process, %u > %u", currentBlock, P25_MAX_PDU_BLOCKS); - - delete status; - m_status.erase(peerId); return false; } // block 0 is always the PDU header block if (currentBlock == 0U) { - bool ret = status->header.decode(buffer); + bool ret = status->assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES, true); if (!ret) { - LogWarning(LOG_P25, P25_PDU_STR ", unfixable RF 1/2 rate header data"); - Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); - - delete status; - m_status.erase(peerId); - return true; + status->streamId = 0U; + return false; } LogInfoEx(LOG_P25, P25_PDU_STR ", peerId = %u, ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, hdrOffset = %u, llId = %u", - peerId, status->header.getAckNeeded(), status->header.getOutbound(), status->header.getFormat(), status->header.getSAP(), status->header.getFullMessage(), - status->header.getBlocksToFollow(), status->header.getPadLength(), status->header.getPacketLength(), status->header.getSynchronize(), status->header.getNs(), status->header.getFSN(), - status->header.getHeaderOffset(), status->header.getLLId()); + peerId, status->assembler.dataHeader.getAckNeeded(), status->assembler.dataHeader.getOutbound(), status->assembler.dataHeader.getFormat(), status->assembler.dataHeader.getSAP(), status->assembler.dataHeader.getFullMessage(), + status->assembler.dataHeader.getBlocksToFollow(), status->assembler.dataHeader.getPadLength(), status->assembler.dataHeader.getPacketLength(), status->assembler.dataHeader.getSynchronize(), status->assembler.dataHeader.getNs(), + status->assembler.dataHeader.getFSN(), status->assembler.dataHeader.getHeaderOffset(), status->assembler.dataHeader.getLLId()); // make sure we don't get a PDU with more blocks then we support - if (status->header.getBlocksToFollow() >= P25_MAX_PDU_BLOCKS) { - LogError(LOG_P25, P25_PDU_STR ", too many PDU blocks to process, %u > %u", status->header.getBlocksToFollow(), P25_MAX_PDU_BLOCKS); - - delete status; - m_status.erase(peerId); + if (status->assembler.dataHeader.getBlocksToFollow() >= P25_MAX_PDU_BLOCKS) { + LogError(LOG_P25, P25_PDU_STR ", too many PDU blocks to process, %u > %u", status->assembler.dataHeader.getBlocksToFollow(), P25_MAX_PDU_BLOCKS); + status->streamId = 0U; return false; } status->hasRxHeader = true; - status->llId = status->header.getLLId(); + status->llId = status->assembler.dataHeader.getLLId(); m_readyForNextPkt[status->llId] = true; // is this a response header? - if (status->header.getFormat() == PDUFormatType::RSP) { + if (status->assembler.dataHeader.getFormat() == PDUFormatType::RSP) { dispatch(peerId); - - delete status; - m_status.erase(peerId); + status->streamId = 0U; return true; } @@ -158,238 +203,76 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return true; } - ::memcpy(status->netPDU + ((currentBlock - 1U) * blockLength), data + 24U, blockLength); - status->dataOffset += blockLength; - status->netPDUCount++; - status->dataBlockCnt++; - - if (status->hasRxHeader && (status->dataBlockCnt >= status->header.getBlocksToFollow())) { - // is the source ID a blacklisted ID? - lookups::RadioId rid = m_network->m_ridLookup->find(status->header.getLLId()); - if (!rid.radioDefault()) { - if (!rid.radioEnabled()) { - // report error event to InfluxDB - if (m_network->m_enableInfluxDB) { - influxdb::QueryBuilder() - .meas("call_error_event") - .tag("peerId", std::to_string(peerId)) - .tag("streamId", std::to_string(streamId)) - .tag("srcId", std::to_string(status->header.getLLId())) - .tag("dstId", std::to_string(status->header.getLLId())) - .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) - .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) - .requestAsync(m_network->m_influxServer); - } - - delete status; - m_status.erase(peerId); - return false; - } - } - - uint32_t blocksToFollow = status->header.getBlocksToFollow(); - uint32_t offset = 0U; - uint32_t dataOffset = 0U; - - uint8_t buffer[P25_PDU_FEC_LENGTH_BYTES]; - - status->dataBlockCnt = 0U; - - // process all blocks in the data stream - status->pduUserData = new uint8_t[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; - ::memset(status->pduUserData, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); - - // process second header if we're using enhanced addressing - if (status->header.getSAP() == PDUSAP::EXT_ADDR && - status->header.getFormat() == PDUFormatType::UNCONFIRMED) { - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - ::memcpy(buffer, status->netPDU, P25_PDU_FEC_LENGTH_BYTES); - - bool ret = status->header.decodeExtAddr(buffer); - if (!ret) { - LogWarning(LOG_P25, P25_PDU_STR ", unfixable RF 1/2 rate second header data"); - Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_HEADER_LENGTH_BYTES); - - delete status; - m_status.erase(peerId); - - return false; - } - - LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, extended address, sap = $%02X, srcLlId = %u", - status->header.getEXSAP(), status->header.getSrcLLId()); - - status->extendedAddress = true; - status->llId = status->header.getSrcLLId(); - - offset += P25_PDU_FEC_LENGTH_BYTES; - blocksToFollow--; - - // if we are using a secondary header place it in the PDU user data buffer - status->header.getExtAddrData(status->pduUserData + dataOffset); - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - status->pduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; - } - - // process second header if we're using auxiliary ES - if ((status->header.getSAP() == PDUSAP::ENC_USER_DATA || status->header.getSAP() == PDUSAP::ENC_KMM) && - status->header.getFormat() == PDUFormatType::UNCONFIRMED) { - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - ::memcpy(buffer, status->netPDU, P25_PDU_FEC_LENGTH_BYTES); - - bool ret = status->header.decodeAuxES(buffer); - if (!ret) { - LogWarning(LOG_P25, P25_PDU_STR ", unfixable RF 1/2 rate second header data"); - Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_HEADER_LENGTH_BYTES); - - delete status; - m_status.erase(peerId); - - return false; - } - - LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, auxiliary ES, algoId = $%02X, kId = $%04X", - status->header.getAlgId(), status->header.getKId()); - - if (status->header.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - status->header.getMI(mi); - - LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - - status->auxiliaryES = true; - - offset += P25_PDU_FEC_LENGTH_BYTES; - blocksToFollow--; - - // if we are using a secondary header place it in the PDU user data buffer - status->header.getAuxiliaryESData(status->pduUserData + dataOffset); - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - status->pduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; - } - - // decode data blocks - for (uint32_t i = 0U; i < blocksToFollow; i++) { - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - ::memcpy(buffer, status->netPDU + offset, P25_PDU_FEC_LENGTH_BYTES); - - bool ret = status->blockData[i].decode(buffer, status->header); - if (ret) { - // if we are getting unconfirmed or confirmed blocks, and if we've reached the total number of blocks - // set this block as the last block for full packet CRC - if ((status->header.getFormat() == PDUFormatType::CONFIRMED) || (status->header.getFormat() == PDUFormatType::UNCONFIRMED)) { - if ((status->dataBlockCnt + 1U) == blocksToFollow) { - status->blockData[i].setLastBlock(true); - } - } - - // are we processing extended address data from the first block? - if (status->header.getSAP() == PDUSAP::EXT_ADDR && status->header.getFormat() == PDUFormatType::CONFIRMED && - status->blockData[i].getSerialNo() == 0U) { - uint8_t secondHeader[P25_PDU_HEADER_LENGTH_BYTES]; - ::memset(secondHeader, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); - status->blockData[i].getData(secondHeader); - - status->header.decodeExtAddr(secondHeader); - LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", - status->blockData[i].getSerialNo(), status->blockData[i].getFormat(), status->blockData[i].getLastBlock(), - status->header.getEXSAP(), status->header.getSrcLLId()); - - status->extendedAddress = true; - } - else { - // are we processing auxiliary ES data from the first block? - if ((status->header.getSAP() == PDUSAP::ENC_USER_DATA || status->header.getSAP() == PDUSAP::ENC_KMM) && - status->header.getFormat() == PDUFormatType::CONFIRMED && status->blockData[i].getSerialNo() == 0U) { - uint8_t secondHeader[P25_PDU_HEADER_LENGTH_BYTES]; - ::memset(secondHeader, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); - status->blockData[i].getData(secondHeader); - - status->header.decodeAuxES(secondHeader); - LogInfoEx(LOG_NET, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, algoId = $%02X, kId = $%04X", - status->blockData[i].getSerialNo(), status->blockData[i].getFormat(), status->blockData[i].getLastBlock(), status->header.getEXSAP(), - status->header.getAlgId(), status->header.getKId()); - - if (status->header.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - status->header.getMI(mi); - - LogInfoEx(LOG_NET, P25_PDU_STR ", ISP, Enc Sync, block %u, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - status->blockData[i].getSerialNo(), mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - - status->auxiliaryES = true; - } else { - LogInfoEx(LOG_P25, P25_PDU_STR ", peerId = %u, block %u, fmt = $%02X, lastBlock = %u", - peerId, (status->header.getFormat() == PDUFormatType::CONFIRMED) ? status->blockData[i].getSerialNo() : status->dataBlockCnt, status->blockData[i].getFormat(), - status->blockData[i].getLastBlock()); + status->callBusy = true; + bool ret = status->assembler.disassemble(data + 24U, blockLength); + if (!ret) { + status->callBusy = false; + return false; + } + else { + if (status->hasRxHeader && status->assembler.getComplete()) { + // is the source ID a blacklisted ID? + lookups::RadioId rid = m_network->m_ridLookup->find(status->assembler.dataHeader.getLLId()); + if (!rid.radioDefault()) { + if (!rid.radioEnabled()) { + // report error event to InfluxDB + if (m_network->m_enableInfluxDB) { + influxdb::QueryBuilder() + .meas("call_error_event") + .tag("peerId", std::to_string(peerId)) + .tag("streamId", std::to_string(streamId)) + .tag("srcId", std::to_string(status->assembler.dataHeader.getLLId())) + .tag("dstId", std::to_string(status->assembler.dataHeader.getLLId())) + .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .requestAsync(m_network->m_influxServer); } - } - status->blockData[i].getData(status->pduUserData + dataOffset); - - // is this the first unconfirmed data block after a auxiliary ES header? - if (i == 0U && status->header.getFormat() == PDUFormatType::UNCONFIRMED && status->auxiliaryES) { - uint8_t exSAP = status->pduUserData[0U]; // first byte of the first data block after an aux ES header is the extended SAP - status->header.setEXSAP(exSAP); + m_status.erase(peerId); + delete status; + status = nullptr; + return false; } - - dataOffset += (status->header.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; - status->pduUserDataLength = dataOffset; - - status->dataBlockCnt++; } - else { - if (status->blockData[i].getFormat() == PDUFormatType::CONFIRMED) - LogWarning(LOG_P25, P25_PDU_STR ", unfixable PDU data (3/4 rate or CRC), block %u", i); - else - LogWarning(LOG_P25, P25_PDU_STR ", unfixable PDU data (1/2 rate or CRC), block %u", i); - if (m_network->m_dumpPacketData) { - Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); - } - } + status->callBusy = true; - offset += P25_PDU_FEC_LENGTH_BYTES; - } + // process all blocks in the data stream + status->pduUserDataLength = status->assembler.getUserDataLength(); + status->pduUserData = new uint8_t[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + ::memset(status->pduUserData, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); - if (status->dataBlockCnt < blocksToFollow) { - LogWarning(LOG_P25, P25_PDU_STR ", incomplete PDU (%d / %d blocks), peerId = %u, llId = %u", status->dataBlockCnt, blocksToFollow, peerId, status->llId); - } + status->assembler.getUserData(status->pduUserData); - // dispatch the PDU data - if (status->dataBlockCnt > 0U && status->hasRxHeader) { + // dispatch the PDU data dispatch(peerId); - } - uint64_t duration = hrc::diff(pktTime, status->callStartTime); - uint32_t srcId = (status->extendedAddress) ? status->header.getSrcLLId() : status->header.getLLId(); - uint32_t dstId = status->header.getLLId(); - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Data Call End, peer = %u, srcId = %u, dstId = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", - peerId, srcId, dstId, status->header.getBlocksToFollow(), duration / 1000, streamId, fromUpstream); - - // report call event to InfluxDB - if (m_network->m_enableInfluxDB) { - influxdb::QueryBuilder() - .meas("call_event") - .tag("peerId", std::to_string(peerId)) - .tag("mode", "P25") - .tag("streamId", std::to_string(streamId)) - .tag("srcId", std::to_string(srcId)) - .tag("dstId", std::to_string(dstId)) - .field("duration", duration) - .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) - .requestAsync(m_network->m_influxServer); - } + uint64_t duration = hrc::diff(pktTime, status->callStartTime); + uint32_t srcId = (status->assembler.getExtendedAddress()) ? status->assembler.dataHeader.getSrcLLId() : status->assembler.dataHeader.getLLId(); + uint32_t dstId = status->assembler.dataHeader.getLLId(); + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Data Call End, peer = %u, srcId = %u, dstId = %u, blocks = %u, duration = %u, streamId = %u, fromUpstream = %u", + peerId, srcId, dstId, status->assembler.dataHeader.getBlocksToFollow(), duration / 1000, streamId, fromUpstream); + + // report call event to InfluxDB + if (m_network->m_enableInfluxDB) { + influxdb::QueryBuilder() + .meas("call_event") + .tag("peerId", std::to_string(peerId)) + .tag("mode", "P25") + .tag("streamId", std::to_string(streamId)) + .tag("srcId", std::to_string(srcId)) + .tag("dstId", std::to_string(dstId)) + .field("duration", duration) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .requestAsync(m_network->m_influxServer); + } - delete status; - m_status.erase(peerId); + m_status.erase(peerId); + delete status; + status = nullptr; + } else { + status->callBusy = false; + } } return true; @@ -603,6 +486,38 @@ void P25PacketData::clock(uint32_t ms) } } +/* Helper to cleanup any call's left in a dangling state without any further updates. */ + +void P25PacketData::cleanupStale() +{ + // check to see if any peers have been quiet (no ping) longer than allowed + std::vector peersToRemove = std::vector(); + m_status.lock(false); + for (auto peerStatus : m_status) { + uint32_t id = peerStatus.first; + RxStatus* status = peerStatus.second; + if (status != nullptr) { + uint64_t lastPktDuration = hrc::diff(hrc::now(), status->lastPacket); + if ((lastPktDuration / 1000) > 10U) { + LogWarning(LOG_P25, "P25, Data Call Timeout, lasted more then %us with no further updates", 10U); + status->callBusy = true; // force flag the call busy + peersToRemove.push_back(id); + } + } + } + m_status.unlock(); + + // remove any peers + for (uint32_t peerId : peersToRemove) { + RxStatus* status = m_status[peerId]; + if (status != nullptr) { + m_status.erase(peerId); + delete status; + status = nullptr; + } + } +} + // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- @@ -618,56 +533,36 @@ void P25PacketData::dispatch(uint32_t peerId) return; } - bool crcValid = false; - if (status->header.getBlocksToFollow() > 0U) { - if (status->pduUserDataLength < 4U) { - LogError(LOG_P25, P25_PDU_STR ", illegal PDU packet length, peer = %u, llId = %u, blocks %u, len %u", - peerId, status->header.getLLId(), status->header.getBlocksToFollow(), status->pduUserDataLength); - return; - } - - crcValid = edac::CRC::checkCRC32(status->pduUserData, status->pduUserDataLength); - if (!crcValid) { - LogError(LOG_P25, P25_PDU_STR ", failed CRC-32 check, peer = %u, llId = %u, blocks %u, len %u", - peerId, status->header.getLLId(), status->header.getBlocksToFollow(), status->pduUserDataLength); - return; - } - } - - if (m_network->m_dumpPacketData && status->dataBlockCnt > 0U) { - Utils::dump(1U, "P25, ISP PDU Packet", status->pduUserData, status->pduUserDataLength); - } - - if (status->header.getFormat() == PDUFormatType::RSP) { + if (status->assembler.dataHeader.getFormat() == PDUFormatType::RSP) { LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, peer = %u, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", - peerId, status->header.getFormat(), status->header.getResponseClass(), status->header.getResponseType(), status->header.getResponseStatus(), - status->header.getLLId(), status->header.getSrcLLId()); + peerId, status->assembler.dataHeader.getFormat(), status->assembler.dataHeader.getResponseClass(), status->assembler.dataHeader.getResponseType(), status->assembler.dataHeader.getResponseStatus(), + status->assembler.dataHeader.getLLId(), status->assembler.dataHeader.getSrcLLId()); // bryanb: this is naive and possibly error prone - m_readyForNextPkt[status->header.getSrcLLId()] = true; + m_readyForNextPkt[status->assembler.dataHeader.getSrcLLId()] = true; - if (status->header.getResponseClass() == PDUAckClass::ACK && status->header.getResponseType() == PDUAckType::ACK) { + if (status->assembler.dataHeader.getResponseClass() == PDUAckClass::ACK && status->assembler.dataHeader.getResponseType() == PDUAckType::ACK) { LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, OSP ACK, peer = %u, llId = %u, all blocks received OK, n = %u", - peerId, status->header.getLLId(), status->header.getResponseStatus()); + peerId, status->assembler.dataHeader.getLLId(), status->assembler.dataHeader.getResponseStatus()); } else { - if (status->header.getResponseClass() == PDUAckClass::NACK) { - switch (status->header.getResponseType()) { + if (status->assembler.dataHeader.getResponseClass() == PDUAckClass::NACK) { + switch (status->assembler.dataHeader.getResponseType()) { case PDUAckType::NACK_ILLEGAL: LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, illegal format, peer = %u, llId = %u", - peerId, status->header.getLLId()); + peerId, status->assembler.dataHeader.getLLId()); break; case PDUAckType::NACK_PACKET_CRC: LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet CRC error, peer = %u, llId = %u, n = %u", - peerId, status->header.getLLId(), status->header.getResponseStatus()); + peerId, status->assembler.dataHeader.getLLId(), status->assembler.dataHeader.getResponseStatus()); break; case PDUAckType::NACK_SEQ: case PDUAckType::NACK_OUT_OF_SEQ: LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet out of sequence, peer = %u, llId = %u, seqNo = %u", - peerId, status->header.getLLId(), status->header.getResponseStatus()); + peerId, status->assembler.dataHeader.getLLId(), status->assembler.dataHeader.getResponseStatus()); break; case PDUAckType::NACK_UNDELIVERABLE: LogInfoEx(LOG_P25, P25_PDU_STR ", ISP, response, OSP NACK, packet undeliverable, peer = %u, llId = %u, n = %u", - peerId, status->header.getLLId(), status->header.getResponseStatus()); + peerId, status->assembler.dataHeader.getLLId(), status->assembler.dataHeader.getResponseStatus()); break; default: @@ -679,13 +574,13 @@ void P25PacketData::dispatch(uint32_t peerId) return; } - if (status->header.getFormat() == PDUFormatType::UNCONFIRMED) { - m_readyForNextPkt[status->header.getSrcLLId()] = true; + if (status->assembler.dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) { + m_readyForNextPkt[status->assembler.dataHeader.getSrcLLId()] = true; } - uint8_t sap = (status->extendedAddress) ? status->header.getEXSAP() : status->header.getSAP(); - if (status->auxiliaryES) - sap = status->header.getEXSAP(); + uint8_t sap = (status->assembler.getExtendedAddress()) ? status->assembler.dataHeader.getEXSAP() : status->assembler.dataHeader.getSAP(); + if (status->assembler.getAuxiliaryES()) + sap = status->assembler.dataHeader.getEXSAP(); // handle standard P25 service access points switch (sap) { @@ -700,7 +595,7 @@ void P25PacketData::dispatch(uint32_t peerId) uint8_t arpPacket[P25_PDU_ARP_PCKT_LENGTH]; ::memset(arpPacket, 0x00U, P25_PDU_ARP_PCKT_LENGTH); - ::memcpy(arpPacket, status->pduUserData + 12U, P25_PDU_ARP_PCKT_LENGTH); + ::memcpy(arpPacket, status->pduUserData, P25_PDU_ARP_PCKT_LENGTH); uint16_t opcode = GET_UINT16(arpPacket, 6U); uint32_t srcHWAddr = GET_UINT24(arpPacket, 8U); @@ -745,20 +640,14 @@ void P25PacketData::dispatch(uint32_t peerId) if (!m_network->m_host->m_vtunEnabled) break; - int dataPktOffset = 0U; - if (status->header.getFormat() == PDUFormatType::CONFIRMED && status->extendedAddress) - dataPktOffset = 4U; - if (status->header.getFormat() == PDUFormatType::UNCONFIRMED && status->extendedAddress) - dataPktOffset = 12U; - - uint32_t srcLlId = status->header.getSrcLLId(); - if (!status->extendedAddress) - srcLlId = status->header.getLLId(); - uint32_t dstLlId = status->header.getLLId(); - if (!status->extendedAddress) + uint32_t srcLlId = status->assembler.dataHeader.getSrcLLId(); + if (!status->assembler.getExtendedAddress()) + srcLlId = status->assembler.dataHeader.getLLId(); + uint32_t dstLlId = status->assembler.dataHeader.getLLId(); + if (!status->assembler.getExtendedAddress()) dstLlId = WUID_FNE; - struct ip* ipHeader = (struct ip*)(status->pduUserData + dataPktOffset); + struct ip* ipHeader = (struct ip*)(status->pduUserData); char srcIp[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(ipHeader->ip_src), srcIp, INET_ADDRSTRLEN); @@ -771,37 +660,39 @@ void P25PacketData::dispatch(uint32_t peerId) // reflect broadcast messages back to the CAI network bool handled = false; - if (status->header.getLLId() == WUID_ALL) { + if (status->assembler.dataHeader.getLLId() == WUID_ALL) { LogInfoEx(LOG_P25, "PDU -> VTUN, IP Data, repeated to CAI, broadcast packet, dstIp = %s (%u)", - dstIp, status->header.getLLId()); + dstIp, status->assembler.dataHeader.getLLId()); - dispatchUserFrameToFNE(status->header, status->extendedAddress, status->auxiliaryES, status->pduUserData); + dispatchUserFrameToFNE(status->assembler.dataHeader, status->assembler.getExtendedAddress(), + status->assembler.getAuxiliaryES(), 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(); }); + auto arpEntry = std::find_if(m_arpTable.begin(), m_arpTable.end(), [=](ArpTablePair x) { return x.first == status->assembler.dataHeader.getSrcLLId(); }); if (arpEntry == m_arpTable.end()) { uint32_t srcProtoAddr = Utils::reverseEndian(ipHeader->ip_src.s_addr); - LogInfoEx(LOG_P25, 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); + LogInfoEx(LOG_P25, P25_PDU_STR ", adding ARP entry, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), status->assembler.dataHeader.getSrcLLId()); + m_arpTable[status->assembler.dataHeader.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(); }); + auto arpEntry = std::find_if(m_arpTable.begin(), m_arpTable.end(), [=](ArpTablePair x) { return x.first == status->assembler.dataHeader.getLLId(); }); if (arpEntry != m_arpTable.end()) { LogInfoEx(LOG_P25, "PDU -> VTUN, IP Data, repeated to CAI, destination IP has a CAI ARP table entry, dstIp = %s (%u)", - dstIp, status->header.getLLId()); + dstIp, status->assembler.dataHeader.getLLId()); - dispatchUserFrameToFNE(status->header, status->extendedAddress, status->auxiliaryES, status->pduUserData); + dispatchUserFrameToFNE(status->assembler.dataHeader, status->assembler.getExtendedAddress(), status->assembler.getAuxiliaryES(), + 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(); }); + auto arpEntry = std::find_if(m_arpTable.begin(), m_arpTable.end(), [=](ArpTablePair x) { return x.first == status->assembler.dataHeader.getSrcLLId(); }); if (arpEntry == m_arpTable.end()) { uint32_t srcProtoAddr = Utils::reverseEndian(ipHeader->ip_src.s_addr); - LogInfoEx(LOG_P25, 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); + LogInfoEx(LOG_P25, P25_PDU_STR ", adding ARP entry, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), status->assembler.dataHeader.getSrcLLId()); + m_arpTable[status->assembler.dataHeader.getSrcLLId()] = Utils::reverseEndian(ipHeader->ip_src.s_addr); } } @@ -810,7 +701,7 @@ void P25PacketData::dispatch(uint32_t peerId) srcIp, srcLlId, dstIp, dstLlId, pktLen, proto); DECLARE_UINT8_ARRAY(ipFrame, pktLen); - ::memcpy(ipFrame, status->pduUserData + dataPktOffset, pktLen); + ::memcpy(ipFrame, status->pduUserData, pktLen); #if DEBUG_P25_PDU_DATA Utils::dump(1U, "P25, P25PacketData::dispatch(), ipFrame", ipFrame, pktLen); #endif @@ -820,13 +711,13 @@ void P25PacketData::dispatch(uint32_t peerId) // if the packet is unhandled and sent off to VTUN; ack the packet so the sender knows we received it if (!handled) { - if (status->extendedAddress) { + if (status->assembler.getExtendedAddress()) { m_readyForNextPkt[srcLlId] = true; - write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->header.getNs(), srcLlId, + write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->assembler.dataHeader.getNs(), srcLlId, true, dstLlId); } else { m_readyForNextPkt[srcLlId] = true; - write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->header.getNs(), srcLlId, false); + write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->assembler.dataHeader.getNs(), srcLlId, false); } } #endif // !defined(_WIN32) @@ -835,7 +726,7 @@ void P25PacketData::dispatch(uint32_t peerId) case PDUSAP::CONV_DATA_REG: { LogInfoEx(LOG_P25, P25_PDU_STR ", CONV_DATA_REG (Conventional Data Registration), peer = %u, blocksToFollow = %u", - peerId, status->header.getBlocksToFollow()); + peerId, status->assembler.dataHeader.getBlocksToFollow()); processConvDataReg(status); } @@ -843,7 +734,7 @@ void P25PacketData::dispatch(uint32_t peerId) case PDUSAP::SNDCP_CTRL_DATA: { LogInfoEx(LOG_P25, P25_PDU_STR ", SNDCP_CTRL_DATA (SNDCP Control Data), peer = %u, blocksToFollow = %u", - peerId, status->header.getBlocksToFollow()); + peerId, status->assembler.dataHeader.getBlocksToFollow()); processSNDCPControl(status); } @@ -852,10 +743,11 @@ void P25PacketData::dispatch(uint32_t peerId) case PDUSAP::ENC_KMM: { LogInfoEx(LOG_P25, P25_PDU_STR ", KMM (Key Management Message), peer = %u, blocksToFollow = %u", - peerId, status->header.getBlocksToFollow()); + peerId, status->assembler.dataHeader.getBlocksToFollow()); bool encrypted = (sap == PDUSAP::ENC_KMM); - m_network->m_p25OTARService->processDLD(status->pduUserData, status->pduUserDataLength, status->llId, status->header.getNs(), encrypted); + m_network->m_p25OTARService->processDLD(status->pduUserData, status->pduUserDataLength, status->llId, + status->assembler.dataHeader.getNs(), encrypted); } break; default: @@ -870,8 +762,8 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) { RxStatus* status = m_status[peerId]; - uint32_t srcId = (status->extendedAddress) ? status->header.getSrcLLId() : status->header.getLLId(); - uint32_t dstId = status->header.getLLId(); + uint32_t srcId = (status->assembler.getExtendedAddress()) ? status->assembler.dataHeader.getSrcLLId() : status->assembler.dataHeader.getLLId(); + uint32_t dstId = status->assembler.dataHeader.getLLId(); /* ** MASTER TRAFFIC @@ -881,8 +773,8 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) if (m_network->m_peers.size() > 0U) { for (auto peer : m_network->m_peers) { if (peerId != peer.first) { - write_PDU_User(peer.first, peerId, nullptr, status->header, status->extendedAddress, - status->auxiliaryES, status->pduUserData); + write_PDU_User(peer.first, peerId, nullptr, status->assembler.dataHeader, status->assembler.getExtendedAddress(), + status->assembler.getAuxiliaryES(), status->pduUserData); if (m_network->m_debug) { LogDebug(LOG_P25, "srcPeer = %u, dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", peerId, peer.first, DUID::PDU, srcId, dstId); @@ -908,8 +800,8 @@ void P25PacketData::dispatchToFNE(uint32_t peerId) continue; } - write_PDU_User(dstPeerId, peerId, peer.second, status->header, status->extendedAddress, - status->auxiliaryES, status->pduUserData); + write_PDU_User(dstPeerId, peerId, peer.second, status->assembler.dataHeader, status->assembler.getExtendedAddress(), + status->assembler.getAuxiliaryES(), status->pduUserData); if (m_network->m_debug) { LogDebug(LOG_P25, "srcPeer = %u, dstPeer = %u, duid = $%02X, srcId = %u, dstId = %u", peerId, dstPeerId, DUID::PDU, srcId, dstId); @@ -999,7 +891,7 @@ bool P25PacketData::processSNDCPControl(RxStatus* status) return false; } - uint32_t llId = status->header.getLLId(); + uint32_t llId = status->assembler.dataHeader.getLLId(); switch (packet->getPDUType()) { case SNDCP_PDUType::ACT_TDS_CTX: @@ -1146,141 +1038,17 @@ void P25PacketData::write_PDU_User(uint32_t peerId, uint32_t srcPeerId, network: if (pduUserData == nullptr) pktSeq = RTP_END_OF_CALL_SEQ; - uint8_t buffer[P25_PDU_FEC_LENGTH_BYTES]; - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - - uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); - - if (m_network->m_verbosePacketData) - LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, peerId = %u, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", - peerId, dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), - dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(), dataHeader.getSynchronize(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), - dataHeader.getHeaderOffset(), dataHeader.getLLId()); - - // generate the PDU header and 1/2 rate Trellis - dataHeader.encode(buffer); - writeNetwork(peerId, srcPeerId, peerNet, dataHeader, 0U, buffer, P25_PDU_FEC_LENGTH_BYTES, pktSeq, streamId); - - if (pduUserData == nullptr) - return; - - ++pktSeq; - uint32_t packetLength = dataHeader.getPDULength(); - - if (blocksToFollow > 0U) { - uint32_t dataOffset = 0U; - uint32_t networkBlock = 1U; - - // if using extended addressing, generate the second PDU header - if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { - dataHeader.encodeExtAddr(pduUserData, true); - - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - dataHeader.encodeExtAddr(buffer); - writeNetwork(peerId, srcPeerId, peerNet, dataHeader, 1U, buffer, P25_PDU_FEC_LENGTH_BYTES, pktSeq, streamId); - ++pktSeq; - - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - - blocksToFollow--; - networkBlock++; - - if (m_network->m_verbosePacketData) - LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", - dataHeader.getEXSAP(), dataHeader.getSrcLLId()); - } - - // if using auxiliary ES, generate the second PDU header - if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && - auxiliaryES) { - dataHeader.encodeAuxES(pduUserData, true); - - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - dataHeader.encodeAuxES(buffer); - writeNetwork(peerId, srcPeerId, peerNet, dataHeader, 1U, buffer, P25_PDU_FEC_LENGTH_BYTES, pktSeq, streamId); - ++pktSeq; - - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - - blocksToFollow--; - networkBlock++; - - if (m_network->m_verbosePacketData) { - LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", - dataHeader.getAlgId(), dataHeader.getKId()); - - if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - dataHeader.getMI(mi); - - LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - } - - // are we processing extended address data from the first block? - if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { - dataHeader.encodeExtAddr(pduUserData); - - if (m_network->m_verbosePacketData) - LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, sap = $%02X, srcLlId = %u", - dataHeader.getEXSAP(), dataHeader.getSrcLLId()); - } - - // are we processing auxiliary ES data from the first block? - if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && - auxiliaryES) { - dataHeader.encodeAuxES(pduUserData); - - if (m_network->m_verbosePacketData) { - LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", - dataHeader.getAlgId(), dataHeader.getKId()); - - if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - dataHeader.getMI(mi); - - LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - } - - if (dataHeader.getFormat() != PDUFormatType::AMBT) { - edac::CRC::addCRC32(pduUserData, packetLength); - } - - if (m_network->m_dumpPacketData) { - Utils::dump("P25, OSP PDU User Data", pduUserData, packetLength); - } - - // generate the PDU data - for (uint32_t i = 0U; i < blocksToFollow; i++) { - data::DataBlock dataBlock = data::DataBlock(); - dataBlock.setFormat(dataHeader); - dataBlock.setSerialNo(i); - dataBlock.setData(pduUserData + dataOffset); - - if (m_network->m_verbosePacketData) - LogInfoEx(LOG_P25, P25_PDU_STR ", OSP, peerId = %u, block %u, fmt = $%02X, lastBlock = %u", - peerId, (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? dataBlock.getSerialNo() : i, dataBlock.getFormat(), - dataBlock.getLastBlock()); - - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - dataBlock.encode(buffer); - writeNetwork(peerId, srcPeerId, peerNet, dataHeader, networkBlock, buffer, P25_PDU_FEC_LENGTH_BYTES, (dataBlock.getLastBlock()) ? RTP_END_OF_CALL_SEQ : pktSeq, streamId); - ++pktSeq; - - dataOffset += (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; - - networkBlock++; - } - } + UserContext* context = new UserContext(); + context->obj = this; + context->peerId = peerId; + context->srcPeerId = srcPeerId; + context->peerNet = peerNet; + context->header = new data::DataHeader(dataHeader); + context->pktSeq = pktSeq; + context->streamId = streamId; + + m_assembler->assemble(dataHeader, extendedAddress, auxiliaryES, pduUserData, nullptr, context); + delete context; } /* Write data processed to the network. */ diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index 751f520b..05c0cf76 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -21,8 +21,9 @@ #include "common/concurrent/deque.h" #include "common/concurrent/unordered_map.h" #include "common/p25/P25Defines.h" -#include "common/p25/data/DataBlock.h" +#include "common/p25/data/Assembler.h" #include "common/p25/data/DataHeader.h" +#include "common/p25/data/DataBlock.h" #include "network/FNENetwork.h" #include "network/PeerNetwork.h" #include "network/callhandler/TagP25Data.h" @@ -104,10 +105,31 @@ namespace network */ void clock(uint32_t ms); + /** + * @brief Helper to cleanup any call's left in a dangling state without any further updates. + */ + void cleanupStale(); + private: FNENetwork* m_network; TagP25Data* m_tag; + p25::data::Assembler* m_assembler; + + /** + * @brief Represents the data required for a PDU assembler custom writer context. + * @ingroup fne_network + */ + struct UserContext { + void* obj; //!< Instance of the P25PacketData class. + uint32_t peerId; //!< Peer ID for this request. + uint32_t srcPeerId; //!< Source Peer ID for this request. + network::PeerNetwork* peerNet; //!< Instance of the peer network for an upstream peer. + p25::data::DataHeader* header; //!< PDU data header. + uint16_t pktSeq; //!< Packet sequence. + uint32_t streamId; //!< Stream ID. + }; + /** * @brief Represents a queued data frame from the VTUN. */ @@ -132,19 +154,15 @@ namespace network class RxStatus { public: system_clock::hrc::hrc_t callStartTime; + system_clock::hrc::hrc_t lastPacket; uint32_t llId; uint32_t streamId; uint32_t peerId; - p25::data::DataBlock* blockData; - p25::data::DataHeader header; + p25::data::Assembler assembler; bool hasRxHeader; - bool extendedAddress; - bool auxiliaryES; - uint32_t dataOffset; - uint8_t dataBlockCnt; - uint8_t* netPDU; - uint32_t netPDUCount; + + bool callBusy; uint8_t* pduUserData; uint32_t pduUserDataLength; @@ -156,23 +174,12 @@ namespace network llId(0U), streamId(0U), peerId(0U), - blockData(nullptr), - header(), + assembler(), hasRxHeader(false), - extendedAddress(false), - auxiliaryES(false), - dataOffset(0U), - dataBlockCnt(0U), - netPDU(nullptr), - netPDUCount(0U), + callBusy(false), pduUserData(nullptr), pduUserDataLength(0U) { - blockData = new p25::data::DataBlock[P25DEF::P25_MAX_PDU_BLOCKS]; - - netPDU = new uint8_t[P25DEF::P25_PDU_FRAME_LENGTH_BYTES + 2U]; - ::memset(netPDU, 0x00U, P25DEF::P25_PDU_FRAME_LENGTH_BYTES + 2U); - pduUserData = new uint8_t[P25DEF::P25_MAX_PDU_BLOCKS * P25DEF::P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; ::memset(pduUserData, 0x00U, P25DEF::P25_MAX_PDU_BLOCKS * P25DEF::P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); } @@ -181,10 +188,6 @@ namespace network */ ~RxStatus() { - if (blockData != nullptr) - delete[] blockData; - if (netPDU != nullptr) - delete[] netPDU; if (pduUserData != nullptr) delete[] pduUserData; } diff --git a/src/host/p25/packet/Data.cpp b/src/host/p25/packet/Data.cpp index 8095f2e2..67d60a9e 100644 --- a/src/host/p25/packet/Data.cpp +++ b/src/host/p25/packet/Data.cpp @@ -47,11 +47,8 @@ const uint32_t SNDCP_STANDBY_TIMEOUT = 60U; void Data::resetRF() { - m_rfDataBlockCnt = 0U; m_rfPDUCount = 0U; m_rfPDUBits = 0U; - - m_rfDataHeader.reset(); } /* Process a data frame from the RF interface. */ @@ -82,10 +79,6 @@ bool Data::process(uint8_t* data, uint32_t len) m_inbound = true; if (m_p25->m_rfState != RS_RF_DATA) { - m_rfDataHeader.reset(); - m_rfExtendedAddress = false; - m_rfAuxiliaryES = false; - m_rfDataBlockCnt = 0U; m_rfPDUCount = 0U; m_rfPDUBits = 0U; @@ -105,40 +98,14 @@ bool Data::process(uint8_t* data, uint32_t len) ::memset(buffer, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); uint32_t bits = P25Utils::decode(data + 2U, buffer, start, start + P25_PDU_FRAME_LENGTH_BITS); - m_rfPDUBits = Utils::getBits(buffer, m_rfPDU, 0U, bits); + m_rfPDUBits += Utils::getBits(buffer, m_rfPDU, 0U, bits); - uint32_t offset = P25_PREAMBLE_LENGTH_BITS + P25_PDU_FEC_LENGTH_BITS; if (m_rfPDUCount == 0U) { ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); Utils::getBitRange(m_rfPDU, buffer, P25_PREAMBLE_LENGTH_BITS, P25_PDU_FEC_LENGTH_BITS); - bool ret = m_rfDataHeader.decode(buffer); - if (!ret) { - LogWarning(LOG_RF, P25_PDU_STR ", unfixable RF 1/2 rate header data"); - Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); - - m_rfDataHeader.reset(); - m_rfExtendedAddress = false; - m_rfAuxiliaryES = false; - m_rfPDUCount = 0U; - m_rfPDUBits = 0U; - m_p25->m_rfState = m_prevRfState; - return false; - } - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", - m_rfDataHeader.getAckNeeded(), m_rfDataHeader.getOutbound(), m_rfDataHeader.getFormat(), m_rfDataHeader.getMFId(), m_rfDataHeader.getSAP(), m_rfDataHeader.getFullMessage(), - m_rfDataHeader.getBlocksToFollow(), m_rfDataHeader.getPadLength(), m_rfDataHeader.getPacketLength(), m_rfDataHeader.getSynchronize(), m_rfDataHeader.getNs(), m_rfDataHeader.getFSN(), m_rfDataHeader.getLastFragment(), - m_rfDataHeader.getHeaderOffset(), m_rfDataHeader.getLLId()); - } - - // make sure we don't get a PDU with more blocks then we support - if (m_rfDataHeader.getBlocksToFollow() >= P25_MAX_PDU_BLOCKS) { - LogError(LOG_RF, P25_PDU_STR ", ISP, too many PDU blocks to process, %u > %u", m_rfDataHeader.getBlocksToFollow(), P25_MAX_PDU_BLOCKS); - - m_rfDataHeader.reset(); - m_rfExtendedAddress = false; - m_rfAuxiliaryES = false; + bool ret = m_rfAssembler->disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES, true); + if (!ret) { m_rfPDUCount = 0U; m_rfPDUBits = 0U; m_p25->m_rfState = m_prevRfState; @@ -146,431 +113,269 @@ bool Data::process(uint8_t* data, uint32_t len) } // if we're a dedicated CC or in control only mode, we only want to handle AMBTs. Otherwise return - if ((m_p25->m_dedicatedControl || m_p25->m_controlOnly) && m_rfDataHeader.getFormat() != PDUFormatType::AMBT) { + if ((m_p25->m_dedicatedControl || m_p25->m_controlOnly) && m_rfAssembler->dataHeader.getFormat() != PDUFormatType::AMBT) { if (m_debug) { LogDebug(LOG_RF, "CC only mode, ignoring non-AMBT PDU from RF"); } m_p25->m_ccHalted = false; - m_rfDataHeader.reset(); - m_rfExtendedAddress = false; - m_rfAuxiliaryES = false; m_rfPDUCount = 0U; m_rfPDUBits = 0U; m_p25->m_rfState = m_prevRfState; return false; } - } - - if (m_p25->m_rfState == RS_RF_DATA) { - uint32_t blocksToFollow = m_rfDataHeader.getBlocksToFollow(); - uint32_t dataOffset = 0U; - // process second header if we're using enhanced addressing - if (m_rfDataHeader.getSAP() == PDUSAP::EXT_ADDR && - m_rfDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) { - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - Utils::getBitRange(m_rfPDU, buffer, offset, P25_PDU_FEC_LENGTH_BITS); - bool ret = m_rfDataHeader.decodeExtAddr(buffer); - if (!ret) { - LogWarning(LOG_RF, P25_PDU_STR ", unfixable RF 1/2 rate second header data"); - Utils::dump(1U, "P25, Unfixable PDU Data", m_rfPDU + offset, P25_PDU_HEADER_LENGTH_BYTES); - - m_rfDataHeader.reset(); - m_rfPDUCount = 0U; - m_rfPDUBits = 0U; - m_p25->m_rfState = m_prevRfState; - return false; - } - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, extended address, sap = $%02X, srcLlId = %u", - m_rfDataHeader.getEXSAP(), m_rfDataHeader.getSrcLLId()); - } + // did we receive a response header? + if (m_rfAssembler->dataHeader.getFormat() == PDUFormatType::RSP) { + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", + m_rfAssembler->dataHeader.getFormat(), m_rfAssembler->dataHeader.getResponseClass(), m_rfAssembler->dataHeader.getResponseType(), m_rfAssembler->dataHeader.getResponseStatus(), + m_rfAssembler->dataHeader.getLLId(), m_rfAssembler->dataHeader.getSrcLLId()); + + if (m_rfAssembler->dataHeader.getResponseClass() == PDUAckClass::ACK && m_rfAssembler->dataHeader.getResponseType() == PDUAckType::ACK) { + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK, llId = %u, all blocks received OK, n = %u", + m_rfAssembler->dataHeader.getLLId(), m_rfAssembler->dataHeader.getResponseStatus()); + if (m_retryPDUData != nullptr && m_retryPDUBitLength > 0U) { + delete m_retryPDUData; + m_retryPDUData = nullptr; + + m_retryPDUBitLength = 0U; + m_retryCount = 0U; + } + } else { + if (m_rfAssembler->dataHeader.getResponseClass() == PDUAckClass::NACK) { + switch (m_rfAssembler->dataHeader.getResponseType()) { + case PDUAckType::NACK_ILLEGAL: + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, illegal format, llId = %u", + m_rfAssembler->dataHeader.getLLId()); + break; + case PDUAckType::NACK_PACKET_CRC: + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet CRC error, llId = %u, n = %u", + m_rfAssembler->dataHeader.getLLId(), m_rfAssembler->dataHeader.getResponseStatus()); + break; + case PDUAckType::NACK_SEQ: + case PDUAckType::NACK_OUT_OF_SEQ: + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet out of sequence, llId = %u, seqNo = %u", + m_rfAssembler->dataHeader.getLLId(), m_rfAssembler->dataHeader.getResponseStatus()); + break; + case PDUAckType::NACK_UNDELIVERABLE: + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet undeliverable, llId = %u, n = %u", + m_rfAssembler->dataHeader.getLLId(), m_rfAssembler->dataHeader.getResponseStatus()); + break; - m_rfExtendedAddress = true; + default: + break; + } + } else if (m_rfAssembler->dataHeader.getResponseClass() == PDUAckClass::ACK_RETRY) { + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK RETRY, llId = %u", + m_rfAssembler->dataHeader.getLLId()); - offset += P25_PDU_FEC_LENGTH_BITS; - m_rfPDUCount++; - blocksToFollow--; + // really this is supposed to check the bit field in the included response + // and only return those bits -- but we're responding with the entire previous packet... + if (m_retryPDUData != nullptr && m_retryPDUBitLength > 0U) { + if (m_retryCount < MAX_PDU_RETRY_CNT) { + m_p25->writeRF_Preamble(); + writeRF_PDU(m_retryPDUData, m_retryPDUBitLength, false, true); + m_retryCount++; + } + else { + delete m_retryPDUData; + m_retryPDUData = nullptr; - // if we are using a secondary header place it in the PDU user data buffer - m_rfDataHeader.getExtAddrData(m_rfPduUserData + dataOffset); - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - m_rfPduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; - } + m_retryPDUBitLength = 0U; + m_retryCount = 0U; - // process second header if we're using auxiliary ES - if ((m_rfDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_rfDataHeader.getSAP() == PDUSAP::ENC_KMM) && - m_rfDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) { - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - Utils::getBitRange(m_rfPDU, buffer, offset, P25_PDU_FEC_LENGTH_BITS); - bool ret = m_rfDataHeader.decodeAuxES(buffer); - if (!ret) { - LogWarning(LOG_RF, P25_PDU_STR ", unfixable RF 1/2 rate second header data"); - Utils::dump(1U, "P25, Unfixable PDU Data", m_rfPDU + offset, P25_PDU_HEADER_LENGTH_BYTES); + LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK RETRY, llId = %u, exceeded retries, undeliverable", + m_rfAssembler->dataHeader.getLLId()); - m_rfDataHeader.reset(); - m_rfPDUCount = 0U; - m_rfPDUBits = 0U; - m_p25->m_rfState = m_prevRfState; - return false; + writeRF_PDU_Ack_Response(PDUAckClass::NACK, PDUAckType::NACK_UNDELIVERABLE, m_rfAssembler->dataHeader.getNs(), m_rfAssembler->dataHeader.getLLId(), m_rfAssembler->dataHeader.getSrcLLId()); + } + } + } } - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, auxiliary ES, algoId = $%02X, kId = $%04X", - m_rfDataHeader.getAlgId(), m_rfDataHeader.getKId()); + // rewrite the response to the network + writeNetwork(0U, buffer, P25_PDU_FEC_LENGTH_BYTES, true); - if (m_rfDataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - m_rfDataHeader.getMI(mi); - - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } + // only repeat the PDU locally if the packet isn't for the FNE + if (m_repeatPDU && m_rfAssembler->dataHeader.getLLId() != WUID_FNE) { + writeRF_PDU_Ack_Response(m_rfAssembler->dataHeader.getResponseClass(), m_rfAssembler->dataHeader.getResponseType(), m_rfAssembler->dataHeader.getResponseStatus(), + m_rfAssembler->dataHeader.getLLId(), m_rfAssembler->dataHeader.getSrcLLId()); } - m_rfAuxiliaryES = true; - - offset += P25_PDU_FEC_LENGTH_BITS; - m_rfPDUCount++; - blocksToFollow--; + m_rfPDUCount = 0U; + m_rfPDUBits = 0U; + m_rfPduUserDataLength = 0U; + ::memset(m_rfPDU, 0x00U, P25_PDU_FRAME_LENGTH_BYTES + 2U); - // if we are using a secondary header place it in the PDU user data buffer - m_rfDataHeader.getAuxiliaryESData(m_rfPduUserData + dataOffset); - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - m_rfPduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; + m_p25->m_rfState = RS_RF_LISTENING; + m_inbound = false; + return true; } - uint32_t srcId = (m_rfExtendedAddress) ? m_rfDataHeader.getSrcLLId() : m_rfDataHeader.getLLId(); - uint32_t dstId = m_rfDataHeader.getLLId(); - m_rfPDUCount++; - uint32_t bitLength = ((blocksToFollow + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; + } - m_rfDataBlockCnt = 0U; + if (m_p25->m_rfState == RS_RF_DATA) { + uint32_t blocksToFollow = m_rfAssembler->dataHeader.getBlocksToFollow(); + uint32_t bitLength = ((blocksToFollow + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; if (m_rfPDUBits >= bitLength) { - // process all blocks in the data stream - // if the primary header has a header offset ensure data if offset by that amount - if (m_rfDataHeader.getHeaderOffset() > 0U) { - offset += m_rfDataHeader.getHeaderOffset() * 8; - m_rfPduUserDataLength -= m_rfDataHeader.getHeaderOffset(); - } - - // decode data blocks - for (uint32_t i = 0U; i < blocksToFollow; i++) { + for (uint32_t i = P25_PREAMBLE_LENGTH_BITS + P25_PDU_FEC_LENGTH_BITS; i < bitLength; i += P25_PDU_FEC_LENGTH_BITS) { + LogDebugEx(LOG_P25, "Data::process()", "blocksToFollow = %u, bitLength = %u, rfPDUBits = %u, rfPDUCount = %u", blocksToFollow, bitLength, m_rfPDUBits, m_rfPDUCount); ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - Utils::getBitRange(m_rfPDU, buffer, offset, P25_PDU_FEC_LENGTH_BITS); - bool ret = m_rfData[i].decode(buffer, m_rfDataHeader); - if (ret) { - // if we are getting unconfirmed or confirmed blocks, and if we've reached the total number of blocks - // set this block as the last block for full packet CRC - if ((m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) || (m_rfDataHeader.getFormat() == PDUFormatType::UNCONFIRMED)) { - if ((m_rfDataBlockCnt + 1U) == blocksToFollow) { - m_rfData[i].setLastBlock(true); - } - } + Utils::getBitRange(m_rfPDU, buffer, i, P25_PDU_FEC_LENGTH_BITS); + + bool ret = m_rfAssembler->disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES); + if (!ret) { + m_rfPDUCount = 0U; + m_rfPDUBits = 0U; + m_p25->m_rfState = m_prevRfState; + return false; + } - // are we processing extended address data from the first block? - if (m_rfDataHeader.getSAP() == PDUSAP::EXT_ADDR && m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED && - m_rfData[i].getSerialNo() == 0U) { - uint8_t secondHeader[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES]; - ::memset(secondHeader, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); - m_rfData[i].getData(secondHeader); - - m_rfDataHeader.decodeExtAddr(secondHeader); - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", - m_rfData[i].getSerialNo(), m_rfData[i].getFormat(), m_rfData[i].getLastBlock(), m_rfDataHeader.getEXSAP(), m_rfDataHeader.getSrcLLId()); - } + m_rfPDUCount++; + } + } else { + LogDebugEx(LOG_P25, "Data::process()", "blocksToFollow = %u, bitLength = %u, rfPDUBits = %u, rfPDUCount = %u", blocksToFollow, bitLength, m_rfPDUBits, m_rfPDUCount); + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(m_rfPDU, buffer, P25_PREAMBLE_LENGTH_BITS + P25_PDU_FEC_LENGTH_BITS, P25_PDU_FEC_LENGTH_BITS); - srcId = m_rfDataHeader.getSrcLLId(); - m_rfExtendedAddress = true; - } - else { - // are we processing auxiliary ES data from the first block? - if ((m_rfDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_rfDataHeader.getSAP() == PDUSAP::ENC_KMM) && - m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED && m_rfData[i].getSerialNo() == 0U) { - uint8_t secondHeader[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES]; - ::memset(secondHeader, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); - m_rfData[i].getData(secondHeader); - - m_rfDataHeader.decodeAuxES(secondHeader); - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, algoId = $%02X, kId = $%04X", - m_rfData[i].getSerialNo(), m_rfData[i].getFormat(), m_rfData[i].getLastBlock(), m_rfDataHeader.getEXSAP(), - m_rfDataHeader.getAlgId(), m_rfDataHeader.getKId()); - - if (m_rfDataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - m_rfDataHeader.getMI(mi); - - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, Enc Sync, block %u, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - m_rfData[i].getSerialNo(), mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - - m_rfAuxiliaryES = true; - } - else { - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, block %u, fmt = $%02X, lastBlock = %u", - (m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_rfData[i].getSerialNo() : m_rfDataBlockCnt, m_rfData[i].getFormat(), - m_rfData[i].getLastBlock()); - } - } - } + bool ret = m_rfAssembler->disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES); + if (!ret) { + m_rfPDUCount = 0U; + m_rfPDUBits = 0U; + m_p25->m_rfState = m_prevRfState; + return false; + } - m_rfData[i].getData(m_rfPduUserData + dataOffset); + m_rfPDUCount++; + } - // is this the first unconfirmed data block after a auxiliary ES header? - if (i == 0U && m_rfDataHeader.getFormat() == PDUFormatType::UNCONFIRMED && m_rfAuxiliaryES) { - uint8_t exSAP = m_rfPduUserData[0U]; // first byte of the first data block after an aux ES header is the extended SAP - m_rfDataHeader.setEXSAP(exSAP); - } + if (m_rfAssembler->getComplete()) { + m_rfPduUserDataLength = m_rfAssembler->getUserDataLength(); + m_rfAssembler->getUserData(m_rfPduUserData); - dataOffset += (m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; - m_rfPduUserDataLength = dataOffset; - m_rfDataBlockCnt++; - } - else { - if (m_rfData[i].getFormat() == PDUFormatType::CONFIRMED) { - LogWarning(LOG_RF, P25_PDU_STR ", unfixable PDU data (3/4 rate or CRC), block %u", i); - - // to prevent data block offset errors fill the bad block with 0's - uint8_t blankBuf[P25_PDU_CONFIRMED_DATA_LENGTH_BYTES]; - ::memset(blankBuf, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); - ::memcpy(m_rfPduUserData + dataOffset, blankBuf, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); - dataOffset += P25_PDU_CONFIRMED_DATA_LENGTH_BYTES; - m_rfPduUserDataLength = dataOffset; - } - else { - LogWarning(LOG_RF, P25_PDU_STR ", unfixable PDU data (1/2 rate or CRC), block %u", i); - - // to prevent data block offset errors fill the bad block with 0's - uint8_t blankBuf[P25_PDU_UNCONFIRMED_LENGTH_BYTES]; - ::memset(blankBuf, 0x00U, P25_PDU_UNCONFIRMED_LENGTH_BYTES); - ::memcpy(m_rfPduUserData + dataOffset, blankBuf, P25_PDU_UNCONFIRMED_LENGTH_BYTES); - dataOffset += P25_PDU_UNCONFIRMED_LENGTH_BYTES; - m_rfPduUserDataLength = dataOffset; - } + uint8_t sap = (m_rfAssembler->getExtendedAddress()) ? m_rfAssembler->dataHeader.getEXSAP() : m_rfAssembler->dataHeader.getSAP(); + if (m_rfAssembler->getAuxiliaryES()) + sap = m_rfAssembler->dataHeader.getEXSAP(); - if (m_dumpPDUData) { - Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); + uint32_t srcId = (m_rfAssembler->getExtendedAddress()) ? m_rfAssembler->dataHeader.getSrcLLId() : m_rfAssembler->dataHeader.getLLId(); + uint32_t dstId = m_rfAssembler->dataHeader.getLLId(); + + // handle standard P25 service access points + switch (sap) { + case PDUSAP::ARP: + { + /* bryanb: quick and dirty ARP logging */ + uint8_t arpPacket[P25_PDU_ARP_PCKT_LENGTH]; + ::memset(arpPacket, 0x00U, P25_PDU_ARP_PCKT_LENGTH); + ::memcpy(arpPacket, m_rfPduUserData, P25_PDU_ARP_PCKT_LENGTH); + + uint16_t opcode = GET_UINT16(arpPacket, 6U); + uint32_t srcHWAddr = GET_UINT24(arpPacket, 8U); + uint32_t srcProtoAddr = GET_UINT32(arpPacket, 11U); + //uint32_t tgtHWAddr = GET_UINT24(arpPacket, 15U); + uint32_t tgtProtoAddr = GET_UINT32(arpPacket, 18U); + + if (m_verbose) { + if (opcode == P25_PDU_ARP_REQUEST) { + LogInfoEx(LOG_RF, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); + } else if (opcode == P25_PDU_ARP_REPLY) { + LogInfoEx(LOG_RF, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); } } - offset += P25_PDU_FEC_LENGTH_BITS; + writeNet_PDU_User(m_rfAssembler->dataHeader, m_rfAssembler->getExtendedAddress(), m_rfAssembler->getAuxiliaryES(), + m_rfPduUserData); + writeRF_PDU_Buffered(); // re-generate buffered PDU and send it on } + break; + case PDUSAP::SNDCP_CTRL_DATA: + { + if (m_rfAssembler->getUndecodableBlockCount() > 0U) + break; - if (m_rfDataHeader.getBlocksToFollow() > 0U) { - bool crcRet = edac::CRC::checkCRC32(m_rfPduUserData, m_rfPduUserDataLength); - if (!crcRet) { - LogWarning(LOG_RF, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", m_rfDataHeader.getBlocksToFollow(), m_rfPduUserDataLength); - if (!m_p25->m_ignorePDUCRC) { - uint8_t sap = (m_rfExtendedAddress) ? m_rfDataHeader.getEXSAP() : m_rfDataHeader.getSAP(); - if (sap != PDUSAP::TRUNK_CTRL) // ignore CRC errors on TSBK data - writeRF_PDU_Ack_Response(PDUAckClass::NACK, PDUAckType::NACK_PACKET_CRC, m_rfDataHeader.getNs(), (m_rfExtendedAddress) ? m_rfDataHeader.getSrcLLId() : m_rfDataHeader.getLLId(), - m_rfExtendedAddress, WUID_FNE); - } + if (m_verbose) { + LogInfoEx(LOG_RF, P25_PDU_STR ", SNDCP_CTRL_DATA (SNDCP Control Data), blocksToFollow = %u", + m_rfAssembler->dataHeader.getBlocksToFollow()); } - } - - if (m_dumpPDUData && m_rfDataBlockCnt > 0U) { - Utils::dump(1U, "P25, PDU Packet", m_rfPduUserData, m_rfPduUserDataLength); - } - if (m_rfDataBlockCnt < blocksToFollow) { - LogWarning(LOG_RF, P25_PDU_STR ", incomplete PDU (%d / %d blocks)", m_rfDataBlockCnt, blocksToFollow); + processSNDCPControl(m_rfPduUserData); + writeNet_PDU_User(m_rfAssembler->dataHeader, m_rfAssembler->getExtendedAddress(), m_rfAssembler->getAuxiliaryES(), + m_rfPduUserData); } + break; + case PDUSAP::CONV_DATA_REG: + { + if (m_rfAssembler->getUndecodableBlockCount() > 0U) { + break; + } - // did we receive a response header? - if (m_rfDataHeader.getFormat() == PDUFormatType::RSP) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", - m_rfDataHeader.getFormat(), m_rfDataHeader.getResponseClass(), m_rfDataHeader.getResponseType(), m_rfDataHeader.getResponseStatus(), - m_rfDataHeader.getLLId(), m_rfDataHeader.getSrcLLId()); - - if (m_rfDataHeader.getResponseClass() == PDUAckClass::ACK && m_rfDataHeader.getResponseType() == PDUAckType::ACK) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK, llId = %u, all blocks received OK, n = %u", - m_rfDataHeader.getLLId(), m_rfDataHeader.getResponseStatus()); - if (m_retryPDUData != nullptr && m_retryPDUBitLength > 0U) { - delete m_retryPDUData; - m_retryPDUData = nullptr; - - m_retryPDUBitLength = 0U; - m_retryCount = 0U; - } - } else { - if (m_rfDataHeader.getResponseClass() == PDUAckClass::NACK) { - switch (m_rfDataHeader.getResponseType()) { - case PDUAckType::NACK_ILLEGAL: - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, illegal format, llId = %u", - m_rfDataHeader.getLLId()); - break; - case PDUAckType::NACK_PACKET_CRC: - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet CRC error, llId = %u, n = %u", - m_rfDataHeader.getLLId(), m_rfDataHeader.getResponseStatus()); - break; - case PDUAckType::NACK_SEQ: - case PDUAckType::NACK_OUT_OF_SEQ: - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet out of sequence, llId = %u, seqNo = %u", - m_rfDataHeader.getLLId(), m_rfDataHeader.getResponseStatus()); - break; - case PDUAckType::NACK_UNDELIVERABLE: - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet undeliverable, llId = %u, n = %u", - m_rfDataHeader.getLLId(), m_rfDataHeader.getResponseStatus()); - break; - - default: - break; - } - } else if (m_rfDataHeader.getResponseClass() == PDUAckClass::ACK_RETRY) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK RETRY, llId = %u", - m_rfDataHeader.getLLId()); - - // really this is supposed to check the bit field in the included response - // and only return those bits -- but we're responding with the entire previous packet... - if (m_retryPDUData != nullptr && m_retryPDUBitLength > 0U) { - if (m_retryCount < MAX_PDU_RETRY_CNT) { - m_p25->writeRF_Preamble(); - writeRF_PDU(m_retryPDUData, m_retryPDUBitLength, false, true); - m_retryCount++; - } - else { - delete m_retryPDUData; - m_retryPDUData = nullptr; - - m_retryPDUBitLength = 0U; - m_retryCount = 0U; - - LogInfoEx(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(), m_rfDataHeader.getSrcLLId()); - } - } - } + if (m_verbose) { + LogInfoEx(LOG_RF, P25_PDU_STR ", CONV_DATA_REG (Conventional Data Registration), blocksToFollow = %u", + m_rfAssembler->dataHeader.getBlocksToFollow()); } - // rewrite the response to the network - writeNetwork(0U, buffer, P25_PDU_FEC_LENGTH_BYTES, true); + processConvDataReg(m_rfPduUserData); + writeNet_PDU_User(m_rfAssembler->dataHeader, m_rfAssembler->getExtendedAddress(), m_rfAssembler->getAuxiliaryES(), + m_rfPduUserData); + } + break; + case PDUSAP::UNENC_KMM: + case PDUSAP::ENC_KMM: + { + if (m_rfAssembler->getUndecodableBlockCount() > 0U) + break; - // only repeat the PDU locally if the packet isn't for the FNE - if (m_repeatPDU && m_rfDataHeader.getLLId() != WUID_FNE) { - writeRF_PDU_Ack_Response(m_rfDataHeader.getResponseClass(), m_rfDataHeader.getResponseType(), m_rfDataHeader.getResponseStatus(), - m_rfDataHeader.getLLId(), m_rfDataHeader.getSrcLLId()); + if (m_verbose) { + LogInfoEx(LOG_RF, P25_PDU_STR ", KMM (Key Management Message), blocksToFollow = %u", + m_rfAssembler->dataHeader.getBlocksToFollow()); } - } - else { - uint8_t sap = (m_rfExtendedAddress) ? m_rfDataHeader.getEXSAP() : m_rfDataHeader.getSAP(); - if (m_rfAuxiliaryES) - sap = m_rfDataHeader.getEXSAP(); - - // handle standard P25 service access points - switch (sap) { - case PDUSAP::ARP: - { - /* bryanb: quick and dirty ARP logging */ - uint8_t arpPacket[P25_PDU_ARP_PCKT_LENGTH]; - ::memset(arpPacket, 0x00U, P25_PDU_ARP_PCKT_LENGTH); - ::memcpy(arpPacket, m_rfPduUserData + P25_PDU_HEADER_LENGTH_BYTES, P25_PDU_ARP_PCKT_LENGTH); - - uint16_t opcode = GET_UINT16(arpPacket, 6U); - uint32_t srcHWAddr = GET_UINT24(arpPacket, 8U); - uint32_t srcProtoAddr = GET_UINT32(arpPacket, 11U); - //uint32_t tgtHWAddr = GET_UINT24(arpPacket, 15U); - uint32_t tgtProtoAddr = GET_UINT32(arpPacket, 18U); - if (m_verbose) { - if (opcode == P25_PDU_ARP_REQUEST) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); - } else if (opcode == P25_PDU_ARP_REPLY) { - LogInfoEx(LOG_RF, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); - } - } + writeNet_PDU_User(m_rfAssembler->dataHeader, m_rfAssembler->getExtendedAddress(), m_rfAssembler->getAuxiliaryES(), + m_rfPduUserData); + } + break; + case PDUSAP::TRUNK_CTRL: + { + if (m_rfAssembler->getUndecodableBlockCount() > 0U) + break; - writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfAuxiliaryES, m_rfPduUserData); - writeRF_PDU_Buffered(); // re-generate buffered PDU and send it on + if (m_verbose) { + LogInfoEx(LOG_RF, P25_PDU_STR ", TRUNK_CTRL (Alternate MBT Packet), lco = $%02X, blocksToFollow = %u", + m_rfAssembler->dataHeader.getAMBTOpcode(), m_rfAssembler->dataHeader.getBlocksToFollow()); } - break; - case PDUSAP::SNDCP_CTRL_DATA: - { - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", SNDCP_CTRL_DATA (SNDCP Control Data), blocksToFollow = %u", - m_rfDataHeader.getBlocksToFollow()); - } - processSNDCPControl(m_rfPduUserData); - writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfAuxiliaryES, m_rfPduUserData); - } - break; - case PDUSAP::CONV_DATA_REG: - { - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", CONV_DATA_REG (Conventional Data Registration), blocksToFollow = %u", - m_rfDataHeader.getBlocksToFollow()); - } + m_p25->m_control->processMBT(m_rfAssembler->dataHeader, m_rfAssembler->dataBlocks); + } + break; + default: + writeNet_PDU_User(m_rfAssembler->dataHeader, m_rfAssembler->getExtendedAddress(), m_rfAssembler->getAuxiliaryES(), + m_rfPduUserData); - processConvDataReg(m_rfPduUserData); - writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfAuxiliaryES, m_rfPduUserData); - } - break; - case PDUSAP::UNENC_KMM: - case PDUSAP::ENC_KMM: - { - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", KMM (Key Management Message), blocksToFollow = %u", - m_rfDataHeader.getBlocksToFollow()); - } + // only repeat the PDU locally if the packet isn't for the FNE + if (m_repeatPDU && m_rfAssembler->dataHeader.getLLId() != WUID_FNE) { + ::ActivityLog("P25", true, "RF data transmission from %u to %u, %u blocks", srcId, dstId, m_rfAssembler->dataHeader.getBlocksToFollow()); + LogInfoEx(LOG_RF, "P25 Data Call (Local Repeat), srcId = %u, dstId = %u", srcId, dstId); - writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfAuxiliaryES, m_rfPduUserData); - } - break; - case PDUSAP::TRUNK_CTRL: - { if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", TRUNK_CTRL (Alternate MBT Packet), lco = $%02X, blocksToFollow = %u", - m_rfDataHeader.getAMBTOpcode(), m_rfDataHeader.getBlocksToFollow()); + LogInfoEx(LOG_RF, P25_PDU_STR ", repeating PDU, llId = %u", (m_rfAssembler->getExtendedAddress()) ? m_rfAssembler->dataHeader.getSrcLLId() : m_rfAssembler->dataHeader.getLLId()); } - m_p25->m_control->processMBT(m_rfDataHeader, m_rfData); + writeRF_PDU_Buffered(); // re-generate buffered PDU and send it on + ::ActivityLog("P25", true, "end of RF data transmission"); } break; - default: - writeNet_PDU_User(m_rfDataHeader, m_rfExtendedAddress, m_rfAuxiliaryES, m_rfPduUserData); - - // only repeat the PDU locally if the packet isn't for the FNE - if (m_repeatPDU && m_rfDataHeader.getLLId() != WUID_FNE) { - ::ActivityLog("P25", true, "RF data transmission from %u to %u, %u blocks", srcId, dstId, m_rfDataHeader.getBlocksToFollow()); - LogInfoEx(LOG_RF, "P25 Data Call (Local Repeat), srcId = %u, dstId = %u", srcId, dstId); - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", repeating PDU, llId = %u", (m_rfExtendedAddress) ? m_rfDataHeader.getSrcLLId() : m_rfDataHeader.getLLId()); - } - - writeRF_PDU_Buffered(); // re-generate buffered PDU and send it on - ::ActivityLog("P25", true, "end of RF data transmission"); - } - break; - } } - m_rfDataHeader.reset(); - m_rfExtendedAddress = false; - m_rfAuxiliaryES = false; - m_rfDataBlockCnt = 0U; m_rfPDUCount = 0U; m_rfPDUBits = 0U; m_rfPduUserDataLength = 0U; + ::memset(m_rfPDU, 0x00U, P25_PDU_FRAME_LENGTH_BYTES + 2U); m_p25->m_rfState = RS_RF_LISTENING; - } // switch (m_rfDataHeader.getSAP()) + } } m_inbound = false; @@ -588,98 +393,56 @@ bool Data::process(uint8_t* data, uint32_t len) bool Data::processNetwork(uint8_t* data, uint32_t len, uint8_t currentBlock, uint32_t blockLength) { if ((m_p25->m_netState != RS_NET_DATA) || (currentBlock == 0U)) { - m_netDataHeader.reset(); - m_netDataOffset = 0U; - m_netDataBlockCnt = 0U; - m_netPDUCount = 0U; - - ::memset(m_netPDU, 0x00U, P25_PDU_FRAME_LENGTH_BYTES + 2U); - m_p25->m_netState = RS_NET_DATA; m_inbound = false; - uint8_t buffer[P25_PDU_FEC_LENGTH_BYTES]; - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - ::memcpy(buffer, data + 24U, P25_PDU_FEC_LENGTH_BYTES); - - bool ret = m_netDataHeader.decode(buffer); + bool ret = m_netAssembler->disassemble(data + 24U, blockLength, true); if (!ret) { - LogWarning(LOG_NET, P25_PDU_STR ", unfixable RF 1/2 rate header data"); - Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); - - m_netDataHeader.reset(); - m_netDataBlockCnt = 0U; - m_netPDUCount = 0U; - m_p25->m_netState = RS_NET_IDLE; - return false; - } - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, ack = %u, outbound = %u, fmt = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, hdrOffset = %u, llId = %u", - m_netDataHeader.getAckNeeded(), m_netDataHeader.getOutbound(), m_netDataHeader.getFormat(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMessage(), - m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadLength(), m_netDataHeader.getPacketLength(), m_netDataHeader.getSynchronize(), m_netDataHeader.getNs(), m_netDataHeader.getFSN(), - m_netDataHeader.getHeaderOffset(), m_netDataHeader.getLLId()); - } - - // make sure we don't get a PDU with more blocks then we support - if (m_netDataHeader.getBlocksToFollow() >= P25_MAX_PDU_BLOCKS) { - LogError(LOG_NET, P25_PDU_STR ", too many PDU blocks to process, %u > %u", m_netDataHeader.getBlocksToFollow(), P25_MAX_PDU_BLOCKS); - - m_netDataHeader.reset(); - m_netDataOffset = 0U; - m_netDataBlockCnt = 0U; - m_netPDUCount = 0U; m_p25->m_netState = RS_NET_IDLE; return false; } // if we're a dedicated CC or in control only mode, we only want to handle AMBTs. Otherwise return - if ((m_p25->m_dedicatedControl || m_p25->m_controlOnly) && m_netDataHeader.getFormat() != PDUFormatType::AMBT) { + if ((m_p25->m_dedicatedControl || m_p25->m_controlOnly) && m_netAssembler->dataHeader.getFormat() != PDUFormatType::AMBT) { if (m_debug) { LogDebug(LOG_NET, "CC only mode, ignoring non-AMBT PDU from network"); } - m_netDataHeader.reset(); - m_netDataOffset = 0U; - m_netDataBlockCnt = 0U; - m_netPDUCount = 0U; m_p25->m_netState = RS_NET_IDLE; return false; } - m_netPDUCount++; - // did we receive a response header? - if (m_netDataHeader.getFormat() == PDUFormatType::RSP) { + if (m_netAssembler->dataHeader.getFormat() == PDUFormatType::RSP) { m_p25->m_netState = RS_NET_IDLE; if (m_verbose) { LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u", - m_netDataHeader.getFormat(), m_netDataHeader.getResponseClass(), m_netDataHeader.getResponseType(), m_netDataHeader.getResponseStatus(), - m_netDataHeader.getLLId(), m_netDataHeader.getSrcLLId()); + m_netAssembler->dataHeader.getFormat(), m_netAssembler->dataHeader.getResponseClass(), m_netAssembler->dataHeader.getResponseType(), m_netAssembler->dataHeader.getResponseStatus(), + m_netAssembler->dataHeader.getLLId(), m_netAssembler->dataHeader.getSrcLLId()); - if (m_netDataHeader.getResponseClass() == PDUAckClass::ACK && m_netDataHeader.getResponseType() == PDUAckType::ACK) { + if (m_netAssembler->dataHeader.getResponseClass() == PDUAckClass::ACK && m_netAssembler->dataHeader.getResponseType() == PDUAckType::ACK) { LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP ACK, llId = %u, all blocks received OK, n = %u", - m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); + m_netAssembler->dataHeader.getLLId(), m_netAssembler->dataHeader.getResponseStatus()); } else { - if (m_netDataHeader.getResponseClass() == PDUAckClass::NACK) { - switch (m_netDataHeader.getResponseType()) { + if (m_netAssembler->dataHeader.getResponseClass() == PDUAckClass::NACK) { + switch (m_netAssembler->dataHeader.getResponseType()) { case PDUAckType::NACK_ILLEGAL: LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, illegal format, llId = %u", - m_netDataHeader.getLLId()); + m_netAssembler->dataHeader.getLLId()); break; case PDUAckType::NACK_PACKET_CRC: LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet CRC error, llId = %u, n = %u", - m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); + m_netAssembler->dataHeader.getLLId(), m_netAssembler->dataHeader.getResponseStatus()); break; case PDUAckType::NACK_SEQ: case PDUAckType::NACK_OUT_OF_SEQ: LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet out of sequence, llId = %u, seqNo = %u", - m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); + m_netAssembler->dataHeader.getLLId(), m_netAssembler->dataHeader.getResponseStatus()); break; case PDUAckType::NACK_UNDELIVERABLE: LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, response, OSP NACK, packet undeliverable, llId = %u, n = %u", - m_netDataHeader.getLLId(), m_netDataHeader.getResponseStatus()); + m_netAssembler->dataHeader.getLLId(), m_netAssembler->dataHeader.getResponseStatus()); break; default: @@ -689,16 +452,8 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint8_t currentBlock, uin } } - writeRF_PDU_Ack_Response(m_netDataHeader.getResponseClass(), m_netDataHeader.getResponseType(), m_netDataHeader.getResponseStatus(), - m_netDataHeader.getLLId(), (m_netDataHeader.getSrcLLId() > 0U), m_netDataHeader.getSrcLLId()); - - m_netDataHeader.reset(); - m_netExtendedAddress = false; - m_netAuxiliaryES = false; - m_netDataOffset = 0U; - m_netDataBlockCnt = 0U; - m_netPDUCount = 0U; - m_netPduUserDataLength = 0U; + writeRF_PDU_Ack_Response(m_netAssembler->dataHeader.getResponseClass(), m_netAssembler->dataHeader.getResponseType(), m_netAssembler->dataHeader.getResponseStatus(), + m_netAssembler->dataHeader.getLLId(), (m_netAssembler->dataHeader.getSrcLLId() > 0U), m_netAssembler->dataHeader.getSrcLLId()); } return true; @@ -710,264 +465,67 @@ bool Data::processNetwork(uint8_t* data, uint32_t len, uint8_t currentBlock, uin return false; // bail } - m_inbound = false; // forcibly set inbound to false - ::memcpy(m_netPDU + ((currentBlock - 1U) * blockLength), data + 24U, blockLength); - m_netDataOffset += blockLength; - m_netPDUCount++; - m_netDataBlockCnt++; - - if (m_netDataBlockCnt >= m_netDataHeader.getBlocksToFollow()) { - uint32_t blocksToFollow = m_netDataHeader.getBlocksToFollow(); - uint32_t offset = 0U; - uint32_t dataOffset = 0U; - - uint8_t buffer[P25_PDU_FEC_LENGTH_BYTES]; - - // process second header if we're using enhanced addressing - if (m_netDataHeader.getSAP() == PDUSAP::EXT_ADDR && - m_netDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) { - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - ::memcpy(buffer, m_netPDU, P25_PDU_FEC_LENGTH_BYTES); - - bool ret = m_netDataHeader.decodeExtAddr(buffer); - if (!ret) { - LogWarning(LOG_NET, P25_PDU_STR ", unfixable RF 1/2 rate second header data"); - Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_HEADER_LENGTH_BYTES); - - m_netDataHeader.reset(); - m_netDataBlockCnt = 0U; - m_netPDUCount = 0U; - m_p25->m_netState = RS_NET_IDLE; - return false; - } - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, extended address, sap = $%02X, srcLlId = %u", - m_netDataHeader.getEXSAP(), m_netDataHeader.getSrcLLId()); - } - - m_netExtendedAddress = true; - - offset += P25_PDU_FEC_LENGTH_BYTES; - blocksToFollow--; - - // if we are using a secondary header place it in the PDU user data buffer - m_netDataHeader.getExtAddrData(m_netPduUserData + dataOffset); - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - m_netPduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; - } - - // process second header if we're using auxiliary ES - if ((m_netDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_netDataHeader.getSAP() == PDUSAP::ENC_KMM) && - m_netDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) { - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - ::memcpy(buffer, m_netPDU, P25_PDU_FEC_LENGTH_BYTES); - - bool ret = m_netDataHeader.decodeAuxES(buffer); - if (!ret) { - LogWarning(LOG_NET, P25_PDU_STR ", unfixable RF 1/2 rate second header data"); - Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_HEADER_LENGTH_BYTES); - - m_netDataHeader.reset(); - m_netDataBlockCnt = 0U; - m_netPDUCount = 0U; - m_p25->m_netState = RS_NET_IDLE; - return false; - } - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, auxiliary ES, algoId = $%02X, kId = $%04X", - m_netDataHeader.getAlgId(), m_netDataHeader.getKId()); - - if (m_netDataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - m_netDataHeader.getMI(mi); - - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - - m_netAuxiliaryES = true; - - offset += P25_PDU_FEC_LENGTH_BYTES; - blocksToFollow--; - - // if we are using a secondary header place it in the PDU user data buffer - m_netDataHeader.getExtAddrData(m_netPduUserData + dataOffset); - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - m_netPduUserDataLength += P25_PDU_HEADER_LENGTH_BYTES; - } + bool ret = m_netAssembler->disassemble(data + 24U, blockLength); + if (!ret) { + m_p25->m_netState = RS_NET_IDLE; + return false; + } + else { + if (m_netAssembler->getComplete()) { + m_netPduUserDataLength = m_netAssembler->getUserDataLength(); + m_netAssembler->getUserData(m_netPduUserData); - m_netDataBlockCnt = 0U; + uint32_t srcId = (m_netAssembler->getExtendedAddress()) ? m_netAssembler->dataHeader.getSrcLLId() : m_netAssembler->dataHeader.getLLId(); + uint32_t dstId = m_netAssembler->dataHeader.getLLId(); - // decode data blocks - for (uint32_t i = 0U; i < blocksToFollow; i++) { - ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - ::memcpy(buffer, m_netPDU + offset, P25_PDU_FEC_LENGTH_BYTES); - - bool ret = m_netData[i].decode(buffer, m_netDataHeader); - if (ret) { - // if we are getting unconfirmed or confirmed blocks, and if we've reached the total number of blocks - // set this block as the last block for full packet CRC - if ((m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) || (m_netDataHeader.getFormat() == PDUFormatType::UNCONFIRMED)) { - if ((m_netDataBlockCnt + 1U) == blocksToFollow) { - m_netData[i].setLastBlock(true); - } - } - - // are we processing extended address data from the first block? - if (m_netDataHeader.getSAP() == PDUSAP::EXT_ADDR && m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED && - m_netData[i].getSerialNo() == 0U) { - uint8_t secondHeader[P25_PDU_HEADER_LENGTH_BYTES]; - ::memset(secondHeader, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); - m_netData[i].getData(secondHeader); + uint8_t sap = (m_netAssembler->getExtendedAddress()) ? m_netAssembler->dataHeader.getEXSAP() : m_netAssembler->dataHeader.getSAP(); + if (m_netAssembler->getAuxiliaryES()) + sap = m_netAssembler->dataHeader.getEXSAP(); - m_netDataHeader.decodeExtAddr(secondHeader); - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, srcLlId = %u", - m_netData[i].getSerialNo(), m_netData[i].getFormat(), m_netData[i].getLastBlock(), m_netDataHeader.getEXSAP(), m_netDataHeader.getSrcLLId()); + // handle standard P25 service access points + switch (sap) { + case PDUSAP::ARP: + { + /* bryanb: quick and dirty ARP logging */ + uint8_t arpPacket[P25_PDU_ARP_PCKT_LENGTH]; + ::memset(arpPacket, 0x00U, P25_PDU_ARP_PCKT_LENGTH); + ::memcpy(arpPacket, m_netPduUserData, P25_PDU_ARP_PCKT_LENGTH); + + uint16_t opcode = GET_UINT16(arpPacket, 6U); + uint32_t srcHWAddr = GET_UINT24(arpPacket, 8U); + uint32_t srcProtoAddr = GET_UINT32(arpPacket, 11U); + //uint32_t tgtHWAddr = GET_UINT24(arpPacket, 15U); + uint32_t tgtProtoAddr = GET_UINT32(arpPacket, 18U); + + if (m_verbose) { + if (opcode == P25_PDU_ARP_REQUEST) { + LogInfoEx(LOG_NET, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); + } else if (opcode == P25_PDU_ARP_REPLY) { + LogInfoEx(LOG_NET, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); } - - m_netExtendedAddress = true; - } - else { - // are we processing auxiliary ES data from the first block? - if ((m_netDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_netDataHeader.getSAP() == PDUSAP::ENC_KMM) && - m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED && m_netData[i].getSerialNo() == 0U) { - uint8_t secondHeader[P25_PDU_HEADER_LENGTH_BYTES]; - ::memset(secondHeader, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); - m_netData[i].getData(secondHeader); - - m_netDataHeader.decodeAuxES(secondHeader); - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u, sap = $%02X, algoId = $%02X, kId = $%04X", - m_netData[i].getSerialNo(), m_netData[i].getFormat(), m_netData[i].getLastBlock(), m_netDataHeader.getEXSAP(), - m_netDataHeader.getAlgId(), m_netDataHeader.getKId()); - - if (m_netDataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - m_netDataHeader.getMI(mi); - - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, Enc Sync, block %u, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - m_netData[i].getSerialNo(), mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - - m_netAuxiliaryES = true; - } - else { - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE ISP, block %u, fmt = $%02X, lastBlock = %u", - (m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_netData[i].getSerialNo() : m_netDataBlockCnt, m_netData[i].getFormat(), - m_netData[i].getLastBlock()); - } - } - } - - m_netData[i].getData(m_netPduUserData + dataOffset); - - // is this the first unconfirmed data block after a auxiliary ES header? - if (i == 0U && m_netDataHeader.getFormat() == PDUFormatType::UNCONFIRMED && m_netAuxiliaryES) { - uint8_t exSAP = m_netPduUserData[0U]; // first byte of the first data block after an aux ES header is the extended SAP - m_netDataHeader.setEXSAP(exSAP); } - dataOffset += (m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; - m_netPduUserDataLength = dataOffset; - - m_netDataBlockCnt++; - } - else { - if (m_netData[i].getFormat() == PDUFormatType::CONFIRMED) - LogWarning(LOG_NET, P25_PDU_STR ", unfixable PDU data (3/4 rate or CRC), block %u", i); - else - LogWarning(LOG_NET, P25_PDU_STR ", unfixable PDU data (1/2 rate or CRC), block %u", i); - - if (m_dumpPDUData) { - Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); - } + writeNet_PDU_Buffered(); // re-generate buffered PDU and send it on } + break; + default: + ::ActivityLog("P25", false, "Net data transmission from %u to %u, %u blocks", srcId, dstId, m_netAssembler->dataHeader.getBlocksToFollow()); + LogInfoEx(LOG_NET, "P25 Data Call, srcId = %u, dstId = %u", srcId, dstId); - offset += P25_PDU_FEC_LENGTH_BYTES; - } - - if (m_netDataHeader.getBlocksToFollow() > 0U) { - bool crcRet = edac::CRC::checkCRC32(m_netPduUserData, m_netPduUserDataLength); - if (!crcRet) { - LogWarning(LOG_NET, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", m_netDataHeader.getBlocksToFollow(), m_netPduUserDataLength); - } - } - - if (m_dumpPDUData && m_netDataBlockCnt > 0U) { - Utils::dump(1U, "P25, PDU Packet", m_netPduUserData, m_netPduUserDataLength); - } - - if (m_netDataBlockCnt < blocksToFollow) { - LogWarning(LOG_NET, P25_PDU_STR ", incomplete PDU (%d / %d blocks)", m_netDataBlockCnt, blocksToFollow); - } - - uint32_t srcId = (m_netExtendedAddress) ? m_netDataHeader.getSrcLLId() : m_netDataHeader.getLLId(); - uint32_t dstId = m_netDataHeader.getLLId(); - - uint8_t sap = (m_netExtendedAddress) ? m_netDataHeader.getEXSAP() : m_netDataHeader.getSAP(); - if (m_netAuxiliaryES) - sap = m_netDataHeader.getEXSAP(); - - // handle standard P25 service access points - switch (sap) { - case PDUSAP::ARP: - { - /* bryanb: quick and dirty ARP logging */ - uint8_t arpPacket[P25_PDU_ARP_PCKT_LENGTH]; - ::memset(arpPacket, 0x00U, P25_PDU_ARP_PCKT_LENGTH); - ::memcpy(arpPacket, m_netPduUserData + P25_PDU_HEADER_LENGTH_BYTES, P25_PDU_ARP_PCKT_LENGTH); - - uint16_t opcode = GET_UINT16(arpPacket, 6U); - uint32_t srcHWAddr = GET_UINT24(arpPacket, 8U); - uint32_t srcProtoAddr = GET_UINT32(arpPacket, 11U); - //uint32_t tgtHWAddr = GET_UINT24(arpPacket, 15U); - uint32_t tgtProtoAddr = GET_UINT32(arpPacket, 18U); - - if (m_verbose) { - if (opcode == P25_PDU_ARP_REQUEST) { - LogInfoEx(LOG_NET, P25_PDU_STR ", ARP request, who has %s? tell %s (%u)", __IP_FROM_UINT(tgtProtoAddr).c_str(), __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); - } else if (opcode == P25_PDU_ARP_REPLY) { - LogInfoEx(LOG_NET, P25_PDU_STR ", ARP reply, %s is at %u", __IP_FROM_UINT(srcProtoAddr).c_str(), srcHWAddr); + if (m_verbose) { + LogInfoEx(LOG_NET, P25_PDU_STR ", transmitting network PDU, llId = %u", (m_netAssembler->getExtendedAddress()) ? m_netAssembler->dataHeader.getSrcLLId() : m_netAssembler->dataHeader.getLLId()); } - } - writeNet_PDU_Buffered(); // re-generate buffered PDU and send it on - } - break; - default: - ::ActivityLog("P25", false, "Net data transmission from %u to %u, %u blocks", srcId, dstId, m_netDataHeader.getBlocksToFollow()); - LogInfoEx(LOG_NET, "P25 Data Call, srcId = %u, dstId = %u", srcId, dstId); + writeNet_PDU_Buffered(); // re-generate buffered PDU and send it on - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", transmitting network PDU, llId = %u", (m_netExtendedAddress) ? m_netDataHeader.getSrcLLId() : m_netDataHeader.getLLId()); + ::ActivityLog("P25", false, "end of Net data transmission"); + break; } - writeNet_PDU_Buffered(); // re-generate buffered PDU and send it on - - ::ActivityLog("P25", false, "end of Net data transmission"); - break; + m_netPduUserDataLength = 0U; + m_p25->m_netState = RS_NET_IDLE; + m_p25->m_network->resetP25(); } - - m_netDataHeader.reset(); - m_netExtendedAddress = false; - m_netAuxiliaryES = false; - m_netDataOffset = 0U; - m_netDataBlockCnt = 0U; - m_netPDUCount = 0U; - m_netPduUserDataLength = 0U; - - m_p25->m_netState = RS_NET_IDLE; - m_p25->m_network->resetP25(); } } @@ -1000,147 +558,10 @@ void Data::writeRF_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, m_p25->writeRF_TDU(true, imm); - uint32_t bitLength = ((dataHeader.getBlocksToFollow() + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; - if (dataHeader.getPadLength() > 0U) - bitLength += (dataHeader.getPadLength() * 8U); - - uint32_t offset = P25_PREAMBLE_LENGTH_BITS; - - DECLARE_UINT8_ARRAY(data, (bitLength / 8U) + 1U); - - uint8_t block[P25_PDU_FEC_LENGTH_BYTES]; - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - - uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", - dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), - dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(), dataHeader.getSynchronize(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), - dataHeader.getHeaderOffset(), bitLength, dataHeader.getLLId()); - } - - // generate the PDU header and 1/2 rate Trellis - dataHeader.encode(block); - Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); - offset += P25_PDU_FEC_LENGTH_BITS; - - if (blocksToFollow > 0U) { - uint32_t dataOffset = 0U; - uint32_t packetLength = dataHeader.getPDULength(); - - // if using extended addressing, generate the second PDU header - if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { - dataHeader.encodeExtAddr(pduUserData, true); - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - dataHeader.encodeExtAddr(block); - Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); - - bitLength += P25_PDU_FEC_LENGTH_BITS; - - offset += P25_PDU_FEC_LENGTH_BITS; - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - - blocksToFollow--; - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", - dataHeader.getEXSAP(), dataHeader.getSrcLLId()); - } - } - - // if using auxiliary ES, generate the second PDU header - if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && - auxiliaryES) { - dataHeader.encodeAuxES(pduUserData, true); - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - dataHeader.encodeAuxES(block); - writeNetwork(1U, block, P25_PDU_FEC_LENGTH_BYTES, false); - - bitLength += P25_PDU_FEC_LENGTH_BITS; - - offset += P25_PDU_FEC_LENGTH_BITS; - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - - blocksToFollow--; - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", - dataHeader.getAlgId(), dataHeader.getKId()); - - if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - dataHeader.getMI(mi); + uint32_t bitLength = 0U; + UInt8Array data = m_rfAssembler->assemble(dataHeader, extendedAddress, auxiliaryES, pduUserData, &bitLength); - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - } - - // are we processing extended address data from the first block? - if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { - dataHeader.encodeExtAddr(pduUserData); - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", - dataHeader.getEXSAP(), dataHeader.getSrcLLId()); - } - } - - // are we processing auxiliary ES data from the first block? - if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && - auxiliaryES) { - dataHeader.encodeAuxES(pduUserData); - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", - dataHeader.getAlgId(), dataHeader.getKId()); - - if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - dataHeader.getMI(mi); - - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - } - - if (dataHeader.getFormat() != PDUFormatType::AMBT) { - edac::CRC::addCRC32(pduUserData, packetLength); - } - - // generate the PDU data - for (uint32_t i = 0U; i < blocksToFollow; i++) { - DataBlock dataBlock = DataBlock(); - dataBlock.setFormat(dataHeader); - dataBlock.setSerialNo(i); - dataBlock.setData(pduUserData + dataOffset); - dataBlock.setLastBlock((i + 1U) == blocksToFollow); - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u", - (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? dataBlock.getSerialNo() : i, dataBlock.getFormat(), - dataBlock.getLastBlock()); - } - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - dataBlock.encode(block); - Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); - - offset += P25_PDU_FEC_LENGTH_BITS; - dataOffset += (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; - } - } - - writeRF_PDU(data, bitLength, imm); + writeRF_PDU(data.get(), bitLength, imm); } /* Helper to write user data as a P25 PDU packet. */ @@ -1149,141 +570,8 @@ void Data::writeNet_PDU_User(data::DataHeader& dataHeader, bool extendedAddress, { assert(pduUserData != nullptr); - uint32_t bitLength = ((dataHeader.getBlocksToFollow() + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; - if (dataHeader.getPadLength() > 0U) - bitLength += (dataHeader.getPadLength() * 8U); - - uint8_t block[P25_PDU_FEC_LENGTH_BYTES]; - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - - uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", - dataHeader.getAckNeeded(), dataHeader.getOutbound(), dataHeader.getFormat(), dataHeader.getMFId(), dataHeader.getSAP(), dataHeader.getFullMessage(), - dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(), dataHeader.getSynchronize(), dataHeader.getNs(), dataHeader.getFSN(), dataHeader.getLastFragment(), - dataHeader.getHeaderOffset(), bitLength, dataHeader.getLLId()); - } - - // generate the PDU header and 1/2 rate Trellis - dataHeader.encode(block); - writeNetwork(0U, block, P25_PDU_FEC_LENGTH_BYTES, false); - - if (blocksToFollow > 0U) { - uint32_t dataOffset = 0U; - uint32_t packetLength = dataHeader.getPDULength(); - uint32_t netDataBlockCnt = 1U; - - // if using extended addressing, generate the second PDU header - if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { - dataHeader.encodeExtAddr(pduUserData, true); - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - dataHeader.encodeExtAddr(block); - writeNetwork(1U, block, P25_PDU_FEC_LENGTH_BYTES, false); - netDataBlockCnt++; - - bitLength += P25_PDU_FEC_LENGTH_BITS; - - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - - blocksToFollow--; - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, extended address, sap = $%02X, srcLlId = %u", - dataHeader.getEXSAP(), dataHeader.getSrcLLId()); - } - } - - // if using auxiliary ES, generate the second PDU header - if ((dataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && - auxiliaryES) { - dataHeader.encodeAuxES(pduUserData, true); - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - dataHeader.encodeAuxES(block); - writeNetwork(1U, block, P25_PDU_FEC_LENGTH_BYTES, false); - netDataBlockCnt++; - - bitLength += P25_PDU_FEC_LENGTH_BITS; - - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - - blocksToFollow--; - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, auxiliary ES, algId = $%02X, kId = $%04X", - dataHeader.getAlgId(), dataHeader.getKId()); - - if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - dataHeader.getMI(mi); - - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - } - - // are we processing extended address data from the first block? - if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::EXT_ADDR) && extendedAddress) { - dataHeader.encodeExtAddr(pduUserData); - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, extended address, sap = $%02X, srcLlId = %u", - dataHeader.getEXSAP(), dataHeader.getSrcLLId()); - } - } - - // are we processing auxiliary ES data from the first block? - if ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) && (dataHeader.getSAP() == PDUSAP::ENC_USER_DATA || dataHeader.getSAP() == PDUSAP::ENC_KMM) && - auxiliaryES) { - dataHeader.encodeAuxES(pduUserData); - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, auxiliary ES, algId = $%02X, kId = $%04X", - dataHeader.getAlgId(), dataHeader.getKId()); - - if (dataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - dataHeader.getMI(mi); - - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - } - - if (dataHeader.getFormat() != PDUFormatType::AMBT) { - edac::CRC::addCRC32(pduUserData, packetLength); - } - - // generate the PDU data - for (uint32_t i = 0U; i < blocksToFollow; i++) { - DataBlock dataBlock = DataBlock(); - dataBlock.setFormat(dataHeader); - dataBlock.setSerialNo(i); - dataBlock.setData(pduUserData + dataOffset); - dataBlock.setLastBlock((i + 1U) == blocksToFollow); - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", FNE OSP, block %u, fmt = $%02X, lastBlock = %u", - (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? dataBlock.getSerialNo() : i, dataBlock.getFormat(), - dataBlock.getLastBlock()); - } - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - dataBlock.encode(block); - writeNetwork(netDataBlockCnt, block, P25_PDU_FEC_LENGTH_BYTES, dataBlock.getLastBlock()); - netDataBlockCnt++; - - dataOffset += (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; - } - } + uint32_t bitLength = 0U; + m_netAssembler->assemble(dataHeader, extendedAddress, auxiliaryES, pduUserData, &bitLength); } /* Updates the processor by the passed number of milliseconds. */ @@ -1438,22 +726,11 @@ void Data::sndcpReset(uint32_t llId, bool callTerm) Data::Data(Control* p25, bool dumpPDUData, bool repeatPDU, bool debug, bool verbose) : m_p25(p25), m_prevRfState(RS_RF_LISTENING), - m_rfData(nullptr), - m_rfDataHeader(), - m_rfExtendedAddress(false), - m_rfAuxiliaryES(false), - m_rfDataBlockCnt(0U), + m_rfAssembler(nullptr), m_rfPDU(nullptr), m_rfPDUCount(0U), m_rfPDUBits(0U), - m_netData(nullptr), - m_netDataHeader(), - m_netExtendedAddress(false), - m_netAuxiliaryES(false), - m_netDataOffset(0U), - m_netDataBlockCnt(0U), - m_netPDU(nullptr), - m_netPDUCount(0U), + m_netAssembler(nullptr), m_retryPDUData(nullptr), m_retryPDUBitLength(0U), m_retryCount(0U), @@ -1471,15 +748,18 @@ Data::Data(Control* p25, bool dumpPDUData, bool repeatPDU, bool debug, bool verb m_verbose(verbose), m_debug(debug) { - m_rfData = new data::DataBlock[P25_MAX_PDU_BLOCKS]; + data::Assembler::setVerbose(verbose); + data::Assembler::setDumpPDUData(dumpPDUData); + + m_rfAssembler = new Assembler(); + m_netAssembler = new Assembler(); m_rfPDU = new uint8_t[P25_PDU_FRAME_LENGTH_BYTES + 2U]; ::memset(m_rfPDU, 0x00U, P25_PDU_FRAME_LENGTH_BYTES + 2U); - m_netData = new data::DataBlock[P25_MAX_PDU_BLOCKS]; - - m_netPDU = new uint8_t[P25_PDU_FRAME_LENGTH_BYTES + 2U]; - ::memset(m_netPDU, 0x00U, P25_PDU_FRAME_LENGTH_BYTES + 2U); + m_netAssembler->setBlockWriter([=](const void* userContext, const uint8_t currentBlock, const uint8_t *data, uint32_t len, bool lastBlock) { + writeNetwork(currentBlock, data, len, lastBlock); + }); m_rfPduUserData = new uint8_t[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; ::memset(m_rfPduUserData, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); @@ -1497,10 +777,10 @@ Data::Data(Control* p25, bool dumpPDUData, bool repeatPDU, bool debug, bool verb Data::~Data() { - delete[] m_rfData; - delete[] m_netData; + delete m_rfAssembler; + delete m_netAssembler; + delete[] m_rfPDU; - delete[] m_netPDU; if (m_retryPDUData != nullptr) delete m_retryPDUData; @@ -1551,7 +831,7 @@ bool Data::processConvDataReg(const uint8_t* pduUserData) } // acknowledge - writeRF_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, m_rfDataHeader.getNs(), llId, false); + writeRF_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, m_rfAssembler->dataHeader.getNs(), llId, false); if (hasLLIdFNEReg(llId)) { // remove dynamic FNE registration table entry @@ -1590,7 +870,7 @@ bool Data::processSNDCPControl(const uint8_t* pduUserData) return false; } - uint32_t llId = m_rfDataHeader.getLLId(); + uint32_t llId = m_rfAssembler->dataHeader.getLLId(); switch (packet->getPDUType()) { case SNDCP_PDUType::ACT_TDS_CTX: @@ -1609,7 +889,7 @@ bool Data::processSNDCPControl(const uint8_t* pduUserData) rspHeader.setAckNeeded(true); rspHeader.setOutbound(true); rspHeader.setSAP(PDUSAP::SNDCP_CTRL_DATA); - rspHeader.setNs(m_rfDataHeader.getNs()); + rspHeader.setNs(m_rfAssembler->dataHeader.getNs()); rspHeader.setLLId(llId); rspHeader.setBlocksToFollow(1U); @@ -1704,7 +984,7 @@ bool Data::processSNDCPControl(const uint8_t* pduUserData) isp->getDeactType()); } - writeRF_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, m_rfDataHeader.getNs(), llId, false); + writeRF_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, m_rfAssembler->dataHeader.getNs(), llId, false); sndcpReset(llId, true); } break; @@ -1722,7 +1002,7 @@ bool Data::processSNDCPControl(const uint8_t* pduUserData) /* Write data processed from RF to the network. */ -void Data::writeNetwork(const uint8_t currentBlock, const uint8_t *data, uint32_t len, bool lastBlock) +void Data::writeNetwork(const uint8_t currentBlock, const uint8_t* data, uint32_t len, bool lastBlock) { assert(data != nullptr); @@ -1732,7 +1012,7 @@ void Data::writeNetwork(const uint8_t currentBlock, const uint8_t *data, uint32_ if (m_p25->m_rfTimeout.isRunning() && m_p25->m_rfTimeout.hasExpired()) return; - m_p25->m_network->writeP25PDU(m_rfDataHeader, currentBlock, data, len, lastBlock); + m_p25->m_network->writeP25PDU(m_rfAssembler->dataHeader, currentBlock, data, len, lastBlock); } /* Helper to write a P25 PDU packet. */ @@ -1800,290 +1080,18 @@ void Data::writeRF_PDU(const uint8_t* pdu, uint32_t bitLength, bool imm, bool ac void Data::writeNet_PDU_Buffered() { - uint32_t bitLength = ((m_netDataHeader.getBlocksToFollow() + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; - if (m_netDataHeader.getPadLength() > 0U) - bitLength += (m_netDataHeader.getPadLength() * 8U); - - uint32_t offset = P25_PREAMBLE_LENGTH_BITS; - - DECLARE_UINT8_ARRAY(data, (bitLength / 8U) + 1U); - - uint8_t block[P25_PDU_FEC_LENGTH_BYTES]; - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - - uint32_t blocksToFollow = m_netDataHeader.getBlocksToFollow(); - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", - m_netDataHeader.getAckNeeded(), m_netDataHeader.getOutbound(), m_netDataHeader.getFormat(), m_netDataHeader.getMFId(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMessage(), - m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadLength(), m_netDataHeader.getNs(), m_netDataHeader.getFSN(), m_netDataHeader.getLastFragment(), - m_netDataHeader.getHeaderOffset(), bitLength, m_netDataHeader.getLLId()); - } - - // generate the PDU header and 1/2 rate Trellis - m_netDataHeader.encode(block); - Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); - offset += P25_PDU_FEC_LENGTH_BITS; - - if (blocksToFollow > 0U) { - uint32_t dataOffset = 0U; - - // if using extended addressing, generate the second PDU header - if ((m_netDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (m_netDataHeader.getSAP() == PDUSAP::EXT_ADDR) && m_netExtendedAddress) { - m_netDataHeader.encodeExtAddr(m_netPduUserData, true); - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - m_netDataHeader.encodeExtAddr(block); - Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); - - bitLength += P25_PDU_FEC_LENGTH_BITS; - offset += P25_PDU_FEC_LENGTH_BITS; - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - - blocksToFollow--; - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", - m_netDataHeader.getEXSAP(), m_netDataHeader.getSrcLLId()); - } - } - - // if using auxiliary ES, generate the second PDU header - if ((m_netDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (m_netDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_netDataHeader.getSAP() == PDUSAP::ENC_KMM) && - m_netAuxiliaryES) { - m_netDataHeader.encodeAuxES(m_netPduUserData, true); - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - m_netDataHeader.encodeAuxES(block); - writeNetwork(1U, block, P25_PDU_FEC_LENGTH_BYTES, false); - - bitLength += P25_PDU_FEC_LENGTH_BITS; - - offset += P25_PDU_FEC_LENGTH_BITS; - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - - blocksToFollow--; - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", - m_netDataHeader.getAlgId(), m_netDataHeader.getKId()); - - if (m_netDataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - m_netDataHeader.getMI(mi); - - LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - } - - // are we processing extended address data from the first block? - if ((m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) && (m_netDataHeader.getSAP() == PDUSAP::EXT_ADDR) && m_netExtendedAddress) { - m_netDataHeader.encodeExtAddr(m_netPduUserData); - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", - m_netDataHeader.getEXSAP(), m_netDataHeader.getSrcLLId()); - } - } - - // are we processing auxiliary ES data from the first block? - if ((m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) && (m_netDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_netDataHeader.getSAP() == PDUSAP::ENC_KMM) && - m_netAuxiliaryES) { - m_netDataHeader.encodeAuxES(m_netPduUserData); - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", - m_netDataHeader.getAlgId(), m_netDataHeader.getKId()); - - if (m_netDataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - m_netDataHeader.getMI(mi); - - LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - } - - edac::CRC::addCRC32(m_netPduUserData, m_netPduUserDataLength); - - if (m_dumpPDUData) { - Utils::dump("P25, OSP PDU User Data (NET)", m_netPduUserData, m_netPduUserDataLength); - } - - // generate the PDU data - for (uint32_t i = 0U; i < blocksToFollow; i++) { - m_netData[i].setFormat(m_netDataHeader); - m_netData[i].setSerialNo(i); - m_netData[i].setData(m_netPduUserData + dataOffset); - - if (m_verbose) { - LogInfoEx(LOG_NET, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u", - (m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_netData[i].getSerialNo() : i, m_netData[i].getFormat(), - m_netData[i].getLastBlock()); - } - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - m_netData[i].encode(block); - Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); - - offset += P25_PDU_FEC_LENGTH_BITS; - dataOffset += (m_netDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; - } - } - - writeRF_PDU(data, bitLength); + uint32_t bitLength = 0U; + UInt8Array data = m_rfAssembler->assemble(m_netAssembler->dataHeader, m_netAssembler->getExtendedAddress(), m_netAssembler->getAuxiliaryES(), m_netPduUserData, &bitLength); + writeRF_PDU(data.get(), bitLength); } /* Helper to re-write a received P25 PDU packet. */ void Data::writeRF_PDU_Buffered() { - uint32_t bitLength = ((m_rfDataHeader.getBlocksToFollow() + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; - if (m_rfDataHeader.getPadLength() > 0U) - bitLength += (m_rfDataHeader.getPadLength() * 8U); - - uint32_t offset = P25_PREAMBLE_LENGTH_BITS; - - DECLARE_UINT8_ARRAY(data, (bitLength / 8U) + 1U); - - uint8_t block[P25_PDU_FEC_LENGTH_BYTES]; - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - - uint32_t blocksToFollow = m_rfDataHeader.getBlocksToFollow(); - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, bitLength = %u, llId = %u", - m_rfDataHeader.getAckNeeded(), m_rfDataHeader.getOutbound(), m_rfDataHeader.getFormat(), m_rfDataHeader.getMFId(), m_rfDataHeader.getSAP(), m_rfDataHeader.getFullMessage(), - m_rfDataHeader.getBlocksToFollow(), m_rfDataHeader.getPadLength(), m_rfDataHeader.getNs(), m_rfDataHeader.getFSN(), m_rfDataHeader.getLastFragment(), - m_rfDataHeader.getHeaderOffset(), bitLength, m_rfDataHeader.getLLId()); - } - - // generate the PDU header and 1/2 rate Trellis - m_rfDataHeader.encode(block); - Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); - offset += P25_PDU_FEC_LENGTH_BITS; - - if (blocksToFollow > 0U) { - uint32_t dataOffset = 0U; - - // if using extended addressing, generate the second PDU header - if ((m_rfDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (m_rfDataHeader.getSAP() == PDUSAP::EXT_ADDR) && m_rfExtendedAddress) { - m_rfDataHeader.encodeExtAddr(m_rfPduUserData, true); - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - m_rfDataHeader.encodeExtAddr(block); - Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); - - bitLength += P25_PDU_FEC_LENGTH_BITS; - offset += P25_PDU_FEC_LENGTH_BITS; - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - - blocksToFollow--; - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", - m_rfDataHeader.getEXSAP(), m_rfDataHeader.getSrcLLId()); - } - } - - // if using auxiliary ES, generate the second PDU header - if ((m_rfDataHeader.getFormat() == PDUFormatType::UNCONFIRMED) && (m_rfDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_rfDataHeader.getSAP() == PDUSAP::ENC_KMM) && - m_rfAuxiliaryES) { - m_rfDataHeader.encodeAuxES(m_rfPduUserData, true); - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - m_rfDataHeader.encodeAuxES(block); - writeNetwork(1U, block, P25_PDU_FEC_LENGTH_BYTES, false); - - bitLength += P25_PDU_FEC_LENGTH_BITS; - offset += P25_PDU_FEC_LENGTH_BITS; - dataOffset += P25_PDU_HEADER_LENGTH_BYTES; - - blocksToFollow--; - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", - m_rfDataHeader.getAlgId(), m_rfDataHeader.getKId()); - - if (m_rfDataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - m_rfDataHeader.getMI(mi); - - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - } - - // are we processing extended address data from the first block? - if ((m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) && (m_rfDataHeader.getSAP() == PDUSAP::EXT_ADDR) && m_rfExtendedAddress) { - m_rfDataHeader.encodeExtAddr(m_rfPduUserData); - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, extended address, sap = $%02X, srcLlId = %u", - m_rfDataHeader.getEXSAP(), m_rfDataHeader.getSrcLLId()); - } - } - - // are we processing auxiliary ES data from the first block? - if ((m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) && (m_rfDataHeader.getSAP() == PDUSAP::ENC_USER_DATA || m_rfDataHeader.getSAP() == PDUSAP::ENC_KMM) && - m_netAuxiliaryES) { - m_rfDataHeader.encodeAuxES(m_rfPduUserData); - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, auxiliary ES, algId = $%02X, kId = $%04X", - m_rfDataHeader.getAlgId(), m_rfDataHeader.getKId()); - - if (m_rfDataHeader.getAlgId() != ALGO_UNENCRYPT) { - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - m_rfDataHeader.getMI(mi); - - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, Enc Sync, MI = %02X %02X %02X %02X %02X %02X %02X %02X %02X", - mi[0U], mi[1U], mi[2U], mi[3U], mi[4U], mi[5U], mi[6U], mi[7U], mi[8U]); - } - } - } - - edac::CRC::addCRC32(m_rfPduUserData, m_rfPduUserDataLength); - - if (m_dumpPDUData) { - Utils::dump("P25, OSP PDU User Data (RF)", m_rfPduUserData, m_rfPduUserDataLength); - } - - // generate the PDU data - for (uint32_t i = 0U; i < blocksToFollow; i++) { - m_rfData[i].setFormat(m_rfDataHeader); - m_rfData[i].setSerialNo(i); - m_rfData[i].setData(m_rfPduUserData + dataOffset); - m_rfData[i].setLastBlock((i + 1U) == blocksToFollow); - - if (m_verbose) { - LogInfoEx(LOG_RF, P25_PDU_STR ", OSP, block %u, fmt = $%02X, lastBlock = %u", - (m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? m_rfData[i].getSerialNo() : i, m_rfData[i].getFormat(), - m_rfData[i].getLastBlock()); - } - - ::memset(block, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - m_rfData[i].encode(block); - Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); - - offset += P25_PDU_FEC_LENGTH_BITS; - dataOffset += (m_rfDataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; - } - } - - writeRF_PDU(data, bitLength); + uint32_t bitLength = 0U; + UInt8Array data = m_rfAssembler->assemble(m_rfAssembler->dataHeader, m_rfAssembler->getExtendedAddress(), m_rfAssembler->getAuxiliaryES(), m_rfPduUserData, &bitLength); + writeRF_PDU(data.get(), bitLength); } /* Helper to write a PDU registration response. */ @@ -2098,7 +1106,7 @@ void Data::writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, uint32_t ipA DataHeader rspHeader = DataHeader(); rspHeader.setFormat(PDUFormatType::CONFIRMED); - rspHeader.setMFId(m_rfDataHeader.getMFId()); + rspHeader.setMFId(m_rfAssembler->dataHeader.getMFId()); rspHeader.setAckNeeded(true); rspHeader.setOutbound(true); rspHeader.setSAP(PDUSAP::CONV_DATA_REG); @@ -2117,7 +1125,8 @@ void Data::writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, uint32_t ipA pduUserData[11U] = (ipAddr >> 0) & 0xFFU; } - Utils::dump(1U, "Data::writeRF_PDU_Reg_Response() pduUserData", pduUserData, 12U); + if (m_dumpPDUData) + Utils::dump(1U, "P25, PDU Registration Response", pduUserData, 12U); rspHeader.calculateLength(12U); writeRF_PDU_User(rspHeader, false, false, pduUserData); @@ -2140,7 +1149,7 @@ void Data::writeRF_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint8_t a DataHeader rspHeader = DataHeader(); rspHeader.setFormat(PDUFormatType::RSP); - rspHeader.setMFId(m_rfDataHeader.getMFId()); + rspHeader.setMFId(m_rfAssembler->dataHeader.getMFId()); rspHeader.setOutbound(true); rspHeader.setResponseClass(ackClass); rspHeader.setResponseType(ackType); @@ -2157,7 +1166,7 @@ void Data::writeRF_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint8_t a rspHeader.setBlocksToFollow(0U); - // Generate the PDU header and 1/2 rate Trellis + // generate the PDU header and 1/2 rate Trellis rspHeader.encode(block); Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); diff --git a/src/host/p25/packet/Data.h b/src/host/p25/packet/Data.h index a14451b5..9d125dc0 100644 --- a/src/host/p25/packet/Data.h +++ b/src/host/p25/packet/Data.h @@ -21,6 +21,7 @@ #include "common/p25/data/DataBlock.h" #include "common/p25/data/DataHeader.h" #include "common/p25/data/LowSpeedData.h" +#include "common/p25/data/Assembler.h" #include "common/p25/lc/LC.h" #include "common/Timer.h" #include "p25/Control.h" @@ -130,23 +131,11 @@ namespace p25 RPT_RF_STATE m_prevRfState; - data::DataBlock* m_rfData; - data::DataHeader m_rfDataHeader; - bool m_rfExtendedAddress; - bool m_rfAuxiliaryES; - uint8_t m_rfDataBlockCnt; + data::Assembler* m_rfAssembler; uint8_t* m_rfPDU; uint32_t m_rfPDUCount; uint32_t m_rfPDUBits; - - data::DataBlock* m_netData; - data::DataHeader m_netDataHeader; - bool m_netExtendedAddress; - bool m_netAuxiliaryES; - uint32_t m_netDataOffset; - uint8_t m_netDataBlockCnt; - uint8_t* m_netPDU; - uint32_t m_netPDUCount; + data::Assembler* m_netAssembler; uint8_t* m_retryPDUData; uint32_t m_retryPDUBitLength; diff --git a/tests/p25/PDU_Confirmed_AuxES_Test.cpp b/tests/p25/PDU_Confirmed_AuxES_Test.cpp new file mode 100644 index 00000000..f65878a4 --- /dev/null +++ b/tests/p25/PDU_Confirmed_AuxES_Test.cpp @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/data/Assembler.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::data; + +#include +#include +#include + +TEST_CASE("PDU_Confirmed_AuxES_Test", "[P25 PDU Confirmed Aux ES Test]") { + SECTION("P25_PDU_Confirmed_AuxES_Test") { + bool failed = false; + + INFO("P25 PDU Confirmed Aux ES Test"); + + srand((unsigned int)time(NULL)); + + g_logDisplayLevel = 1U; + + // test PDU data + uint32_t testLength = 30U; + uint8_t testPDUSource[] = + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + }; + + uint8_t encryptMI[] = + { + 0x70, 0x30, 0xF1, 0xF7, 0x65, 0x69, 0x26, 0x67 + }; + + data::Assembler::setVerbose(true); + data::Assembler::setDumpPDUData(true); + + Assembler assembler = Assembler(); + + Utils::dump(2U, "P25_PDU_Confirmed_AuxES_Test, Test Source", testPDUSource, 30U); + + DataHeader dataHeader = DataHeader(); + dataHeader.setFormat(PDUFormatType::CONFIRMED); + dataHeader.setMFId(MFG_STANDARD); + dataHeader.setAckNeeded(true); + dataHeader.setOutbound(true); + dataHeader.setSAP(PDUSAP::ENC_USER_DATA); + dataHeader.setLLId(0x12345U); + dataHeader.setFullMessage(true); + dataHeader.setBlocksToFollow(1U); + + dataHeader.setEXSAP(PDUSAP::USER_DATA); + + dataHeader.setMI(encryptMI); + dataHeader.setAlgId(ALGO_AES_256); + dataHeader.setKId(0x2F62U); + + dataHeader.calculateLength(testLength); + + /* + ** self-sanity check the assembler chain + */ + + uint32_t bitLength = 0U; + UInt8Array ret = assembler.assemble(dataHeader, false, true, testPDUSource, &bitLength); + + LogInfoEx("T", "P25_PDU_Confirmed_AuxES_Test, Assembled Bit Length = %u (%u)", bitLength, bitLength / 8); + Utils::dump(2U, "P25_PDU_Confirmed_AuxES_Test, Assembled PDU", ret.get(), bitLength / 8); + + if (ret == nullptr) + failed = true; + + if (!failed) { + uint8_t buffer[P25_PDU_FRAME_LENGTH_BYTES]; + ::memset(buffer, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); + + // for the purposes of our test we strip the pad bit length from the bit length + bitLength -= dataHeader.getPadLength() * 8U; + + uint32_t blockCnt = 0U; + for (uint32_t i = P25_PREAMBLE_LENGTH_BITS; i < bitLength; i += P25_PDU_FEC_LENGTH_BITS) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(ret.get(), buffer, i, P25_PDU_FEC_LENGTH_BITS); + + LogInfoEx("T", "P25_PDU_Confirmed_AuxES_Test, i = %u", i); + Utils::dump(2U, "buffer", buffer, P25_PDU_FEC_LENGTH_BYTES); + + bool ret = false; + if (blockCnt == 0U) + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES, true); + else + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES); + if (!ret) { + failed = true; + ::LogError("T", "P25_PDU_Confirmed_AuxES_Test, PDU Disassemble, block %u", blockCnt); + } + + blockCnt++; + } + + if (assembler.getComplete()) { + uint8_t pduUserData2[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + uint32_t pduUserDataLength = assembler.getUserDataLength(); + assembler.getUserData(pduUserData2); + + for (uint32_t i = 0; i < pduUserDataLength - 4U; i++) { + if (pduUserData2[i] != testPDUSource[i]) { + ::LogError("T", "P25_PDU_Confirmed_AuxES_Test, INVALID AT IDX %d", i); + failed = true; + } + } + } + } + + REQUIRE(failed==false); + } +} diff --git a/tests/p25/PDU_Confirmed_ConvReg_Test.cpp b/tests/p25/PDU_Confirmed_ConvReg_Test.cpp new file mode 100644 index 00000000..64a40374 --- /dev/null +++ b/tests/p25/PDU_Confirmed_ConvReg_Test.cpp @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/data/Assembler.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::data; + +#include +#include +#include + +TEST_CASE("PDU_Confirmed_ConvReg_Test", "[P25 PDU Confirmed Conv Reg Test]") { + SECTION("P25_PDU_Confirmed_ConvReg_Test") { + bool failed = false; + + INFO("P25 PDU Confirmed Conv Reg Test"); + + srand((unsigned int)time(NULL)); + + g_logDisplayLevel = 1U; + + // data block + uint8_t dataBlock[] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x1C, + 0x2A, 0x6E, 0x12, 0x2A, 0x20, 0x67, 0x0F, 0x79, 0x29, 0x2C, 0x70, 0x9E, 0x0B, 0x32, 0x21, 0x23, + 0x3D, 0x22, 0xED, 0x8C, 0x29, 0x26, 0x50, + + 0x26, 0xE0, 0xB2, 0x22, 0x22, 0xB0, 0x72, 0x20, 0xE2, 0x22, 0x22, 0x59, 0x11, 0xE3, 0x92, 0x22, + 0x22, 0x92, 0x73, 0x21, 0x52, 0x22, 0x22, 0x1F, 0x30 + }; + + // expected PDU user data + uint8_t expectedUserData[] = + { + 0x00, 0x54, 0x36, 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC9, 0x9D, 0x42, 0x56 + }; + + data::Assembler::setVerbose(true); + data::Assembler::setDumpPDUData(true); + + Assembler assembler = Assembler(); + + uint8_t pduUserData[P25_MAX_PDU_BLOCKS * P25_PDU_UNCONFIRMED_LENGTH_BYTES]; + ::memset(pduUserData, 0x00U, P25_MAX_PDU_BLOCKS * P25_PDU_UNCONFIRMED_LENGTH_BYTES); + + /* + ** self-sanity check the assembler chain + */ + + DataHeader rspHeader = DataHeader(); + rspHeader.setFormat(PDUFormatType::CONFIRMED); + rspHeader.setMFId(assembler.dataHeader.getMFId()); + rspHeader.setAckNeeded(true); + rspHeader.setOutbound(true); + rspHeader.setSAP(PDUSAP::CONV_DATA_REG); + rspHeader.setSynchronize(true); + rspHeader.setLLId(0x12345U); + rspHeader.setBlocksToFollow(1U); + + uint32_t regType = PDURegType::ACCEPT; + uint32_t llId = 0x12345U; + uint32_t ipAddr = 0x7F000001; + + pduUserData[0U] = ((regType & 0x0FU) << 4); // Registration Type & Options + pduUserData[1U] = (llId >> 16) & 0xFFU; // Logical Link ID + pduUserData[2U] = (llId >> 8) & 0xFFU; + pduUserData[3U] = (llId >> 0) & 0xFFU; + if (regType == PDURegType::ACCEPT) { + pduUserData[8U] = (ipAddr >> 24) & 0xFFU; // IP Address + pduUserData[9U] = (ipAddr >> 16) & 0xFFU; + pduUserData[10U] = (ipAddr >> 8) & 0xFFU; + pduUserData[11U] = (ipAddr >> 0) & 0xFFU; + } + + Utils::dump(2U, "P25, PDU Registration Response", pduUserData, 12U); + + rspHeader.calculateLength(12U); + uint32_t bitLength = 0U; + UInt8Array ret = assembler.assemble(rspHeader, false, false, pduUserData, &bitLength); + + LogInfoEx("T", "P25_PDU_Confirmed_Large_Test, Assembled Bit Length = %u (%u)", bitLength, bitLength / 8); + Utils::dump(2U, "P25_PDU_Confirmed_Test, Assembled PDU", ret.get(), bitLength / 8); + + if (ret == nullptr) + failed = true; + + if (!failed) { + uint8_t buffer[P25_PDU_FRAME_LENGTH_BYTES]; + ::memset(buffer, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); + + uint32_t blockCnt = 0U; + for (uint32_t i = P25_PREAMBLE_LENGTH_BITS; i < bitLength; i += P25_PDU_FEC_LENGTH_BITS) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(ret.get(), buffer, i, P25_PDU_FEC_LENGTH_BITS); + + Utils::dump(2U, "P25_PDU_Confirmed_Test, Block", buffer, P25_PDU_FEC_LENGTH_BYTES); + + bool ret = false; + if (blockCnt == 0U) + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES, true); + else + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES); + if (!ret) { + failed = true; + ::LogError("T", "P25_PDU_Confirmed_Test, PDU Disassemble, block %u", blockCnt); + } + + blockCnt++; + } + + if (assembler.getComplete()) { + uint8_t pduUserData2[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + uint32_t pduUserDataLength = assembler.getUserDataLength() - 4U; + assembler.getUserData(pduUserData2); + + for (uint32_t i = 0; i < pduUserDataLength; i++) { + if (pduUserData2[i] != pduUserData[i]) { + ::LogError("T", "P25_PDU_Confirmed_Test, INVALID AT IDX %d", i); + failed = true; + } + } + } + } + + /* + ** test disassembly against the static test data block + */ + + if (!failed) { + uint8_t buffer[P25_PDU_FRAME_LENGTH_BYTES]; + ::memset(buffer, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); + + uint32_t blockCnt = 0U; + for (uint32_t i = P25_PREAMBLE_LENGTH_BYTES; i < 64U; i += P25_PDU_FEC_LENGTH_BYTES) { + LogInfoEx("T", "i = %u", i); + + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + ::memcpy(buffer, dataBlock + i, P25_PDU_FEC_LENGTH_BYTES); + + Utils::dump(2U, "P25_PDU_Confirmed_Test, Block", buffer, P25_PDU_FEC_LENGTH_BYTES); + + bool ret = false; + if (blockCnt == 0U) + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES, true); + else + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES); + if (!ret) { + failed = true; + ::LogError("T", "P25_PDU_Confirmed_Test, PDU Disassemble, block %u", blockCnt); + } + + blockCnt++; + } + + if (assembler.getComplete()) { + uint8_t pduUserData2[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + uint32_t pduUserDataLength = assembler.getUserDataLength() - 4U; + assembler.getUserData(pduUserData2); + + for (uint32_t i = 0; i < pduUserDataLength; i++) { + if (pduUserData2[i] != expectedUserData[i]) { + ::LogError("T", "P25_PDU_Confirmed_Test, INVALID AT IDX %d", i); + failed = true; + } + } + } + } + + REQUIRE(failed==false); + } +} diff --git a/tests/p25/PDU_Confirmed_ExtAddr_Test.cpp b/tests/p25/PDU_Confirmed_ExtAddr_Test.cpp new file mode 100644 index 00000000..669e173d --- /dev/null +++ b/tests/p25/PDU_Confirmed_ExtAddr_Test.cpp @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/data/Assembler.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::data; + +#include +#include +#include + +TEST_CASE("PDU_Confirmed_ExtAddr_Test", "[P25 PDU Confirmed Ext Addr Test]") { + SECTION("P25_PDU_Confirmed_ExtAddr_Test") { + bool failed = false; + + INFO("P25 PDU Confirmed Ext Addr Test"); + + srand((unsigned int)time(NULL)); + + g_logDisplayLevel = 1U; + + // test PDU data + uint32_t testLength = 30U; + uint8_t testPDUSource[] = + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + }; + + data::Assembler::setVerbose(true); + data::Assembler::setDumpPDUData(true); + + Assembler assembler = Assembler(); + + Utils::dump(2U, "P25_PDU_Confirmed_ExtAddr_Test, Test Source", testPDUSource, 30U); + + DataHeader dataHeader = DataHeader(); + dataHeader.setFormat(PDUFormatType::CONFIRMED); + dataHeader.setMFId(MFG_STANDARD); + dataHeader.setAckNeeded(true); + dataHeader.setOutbound(true); + dataHeader.setSAP(PDUSAP::EXT_ADDR); + dataHeader.setLLId(0x12345U); + dataHeader.setFullMessage(true); + dataHeader.setBlocksToFollow(1U); + + dataHeader.setEXSAP(PDUSAP::USER_DATA); + dataHeader.setSrcLLId(0x54321U); + + dataHeader.calculateLength(testLength); + + /* + ** self-sanity check the assembler chain + */ + + uint32_t bitLength = 0U; + UInt8Array ret = assembler.assemble(dataHeader, true, false, testPDUSource, &bitLength); + + LogInfoEx("T", "P25_PDU_Confirmed_ExtAddr_Test, Assembled Bit Length = %u (%u)", bitLength, bitLength / 8); + Utils::dump(2U, "P25_PDU_Confirmed_ExtAddr_Test, Assembled PDU", ret.get(), bitLength / 8); + + if (ret == nullptr) + failed = true; + + if (!failed) { + uint8_t buffer[P25_PDU_FRAME_LENGTH_BYTES]; + ::memset(buffer, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); + + // for the purposes of our test we strip the pad bit length from the bit length + bitLength -= dataHeader.getPadLength() * 8U; + + uint32_t blockCnt = 0U; + for (uint32_t i = P25_PREAMBLE_LENGTH_BITS; i < bitLength; i += P25_PDU_FEC_LENGTH_BITS) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(ret.get(), buffer, i, P25_PDU_FEC_LENGTH_BITS); + + LogInfoEx("T", "P25_PDU_Confirmed_ExtAddr_Test, i = %u", i); + Utils::dump(2U, "buffer", buffer, P25_PDU_FEC_LENGTH_BYTES); + + bool ret = false; + if (blockCnt == 0U) + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES, true); + else + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES); + if (!ret) { + failed = true; + ::LogError("T", "P25_PDU_Confirmed_ExtAddr_Test, PDU Disassemble, block %u", blockCnt); + } + + blockCnt++; + } + + if (assembler.getComplete()) { + uint8_t pduUserData2[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + uint32_t pduUserDataLength = assembler.getUserDataLength(); + assembler.getUserData(pduUserData2); + + for (uint32_t i = 0; i < pduUserDataLength - 4U; i++) { + if (pduUserData2[i] != testPDUSource[i]) { + ::LogError("T", "P25_PDU_Confirmed_ExtAddr_Test, INVALID AT IDX %d", i); + failed = true; + } + } + } + } + + REQUIRE(failed==false); + } +} diff --git a/tests/p25/PDU_Confirmed_Large_Test.cpp b/tests/p25/PDU_Confirmed_Large_Test.cpp new file mode 100644 index 00000000..0cf58c96 --- /dev/null +++ b/tests/p25/PDU_Confirmed_Large_Test.cpp @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/data/Assembler.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::data; + +#include +#include +#include + +TEST_CASE("PDU_Confirmed_Large_Test", "[P25 PDU Confirmed Large Test]") { + SECTION("P25_PDU_Confirmed_Large_Test") { + bool failed = false; + + INFO("P25 PDU Confirmed Large Test"); + + srand((unsigned int)time(NULL)); + + g_logDisplayLevel = 1U; + + // test PDU data + uint32_t testLength = 120U; + uint8_t testPDUSource[] = + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + 0x20, 0x54, 0x45, 0x53, 0x54, 0x54, 0x45, 0x53, 0x54, 0x54, 0x45, 0x53, 0x54, 0x20, 0x20, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, + 0x20, 0x54, 0x45, 0x53, 0x54, 0x54, 0x45, 0x53, 0x54, 0x54, 0x45, 0x53, 0x54, 0x20, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21 + }; + + data::Assembler::setVerbose(true); + data::Assembler::setDumpPDUData(true); + + Assembler assembler = Assembler(); + + Utils::dump(2U, "P25_PDU_Confirmed_Large_Test, Test Source", testPDUSource, 120U); + + DataHeader dataHeader = DataHeader(); + dataHeader.setFormat(PDUFormatType::CONFIRMED); + dataHeader.setMFId(MFG_STANDARD); + dataHeader.setAckNeeded(false); + dataHeader.setOutbound(true); + dataHeader.setSAP(PDUSAP::USER_DATA); + dataHeader.setLLId(0x12345U); + dataHeader.setFullMessage(true); + dataHeader.setBlocksToFollow(1U); + + dataHeader.calculateLength(testLength); + + /* + ** self-sanity check the assembler chain + */ + + uint32_t bitLength = 0U; + UInt8Array ret = assembler.assemble(dataHeader, false, false, testPDUSource, &bitLength); + + LogInfoEx("T", "P25_PDU_Confirmed_Large_Test, Assembled Bit Length = %u (%u)", bitLength, bitLength / 8); + Utils::dump(2U, "P25_PDU_Confirmed_Large_Test, Assembled PDU", ret.get(), bitLength / 8); + + if (ret == nullptr) + failed = true; + + if (!failed) { + uint8_t buffer[P25_PDU_FRAME_LENGTH_BYTES]; + ::memset(buffer, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); + + // for the purposes of our test we strip the pad bit length from the bit length + bitLength -= dataHeader.getPadLength() * 8U; + + uint32_t blockCnt = 0U; + for (uint32_t i = P25_PREAMBLE_LENGTH_BITS; i < bitLength; i += P25_PDU_FEC_LENGTH_BITS) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(ret.get(), buffer, i, P25_PDU_FEC_LENGTH_BITS); + + LogInfoEx("T", "P25_PDU_Confirmed_Large_Test, i = %u", i); + Utils::dump(2U, "buffer", buffer, P25_PDU_FEC_LENGTH_BYTES); + + bool ret = false; + if (blockCnt == 0U) + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES, true); + else + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES); + if (!ret) { + failed = true; + ::LogError("T", "P25_PDU_Confirmed_Large_Test, PDU Disassemble, block %u", blockCnt); + } + + blockCnt++; + } + + if (assembler.getComplete()) { + uint8_t pduUserData2[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + uint32_t pduUserDataLength = assembler.getUserDataLength(); + assembler.getUserData(pduUserData2); + + for (uint32_t i = 0; i < pduUserDataLength - 4U; i++) { + if (pduUserData2[i] != testPDUSource[i]) { + ::LogError("T", "P25_PDU_Confirmed_Large_Test, INVALID AT IDX %d", i); + failed = true; + } + } + } + } + + REQUIRE(failed==false); + } +} diff --git a/tests/p25/PDU_Confirmed_Small_Test.cpp b/tests/p25/PDU_Confirmed_Small_Test.cpp new file mode 100644 index 00000000..8b54e0ce --- /dev/null +++ b/tests/p25/PDU_Confirmed_Small_Test.cpp @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/data/Assembler.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::data; + +#include +#include +#include + +TEST_CASE("PDU_Confirmed_Small_Test", "[P25 PDU Confirmed Small Test]") { + SECTION("P25_PDU_Confirmed_Small_Test") { + bool failed = false; + + INFO("P25 PDU Confirmed Small Test"); + + srand((unsigned int)time(NULL)); + + g_logDisplayLevel = 1U; + + // test PDU data + uint32_t testLength = 30U; + uint8_t testPDUSource[] = + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + }; + + data::Assembler::setVerbose(true); + data::Assembler::setDumpPDUData(true); + + Assembler assembler = Assembler(); + + Utils::dump(2U, "PDU_Confirmed_Small_Test, Test Source", testPDUSource, 30U); + + DataHeader dataHeader = DataHeader(); + dataHeader.setFormat(PDUFormatType::CONFIRMED); + dataHeader.setMFId(MFG_STANDARD); + dataHeader.setAckNeeded(true); + dataHeader.setOutbound(true); + dataHeader.setSAP(PDUSAP::USER_DATA); + dataHeader.setLLId(0x12345U); + dataHeader.setFullMessage(true); + dataHeader.setBlocksToFollow(1U); + + dataHeader.calculateLength(testLength); + + /* + ** self-sanity check the assembler chain + */ + + uint32_t bitLength = 0U; + UInt8Array ret = assembler.assemble(dataHeader, false, false, testPDUSource, &bitLength); + + LogInfoEx("T", "PDU_Confirmed_Small_Test, Assembled Bit Length = %u (%u)", bitLength, bitLength / 8); + Utils::dump(2U, "PDU_Confirmed_Small_Test, Assembled PDU", ret.get(), bitLength / 8); + + if (ret == nullptr) + failed = true; + + if (!failed) { + uint8_t buffer[P25_PDU_FRAME_LENGTH_BYTES]; + ::memset(buffer, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); + + // for the purposes of our test we strip the pad bit length from the bit length + bitLength -= dataHeader.getPadLength() * 8U; + + uint32_t blockCnt = 0U; + for (uint32_t i = P25_PREAMBLE_LENGTH_BITS; i < bitLength; i += P25_PDU_FEC_LENGTH_BITS) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(ret.get(), buffer, i, P25_PDU_FEC_LENGTH_BITS); + + LogInfoEx("T", "PDU_Confirmed_Small_Test, i = %u", i); + Utils::dump(2U, "buffer", buffer, P25_PDU_FEC_LENGTH_BYTES); + + bool ret = false; + if (blockCnt == 0U) + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES, true); + else + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES); + if (!ret) { + failed = true; + ::LogError("T", "PDU_Confirmed_Small_Test, PDU Disassemble, block %u", blockCnt); + } + + blockCnt++; + } + + if (assembler.getComplete()) { + uint8_t pduUserData2[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + uint32_t pduUserDataLength = assembler.getUserDataLength(); + assembler.getUserData(pduUserData2); + + for (uint32_t i = 0; i < pduUserDataLength - 4U; i++) { + if (pduUserData2[i] != testPDUSource[i]) { + ::LogError("T", "PDU_Confirmed_Small_Test, INVALID AT IDX %d", i); + failed = true; + } + } + } + } + + REQUIRE(failed==false); + } +} diff --git a/tests/p25/PDU_Unconfirmed_AuxES_Test.cpp b/tests/p25/PDU_Unconfirmed_AuxES_Test.cpp new file mode 100644 index 00000000..f4bac2a9 --- /dev/null +++ b/tests/p25/PDU_Unconfirmed_AuxES_Test.cpp @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/data/Assembler.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::data; + +#include +#include +#include + +TEST_CASE("PDU_Unconfirmed_AuxES_Test", "[P25 PDU Unconfirmed Aux ES Test]") { + SECTION("P25_PDU_Unconfirmed_AuxES_Test") { + bool failed = false; + + INFO("P25 PDU Unconfirmed Aux ES Test"); + + srand((unsigned int)time(NULL)); + + g_logDisplayLevel = 1U; + + // test PDU data + uint32_t testLength = 30U; + uint8_t testPDUSource[] = + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + }; + + uint8_t encryptMI[] = + { + 0x70, 0x30, 0xF1, 0xF7, 0x65, 0x69, 0x26, 0x67 + }; + + data::Assembler::setVerbose(true); + data::Assembler::setDumpPDUData(true); + + Assembler assembler = Assembler(); + + Utils::dump(2U, "P25_PDU_Unconfirmed_AuxES_Test, Test Source", testPDUSource, 30U); + + DataHeader dataHeader = DataHeader(); + dataHeader.setFormat(PDUFormatType::UNCONFIRMED); + dataHeader.setMFId(MFG_STANDARD); + dataHeader.setAckNeeded(true); + dataHeader.setOutbound(true); + dataHeader.setSAP(PDUSAP::ENC_USER_DATA); + dataHeader.setLLId(0x12345U); + dataHeader.setFullMessage(true); + dataHeader.setBlocksToFollow(1U); + + dataHeader.setEXSAP(PDUSAP::USER_DATA); + + dataHeader.setMI(encryptMI); + dataHeader.setAlgId(ALGO_AES_256); + dataHeader.setKId(0x2F62U); + + dataHeader.calculateLength(testLength); + + /* + ** self-sanity check the assembler chain + */ + + uint32_t bitLength = 0U; + UInt8Array ret = assembler.assemble(dataHeader, false, true, testPDUSource, &bitLength); + + LogInfoEx("T", "P25_PDU_Unconfirmed_AuxES_Test, Assembled Bit Length = %u (%u)", bitLength, bitLength / 8); + Utils::dump(2U, "P25_PDU_Unconfirmed_AuxES_Test, Assembled PDU", ret.get(), bitLength / 8); + + if (ret == nullptr) + failed = true; + + if (!failed) { + uint8_t buffer[P25_PDU_FRAME_LENGTH_BYTES]; + ::memset(buffer, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); + + // for the purposes of our test we strip the pad bit length from the bit length + bitLength -= dataHeader.getPadLength() * 8U; + + uint32_t blockCnt = 0U; + for (uint32_t i = P25_PREAMBLE_LENGTH_BITS; i < bitLength; i += P25_PDU_FEC_LENGTH_BITS) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(ret.get(), buffer, i, P25_PDU_FEC_LENGTH_BITS); + + LogInfoEx("T", "P25_PDU_Unconfirmed_AuxES_Test, i = %u", i); + Utils::dump(2U, "buffer", buffer, P25_PDU_FEC_LENGTH_BYTES); + + bool ret = false; + if (blockCnt == 0U) + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES, true); + else + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES); + if (!ret) { + failed = true; + ::LogError("T", "P25_PDU_Unconfirmed_AuxES_Test, PDU Disassemble, block %u", blockCnt); + } + + blockCnt++; + } + + if (assembler.getComplete()) { + uint8_t pduUserData2[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + uint32_t pduUserDataLength = assembler.getUserDataLength(); + assembler.getUserData(pduUserData2); + + for (uint32_t i = 0; i < pduUserDataLength - 4U; i++) { + if (pduUserData2[i] != testPDUSource[i]) { + ::LogError("T", "P25_PDU_Unconfirmed_AuxES_Test, INVALID AT IDX %d", i); + failed = true; + } + } + } + } + + REQUIRE(failed==false); + } +} diff --git a/tests/p25/PDU_Unconfirmed_ExtAddr_Test.cpp b/tests/p25/PDU_Unconfirmed_ExtAddr_Test.cpp new file mode 100644 index 00000000..341859c2 --- /dev/null +++ b/tests/p25/PDU_Unconfirmed_ExtAddr_Test.cpp @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/data/Assembler.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::data; + +#include +#include +#include + +TEST_CASE("PDU_Unconfirmed_ExtAddr_Test", "[P25 PDU Unconfirmed Ext Addr Test]") { + SECTION("P25_PDU_Unconfirmed_ExtAddr_Test") { + bool failed = false; + + INFO("P25 PDU Unconfirmed Ext Addr Test"); + + srand((unsigned int)time(NULL)); + + g_logDisplayLevel = 1U; + + // test PDU data + uint32_t testLength = 30U; + uint8_t testPDUSource[] = + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + }; + + data::Assembler::setVerbose(true); + data::Assembler::setDumpPDUData(true); + + Assembler assembler = Assembler(); + + Utils::dump(2U, "P25_PDU_Unconfirmed_ExtAddr_Test, Test Source", testPDUSource, 30U); + + DataHeader dataHeader = DataHeader(); + dataHeader.setFormat(PDUFormatType::UNCONFIRMED); + dataHeader.setMFId(MFG_STANDARD); + dataHeader.setAckNeeded(true); + dataHeader.setOutbound(true); + dataHeader.setSAP(PDUSAP::EXT_ADDR); + dataHeader.setLLId(0x12345U); + dataHeader.setFullMessage(true); + dataHeader.setBlocksToFollow(1U); + + dataHeader.setEXSAP(PDUSAP::USER_DATA); + dataHeader.setSrcLLId(0x54321U); + + dataHeader.calculateLength(testLength); + + /* + ** self-sanity check the assembler chain + */ + + uint32_t bitLength = 0U; + UInt8Array ret = assembler.assemble(dataHeader, true, false, testPDUSource, &bitLength); + + LogInfoEx("T", "P25_PDU_Unconfirmed_ExtAddr_Test, Assembled Bit Length = %u (%u)", bitLength, bitLength / 8); + Utils::dump(2U, "P25_PDU_Unconfirmed_ExtAddr_Test, Assembled PDU", ret.get(), bitLength / 8); + + if (ret == nullptr) + failed = true; + + if (!failed) { + uint8_t buffer[P25_PDU_FRAME_LENGTH_BYTES]; + ::memset(buffer, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); + + // for the purposes of our test we strip the pad bit length from the bit length + bitLength -= dataHeader.getPadLength() * 8U; + + uint32_t blockCnt = 0U; + for (uint32_t i = P25_PREAMBLE_LENGTH_BITS; i < bitLength; i += P25_PDU_FEC_LENGTH_BITS) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(ret.get(), buffer, i, P25_PDU_FEC_LENGTH_BITS); + + LogInfoEx("T", "P25_PDU_Unconfirmed_ExtAddr_Test, i = %u", i); + Utils::dump(2U, "buffer", buffer, P25_PDU_FEC_LENGTH_BYTES); + + bool ret = false; + if (blockCnt == 0U) + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES, true); + else + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES); + if (!ret) { + failed = true; + ::LogError("T", "P25_PDU_Unconfirmed_ExtAddr_Test, PDU Disassemble, block %u", blockCnt); + } + + blockCnt++; + } + + if (assembler.getComplete()) { + uint8_t pduUserData2[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + uint32_t pduUserDataLength = assembler.getUserDataLength(); + assembler.getUserData(pduUserData2); + + for (uint32_t i = 0; i < pduUserDataLength - 4U; i++) { + if (pduUserData2[i] != testPDUSource[i]) { + ::LogError("T", "P25_PDU_Unconfirmed_ExtAddr_Test, INVALID AT IDX %d", i); + failed = true; + } + } + } + } + + REQUIRE(failed==false); + } +} diff --git a/tests/p25/PDU_Unconfirmed_Test.cpp b/tests/p25/PDU_Unconfirmed_Test.cpp new file mode 100644 index 00000000..7fddb4e0 --- /dev/null +++ b/tests/p25/PDU_Unconfirmed_Test.cpp @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "host/Defines.h" +#include "common/p25/P25Defines.h" +#include "common/p25/data/Assembler.h" +#include "common/Log.h" +#include "common/Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::data; + +#include +#include +#include + +TEST_CASE("PDU_Unconfirmed_Test", "[P25 PDU Unconfirmed Test]") { + SECTION("P25_PDU_Unconfirmed_Test") { + bool failed = false; + + INFO("P25 PDU Unconfirmed Test"); + + srand((unsigned int)time(NULL)); + + g_logDisplayLevel = 1U; + + // test PDU data + uint32_t testLength = 120U; + uint8_t testPDUSource[] = + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + 0x20, 0x54, 0x45, 0x53, 0x54, 0x54, 0x45, 0x53, 0x54, 0x54, 0x45, 0x53, 0x54, 0x20, 0x20, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, + 0x20, 0x54, 0x45, 0x53, 0x54, 0x54, 0x45, 0x53, 0x54, 0x54, 0x45, 0x53, 0x54, 0x20, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21 + }; + + data::Assembler::setVerbose(true); + data::Assembler::setDumpPDUData(true); + + Assembler assembler = Assembler(); + + Utils::dump(2U, "P25_PDU_Unconfirmed_Test, Test Source", testPDUSource, 120U); + + DataHeader dataHeader = DataHeader(); + dataHeader.setFormat(PDUFormatType::UNCONFIRMED); + dataHeader.setMFId(MFG_STANDARD); + dataHeader.setAckNeeded(false); + dataHeader.setOutbound(true); + dataHeader.setSAP(PDUSAP::USER_DATA); + dataHeader.setLLId(0x12345U); + dataHeader.setFullMessage(true); + dataHeader.setBlocksToFollow(1U); + + dataHeader.calculateLength(testLength); + + /* + ** self-sanity check the assembler chain + */ + + uint32_t bitLength = 0U; + UInt8Array ret = assembler.assemble(dataHeader, false, false, testPDUSource, &bitLength); + + LogInfoEx("T", "P25_PDU_Confirmed_Large_Test, Assembled Bit Length = %u (%u)", bitLength, bitLength / 8); + Utils::dump(2U, "P25_PDU_Unconfirmed_Test, Assembled PDU", ret.get(), bitLength / 8); + + if (ret == nullptr) + failed = true; + + if (!failed) { + uint8_t buffer[P25_PDU_FRAME_LENGTH_BYTES]; + ::memset(buffer, 0x00U, P25_PDU_FRAME_LENGTH_BYTES); + + // for the purposes of our test we strip the pad bit length from the bit length + bitLength -= dataHeader.getPadLength() * 8U; + + uint32_t blockCnt = 0U; + for (uint32_t i = P25_PREAMBLE_LENGTH_BITS; i < bitLength; i += P25_PDU_FEC_LENGTH_BITS) { + ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); + Utils::getBitRange(ret.get(), buffer, i, P25_PDU_FEC_LENGTH_BITS); + + bool ret = false; + if (blockCnt == 0U) + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES, true); + else + ret = assembler.disassemble(buffer, P25_PDU_FEC_LENGTH_BYTES); + if (!ret) { + failed = true; + ::LogError("T", "P25_PDU_Unconfirmed_Test, PDU Disassemble, block %u", blockCnt); + } + + blockCnt++; + } + + if (assembler.getComplete()) { + uint8_t pduUserData2[P25_MAX_PDU_BLOCKS * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U]; + uint32_t pduUserDataLength = assembler.getUserDataLength(); + assembler.getUserData(pduUserData2); + + for (uint32_t i = 0; i < pduUserDataLength - 4U; i++) { + if (pduUserData2[i] != testPDUSource[i]) { + ::LogError("T", "P25_PDU_Unconfirmed_Test, INVALID AT IDX %d", i); + failed = true; + } + } + } + } + + REQUIRE(failed==false); + } +} From fd91ed6c4736af0c220fa5d7b7efe9483cdc2125 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 8 Nov 2025 14:05:54 -0500 Subject: [PATCH 144/200] remove left over debug code; fix AMBT CRC-32 calculation error, AMBTs calculate the CRC-32 for the PDU themselves the Assembler does not need to do it; correct default value of p25TxNAC; --- src/common/p25/P25Defines.h | 4 ++-- src/common/p25/data/Assembler.cpp | 8 ++++---- src/host/Host.Config.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/common/p25/P25Defines.h b/src/common/p25/P25Defines.h index 68b17157..0dab98b6 100644 --- a/src/common/p25/P25Defines.h +++ b/src/common/p25/P25Defines.h @@ -244,9 +244,9 @@ namespace p25 const uint32_t SYS_SRV_TRUNK = SYS_SRV_DEFAULT | SystemService::GROUP_AFF | SystemService::UNIT_REG | SystemService::GROUP_AFF_Q; /** @} */ - /** @brief Conventional/Failur/Valid/Networked Flags */ + /** @brief Conventional/Failure/Valid/Networked Flags */ namespace CFVA { - /** @brief Conventional/Failur/Valid/Networked Flags */ + /** @brief Conventional/Failure/Valid/Networked Flags */ enum : uint8_t { CONV = 0x08U, //!< Conventional FAILURE = 0x04U, //!< Failure diff --git a/src/common/p25/data/Assembler.cpp b/src/common/p25/data/Assembler.cpp index d6edb5fb..357cb22a 100644 --- a/src/common/p25/data/Assembler.cpp +++ b/src/common/p25/data/Assembler.cpp @@ -417,13 +417,10 @@ UInt8Array Assembler::assemble(data::DataHeader& dataHeader, bool extendedAddres #if DEBUG_P25_PDU_DATA LogDebugEx(LOG_P25, "Assembler::assemble()", "packetLength = %u, secondHeaderOffset = %u, padLength = %u, pduLength = %u", packetLength, secondHeaderOffset, padLength, pduLength); #endif - ::memcpy(packetData + secondHeaderOffset, pduUserData, packetLength); - if (dataHeader.getFormat() != PDUFormatType::AMBT) { + ::memcpy(packetData + secondHeaderOffset, pduUserData, packetLength); edac::CRC::addCRC32(packetData, packetLength + 4U); - Utils::dump(2U, "packetData", packetData, packetLength + 4U); - if (padLength > 0U) { // move the CRC-32 to the end of the packet data after the padding uint8_t crcBytes[4U]; @@ -431,6 +428,9 @@ UInt8Array Assembler::assemble(data::DataHeader& dataHeader, bool extendedAddres ::memset(packetData + packetLength, 0x00U, 4U); ::memcpy(packetData + (packetLength + padLength), crcBytes, 4U); } + } else { + // our AMBTs have a pre-calculated CRC-32 -- we don't need to do it ourselves + ::memcpy(packetData + secondHeaderOffset, pduUserData, pduLength); } #if DEBUG_P25_PDU_DATA diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp index 23d4a91c..d1bec451 100644 --- a/src/host/Host.Config.cpp +++ b/src/host/Host.Config.cpp @@ -329,7 +329,7 @@ bool Host::readParams() m_p25NAC = (uint32_t)::strtoul(rfssConfig["nac"].as("F7E").c_str(), NULL, 16); m_p25NAC = p25::P25Utils::nac(m_p25NAC); - uint32_t p25TxNAC = (uint32_t)::strtoul(rfssConfig["txNAC"].as("293").c_str(), NULL, 16); + uint32_t p25TxNAC = (uint32_t)::strtoul(rfssConfig["txNAC"].as("F7E").c_str(), NULL, 16); if (p25TxNAC == m_p25NAC) { LogWarning(LOG_HOST, "Only use txNAC when split NAC operations are needed. nac and txNAC should not be the same!"); } From ebaf24baf15d115beee138971974aaace007e4c2 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 9 Nov 2025 20:43:50 -0500 Subject: [PATCH 145/200] add some better error handling for NetRPC; --- src/common/network/NetRPC.cpp | 42 +++++++++++++++++++++++++++++++++- src/common/network/NetRPC.h | 7 ++++-- src/common/network/RPCHeader.h | 1 + 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/common/network/NetRPC.cpp b/src/common/network/NetRPC.cpp index d16d96da..eb5308e8 100644 --- a/src/common/network/NetRPC.cpp +++ b/src/common/network/NetRPC.cpp @@ -318,11 +318,51 @@ void NetRPC::close() m_socket->close(); } +/* Helper to register an RPC handler. */ + +bool NetRPC::registerHandler(uint16_t func, RPCType handler) +{ + // 32767 is the maximum possible function + if (func > RPC_MAX_FUNC) + return false; + + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), [&](RPCHandlerMapPair x) { return x.first == func; }); + if (it != m_handlers.end()) { + LogError(LOG_HOST, "NetRPC::registerHandler() can't register RPC $%04X already registered. BUGBUG.", func); + return false; // handler is already registered + } + + if (m_debug) + LogDebugEx(LOG_HOST, "NetRPC::registerHandler()", "registering RPC $%04X", func); + + m_handlers[func] = handler; + return true; +} + +/* Helper to unregister an RPC handler. */ + +bool NetRPC::unregisterHandler(uint16_t func) +{ + // 32767 is the maximum possible function + if (func > RPC_MAX_FUNC) + return false; + + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), [&](RPCHandlerMapPair x) { return x.first == func; }); + if (it != m_handlers.end()) { + if (m_debug) + LogDebugEx(LOG_HOST, "NetRPC::registerHandler()", "unregistering RPC $%04X", func); + + m_handlers.erase(func); + return true; + } + + return false; +} + // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- - /* Writes an RPC reply to the network. */ bool NetRPC::reply(uint16_t func, json::object& reply, sockaddr_storage& address, uint32_t addrLen) diff --git a/src/common/network/NetRPC.h b/src/common/network/NetRPC.h index 53d3f112..8bf897b0 100644 --- a/src/common/network/NetRPC.h +++ b/src/common/network/NetRPC.h @@ -141,13 +141,15 @@ namespace network * @brief Helper to register an RPC handler. * @param func Function opcode. * @param handler Function handler. + * @returns bool True, if handler is registered, otherwise false. */ - void registerHandler(uint16_t func, RPCType handler) { m_handlers[func] = handler; } + bool registerHandler(uint16_t func, RPCType handler); /** * @brief Helper to unregister an RPC handler. * @param func Function opcode. + * @returns bool True, if handler is unregistered, otherwise false. */ - void unregisterHandler(uint16_t func) { m_handlers.erase(func); } + bool unregisterHandler(uint16_t func); private: std::string m_address; @@ -160,6 +162,7 @@ namespace network std::string m_password; + typedef std::pair RPCHandlerMapPair; std::map m_handlers; std::map m_handlerReplied; diff --git a/src/common/network/RPCHeader.h b/src/common/network/RPCHeader.h index 40caa92a..ac231d85 100644 --- a/src/common/network/RPCHeader.h +++ b/src/common/network/RPCHeader.h @@ -24,6 +24,7 @@ #define RPC_HEADER_LENGTH_BYTES 8 #define RPC_REPLY_FUNC 0x8000U +#define RPC_MAX_FUNC 0x7FFFU namespace network { From 8e509e0f8fdde8bbecbe0bffc2bcad1f9e36ea5c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 9 Nov 2025 22:29:17 -0500 Subject: [PATCH 146/200] validate LC_CALL_TERM peer ID before allowing them to repeat, this fixes an issue where an errant peer on the network could spam LC_CALL_TERM to cause trunked nodes to terminate/kill a call in progress; --- src/fne/network/callhandler/TagP25Data.cpp | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 367fad41..1819f703 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -181,6 +181,39 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId return false; } + // special case: if we've received a TSDU and its an LC_CALL_TERM; lets validate the source peer ID, + // LC_CALL_TERMs should only be sourced from the peer that initiated the call; other peers should not be + // transmitting LC_CALL_TERMs for the call + if (duid == DUID::TSDU && tsbk->getLCO() == LCO::CALL_TERM) { + if (dstId == 0U) { + LogWarning(LOG_NET, "P25, invalid TSDU, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream); + return false; + } + + RxStatus status = m_status[dstId]; + + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { + if (x.second.dstId == dstId) { + if (x.second.activeCall) + return true; + } + return false; + }); + if (it != m_status.end()) { + if (status.peerId != peerId) { + LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Illegal Call Termination, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); + return false; + } else { + #define REQ_CALL_END_LOG "P25, Requested Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, REQ_CALL_END_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, REQ_CALL_END_LOG); + } + } + } + // specifically only check the following logic for end of call or voice frames if (duid != DUID::TSDU && duid != DUID::PDU) { // is this the end of the call stream? From fb0ff63bab1ac372db16f88c975e738b1429b1cf Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 10 Nov 2025 21:12:13 -0500 Subject: [PATCH 147/200] fix issue with dvmpatch not properly evaluating the destination TEK; --- src/patch/HostPatch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/patch/HostPatch.cpp b/src/patch/HostPatch.cpp index c12eaa64..133ce2a9 100644 --- a/src/patch/HostPatch.cpp +++ b/src/patch/HostPatch.cpp @@ -374,7 +374,7 @@ bool HostPatch::createNetwork() m_tekSrcKeyId = 0U; // destination TEK parameters - yaml::Node dstTekConf = networkConf["srcTek"]; + yaml::Node dstTekConf = networkConf["dstTek"]; bool tekDstEnable = dstTekConf["enable"].as(false); std::string tekDstAlgo = dstTekConf["tekAlgo"].as(); std::transform(tekDstAlgo.begin(), tekDstAlgo.end(), tekDstAlgo.begin(), ::tolower); From bce120284dcea6aeac005666a7eeaa90105bfd3b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 10 Nov 2025 21:26:21 -0500 Subject: [PATCH 148/200] add support to patch to *disable* enc processing and allow frames to pass transparently; implement DES for bridge and patch; --- src/bridge/HostBridge.cpp | 9 +++++++++ src/patch/HostPatch.cpp | 41 ++++++++++++++++++++++++++++++++------- src/patch/HostPatch.h | 2 ++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index aaf3742b..1ced0b1d 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -64,6 +64,7 @@ const int NUMBER_OF_BUFFERS = 32; #define LOCAL_CALL "Local Traffic" #define UDP_CALL "UDP Traffic" +#define TEK_DES "des" #define TEK_AES "aes" #define TEK_ARC4 "arc4" @@ -1068,6 +1069,8 @@ bool HostBridge::createNetwork() m_tekAlgoId = P25DEF::ALGO_AES_256; else if (tekAlgo == TEK_ARC4) m_tekAlgoId = P25DEF::ALGO_ARC4; + else if (tekAlgo == TEK_DES) + m_tekAlgoId = P25DEF::ALGO_DES; else { ::LogError(LOG_HOST, "Invalid TEK algorithm specified, must be \"aes\" or \"adp\"."); m_tekAlgoId = P25DEF::ALGO_UNENCRYPT; @@ -2238,6 +2241,9 @@ void HostBridge::decodeP25AudioFrame(uint8_t* ldu, uint32_t srcId, uint32_t dstI case P25DEF::ALGO_ARC4: m_p25Crypto->cryptARC4_IMBE(imbe, (p25N == 1U) ? DUID::LDU1 : DUID::LDU2); break; + case P25DEF::ALGO_DES: + m_p25Crypto->cryptDES_IMBE(imbe, (p25N == 1U) ? DUID::LDU1 : DUID::LDU2); + break; default: LogError(LOG_HOST, "unsupported TEK algorithm, tekAlgoId = $%02X", m_tekAlgoId); break; @@ -2423,6 +2429,9 @@ void HostBridge::encodeP25AudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_ case P25DEF::ALGO_ARC4: m_p25Crypto->cryptARC4_IMBE(imbe, (m_p25N < 9U) ? DUID::LDU1 : DUID::LDU2); break; + case P25DEF::ALGO_DES: + m_p25Crypto->cryptDES_IMBE(imbe, (m_p25N < 9U) ? DUID::LDU1 : DUID::LDU2); + break; default: LogError(LOG_HOST, "unsupported TEK algorithm, tekAlgoId = $%02X", m_tekAlgoId); break; diff --git a/src/patch/HostPatch.cpp b/src/patch/HostPatch.cpp index 133ce2a9..563d49d8 100644 --- a/src/patch/HostPatch.cpp +++ b/src/patch/HostPatch.cpp @@ -49,6 +49,7 @@ using namespace network::udp; // Constants // --------------------------------------------------------------------------- +#define TEK_DES "des" #define TEK_AES "aes" #define TEK_ARC4 "arc4" @@ -89,8 +90,10 @@ HostPatch::HostPatch(const std::string& confFile) : m_callAlgoId(P25DEF::ALGO_UNENCRYPT), m_rxStartTime(0U), m_rxStreamId(0U), + m_tekSrcEnable(false), m_tekSrcAlgoId(P25DEF::ALGO_UNENCRYPT), m_tekSrcKeyId(0U), + m_tekDstEnable(false), m_tekDstAlgoId(P25DEF::ALGO_UNENCRYPT), m_tekDstKeyId(0U), m_requestedSrcTek(false), @@ -351,6 +354,7 @@ bool HostPatch::createNetwork() m_dstSlot = (uint8_t)networkConf["destinationSlot"].as(1U); // source TEK parameters + m_tekSrcEnable = false; yaml::Node srcTekConf = networkConf["srcTek"]; bool tekSrcEnable = srcTekConf["enable"].as(false); std::string tekSrcAlgo = srcTekConf["tekAlgo"].as(); @@ -361,8 +365,10 @@ bool HostPatch::createNetwork() m_tekSrcAlgoId = P25DEF::ALGO_AES_256; else if (tekSrcAlgo == TEK_ARC4) m_tekSrcAlgoId = P25DEF::ALGO_ARC4; + else if (tekSrcAlgo == TEK_DES) + m_tekSrcAlgoId = P25DEF::ALGO_DES; else { - ::LogError(LOG_HOST, "Invalid TEK algorithm specified, must be \"aes\" or \"adp\"."); + ::LogError(LOG_HOST, "Invalid TEK algorithm specified, must be \"aes\" or \"arc4\"."); m_tekSrcAlgoId = P25DEF::ALGO_UNENCRYPT; m_tekSrcKeyId = 0U; } @@ -372,8 +378,11 @@ bool HostPatch::createNetwork() m_tekSrcAlgoId = P25DEF::ALGO_UNENCRYPT; if (m_tekSrcAlgoId == P25DEF::ALGO_UNENCRYPT) m_tekSrcKeyId = 0U; + if (m_tekSrcAlgoId != P25DEF::ALGO_UNENCRYPT) + m_tekSrcEnable = true; // destination TEK parameters + m_tekDstEnable = false; yaml::Node dstTekConf = networkConf["dstTek"]; bool tekDstEnable = dstTekConf["enable"].as(false); std::string tekDstAlgo = dstTekConf["tekAlgo"].as(); @@ -384,8 +393,10 @@ bool HostPatch::createNetwork() m_tekDstAlgoId = P25DEF::ALGO_AES_256; else if (tekDstAlgo == TEK_ARC4) m_tekDstAlgoId = P25DEF::ALGO_ARC4; + else if (tekDstAlgo == TEK_DES) + m_tekDstAlgoId = P25DEF::ALGO_DES; else { - ::LogError(LOG_HOST, "Invalid TEK algorithm specified, must be \"aes\" or \"adp\"."); + ::LogError(LOG_HOST, "Invalid TEK algorithm specified, must be \"aes\" or \"arc4\"."); m_tekDstAlgoId = P25DEF::ALGO_UNENCRYPT; m_tekDstKeyId = 0U; } @@ -395,6 +406,8 @@ bool HostPatch::createNetwork() m_tekDstAlgoId = P25DEF::ALGO_UNENCRYPT; if (m_tekDstAlgoId == P25DEF::ALGO_UNENCRYPT) m_tekDstKeyId = 0U; + if (m_tekDstAlgoId != P25DEF::ALGO_UNENCRYPT) + m_tekDstEnable = true; m_twoWayPatch = networkConf["twoWay"].as(false); @@ -947,6 +960,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) return; bool reverseEncrypt = false; + bool tekEnable = m_tekSrcEnable; uint32_t actualDstId = m_srcTGId; uint8_t tekAlgoId = m_tekSrcAlgoId; uint16_t tekKeyId = m_tekSrcKeyId; @@ -956,6 +970,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) if (m_twoWayPatch) { if (dstId == m_dstTGId) { actualDstId = m_srcTGId; + tekEnable = m_tekDstEnable; tekAlgoId = m_tekDstAlgoId; tekKeyId = m_tekDstKeyId; reverseEncrypt = true; @@ -975,7 +990,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) uint8_t frameType = buffer[180U]; if (frameType == FrameType::HDU_VALID) { m_callAlgoId = buffer[181U]; - if (m_callAlgoId != ALGO_UNENCRYPT) { + if (tekEnable && m_callAlgoId != ALGO_UNENCRYPT) { callKID = GET_UINT16(buffer, 182U); if (m_callAlgoId != tekAlgoId && callKID != tekKeyId) { @@ -1121,7 +1136,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) LogInfoEx(LOG_NET, P25_LDU1_STR " audio, srcId = %u, dstId = %u", srcId, dstId); - if (tekAlgoId != ALGO_UNENCRYPT && tekKeyId != 0U) { + if (tekEnable && tekAlgoId != ALGO_UNENCRYPT && tekKeyId != 0U) { cryptP25AudioFrame(netLDU, reverseEncrypt, 1U); } @@ -1149,7 +1164,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) } // the previous is nice and all -- but if we're cross-encrypting, we need to use the TEK - if (tekAlgoId != ALGO_UNENCRYPT && tekKeyId != 0U) { + if (tekEnable && tekAlgoId != ALGO_UNENCRYPT && tekKeyId != 0U) { control.setAlgId(tekAlgoId); control.setKId(tekKeyId); @@ -1221,7 +1236,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) LogInfoEx(LOG_NET, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", dfsiLC.control()->getAlgId(), dfsiLC.control()->getKId()); - if (tekAlgoId != ALGO_UNENCRYPT && tekKeyId != 0U) { + if (tekEnable && tekAlgoId != ALGO_UNENCRYPT && tekKeyId != 0U) { cryptP25AudioFrame(netLDU, reverseEncrypt, 2U); } @@ -1231,7 +1246,7 @@ void HostPatch::processP25Network(uint8_t* buffer, uint32_t length) control.setDstId(actualDstId); // set the algo ID and key ID - if (tekAlgoId != ALGO_UNENCRYPT && tekKeyId != 0U) { + if (tekEnable && tekAlgoId != ALGO_UNENCRYPT && tekKeyId != 0U) { control.setAlgId(tekAlgoId); control.setKId(tekKeyId); @@ -1336,6 +1351,9 @@ void HostPatch::cryptP25AudioFrame(uint8_t* ldu, bool reverseEncrypt, uint8_t p2 case P25DEF::ALGO_ARC4: m_p25SrcCrypto->cryptARC4_IMBE(imbe, (p25N == 1U) ? DUID::LDU1 : DUID::LDU2); break; + case P25DEF::ALGO_DES: + m_p25SrcCrypto->cryptDES_IMBE(imbe, (p25N == 1U) ? DUID::LDU1 : DUID::LDU2); + break; default: LogError(LOG_HOST, "Unsupported TEK algorithm, tekAlgoId = $%02X", tekSrcAlgoId); break; @@ -1349,6 +1367,9 @@ void HostPatch::cryptP25AudioFrame(uint8_t* ldu, bool reverseEncrypt, uint8_t p2 case P25DEF::ALGO_ARC4: m_p25DstCrypto->cryptARC4_IMBE(imbe, (p25N == 1U) ? DUID::LDU1 : DUID::LDU2); break; + case P25DEF::ALGO_DES: + m_p25DstCrypto->cryptDES_IMBE(imbe, (p25N == 1U) ? DUID::LDU1 : DUID::LDU2); + break; default: LogError(LOG_HOST, "Unsupported TEK algorithm, tekAlgoId = $%02X", tekDstAlgoId); break; @@ -1367,6 +1388,9 @@ void HostPatch::cryptP25AudioFrame(uint8_t* ldu, bool reverseEncrypt, uint8_t p2 case P25DEF::ALGO_ARC4: m_p25DstCrypto->cryptARC4_IMBE(imbe, (p25N == 1U) ? DUID::LDU1 : DUID::LDU2); break; + case P25DEF::ALGO_DES: + m_p25DstCrypto->cryptDES_IMBE(imbe, (p25N == 1U) ? DUID::LDU1 : DUID::LDU2); + break; default: LogError(LOG_HOST, "Unsupported TEK algorithm, tekAlgoId = $%02X", tekDstAlgoId); break; @@ -1380,6 +1404,9 @@ void HostPatch::cryptP25AudioFrame(uint8_t* ldu, bool reverseEncrypt, uint8_t p2 case P25DEF::ALGO_ARC4: m_p25SrcCrypto->cryptARC4_IMBE(imbe, (p25N == 1U) ? DUID::LDU1 : DUID::LDU2); break; + case P25DEF::ALGO_DES: + m_p25SrcCrypto->cryptDES_IMBE(imbe, (p25N == 1U) ? DUID::LDU1 : DUID::LDU2); + break; default: LogError(LOG_HOST, "Unsupported TEK algorithm, tekAlgoId = $%02X", tekSrcAlgoId); break; diff --git a/src/patch/HostPatch.h b/src/patch/HostPatch.h index b48e2206..9c3bf237 100644 --- a/src/patch/HostPatch.h +++ b/src/patch/HostPatch.h @@ -99,8 +99,10 @@ class HOST_SW_API HostPatch { uint64_t m_rxStartTime; uint32_t m_rxStreamId; + bool m_tekSrcEnable; uint8_t m_tekSrcAlgoId; uint16_t m_tekSrcKeyId; + bool m_tekDstEnable; uint8_t m_tekDstAlgoId; uint16_t m_tekDstKeyId; bool m_requestedSrcTek; From 13b5dd414364d4f6e948c20b2fde0e576cb458b6 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 10 Nov 2025 22:24:09 -0500 Subject: [PATCH 149/200] reorganize application files, I've wanted to do this for a while JSON isn't really a network service, and REST API while it is a network service, I want to be in a separate namespace; --- src/bridge/network/PeerNetwork.cpp | 2 +- src/common/CMakeLists.txt | 9 +- src/common/{network => }/json/json.h | 0 src/common/network/BaseNetwork.h | 2 +- src/common/network/NetRPC.cpp | 2 +- src/common/network/NetRPC.h | 2 +- src/common/network/Network.cpp | 2 +- src/common/network/rest/RequestDispatcher.h | 335 ------------------ .../network/rest/http/ClientConnection.h | 242 ------------- src/common/network/rest/http/HTTPClient.h | 199 ----------- src/common/network/rest/http/HTTPHeaders.h | 152 -------- src/common/network/rest/http/HTTPLexer.h | 196 ---------- src/common/network/rest/http/HTTPPayload.h | 144 -------- .../network/rest/http/HTTPRequestHandler.h | 86 ----- src/common/network/rest/http/HTTPServer.h | 163 --------- .../rest/http/SecureClientConnection.h | 264 -------------- .../network/rest/http/SecureHTTPClient.h | 206 ----------- .../network/rest/http/SecureHTTPServer.h | 191 ---------- .../rest/http/SecureServerConnection.h | 287 --------------- .../network/rest/http/ServerConnection.h | 267 -------------- .../rest/http/ServerConnectionManager.h | 97 ----- src/common/restapi/RequestDispatcher.h | 331 +++++++++++++++++ src/common/restapi/http/ClientConnection.h | 239 +++++++++++++ src/common/restapi/http/HTTPClient.h | 196 ++++++++++ src/common/restapi/http/HTTPHeaders.h | 149 ++++++++ .../rest => restapi}/http/HTTPLexer.cpp | 6 +- src/common/restapi/http/HTTPLexer.h | 193 ++++++++++ .../rest => restapi}/http/HTTPPayload.cpp | 4 +- src/common/restapi/http/HTTPPayload.h | 141 ++++++++ .../http/HTTPRequestHandler.cpp | 6 +- src/common/restapi/http/HTTPRequestHandler.h | 83 +++++ src/common/restapi/http/HTTPServer.h | 160 +++++++++ .../restapi/http/SecureClientConnection.h | 261 ++++++++++++++ src/common/restapi/http/SecureHTTPClient.h | 203 +++++++++++ src/common/restapi/http/SecureHTTPServer.h | 188 ++++++++++ .../restapi/http/SecureServerConnection.h | 284 +++++++++++++++ src/common/restapi/http/ServerConnection.h | 264 ++++++++++++++ .../restapi/http/ServerConnectionManager.h | 94 +++++ src/fne/CMakeLists.txt | 2 + src/fne/HostFNE.h | 2 +- src/fne/network/FNENetwork.cpp | 2 +- src/fne/network/FNENetwork.h | 4 +- src/fne/network/PeerNetwork.cpp | 2 +- src/fne/network/SpanningTree.h | 2 +- src/fne/{network => restapi}/RESTAPI.cpp | 9 +- src/fne/{network => restapi}/RESTAPI.h | 76 ++-- src/fne/{network => restapi}/RESTDefines.h | 2 +- src/host/CMakeLists.txt | 2 + src/host/Host.h | 4 +- src/host/modem/Modem.h | 2 +- src/host/{network => restapi}/RESTAPI.cpp | 8 +- src/host/{network => restapi}/RESTAPI.h | 82 ++--- src/host/{network => restapi}/RESTDefines.h | 0 src/monitor/InhibitSubscriberWnd.h | 2 +- src/monitor/NodeStatusWnd.h | 6 +- src/monitor/PageSubscriberWnd.h | 2 +- src/monitor/RadioCheckSubscriberWnd.h | 2 +- src/monitor/TransmitWndBase.h | 4 +- src/monitor/UninhibitSubscriberWnd.h | 2 +- src/patch/network/PeerNetwork.cpp | 2 +- src/remote/RESTClient.cpp | 14 +- src/remote/RESTClient.h | 6 +- src/remote/RESTClientMain.cpp | 4 +- src/sysview/AffListWnd.h | 4 +- src/sysview/HostWS.cpp | 6 +- src/sysview/PeerListWnd.h | 4 +- src/sysview/network/PeerNetwork.cpp | 2 +- 67 files changed, 2935 insertions(+), 2974 deletions(-) rename src/common/{network => }/json/json.h (100%) delete mode 100644 src/common/network/rest/RequestDispatcher.h delete mode 100644 src/common/network/rest/http/ClientConnection.h delete mode 100644 src/common/network/rest/http/HTTPClient.h delete mode 100644 src/common/network/rest/http/HTTPHeaders.h delete mode 100644 src/common/network/rest/http/HTTPLexer.h delete mode 100644 src/common/network/rest/http/HTTPPayload.h delete mode 100644 src/common/network/rest/http/HTTPRequestHandler.h delete mode 100644 src/common/network/rest/http/HTTPServer.h delete mode 100644 src/common/network/rest/http/SecureClientConnection.h delete mode 100644 src/common/network/rest/http/SecureHTTPClient.h delete mode 100644 src/common/network/rest/http/SecureHTTPServer.h delete mode 100644 src/common/network/rest/http/SecureServerConnection.h delete mode 100644 src/common/network/rest/http/ServerConnection.h delete mode 100644 src/common/network/rest/http/ServerConnectionManager.h create mode 100644 src/common/restapi/RequestDispatcher.h create mode 100644 src/common/restapi/http/ClientConnection.h create mode 100644 src/common/restapi/http/HTTPClient.h create mode 100644 src/common/restapi/http/HTTPHeaders.h rename src/common/{network/rest => restapi}/http/HTTPLexer.cpp (98%) create mode 100644 src/common/restapi/http/HTTPLexer.h rename src/common/{network/rest => restapi}/http/HTTPPayload.cpp (99%) create mode 100644 src/common/restapi/http/HTTPPayload.h rename src/common/{network/rest => restapi}/http/HTTPRequestHandler.cpp (96%) create mode 100644 src/common/restapi/http/HTTPRequestHandler.h create mode 100644 src/common/restapi/http/HTTPServer.h create mode 100644 src/common/restapi/http/SecureClientConnection.h create mode 100644 src/common/restapi/http/SecureHTTPClient.h create mode 100644 src/common/restapi/http/SecureHTTPServer.h create mode 100644 src/common/restapi/http/SecureServerConnection.h create mode 100644 src/common/restapi/http/ServerConnection.h create mode 100644 src/common/restapi/http/ServerConnectionManager.h rename src/fne/{network => restapi}/RESTAPI.cpp (99%) rename src/fne/{network => restapi}/RESTAPI.h (84%) rename src/fne/{network => restapi}/RESTDefines.h (98%) rename src/host/{network => restapi}/RESTAPI.cpp (99%) rename src/host/{network => restapi}/RESTAPI.h (84%) rename src/host/{network => restapi}/RESTDefines.h (100%) diff --git a/src/bridge/network/PeerNetwork.cpp b/src/bridge/network/PeerNetwork.cpp index 449d68b8..31e75d0c 100644 --- a/src/bridge/network/PeerNetwork.cpp +++ b/src/bridge/network/PeerNetwork.cpp @@ -11,9 +11,9 @@ #include "common/dmr/data/EMB.h" #include "common/dmr/lc/FullLC.h" #include "common/dmr/SlotType.h" -#include "common/network/json/json.h" #include "common/p25/dfsi/DFSIDefines.h" #include "common/p25/dfsi/LC.h" +#include "common/json/json.h" #include "common/Utils.h" #include "network/PeerNetwork.h" diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 53286d00..47d9ea13 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -47,12 +47,12 @@ file(GLOB common_SRC "src/common/edac/*.cpp" "src/common/lookups/*.cpp" "src/common/network/*.cpp" - "src/common/network/rest/*.cpp" - "src/common/network/rest/http/*.cpp" "src/common/network/sip/*.cpp" "src/common/network/tcp/*.cpp" "src/common/network/udp/*.cpp" "src/common/network/viface/*.cpp" + "src/common/restapi/*.cpp" + "src/common/restapi/http/*.cpp" "src/common/yaml/*.cpp" "src/common/zlib/*.cpp" "src/common/zlib/*.c" @@ -99,15 +99,16 @@ file(GLOB common_INCLUDE "src/common/concurrent/*.h" "src/common/edac/*.h" "src/common/edac/rs/*.h" + "src/common/json/*.h" "src/common/lookups/*.h" "src/common/network/*.h" "src/common/network/json/*.h" - "src/common/network/rest/*.h" - "src/common/network/rest/http/*.h" "src/common/network/sip/*.h" "src/common/network/tcp/*.h" "src/common/network/udp/*.h" "src/common/network/viface/*.h" + "src/common/restapi/*.h" + "src/common/restapi/http/*.h" "src/common/backtrace/*.h" "src/common/yaml/*.h" "src/common/zlib/*.h" diff --git a/src/common/network/json/json.h b/src/common/json/json.h similarity index 100% rename from src/common/network/json/json.h rename to src/common/json/json.h diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index bd4fbec7..9fae95c5 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -32,8 +32,8 @@ #include "common/p25/lc/LC.h" #include "common/p25/Audio.h" #include "common/nxdn/lc/RTCH.h" +#include "common/json/json.h" #include "common/network/FrameQueue.h" -#include "common/network/json/json.h" #include "common/network/udp/Socket.h" #include "common/RingBuffer.h" #include "common/Utils.h" diff --git a/src/common/network/NetRPC.cpp b/src/common/network/NetRPC.cpp index eb5308e8..6005eccc 100644 --- a/src/common/network/NetRPC.cpp +++ b/src/common/network/NetRPC.cpp @@ -10,8 +10,8 @@ #include "Defines.h" #include "common/edac/CRC.h" #include "common/edac/SHA256.h" +#include "common/json/json.h" #include "common/network/RPCHeader.h" -#include "common/network/json/json.h" #include "common/Log.h" #include "common/Thread.h" #include "common/Utils.h" diff --git a/src/common/network/NetRPC.h b/src/common/network/NetRPC.h index 8bf897b0..2401f327 100644 --- a/src/common/network/NetRPC.h +++ b/src/common/network/NetRPC.h @@ -21,9 +21,9 @@ #endif // defined(_WIN32) #include "common/Defines.h" +#include "common/json/json.h" #include "common/network/udp/Socket.h" #include "common/network/RawFrameQueue.h" -#include "common/network/json/json.h" #include #include diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index 72b4901a..1bac4a9a 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -9,8 +9,8 @@ */ #include "Defines.h" #include "common/edac/SHA256.h" -#include "common/network/json/json.h" #include "common/p25/kmm/KMMFactory.h" +#include "common/json/json.h" #include "common/Log.h" #include "common/Utils.h" #include "network/Network.h" diff --git a/src/common/network/rest/RequestDispatcher.h b/src/common/network/rest/RequestDispatcher.h deleted file mode 100644 index 6a3b20fb..00000000 --- a/src/common/network/rest/RequestDispatcher.h +++ /dev/null @@ -1,335 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - Common Library - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2023 Bryan Biedenkapp, N2PLL - * - */ -/** - * @defgroup rest REST Services - * @brief Implementation for REST services. - * @ingroup network_core - * @defgroup http Embedded HTTP Core - * @brief Implementation for basic HTTP services. - * @ingroup rest - * - * @file RequestDispatcher.h - * @ingroup rest - */ -#if !defined(__REST__DISPATCHER_H__) -#define __REST__DISPATCHER_H__ - -#include "common/Defines.h" -#include "common/network/rest/http/HTTPPayload.h" -#include "common/Log.h" - -#include -#include -#include -#include -#include - -namespace network -{ - namespace rest - { - // --------------------------------------------------------------------------- - // Structure Declaration - // --------------------------------------------------------------------------- - - /** - * @brief Structure representing a REST API request match. - * @ingroup rest - */ - struct RequestMatch : std::smatch { - /** - * @brief Initializes a new instance of the RequestMatch structure. - * @param m String matcher. - * @param c Content. - */ - RequestMatch(const std::smatch& m, const std::string& c) : std::smatch(m), content(c) { /* stub */ } - - std::string content; - }; - - // --------------------------------------------------------------------------- - // Structure Declaration - // --------------------------------------------------------------------------- - - /** - * @brief Structure representing a request matcher. - * @ingroup rest - */ - template - struct RequestMatcher { - typedef std::function RequestHandlerType; - - /** - * @brief Initializes a new instance of the RequestMatcher structure. - * @param expression Matching expression. - */ - explicit RequestMatcher(const std::string& expression) : m_expression(expression), m_isRegEx(false) { /* stub */ } - - /** - * @brief Handler for GET requests. - * @param handler GET request handler. - * @return RequestMatcher* Instance of a RequestMatcher. - */ - RequestMatcher& get(RequestHandlerType handler) { - m_handlers[HTTP_GET] = handler; - return *this; - } - /** - * @brief Handler for POST requests. - * @param handler POST request handler. - * @return RequestMatcher* Instance of a RequestMatcher. - */ - RequestMatcher& post(RequestHandlerType handler) { - m_handlers[HTTP_POST] = handler; - return *this; - } - /** - * @brief Handler for PUT requests. - * @param handler PUT request handler. - * @return RequestMatcher* Instance of a RequestMatcher. - */ - RequestMatcher& put(RequestHandlerType handler) { - m_handlers[HTTP_PUT] = handler; - return *this; - } - /** - * @brief Handler for DELETE requests. - * @param handler DELETE request handler. - * @return RequestMatcher* Instance of a RequestMatcher. - */ - RequestMatcher& del(RequestHandlerType handler) { - m_handlers[HTTP_DELETE] = handler; - return *this; - } - /** - * @brief Handler for OPTIONS requests. - * @param handler OPTIONS request handler. - * @return RequestMatcher* Instance of a RequestMatcher. - */ - RequestMatcher& options(RequestHandlerType handler) { - m_handlers[HTTP_OPTIONS] = handler; - return *this; - } - - /** - * @brief Helper to determine if the request matcher is a regular expression. - * @returns bool True, if request matcher is a regular expression, otherwise false. - */ - bool regex() const { return m_isRegEx; } - /** - * @brief Helper to set the regular expression flag. - * @param regEx Flag indicating whether or not the request matcher is a regular expression. - */ - void setRegEx(bool regEx) { m_isRegEx = regEx; } - - /** - * @brief Helper to handle the actual request. - * @param request HTTP request. - * @param reply HTTP reply. - * @param what What matched. - */ - void handleRequest(const Request& request, Reply& reply, const std::smatch &what) { - // dispatching to matching based on handler - RequestMatch match(what, request.content); - auto& handler = m_handlers[request.method]; - if (handler) { - handler(request, reply, match); - } - } - - private: - std::string m_expression; - bool m_isRegEx; - std::map m_handlers; - }; - - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements RESTful web request dispatching. - * @tparam Request HTTP request. - * @tparam Reply HTTP reply. - */ - template - class RequestDispatcher { - typedef RequestMatcher MatcherType; - public: - /** - * @brief Initializes a new instance of the RequestDispatcher class. - */ - RequestDispatcher() : m_basePath(), m_debug(false) { /* stub */ } - /** - * @brief Initializes a new instance of the RequestDispatcher class. - * @param debug Flag indicating whether or not verbose logging should be enabled. - */ - RequestDispatcher(bool debug) : m_basePath(), m_debug(debug) { /* stub */ } - /** - * @brief Initializes a new instance of the RequestDispatcher class. - * @param basePath - * @param debug Flag indicating whether or not verbose logging should be enabled. - */ - RequestDispatcher(const std::string& basePath, bool debug) : m_basePath(basePath), m_debug(debug) { /* stub */ } - - /** - * @brief Helper to match a request patch. - * @param expression Matching expression. - * @param regex Flag indicating whether or not this match is a regular expression. - * @returns MatcherType Instance of a request matcher. - */ - MatcherType& match(const std::string& expression, bool regex = false) - { - MatcherTypePtr& p = m_matchers[expression]; - if (!p) { - if (m_debug) { - ::LogDebug(LOG_REST, "creating RequestDispatcher, expression = %s", expression.c_str()); - } - p = std::make_shared(expression); - } else { - if (m_debug) { - ::LogDebug(LOG_REST, "fetching RequestDispatcher, expression = %s", expression.c_str()); - } - } - - p->setRegEx(regex); - return *p; - } - - /** - * @brief Helper to handle HTTP request. - * @param request HTTP request. - * @param reply HTTP reply. - */ - void handleRequest(const Request& request, Reply& reply) - { - for (const auto& matcher : m_matchers) { - std::smatch what; - if (!matcher.second->regex()) { - if (request.uri.find(matcher.first) != std::string::npos) { - if (m_debug) { - ::LogDebug(LOG_REST, "non-regex endpoint, uri = %s, expression = %s", request.uri.c_str(), matcher.first.c_str()); - } - - //what = matcher.first; - - // ensure CORS headers are added - reply.headers.add("Access-Control-Allow-Origin", "*"); - reply.headers.add("Access-Control-Allow-Methods", "*"); - reply.headers.add("Access-Control-Allow-Headers", "*"); - - if (request.method == HTTP_OPTIONS) { - reply.status = http::HTTPPayload::OK; - } - - matcher.second->handleRequest(request, reply, what); - return; - } - } else { - if (std::regex_match(request.uri, what, std::regex(matcher.first))) { - if (m_debug) { - ::LogDebug(LOG_REST, "regex endpoint, uri = %s, expression = %s", request.uri.c_str(), matcher.first.c_str()); - } - - matcher.second->handleRequest(request, reply, what); - return; - } - } - } - - ::LogError(LOG_REST, "unknown endpoint, uri = %s", request.uri.c_str()); - reply = http::HTTPPayload::statusPayload(http::HTTPPayload::BAD_REQUEST, "application/json"); - } - - private: - typedef std::shared_ptr MatcherTypePtr; - - std::string m_basePath; - std::map m_matchers; - - bool m_debug; - }; - - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements a generic basic request dispatcher. - * @tparam Request HTTP request. - * @tparam Reply HTTP reply. - */ - template - class BasicRequestDispatcher { - public: - typedef std::function RequestHandlerType; - - /** - * @brief Initializes a new instance of the BasicRequestDispatcher class. - */ - BasicRequestDispatcher() { /* stub */ } - /** - * @brief Initializes a new instance of the BasicRequestDispatcher class. - * @param handler Instance of a RequestHandlerType for this dispatcher. - */ - BasicRequestDispatcher(RequestHandlerType handler) : m_handler(handler) { /* stub */ } - - /** - * @brief Helper to handle HTTP request. - * @param request HTTP request. - * @param reply HTTP reply. - */ - void handleRequest(const Request& request, Reply& reply) - { - if (m_handler) { - m_handler(request, reply); - } - } - - private: - RequestHandlerType m_handler; - }; - - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements a generic debug request dispatcher. - * @tparam Request HTTP request. - * @tparam Reply HTTP reply. - */ - template - class DebugRequestDispatcher { - public: - /** - * @brief Initializes a new instance of the DebugRequestDispatcher class. - */ - DebugRequestDispatcher() { /* stub */ } - - /** - * @brief Helper to handle HTTP request. - * @param request HTTP request. - * @param reply HTTP reply. - */ - void handleRequest(const Request& request, Reply& reply) - { - for (auto header : request.headers.headers()) - ::LogDebugEx(LOG_REST, "DebugRequestDispatcher::handleRequest()", "header = %s, value = %s", header.name.c_str(), header.value.c_str()); - - ::LogDebugEx(LOG_REST, "DebugRequestDispatcher::handleRequest()", "content = %s", request.content.c_str()); - } - }; - - typedef RequestDispatcher DefaultRequestDispatcher; - } // namespace rest -} // namespace network - -#endif // __REST__DISPATCHER_H__ diff --git a/src/common/network/rest/http/ClientConnection.h b/src/common/network/rest/http/ClientConnection.h deleted file mode 100644 index 6b2e86dd..00000000 --- a/src/common/network/rest/http/ClientConnection.h +++ /dev/null @@ -1,242 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - Common Library - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file ClientConnection.h - * @ingroup http - */ -#if !defined(__REST_HTTP__CLIENT_CONNECTION_H__) -#define __REST_HTTP__CLIENT_CONNECTION_H__ - -#include "common/Defines.h" -#include "common/network/rest/http/HTTPLexer.h" -#include "common/network/rest/http/HTTPPayload.h" -#include "common/Log.h" - -#include -#include -#include -#include - -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class represents a single connection from a client. - * @tparam RequestHandlerType Type representing a request handler. - * @ingroup http - */ - template - class ClientConnection { - public: - auto operator=(ClientConnection&) -> ClientConnection& = delete; - auto operator=(ClientConnection&&) -> ClientConnection& = delete; - ClientConnection(ClientConnection&) = delete; - - /** - * @brief Initializes a new instance of the ClientConnection class. - * @param socket TCP socket for this connection. - * @param handler Request handler for this connection. - */ - explicit ClientConnection(asio::ip::tcp::socket socket, RequestHandlerType& handler) : - m_socket(std::move(socket)), - m_requestHandler(handler), - m_lexer(HTTPLexer(true)) - { - /* stub */ - } - - /** - * @brief Start the first asynchronous operation for the connection. - */ - void start() { read(); } - /** - * @brief Stop all asynchronous operations associated with the connection. - */ - void stop() - { - try - { - ensureNoLinger(); - if (m_socket.is_open()) { - m_socket.close(); - } - } - catch(const std::exception&) { /* ignore */ } - } - - /** - * @brief Helper to enable the SO_LINGER socket option during shutdown. - */ - void ensureNoLinger() - { - try - { - // enable SO_LINGER timeout 0 - asio::socket_base::linger linger(true, 0); - m_socket.set_option(linger); - } - catch(const asio::system_error& e) - { - asio::error_code ec = e.code(); - if (ec) { - ::LogError(LOG_REST, "ClientConnection::ensureNoLinger(), %s, code = %u", ec.message().c_str(), ec.value()); - } - } - } - - /** - * @brief Perform an synchronous write operation. - * @param request HTTP request payload. - */ - void send(HTTPPayload request) - { - m_sizeToTransfer = m_bytesTransferred = 0U; - request.attachHostHeader(m_socket.remote_endpoint()); - write(request); - } - private: - /** - * @brief Perform an asynchronous read operation. - */ - void read() - { - m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t bytes_transferred) { - if (!ec) { - HTTPLexer::ResultType result; - char* content; - - try - { - if (m_sizeToTransfer > 0U && (m_bytesTransferred + bytes_transferred) < m_sizeToTransfer) { - ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); - m_bytesTransferred += bytes_transferred; - - read(); - } - else { - if (m_sizeToTransfer > 0U) { - // final copy - ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); - m_bytesTransferred += bytes_transferred; - - m_sizeToTransfer = 0U; - bytes_transferred = m_bytesTransferred; - - // reset lexer and re-parse the full content - m_lexer.reset(); - std::tie(result, content) = m_lexer.parse(m_request, m_fullBuffer.data(), m_fullBuffer.data() + bytes_transferred); - } else { - ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); - m_bytesTransferred += bytes_transferred; - - std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); - } - - // determine content length - std::string contentLength = m_request.headers.find("Content-Length"); - if (contentLength != "") { - size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); - - // setup a full read if necessary - if (length > bytes_transferred && m_sizeToTransfer == 0U) { - m_sizeToTransfer = length; - } - - if (m_sizeToTransfer > 0U) { - result = HTTPLexer::CONTINUE; - } else { - m_request.content = std::string(content, length); - } - } - - m_request.headers.add("RemoteHost", m_socket.remote_endpoint().address().to_string()); - - if (result == HTTPLexer::GOOD) { - m_sizeToTransfer = m_bytesTransferred = 0U; - m_requestHandler.handleRequest(m_request, m_reply); - } - else if (result == HTTPLexer::BAD) { - m_sizeToTransfer = m_bytesTransferred = 0U; - return; - } - else { - read(); - } - } - } - catch(const std::exception& e) { ::LogError(LOG_REST, "ClientConnection::read(), %s", ec.message().c_str()); } - } - else if (ec != asio::error::operation_aborted) { - if (ec) { - ::LogError(LOG_REST, "ClientConnection::read(), %s, code = %u", ec.message().c_str(), ec.value()); - } - stop(); - } - }); - } - - /** - * @brief Perform an synchronous write operation. - * @param request HTTP request payload. - */ - void write(HTTPPayload request) - { - try - { - auto buffers = request.toBuffers(); - asio::write(m_socket, buffers); - } - catch(const asio::system_error& e) - { - asio::error_code ec = e.code(); - if (ec) { - ::LogError(LOG_REST, "ClientConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); - - try - { - // initiate graceful connection closure - asio::error_code ignored_ec; - m_socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); - } - catch(const std::exception& e) { - ::LogError(LOG_REST, "ClientConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); - } - } - } - } - - asio::ip::tcp::socket m_socket; - - RequestHandlerType& m_requestHandler; - - std::size_t m_sizeToTransfer; - std::size_t m_bytesTransferred; - std::array m_fullBuffer; - - std::array m_buffer; - - HTTPPayload m_request; - HTTPLexer m_lexer; - HTTPPayload m_reply; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // __REST_HTTP__CLIENT_CONNECTION_H__ diff --git a/src/common/network/rest/http/HTTPClient.h b/src/common/network/rest/http/HTTPClient.h deleted file mode 100644 index 94c2a4f5..00000000 --- a/src/common/network/rest/http/HTTPClient.h +++ /dev/null @@ -1,199 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - Common Library - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2023 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file HTTPClient.h - * @ingroup http - */ -#if !defined(__REST_HTTP__HTTP_CLIENT_H__) -#define __REST_HTTP__HTTP_CLIENT_H__ - -#include "common/Defines.h" -#include "common/network/rest/http/ClientConnection.h" -#include "common/network/rest/http/HTTPRequestHandler.h" -#include "common/Thread.h" - -#include -#include -#include -#include -#include -#include - -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements top-level routines of the HTTP client. - * @tparam RequestHandlerType Type representing a request handler. - * @tparam ConnectionImpl Type representing the connection implementation. - * @ingroup http - */ - template class ConnectionImpl = ClientConnection> - class HTTPClient : private Thread { - public: - auto operator=(HTTPClient&) -> HTTPClient& = delete; - auto operator=(HTTPClient&&) -> HTTPClient& = delete; - HTTPClient(HTTPClient&) = delete; - - /** - * @brief Initializes a new instance of the HTTPClient class. - * @param address Hostname/IP Address. - * @param port Port. - */ - HTTPClient(const std::string& address, uint16_t port) : - m_address(address), - m_port(port), - m_connection(nullptr), - m_ioContext(), - m_socket(m_ioContext), - m_requestHandler() - { - /* stub */ - } - /** - * @brief Finalizes a instance of the HTTPClient class. - */ - ~HTTPClient() override - { - if (m_connection != nullptr) { - close(); - } - } - - /** - * @brief Helper to set the HTTP request handlers. - * @tparam Handler Type representing the request handler. - * @param handler Request handler. - */ - template - void setHandler(Handler&& handler) - { - m_requestHandler = RequestHandlerType(std::forward(handler)); - } - - /** - * @brief Send HTTP request to HTTP server. - * @param request HTTP request. - * @returns True, if request was completed, otherwise false. - */ - bool request(HTTPPayload& request) - { - if (m_completed) { - return false; - } - - asio::post(m_ioContext, [this, request]() { - std::lock_guard guard(m_lock); - { - if (m_connection != nullptr) { - m_connection->send(request); - } - } - }); - - return true; - } - - /** - * @brief Opens connection to the network. - */ - bool open() - { - if (m_completed) { - return false; - } - - return run(); - } - - /** - * @brief Closes connection to the network. - */ - void close() - { - if (m_completed) { - return; - } - - m_completed = true; - m_ioContext.stop(); - - wait(); - } - - private: - /** - * @brief Internal entry point for the ASIO IO context thread. - */ - void entry() override - { - if (m_completed) { - return; - } - - asio::ip::tcp::resolver resolver(m_ioContext); - auto endpoints = resolver.resolve(m_address, std::to_string(m_port)); - - try { - connect(endpoints); - - // the entry() call will block until all asynchronous operations - // have finished - m_ioContext.run(); - } - catch (std::exception&) { /* stub */ } - - if (m_connection != nullptr) { - m_connection->stop(); - } - } - - /** - * @brief Perform an asynchronous connect operation. - * @param endpoints TCP endpoint to connect to. - */ - void connect(asio::ip::basic_resolver_results& endpoints) - { - asio::connect(m_socket, endpoints); - - m_connection = std::make_unique(std::move(m_socket), m_requestHandler); - m_connection->start(); - } - - std::string m_address; - uint16_t m_port; - - typedef ConnectionImpl ConnectionType; - - std::unique_ptr m_connection; - - bool m_completed = false; - asio::io_context m_ioContext; - - asio::ip::tcp::socket m_socket; - - RequestHandlerType m_requestHandler; - - std::mutex m_lock; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // __REST_HTTP__HTTP_CLIENT_H__ diff --git a/src/common/network/rest/http/HTTPHeaders.h b/src/common/network/rest/http/HTTPHeaders.h deleted file mode 100644 index 409e00be..00000000 --- a/src/common/network/rest/http/HTTPHeaders.h +++ /dev/null @@ -1,152 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file HTTPClient.h - * @ingroup http - */ -#if !defined(__REST_HTTP__HTTP_HEADERS_H__) -#define __REST_HTTP__HTTP_HEADERS_H__ - -#include "common/Defines.h" -#include "common/Log.h" -#include "common/Utils.h" - -#include -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Prototypes - // --------------------------------------------------------------------------- - - struct HTTPPayload; - - // --------------------------------------------------------------------------- - // Structure Declaration - // --------------------------------------------------------------------------- - - /** - * @brief Represents HTTP headers. - * @ingroup http - */ - struct HTTPHeaders { - /** - * @brief Structure representing an individual HTTP header. - * @ingroup http - */ - struct Header - { - /** - * @brief Header name. - */ - std::string name; - /** - * @brief Header value. - */ - std::string value; - - /** - * @brief Initializes a new instance of the Header class. - */ - Header() : name{}, value{} { /* stub */} - /** - * @brief Initializes a new instance of the Header class - * @param n Header name. - * @param v Header value. - */ - Header(std::string n, std::string v) : name{n}, value{v} { /* stub */ } - }; - - /** - * @brief Gets the list of HTTP headers. - * @returns std::vector
List of HTTP headers. - */ - std::vector
headers() const { return m_headers; } - /** - * @brief Returns true if the headers are empty. - * @returns bool True, if no HTTP headers are present, otherwise false. - */ - bool empty() const { return m_headers.empty(); } - /** - * @brief Returns the number of headers. - * @returns std::size_t Number of headers. - */ - std::size_t size() const { return m_headers.size(); } - /** - * @brief Clears the list of HTTP headers. - */ - void clearHeaders() { m_headers = std::vector
(); } - /** - * @brief Helper to add a HTTP header. - * @param name Header name. - * @param value Header value. - */ - void add(const std::string& name, const std::string& value) - { - //::LogDebugEx(LOG_REST, "HTTPHeaders::add()", "header = %s, value = %s", name.c_str(), value.c_str()); - for (auto& header : m_headers) { - if (::strtolower(header.name) == ::strtolower(name)) { - header.value = value; - return; - } - } - - m_headers.push_back(Header(name, value)); - //for (auto header : m_headers) - // ::LogDebugEx(LOG_REST, "HTTPHeaders::add()", "m_headers.header = %s, m_headers.value = %s", header.name.c_str(), header.value.c_str()); - } - /** - * @brief Helper to remove a HTTP header. - * @param headerName Header name. - */ - void remove(const std::string headerName) - { - auto header = std::find_if(m_headers.begin(), m_headers.end(), [&](const Header& h) { - return ::strtolower(h.name) == ::strtolower(headerName); - }); - - if (header != m_headers.end()) { - m_headers.erase(header); - } - } - /** - * @brief Helper to find the named HTTP header. - * @param headerName Header name. - * @returns std::string Value of named header (if any). - */ - std::string find(const std::string headerName) const - { - auto header = std::find_if(m_headers.begin(), m_headers.end(), [&](const Header& h) { - return ::strtolower(h.name) == ::strtolower(headerName); - }); - - if (header != m_headers.end()) { - return header->value; - } - else { - return ""; - } - } - - private: - friend struct HTTPPayload; - std::vector
m_headers; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // __REST_HTTP__HTTP_HEADERS_H__ diff --git a/src/common/network/rest/http/HTTPLexer.h b/src/common/network/rest/http/HTTPLexer.h deleted file mode 100644 index c884d46f..00000000 --- a/src/common/network/rest/http/HTTPLexer.h +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file HTTPLexer.h - * @ingroup http - * @file HTTPLexer.cpp - * @ingroup http - */ -#if !defined(__REST_HTTP__HTTP_LEXER_H__) -#define __REST_HTTP__HTTP_LEXER_H__ - -#include "common/Defines.h" - -#include -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Prototypes - // --------------------------------------------------------------------------- - - struct HTTPPayload; - - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements the lexer for incoming payloads. - */ - class HTTPLexer { - public: - /** - * @brief Lexing result. - */ - enum ResultType { GOOD, BAD, INDETERMINATE, CONTINUE }; - - /** - * @brief Initializes a new instance of the HTTPLexer class. - * @param clientLexer Flag indicating this lexer is used for a HTTP client. - */ - HTTPLexer(bool clientLexer); - - /** - * @brief Reset to initial parser state. - */ - void reset(); - - /** - * @brief Parse some data. The enum return value is good when a complete request has - * been parsed, bad if the data is invalid, indeterminate when more data is - * required. The InputIterator return value indicates how much of the input - * has been consumed. - * @tparam InputIterator - * @param payload HTTP request payload. - * @param begin - * @param end - * @returns std::tuple - */ - template - std::tuple parse(HTTPPayload& payload, InputIterator begin, InputIterator end) - { - while (begin != end) { - ResultType result = consume(payload, *begin++); - if (result == GOOD || result == BAD) - return std::make_tuple(result, begin); - } - return std::make_tuple(INDETERMINATE, begin); - } - - /** - * @brief Returns flag indicating whether or not characters have been consumed from the payload. - * @returns True, if characters were consumed, otherwise false. - */ - uint32_t consumed() const { return m_consumed; } - - private: - /** - * @brief Handle the next character of input. - * @param payload HTTP request payload. - * @param input Character. - */ - ResultType consume(HTTPPayload& payload, char input); - - /** - * @brief Check if a byte is an HTTP character. - * @param c Character. - * @returns bool True, if character is an HTTP character, otherwise false. - */ - static bool isChar(int c); - /** - * @brief Check if a byte is an HTTP control character. - * @param c Character. - * @returns bool True, if character is an HTTP control character, otherwise false. - */ - static bool isControl(int c); - /** - * @brief Check if a byte is an HTTP special character. - * @param c Character. - * @returns bool True, if character is an HTTP special character, otherwise false. - */ - static bool isSpecial(int c); - /** - * @brief Check if a byte is an digit. - * @param c Character. - * @returns bool True, if character is a digit, otherwise false. - */ - static bool isDigit(int c); - - /** - * @brief Structure representing lexed HTTP headers. - */ - struct LexedHeader - { - /** - * @brief Header name. - */ - std::string name; - /** - * @brief Header value. - */ - std::string value; - - /** - * @brief Initializes a new instance of the LexedHeader class - */ - LexedHeader() { /* stub */ } - /** - * @brief Initializes a new instance of the LexedHeader class - * @param n Header name. - * @param v Header value. - */ - LexedHeader(const std::string& n, const std::string& v) : name(n), value(v) {} - }; - - std::vector m_headers; - uint16_t m_status; - bool m_clientLexer = false; - uint32_t m_consumed; - - /** - * @brief Lexer machine state. - */ - enum state - { - METHOD_START, //!< HTTP Method Start - METHOD, //!< HTTP Method - URI, //!< HTTP URI - - HTTP_VERSION_H, //!< HTTP Version: H - HTTP_VERSION_T_1, //!< HTTP Version: T - HTTP_VERSION_T_2, //!< HTTP Version: T - HTTP_VERSION_P, //!< HTTP Version: P - HTTP_VERSION_SLASH, //!< HTTP Version: / - HTTP_VERSION_MAJOR_START, //!< HTTP Version Major Start - HTTP_VERSION_MAJOR, //!< HTTP Version Major - HTTP_VERSION_MINOR_START, //!< HTTP Version Minor Start - HTTP_VERSION_MINOR, //!< HTTP Version Minor - - HTTP_STATUS_1, //!< Status Number 1 - HTTP_STATUS_2, //!< Status Number 2 - HTTP_STATUS_3, //!< Status Number 3 - HTTP_STATUS_END, //!< Status End - HTTP_STATUS_MESSAGE_START, //!< Status Message Start - HTTP_STATUS_MESSAGE, //!< Status Message End - - EXPECTING_NEWLINE_1, //!< - - HEADER_LINE_START, //!< Header Line Start - HEADER_LWS, //!< - HEADER_NAME, //!< Header Name - SPACE_BEFORE_HEADER_VALUE, //!< - HEADER_VALUE, //!< Header Value - - EXPECTING_NEWLINE_2, //!< - EXPECTING_NEWLINE_3 //!< - } m_state; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // __REST_HTTP__HTTP_LEXER_H__ diff --git a/src/common/network/rest/http/HTTPPayload.h b/src/common/network/rest/http/HTTPPayload.h deleted file mode 100644 index d35c989b..00000000 --- a/src/common/network/rest/http/HTTPPayload.h +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file HTTPPayload.h - * @ingroup http - * @file HTTPPayload.cpp - * @ingroup http - */ -#if !defined(__REST_HTTP__HTTP_PAYLOAD_H__) -#define __REST_HTTP__HTTP_PAYLOAD_H__ - -#include "common/Defines.h" -#include "common/network/json/json.h" -#include "common/network/rest/http/HTTPHeaders.h" - -#include -#include - -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Constants - // --------------------------------------------------------------------------- - - #define HTTP_GET "GET" - #define HTTP_POST "POST" - #define HTTP_PUT "PUT" - #define HTTP_DELETE "DELETE" - #define HTTP_OPTIONS "OPTIONS" - - // --------------------------------------------------------------------------- - // Structure Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This struct implements a model of a payload to be sent to a - * HTTP client/server. - * @ingroup http - */ - struct HTTPPayload { - /** - * @brief HTTP Status/Response Codes - */ - enum StatusType { - OK = 200, //!< HTTP OK 200 - CREATED = 201, //!< HTTP Created 201 - ACCEPTED = 202, //!< HTTP Accepted 202 - NO_CONTENT = 204, //!< HTTP No Content 204 - - MULTIPLE_CHOICES = 300, //!< HTTP Multiple Choices 300 - MOVED_PERMANENTLY = 301, //!< HTTP Moved Permenantly 301 - MOVED_TEMPORARILY = 302, //!< HTTP Moved Temporarily 302 - NOT_MODIFIED = 304, //!< HTTP Not Modified 304 - - BAD_REQUEST = 400, //!< HTTP Bad Request 400 - UNAUTHORIZED = 401, //!< HTTP Unauthorized 401 - FORBIDDEN = 403, //!< HTTP Forbidden 403 - NOT_FOUND = 404, //!< HTTP Not Found 404 - - INTERNAL_SERVER_ERROR = 500, //!< HTTP Internal Server Error 500 - NOT_IMPLEMENTED = 501, //!< HTTP Not Implemented 501 - BAD_GATEWAY = 502, //!< HTTP Bad Gateway 502 - SERVICE_UNAVAILABLE = 503 //!< HTTP Service Unavailable 503 - } status; - - HTTPHeaders headers; - std::string content; - size_t contentLength; - - std::string method; - std::string uri; - - int httpVersionMajor; - int httpVersionMinor; - - bool isClientPayload = false; - - /** - * @brief Convert the payload into a vector of buffers. The buffers do not own the - * underlying memory blocks, therefore the payload object must remain valid and - * not be changed until the write operation has completed. - * @returns std::vector List of buffers representing the HTTP payload. - */ - std::vector toBuffers(); - - /** - * @brief Prepares payload for transmission by finalizing status and content type. - * @param obj - * @param status HTTP status. - */ - void payload(json::object& obj, StatusType status = OK); - /** - * @brief Prepares payload for transmission by finalizing status and content type. - * @param content - * @param status HTTP status. - * @param contentType HTTP content type. - */ - void payload(std::string& content, StatusType status = OK, const std::string& contentType = "text/html"); - - /** - * @brief Get a request payload. - * @param method HTTP method. - * @param uri HTTP uri. - */ - static HTTPPayload requestPayload(std::string method, std::string uri); - /** - * @brief Get a status payload. - * @param status HTTP status. - * @param contentType HTTP content type. - */ - static HTTPPayload statusPayload(StatusType status, const std::string& contentType = "text/html"); - - /** - * @brief Helper to attach a host TCP stream reader. - * @param remoteEndpoint Endpoint. - */ - void attachHostHeader(const asio::ip::tcp::endpoint remoteEndpoint); - - private: - /** - * @brief Internal helper to ensure the headers are of a default for the given content type. - * @param contentType HTTP content type. - */ - void ensureDefaultHeaders(const std::string& contentType = "text/html"); - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // __REST_HTTP__HTTP_PAYLOAD_H__ diff --git a/src/common/network/rest/http/HTTPRequestHandler.h b/src/common/network/rest/http/HTTPRequestHandler.h deleted file mode 100644 index 41bfbfe7..00000000 --- a/src/common/network/rest/http/HTTPRequestHandler.h +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file HTTPRequestHandler.h - * @ingroup http - * @file HTTPRequestHandler.cpp - * @ingroup http - */ -#if !defined(__REST_HTTP__HTTP_REQUEST_HANDLER_H__) -#define __REST_HTTP__HTTP_REQUEST_HANDLER_H__ - -#include "common/Defines.h" - -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Prototypes - // --------------------------------------------------------------------------- - - struct HTTPPayload; - - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements the common handler for all incoming requests. - * @ingroup http - */ - class HTTPRequestHandler { - public: - auto operator=(HTTPRequestHandler&) -> HTTPRequestHandler& = delete; - HTTPRequestHandler(HTTPRequestHandler&) = delete; - - /** - * @brief Initializes a new instance of the HTTPRequestHandler class. - * @param docRoot Path to the document root to serve. - */ - explicit HTTPRequestHandler(const std::string& docRoot); - /** - * @brief - */ - HTTPRequestHandler(HTTPRequestHandler&&) = default; - - /** - * @brief - */ - HTTPRequestHandler& operator=(HTTPRequestHandler&&) = default; - - /** - * @brief Handle a request and produce a reply. - * @param req HTTP request. - * @param reply HTTP reply. - */ - void handleRequest(const HTTPPayload& req, HTTPPayload& reply); - - private: - /** - * @brief Internal helper to decode an incoming URL. - * @param[in] in Incoming URL string. - * @param[out] out Decoded URL string. - * @returns bool True, if URL decoded, otherwise false. - */ - static bool urlDecode(const std::string& in, std::string& out); - - std::string m_docRoot; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // __REST_HTTP__HTTP_REQUEST_HANDLER_H__ diff --git a/src/common/network/rest/http/HTTPServer.h b/src/common/network/rest/http/HTTPServer.h deleted file mode 100644 index 25f25130..00000000 --- a/src/common/network/rest/http/HTTPServer.h +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file HTTPServer.h - * @ingroup http - */ -#if !defined(__REST_HTTP__HTTP_SERVER_H__) -#define __REST_HTTP__HTTP_SERVER_H__ - -#include "common/Defines.h" -#include "common/network/rest/http/ServerConnection.h" -#include "common/network/rest/http/ServerConnectionManager.h" -#include "common/network/rest/http/HTTPRequestHandler.h" - -#include -#include -#include -#include -#include - -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements top-level routines of the HTTP server. - * @tparam RequestHandlerType Type representing a request handler. - * @tparam ConnectionImpl Type representing the connection implementation. - * @ingroup http - */ - template class ConnectionImpl = ServerConnection> - class HTTPServer { - public: - auto operator=(HTTPServer&) -> HTTPServer& = delete; - auto operator=(HTTPServer&&) -> HTTPServer& = delete; - HTTPServer(HTTPServer&) = delete; - - /** - * @brief Initializes a new instance of the HTTPServer class. - * @param address Hostname/IP Address. - * @param port Port. - * @param debug Flag indicating whether or not verbose logging should be enabled. - */ - explicit HTTPServer(const std::string& address, uint16_t port, bool debug) : - m_ioService(), - m_acceptor(m_ioService), - m_connectionManager(), - m_socket(m_ioService), - m_requestHandler(), - m_debug(debug) - { - // open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR) - asio::ip::address ipAddress = asio::ip::address::from_string(address); - m_endpoint = asio::ip::tcp::endpoint(ipAddress, port); - } - - /** - * @brief Helper to set the HTTP request handlers. - * @tparam Handler Type representing the request handler. - * @param handler Request handler. - */ - template - void setHandler(Handler&& handler) - { - m_requestHandler = RequestHandlerType(std::forward(handler)); - } - - /** - * @brief Open TCP acceptor. - */ - void open() - { - // open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR) - m_acceptor.open(m_endpoint.protocol()); - m_acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); - m_acceptor.set_option(asio::socket_base::keep_alive(true)); - m_acceptor.bind(m_endpoint); - m_acceptor.listen(); - - accept(); - } - - /** - * @brief Run the servers ASIO IO service loop. - */ - void run() - { - // the run() call will block until all asynchronous operations - // have finished; while the server is running, there is always at least one - // asynchronous operation outstanding: the asynchronous accept call waiting - // for new incoming connections - m_ioService.run(); - } - - /** - * @brief Helper to stop running ASIO IO services. - */ - void stop() - { - // the server is stopped by cancelling all outstanding asynchronous - // operations; once all operations have finished the m_ioService::run() - // call will exit - m_acceptor.close(); - m_connectionManager.stopAll(); - } - - private: - /** - * @brief Perform an asynchronous accept operation. - */ - void accept() - { - m_acceptor.async_accept(m_socket, [this](asio::error_code ec) { - // check whether the server was stopped by a signal before this - // completion handler had a chance to run - if (!m_acceptor.is_open()) { - return; - } - - if (!ec) { - m_connectionManager.start(std::make_shared(std::move(m_socket), m_connectionManager, m_requestHandler, false, m_debug)); - } - - accept(); - }); - } - - typedef ConnectionImpl ConnectionType; - typedef std::shared_ptr ConnectionTypePtr; - - asio::io_service m_ioService; - asio::ip::tcp::acceptor m_acceptor; - - asio::ip::tcp::endpoint m_endpoint; - - ServerConnectionManager m_connectionManager; - - asio::ip::tcp::socket m_socket; - - RequestHandlerType m_requestHandler; - bool m_debug; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // __REST_HTTP__HTTP_SERVER_H__ diff --git a/src/common/network/rest/http/SecureClientConnection.h b/src/common/network/rest/http/SecureClientConnection.h deleted file mode 100644 index b6f7f199..00000000 --- a/src/common/network/rest/http/SecureClientConnection.h +++ /dev/null @@ -1,264 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - Common Library - * 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 SecureClientConnection.h - * @ingroup http - */ -#if !defined(__REST_HTTP__SECURE_CLIENT_CONNECTION_H__) -#define __REST_HTTP__SECURE_CLIENT_CONNECTION_H__ - -#if defined(ENABLE_SSL) - -#include "common/Defines.h" -#include "common/network/rest/http/HTTPLexer.h" -#include "common/network/rest/http/HTTPPayload.h" -#include "common/Log.h" - -#include -#include -#include -#include - -#include -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class represents a single connection from a client. - * @tparam RequestHandlerType Type representing a request handler. - * @ingroup http - */ - template - class SecureClientConnection { - public: - auto operator=(SecureClientConnection&) -> SecureClientConnection& = delete; - auto operator=(SecureClientConnection&&) -> SecureClientConnection& = delete; - SecureClientConnection(SecureClientConnection&) = delete; - - /** - * @brief Initializes a new instance of the SecureClientConnection class. - * @param socket TCP socket for this connection. - * @param context SSL context for this connection. - * @param handler Request handler for this connection. - */ - explicit SecureClientConnection(asio::ip::tcp::socket socket, asio::ssl::context& context, RequestHandlerType& handler) : - m_socket(std::move(socket), context), - m_requestHandler(handler), - m_lexer(HTTPLexer(true)) - { - m_socket.set_verify_mode(asio::ssl::verify_none); - m_socket.set_verify_callback(std::bind(&SecureClientConnection::verify_certificate, this, std::placeholders::_1, std::placeholders::_2)); - } - - /** - * @brief Start the first asynchronous operation for the connection. - */ - void start() - { - m_socket.handshake(asio::ssl::stream_base::client); - read(); - } - /** - * @brief Stop all asynchronous operations associated with the connection. - */ - void stop() - { - try - { - ensureNoLinger(); - if (m_socket.lowest_layer().is_open()) { - m_socket.lowest_layer().close(); - } - } - catch(const std::exception&) { /* ignore */ } - } - - /** - * @brief Helper to enable the SO_LINGER socket option during shutdown. - */ - void ensureNoLinger() - { - try - { - // enable SO_LINGER timeout 0 - asio::socket_base::linger linger(true, 0); - m_socket.lowest_layer().set_option(linger); - } - catch(const asio::system_error& e) - { - asio::error_code ec = e.code(); - if (ec) { - ::LogError(LOG_REST, "SecureClientConnection::ensureNoLinger(), %s, code = %u", ec.message().c_str(), ec.value()); - } - } - } - - /** - * @brief Perform an synchronous write operation. - * @param request HTTP request. - */ - void send(HTTPPayload request) - { - request.attachHostHeader(m_socket.lowest_layer().remote_endpoint()); - write(request); - } - private: - /** - * @brief Perform an SSL certificate verification. - * @param preverified Flag indicating the SSL certificate was preverified. - * @param context SSL verification context. - * @returns True, if SSL certificate is valid, otherwise false. - */ - bool verify_certificate(bool preverified, asio::ssl::verify_context& context) - { - return true; // ignore always valid - } - - /** - * @brief Perform an asynchronous read operation. - */ - void read() - { - m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t bytes_transferred) { - if (!ec) { - HTTPLexer::ResultType result; - char* content; - - try - { - if (m_sizeToTransfer > 0U && (m_bytesTransferred + bytes_transferred) < m_sizeToTransfer) { - ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); - m_bytesTransferred += bytes_transferred; - - read(); - } - else { - if (m_sizeToTransfer > 0U) { - // final copy - ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); - m_bytesTransferred += bytes_transferred; - - m_sizeToTransfer = 0U; - bytes_transferred = m_bytesTransferred; - - // reset lexer and re-parse the full content - m_lexer.reset(); - std::tie(result, content) = m_lexer.parse(m_request, m_fullBuffer.data(), m_fullBuffer.data() + bytes_transferred); - } else { - ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); - m_bytesTransferred += bytes_transferred; - - std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); - } - - // determine content length - std::string contentLength = m_request.headers.find("Content-Length"); - if (contentLength != "") { - size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); - - // setup a full read if necessary - if (length > bytes_transferred && m_sizeToTransfer == 0U) { - m_sizeToTransfer = length; - } - - if (m_sizeToTransfer > 0U) { - result = HTTPLexer::CONTINUE; - } else { - m_request.content = std::string(content, length); - } - } - - m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string()); - if (result == HTTPLexer::GOOD) { - m_sizeToTransfer = m_bytesTransferred = 0U; - m_requestHandler.handleRequest(m_request, m_reply); - } - else if (result == HTTPLexer::BAD) { - m_sizeToTransfer = m_bytesTransferred = 0U; - return; - } - else { - read(); - } - } - } - catch(const std::exception& e) { ::LogError(LOG_REST, "SecureClientConnection::read(), %s", ec.message().c_str()); } - } - else if (ec != asio::error::operation_aborted) { - if (ec) { - ::LogError(LOG_REST, "SecureClientConnection::read(), %s, code = %u", ec.message().c_str(), ec.value()); - } - stop(); - } - }); - } - - /** - * @brief Perform an synchronous write operation. - * @param request HTTP request. - */ - void write(HTTPPayload request) - { - try - { - m_socket.handshake(asio::ssl::stream_base::client); - - auto buffers = request.toBuffers(); - asio::write(m_socket, buffers); - } - catch(const asio::system_error& e) - { - asio::error_code ec = e.code(); - if (ec) { - ::LogError(LOG_REST, "SecureClientConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); - - try - { - // initiate graceful connection closure - asio::error_code ignored_ec; - m_socket.lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); - } - catch(const std::exception& e) { - ::LogError(LOG_REST, "SecureClientConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); - } - } - } - } - - asio::ssl::stream m_socket; - - RequestHandlerType& m_requestHandler; - - std::size_t m_sizeToTransfer; - std::size_t m_bytesTransferred; - std::array m_fullBuffer; - - std::array m_buffer; - - HTTPPayload m_request; - HTTPLexer m_lexer; - HTTPPayload m_reply; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // ENABLE_SSL - -#endif // __REST_HTTP__SECURE_CLIENT_CONNECTION_H__ diff --git a/src/common/network/rest/http/SecureHTTPClient.h b/src/common/network/rest/http/SecureHTTPClient.h deleted file mode 100644 index 3e6366a3..00000000 --- a/src/common/network/rest/http/SecureHTTPClient.h +++ /dev/null @@ -1,206 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/** -* Digital Voice Modem - Common Library -* 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 SecureHTTPClient.h - * @ingroup http - */ -#if !defined(__REST_HTTP__SECURE_HTTP_CLIENT_H__) -#define __REST_HTTP__SECURE_HTTP_CLIENT_H__ - -#if defined(ENABLE_SSL) - -#include "common/Defines.h" -#include "common/network/rest/http/SecureClientConnection.h" -#include "common/network/rest/http/HTTPRequestHandler.h" -#include "common/Thread.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements top-level routines of the secure HTTP client. - * @tparam RequestHandlerType Type representing a request handler. - * @tparam ConnectionImpl Type representing the connection implementation. - * @ingroup http - */ - template class ConnectionImpl = SecureClientConnection> - class SecureHTTPClient : private Thread { - public: - auto operator=(SecureHTTPClient&) -> SecureHTTPClient& = delete; - auto operator=(SecureHTTPClient&&) -> SecureHTTPClient& = delete; - SecureHTTPClient(SecureHTTPClient&) = delete; - - /** - * @brief Initializes a new instance of the SecureHTTPClient class. - * @param address Hostname/IP Address. - * @param port Port. - */ - SecureHTTPClient(const std::string& address, uint16_t port) : - m_address(address), - m_port(port), - m_connection(nullptr), - m_ioContext(), - m_context(asio::ssl::context::tlsv12), - m_socket(m_ioContext), - m_requestHandler() - { - /* stub */ - } - /** - * @brief Finalizes a instance of the SecureHTTPClient class. - */ - ~SecureHTTPClient() override - { - if (m_connection != nullptr) { - close(); - } - } - - /** - * @brief Helper to set the HTTP request handlers. - * @tparam Handler Type representing the request handler. - * @param handler Request handler. - */ - template - void setHandler(Handler&& handler) - { - m_requestHandler = RequestHandlerType(std::forward(handler)); - } - - /** - * @brief Send HTTP request to HTTP server. - * @param request HTTP request. - * @returns True, if request was completed, otherwise false. - */ - bool request(HTTPPayload& request) - { - if (m_completed) { - return false; - } - - asio::post(m_ioContext, [this, request]() { - std::lock_guard guard(m_lock); - { - if (m_connection != nullptr) { - m_connection->send(request); - } - } - }); - - return true; - } - - /** - * @brief Opens connection to the network. - */ - bool open() - { - if (m_completed) { - return false; - } - - return run(); - } - - /** - * @brief Closes connection to the network. - */ - void close() - { - if (m_completed) { - return; - } - - m_completed = true; - m_ioContext.stop(); - - wait(); - } - - private: - /** - * @brief Internal entry point for the ASIO IO context thread. - */ - void entry() override - { - if (m_completed) { - return; - } - - asio::ip::tcp::resolver resolver(m_ioContext); - auto endpoints = resolver.resolve(m_address, std::to_string(m_port)); - - try { - connect(endpoints); - - // the entry() call will block until all asynchronous operations - // have finished - m_ioContext.run(); - } - catch (std::exception&) { /* stub */ } - - if (m_connection != nullptr) { - m_connection->stop(); - } - } - - /** - * @brief Perform an asynchronous connect operation. - * @param endpoints TCP endpoint to connect to. - */ - void connect(asio::ip::basic_resolver_results& endpoints) - { - asio::connect(m_socket, endpoints); - - m_connection = std::make_unique(std::move(m_socket), m_context, m_requestHandler); - m_connection->start(); - } - - std::string m_address; - uint16_t m_port; - - typedef ConnectionImpl ConnectionType; - - std::unique_ptr m_connection; - - bool m_completed = false; - asio::io_context m_ioContext; - - asio::ssl::context m_context; - asio::ip::tcp::socket m_socket; - - RequestHandlerType m_requestHandler; - - std::mutex m_lock; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // ENABLE_SSL - -#endif // __REST_HTTP__SECURE_HTTP_CLIENT_H__ diff --git a/src/common/network/rest/http/SecureHTTPServer.h b/src/common/network/rest/http/SecureHTTPServer.h deleted file mode 100644 index c19d5ab3..00000000 --- a/src/common/network/rest/http/SecureHTTPServer.h +++ /dev/null @@ -1,191 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file HTTPServer.h - * @ingroup http - */ -#if !defined(__REST_HTTP__SECURE_HTTP_SERVER_H__) -#define __REST_HTTP__SECURE_HTTP_SERVER_H__ - -#if defined(ENABLE_SSL) - -#include "common/Defines.h" -#include "common/network/rest/http/SecureServerConnection.h" -#include "common/network/rest/http/ServerConnectionManager.h" -#include "common/network/rest/http/HTTPRequestHandler.h" - -#include -#include -#include -#include -#include - -#include -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class implements top-level routines of the secure HTTP server. - * @tparam RequestHandlerType Type representing a request handler. - * @tparam ConnectionImpl Type representing the connection implementation. - * @ingroup http - */ - template class ConnectionImpl = SecureServerConnection> - class SecureHTTPServer { - public: - auto operator=(SecureHTTPServer&) -> SecureHTTPServer& = delete; - auto operator=(SecureHTTPServer&&) -> SecureHTTPServer& = delete; - SecureHTTPServer(SecureHTTPServer&) = delete; - - /** - * @brief Initializes a new instance of the SecureHTTPServer class. - * @param address Hostname/IP Address. - * @param port Port. - * @param debug Flag indicating whether or not verbose logging should be enabled. - */ - explicit SecureHTTPServer(const std::string& address, uint16_t port, bool debug) : - m_ioService(), - m_acceptor(m_ioService), - m_connectionManager(), - m_context(asio::ssl::context::tlsv12), - m_socket(m_ioService), - m_requestHandler(), - m_debug(debug) - { - asio::ip::address ipAddress = asio::ip::address::from_string(address); - m_endpoint = asio::ip::tcp::endpoint(ipAddress, port); - } - - /** - * @brief Helper to set the SSL certificate and private key. - * @param keyFile SSL certificate private key. - * @param certFile SSL certificate. - */ - bool setCertAndKey(const std::string& keyFile, const std::string& certFile) - { - try - { - m_context.use_certificate_chain_file(certFile); - m_context.use_private_key_file(keyFile, asio::ssl::context::pem); - return true; - } - catch(const std::exception& e) { - ::LogError(LOG_REST, "%s", e.what()); - return false; - } - } - - /** - * @brief Helper to set the HTTP request handlers. - * @tparam Handler Type representing the request handler. - * @param handler Request handler. - */ - template - void setHandler(Handler&& handler) - { - m_requestHandler = RequestHandlerType(std::forward(handler)); - } - - /** - * @brief Open TCP acceptor. - */ - void open() - { - // open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR) - m_acceptor.open(m_endpoint.protocol()); - m_acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); - m_acceptor.set_option(asio::socket_base::keep_alive(true)); - m_acceptor.bind(m_endpoint); - m_acceptor.listen(); - - accept(); - } - - /** - * @brief Run the servers ASIO IO service loop. - */ - void run() - { - // the run() call will block until all asynchronous operations - // have finished; while the server is running, there is always at least one - // asynchronous operation outstanding: the asynchronous accept call waiting - // for new incoming connections - m_ioService.run(); - } - - /** - * @brief Helper to stop running ASIO IO services. - */ - void stop() - { - // the server is stopped by cancelling all outstanding asynchronous - // operations; once all operations have finished the m_ioService::run() - // call will exit - m_acceptor.close(); - m_connectionManager.stopAll(); - } - - private: - /** - * @brief Perform an asynchronous accept operation. - */ - void accept() - { - m_acceptor.async_accept(m_socket, [this](asio::error_code ec) { - // check whether the server was stopped by a signal before this - // completion handler had a chance to run - if (!m_acceptor.is_open()) { - return; - } - - if (!ec) { - m_connectionManager.start(std::make_shared(std::move(m_socket), m_context, m_connectionManager, m_requestHandler, false, m_debug)); - } - - accept(); - }); - } - - typedef ConnectionImpl ConnectionType; - typedef std::shared_ptr ConnectionTypePtr; - - asio::io_service m_ioService; - asio::ip::tcp::acceptor m_acceptor; - - asio::ip::tcp::endpoint m_endpoint; - - ServerConnectionManager m_connectionManager; - - asio::ssl::context m_context; - asio::ip::tcp::socket m_socket; - - std::string m_certFile; - std::string m_keyFile; - - RequestHandlerType m_requestHandler; - bool m_debug; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // ENABLE_SSL - -#endif // __REST_HTTP__SECURE_HTTP_SERVER_H__ diff --git a/src/common/network/rest/http/SecureServerConnection.h b/src/common/network/rest/http/SecureServerConnection.h deleted file mode 100644 index 38c22bc6..00000000 --- a/src/common/network/rest/http/SecureServerConnection.h +++ /dev/null @@ -1,287 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file SecureServerConnection.h - * @ingroup http - */ -#if !defined(__REST_HTTP__SECURE_SERVER_CONNECTION_H__) -#define __REST_HTTP__SECURE_SERVER_CONNECTION_H__ - -#if defined(ENABLE_SSL) - -#include "common/Defines.h" -#include "common/network/rest/http/HTTPLexer.h" -#include "common/network/rest/http/HTTPPayload.h" -#include "common/Log.h" - -#include -#include -#include -#include - -#include -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Prototypes - // --------------------------------------------------------------------------- - - template class ServerConnectionManager; - - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class represents a single connection from a client. - * @tparam RequestHandlerType Type representing a request handler. - * @ingroup http - */ - template - class SecureServerConnection : public std::enable_shared_from_this> { - typedef SecureServerConnection selfType; - typedef std::shared_ptr selfTypePtr; - typedef ServerConnectionManager ConnectionManagerType; - public: - auto operator=(SecureServerConnection&) -> SecureServerConnection& = delete; - auto operator=(SecureServerConnection&&) -> SecureServerConnection& = delete; - SecureServerConnection(SecureServerConnection&) = delete; - - /** - * @brief Initializes a new instance of the SecureServerConnection class. - * @param socket TCP socket for this connection. - * @param context SSL context. - * @param manager Connection manager for this connection. - * @param handler Request handler for this connection. - * @param persistent Flag indicating whether or not the connection is persistent. - * @param debug Flag indicating whether or not verbose logging should be enabled. - */ - explicit SecureServerConnection(asio::ip::tcp::socket socket, asio::ssl::context& context, ConnectionManagerType& manager, RequestHandlerType& handler, - bool persistent = false, bool debug = false) : - m_socket(std::move(socket), context), - m_connectionManager(manager), - m_requestHandler(handler), - m_lexer(HTTPLexer(false)), - m_continue(false), - m_contResult(HTTPLexer::INDETERMINATE), - m_persistent(persistent), - m_debug(debug) - { - /* stub */ - } - - /** - * @brief Start the first asynchronous operation for the connection. - */ - void start() { handshake(); } - /** - * @brief Stop all asynchronous operations associated with the connection. - */ - void stop() - { - try - { - if (m_socket.lowest_layer().is_open()) { - m_socket.lowest_layer().close(); - } - } - catch(const std::exception&) { /* ignore */ } - } - - private: - /** - * @brief Perform an asynchronous SSL handshake. - */ - void handshake() - { - if (!m_persistent) { - auto self(this->shared_from_this()); - } - - m_socket.async_handshake(asio::ssl::stream_base::server, [this](asio::error_code ec) { - if (!ec) { - read(); - } - }); - } - - /** - * @brief Perform an asynchronous read operation. - */ - void read() - { - if (!m_persistent) { - auto self(this->shared_from_this()); - } - - m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t recvLength) { - if (!ec) { - HTTPLexer::ResultType result = HTTPLexer::GOOD; - char* content; - - // catch exceptions here so we don't blatently crash the system - try - { - if (!m_continue) { - std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + recvLength); - - m_request.content = std::string(); - std::string contentLength = m_request.headers.find("Content-Length"); - if (contentLength != "" && (::strlen(content) != 0)) { - size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); - m_request.contentLength = length; - m_request.content = std::string(content, length); - } - - m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string()); - - uint32_t consumed = m_lexer.consumed(); - if (result == HTTPLexer::GOOD && consumed == recvLength && - ((m_request.method == HTTP_POST) || (m_request.method == HTTP_PUT))) { - if (m_debug) { - LogDebug(LOG_REST, "HTTPS Partial Request, recvLength = %u, consumed = %u, result = %u", recvLength, consumed, result); - Utils::dump(1U, "SecureServerConnection::read(), m_buffer", (uint8_t*)m_buffer.data(), recvLength); - } - - result = HTTPLexer::INDETERMINATE; - m_continue = true; - } - } else { - if (m_debug) { - LogDebug(LOG_REST, "HTTP Partial Request, recvLength = %u, result = %u", recvLength, result); - Utils::dump(1U, "SecureServerConnection::read(), m_buffer", (uint8_t*)m_buffer.data(), recvLength); - } - - if (m_contResult == HTTPLexer::INDETERMINATE) { - m_request.content = std::string(m_buffer.data(), recvLength); - } else { - m_request.content.append(std::string(m_buffer.data(), recvLength)); - } - - if (m_request.contentLength != 0 && recvLength < m_request.contentLength) { - m_contResult = result = HTTPLexer::CONTINUE; - m_continue = true; - } - } - - if (result == HTTPLexer::GOOD) { - if (m_debug) { - Utils::dump(1U, "SecureServerConnection::read(), HTTPS Request Content", (uint8_t*)m_request.content.c_str(), m_request.content.length()); - } - - m_continue = false; - m_contResult = HTTPLexer::INDETERMINATE; - m_requestHandler.handleRequest(m_request, m_reply); - - if (m_debug) { - Utils::dump(1U, "SecureServerConnection::read(), HTTPS Reply Content", (uint8_t*)m_reply.content.c_str(), m_reply.content.length()); - } - - write(); - } - else if (result == HTTPLexer::BAD) { - m_continue = false; - m_contResult = HTTPLexer::INDETERMINATE; - m_reply = HTTPPayload::statusPayload(HTTPPayload::BAD_REQUEST); - write(); - } - else { - read(); - } - } - catch(const std::exception& e) { - ::LogError(LOG_REST, "SecureServerConnection::read(), %s", ec.message().c_str()); - m_continue = false; - m_contResult = HTTPLexer::INDETERMINATE; - } - } - else if (ec != asio::error::operation_aborted) { - if (ec) { - ::LogError(LOG_REST, "SecureServerConnection::read(), %s, code = %u", ec.message().c_str(), ec.value()); - } - m_connectionManager.stop(this->shared_from_this()); - m_continue = false; - } - }); - } - - /** - * @brief Perform an asynchronous write operation. - */ - void write() - { - if (!m_persistent) { - auto self(this->shared_from_this()); - } else { - m_reply.headers.add("Connection", "keep-alive"); - } - - auto buffers = m_reply.toBuffers(); - asio::async_write(m_socket, buffers, [=](asio::error_code ec, std::size_t) { - if (m_persistent) { - m_lexer.reset(); - m_reply.headers = HTTPHeaders(); - m_reply.status = HTTPPayload::OK; - m_reply.content = ""; - m_request = HTTPPayload(); - read(); - } - else { - if (!ec) { - try - { - // initiate graceful connection closure - asio::error_code ignored_ec; - m_socket.lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); - } - catch(const std::exception& e) { ::LogError(LOG_REST, "%s", ec.message().c_str()); } - } - - if (ec != asio::error::operation_aborted) { - if (ec) { - ::LogError(LOG_REST, "SecureServerConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); - } - m_connectionManager.stop(this->shared_from_this()); - } - } - }); - } - - asio::ssl::stream m_socket; - - ConnectionManagerType& m_connectionManager; - RequestHandlerType& m_requestHandler; - - std::array m_buffer; - - HTTPPayload m_request; - HTTPLexer m_lexer; - HTTPPayload m_reply; - - bool m_continue; - HTTPLexer::ResultType m_contResult; - - bool m_persistent; - bool m_debug; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // ENABLE_SSL - -#endif // __REST_HTTP__SECURE_SERVER_CONNECTION_H__ diff --git a/src/common/network/rest/http/ServerConnection.h b/src/common/network/rest/http/ServerConnection.h deleted file mode 100644 index c7b932a3..00000000 --- a/src/common/network/rest/http/ServerConnection.h +++ /dev/null @@ -1,267 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file ServerConnection.h - * @ingroup http - */ -#if !defined(__REST_HTTP__SERVER_CONNECTION_H__) -#define __REST_HTTP__SERVER_CONNECTION_H__ - -#include "common/Defines.h" -#include "common/network/rest/http/HTTPLexer.h" -#include "common/network/rest/http/HTTPPayload.h" -#include "common/Log.h" -#include "common/Utils.h" - -#include -#include -#include -#include - -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Prototypes - // --------------------------------------------------------------------------- - - template class ServerConnectionManager; - - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief This class represents a single connection from a client. - * @tparam RequestHandlerType Type representing a request handler. - * @ingroup http - */ - template - class ServerConnection : public std::enable_shared_from_this> { - typedef ServerConnection selfType; - typedef std::shared_ptr selfTypePtr; - typedef ServerConnectionManager ConnectionManagerType; - public: - auto operator=(ServerConnection&) -> ServerConnection& = delete; - auto operator=(ServerConnection&&) -> ServerConnection& = delete; - ServerConnection(ServerConnection&) = delete; - - /** - * @brief Initializes a new instance of the ServerConnection class. - * @param socket TCP socket for this connection. - * @param manager Connection manager for this connection. - * @param handler Request handler for this connection. - * @param persistent Flag indicating whether or not the connection is persistent. - * @param debug Flag indicating whether or not verbose logging should be enabled. - */ - explicit ServerConnection(asio::ip::tcp::socket socket, ConnectionManagerType& manager, RequestHandlerType& handler, - bool persistent = false, bool debug = false) : - m_socket(std::move(socket)), - m_connectionManager(manager), - m_requestHandler(handler), - m_lexer(HTTPLexer(false)), - m_continue(false), - m_contResult(HTTPLexer::INDETERMINATE), - m_persistent(persistent), - m_debug(debug) - { - /* stub */ - } - - /** - * @brief Start the first asynchronous operation for the connection. - */ - void start() { read(); } - /** - * @brief Stop all asynchronous operations associated with the connection. - */ - void stop() - { - try - { - if (m_socket.is_open()) { - m_socket.close(); - } - } - catch(const std::exception&) { /* ignore */ } - } - - private: - /** - * @brief Perform an asynchronous read operation. - */ - void read() - { - if (!m_persistent) { - auto self(this->shared_from_this()); - } - - m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t recvLength) { - if (!ec) { - HTTPLexer::ResultType result = HTTPLexer::GOOD; - char* content; - - // catch exceptions here so we don't blatently crash the system - try - { - if (!m_continue) { - std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + recvLength); - - m_request.content = std::string(); - std::string contentLength = m_request.headers.find("Content-Length"); - if (contentLength != "" && (::strlen(content) != 0)) { - size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); - m_request.contentLength = length; - m_request.content = std::string(content, length); - } - - m_request.headers.add("RemoteHost", m_socket.remote_endpoint().address().to_string()); - - uint32_t consumed = m_lexer.consumed(); - if (result == HTTPLexer::GOOD && consumed == recvLength && - ((m_request.method == HTTP_POST) || (m_request.method == HTTP_PUT))) { - if (m_debug) { - LogDebug(LOG_REST, "HTTP Partial Request, recvLength = %u, consumed = %u, result = %u", recvLength, consumed, result); - Utils::dump(1U, "ServerConnection::read(), m_buffer", (uint8_t*)m_buffer.data(), recvLength); - } - - m_contResult = result = HTTPLexer::INDETERMINATE; - m_continue = true; - } - } else { - if (m_debug) { - LogDebug(LOG_REST, "HTTP Partial Request, recvLength = %u, result = %u", recvLength, result); - Utils::dump(1U, "ServerConnection::read(), m_buffer", (uint8_t*)m_buffer.data(), recvLength); - } - - if (m_contResult == HTTPLexer::INDETERMINATE) { - m_request.content = std::string(m_buffer.data(), recvLength); - } else { - m_request.content.append(std::string(m_buffer.data(), recvLength)); - } - - if (m_request.contentLength != 0 && recvLength < m_request.contentLength) { - m_contResult = result = HTTPLexer::CONTINUE; - m_continue = true; - } - } - - if (result == HTTPLexer::GOOD) { - if (m_debug) { - Utils::dump(1U, "ServerConnection::read(), HTTP Request Content", (uint8_t*)m_request.content.c_str(), m_request.content.length()); - } - - m_continue = false; - m_contResult = HTTPLexer::INDETERMINATE; - m_requestHandler.handleRequest(m_request, m_reply); - - if (m_debug) { - Utils::dump(1U, "ServerConnection::read(), HTTP Reply Content", (uint8_t*)m_reply.content.c_str(), m_reply.content.length()); - } - - write(); - } - else if (result == HTTPLexer::BAD) { - m_continue = false; - m_contResult = HTTPLexer::INDETERMINATE; - m_reply = HTTPPayload::statusPayload(HTTPPayload::BAD_REQUEST); - write(); - } - else { - read(); - } - } - catch(const std::exception& e) { - ::LogError(LOG_REST, "ServerConnection::read(), %s", ec.message().c_str()); - m_continue = false; - m_contResult = HTTPLexer::INDETERMINATE; - } - } - else if (ec != asio::error::operation_aborted) { - if (ec) { - ::LogError(LOG_REST, "ServerConnection::read(), %s, code = %u", ec.message().c_str(), ec.value()); - } - m_connectionManager.stop(this->shared_from_this()); - m_continue = false; - m_contResult = HTTPLexer::INDETERMINATE; - } - }); - } - - /** - * @brief Perform an asynchronous write operation. - */ - void write() - { - if (!m_persistent) { - auto self(this->shared_from_this()); - } else { - m_reply.headers.add("Connection", "keep-alive"); - } - - auto buffers = m_reply.toBuffers(); - asio::async_write(m_socket, buffers, [=](asio::error_code ec, std::size_t) { - if (m_persistent) { - m_lexer.reset(); - m_reply.headers = HTTPHeaders(); - m_reply.status = HTTPPayload::OK; - m_reply.content = ""; - m_request = HTTPPayload(); - read(); - } - else { - if (!ec) { - try - { - // initiate graceful connection closure - asio::error_code ignored_ec; - m_socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); - } - catch(const std::exception& e) { ::LogError(LOG_REST, "ServerConnection::write(), %s", ec.message().c_str()); } - } - - if (ec != asio::error::operation_aborted) { - if (ec) { - ::LogError(LOG_REST, "ServerConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); - } - m_connectionManager.stop(this->shared_from_this()); - } - } - }); - } - - asio::ip::tcp::socket m_socket; - - ConnectionManagerType& m_connectionManager; - RequestHandlerType& m_requestHandler; - - std::array m_buffer; - - HTTPPayload m_request; - HTTPLexer m_lexer; - HTTPPayload m_reply; - - bool m_continue; - HTTPLexer::ResultType m_contResult; - - bool m_persistent; - bool m_debug; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // __REST_HTTP__SERVER_CONNECTION_H__ diff --git a/src/common/network/rest/http/ServerConnectionManager.h b/src/common/network/rest/http/ServerConnectionManager.h deleted file mode 100644 index dc4e4840..00000000 --- a/src/common/network/rest/http/ServerConnectionManager.h +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: BSL-1.0 -/* - * Digital Voice Modem - Common Library - * BSL-1.0 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (c) 2003-2013 Christopher M. Kohlhoff - * Copyright (C) 2023 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file ServerConnection.h - * @ingroup http - */ -#if !defined(__REST_HTTP__SERVER_CONNECTION_MANAGER_H__) -#define __REST_HTTP__SERVER_CONNECTION_MANAGER_H__ - -#include "common/Defines.h" - -#include -#include - -namespace network -{ - namespace rest - { - namespace http - { - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief Manages open connections so that they may be cleanly stopped when the server - * needs to shut down. - * @tparam ConnectionPtr - * @ingroup http - */ - template - class ServerConnectionManager { - public: - auto operator=(ServerConnectionManager&) -> ServerConnectionManager& = delete; - auto operator=(ServerConnectionManager&&) -> ServerConnectionManager& = delete; - ServerConnectionManager(ServerConnectionManager&) = delete; - - /** - * @brief Initializes a new instance of the ServerConnectionManager class. - */ - ServerConnectionManager() = default; - - /** - * @brief Add the specified connection to the manager and start it. - * @param c - */ - void start(ConnectionPtr c) - { - std::lock_guard guard(m_lock); - { - m_connections.insert(c); - } - c->start(); - } - - /** - * @brief Stop the specified connection. - * @param c - */ - void stop(ConnectionPtr c) - { - std::lock_guard guard(m_lock); - { - m_connections.erase(c); - } - c->stop(); - } - - /** - * @brief Stop all connections. - */ - void stopAll() - { - for (auto c : m_connections) - c->stop(); - - std::lock_guard guard(m_lock); - m_connections.clear(); - } - - private: - std::set m_connections; - std::mutex m_lock; - }; - } // namespace http - } // namespace rest -} // namespace network - -#endif // __REST_HTTP__SERVER_CONNECTION_MANAGER_H__ diff --git a/src/common/restapi/RequestDispatcher.h b/src/common/restapi/RequestDispatcher.h new file mode 100644 index 00000000..1470be32 --- /dev/null +++ b/src/common/restapi/RequestDispatcher.h @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2023 Bryan Biedenkapp, N2PLL + * + */ +/** + * @defgroup rest REST Services + * @brief Implementation for REST services. + * @defgroup http Embedded HTTP Core + * @brief Implementation for basic HTTP services. + * @ingroup rest + * + * @file RequestDispatcher.h + * @ingroup rest + */ +#if !defined(__REST__DISPATCHER_H__) +#define __REST__DISPATCHER_H__ + +#include "common/Defines.h" +#include "common/restapi/http/HTTPPayload.h" +#include "common/Log.h" + +#include +#include +#include +#include +#include + +namespace restapi +{ + // --------------------------------------------------------------------------- + // Structure Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Structure representing a REST API request match. + * @ingroup rest + */ + struct RequestMatch : std::smatch { + /** + * @brief Initializes a new instance of the RequestMatch structure. + * @param m String matcher. + * @param c Content. + */ + RequestMatch(const std::smatch& m, const std::string& c) : std::smatch(m), content(c) { /* stub */ } + + std::string content; + }; + + // --------------------------------------------------------------------------- + // Structure Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Structure representing a request matcher. + * @ingroup rest + */ + template + struct RequestMatcher { + typedef std::function RequestHandlerType; + + /** + * @brief Initializes a new instance of the RequestMatcher structure. + * @param expression Matching expression. + */ + explicit RequestMatcher(const std::string& expression) : m_expression(expression), m_isRegEx(false) { /* stub */ } + + /** + * @brief Handler for GET requests. + * @param handler GET request handler. + * @return RequestMatcher* Instance of a RequestMatcher. + */ + RequestMatcher& get(RequestHandlerType handler) { + m_handlers[HTTP_GET] = handler; + return *this; + } + /** + * @brief Handler for POST requests. + * @param handler POST request handler. + * @return RequestMatcher* Instance of a RequestMatcher. + */ + RequestMatcher& post(RequestHandlerType handler) { + m_handlers[HTTP_POST] = handler; + return *this; + } + /** + * @brief Handler for PUT requests. + * @param handler PUT request handler. + * @return RequestMatcher* Instance of a RequestMatcher. + */ + RequestMatcher& put(RequestHandlerType handler) { + m_handlers[HTTP_PUT] = handler; + return *this; + } + /** + * @brief Handler for DELETE requests. + * @param handler DELETE request handler. + * @return RequestMatcher* Instance of a RequestMatcher. + */ + RequestMatcher& del(RequestHandlerType handler) { + m_handlers[HTTP_DELETE] = handler; + return *this; + } + /** + * @brief Handler for OPTIONS requests. + * @param handler OPTIONS request handler. + * @return RequestMatcher* Instance of a RequestMatcher. + */ + RequestMatcher& options(RequestHandlerType handler) { + m_handlers[HTTP_OPTIONS] = handler; + return *this; + } + + /** + * @brief Helper to determine if the request matcher is a regular expression. + * @returns bool True, if request matcher is a regular expression, otherwise false. + */ + bool regex() const { return m_isRegEx; } + /** + * @brief Helper to set the regular expression flag. + * @param regEx Flag indicating whether or not the request matcher is a regular expression. + */ + void setRegEx(bool regEx) { m_isRegEx = regEx; } + + /** + * @brief Helper to handle the actual request. + * @param request HTTP request. + * @param reply HTTP reply. + * @param what What matched. + */ + void handleRequest(const Request& request, Reply& reply, const std::smatch &what) { + // dispatching to matching based on handler + RequestMatch match(what, request.content); + auto& handler = m_handlers[request.method]; + if (handler) { + handler(request, reply, match); + } + } + + private: + std::string m_expression; + bool m_isRegEx; + std::map m_handlers; + }; + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class implements RESTful web request dispatching. + * @tparam Request HTTP request. + * @tparam Reply HTTP reply. + */ + template + class RequestDispatcher { + typedef RequestMatcher MatcherType; + public: + /** + * @brief Initializes a new instance of the RequestDispatcher class. + */ + RequestDispatcher() : m_basePath(), m_debug(false) { /* stub */ } + /** + * @brief Initializes a new instance of the RequestDispatcher class. + * @param debug Flag indicating whether or not verbose logging should be enabled. + */ + RequestDispatcher(bool debug) : m_basePath(), m_debug(debug) { /* stub */ } + /** + * @brief Initializes a new instance of the RequestDispatcher class. + * @param basePath + * @param debug Flag indicating whether or not verbose logging should be enabled. + */ + RequestDispatcher(const std::string& basePath, bool debug) : m_basePath(basePath), m_debug(debug) { /* stub */ } + + /** + * @brief Helper to match a request patch. + * @param expression Matching expression. + * @param regex Flag indicating whether or not this match is a regular expression. + * @returns MatcherType Instance of a request matcher. + */ + MatcherType& match(const std::string& expression, bool regex = false) + { + MatcherTypePtr& p = m_matchers[expression]; + if (!p) { + if (m_debug) { + ::LogDebug(LOG_REST, "creating RequestDispatcher, expression = %s", expression.c_str()); + } + p = std::make_shared(expression); + } else { + if (m_debug) { + ::LogDebug(LOG_REST, "fetching RequestDispatcher, expression = %s", expression.c_str()); + } + } + + p->setRegEx(regex); + return *p; + } + + /** + * @brief Helper to handle HTTP request. + * @param request HTTP request. + * @param reply HTTP reply. + */ + void handleRequest(const Request& request, Reply& reply) + { + for (const auto& matcher : m_matchers) { + std::smatch what; + if (!matcher.second->regex()) { + if (request.uri.find(matcher.first) != std::string::npos) { + if (m_debug) { + ::LogDebug(LOG_REST, "non-regex endpoint, uri = %s, expression = %s", request.uri.c_str(), matcher.first.c_str()); + } + + //what = matcher.first; + + // ensure CORS headers are added + reply.headers.add("Access-Control-Allow-Origin", "*"); + reply.headers.add("Access-Control-Allow-Methods", "*"); + reply.headers.add("Access-Control-Allow-Headers", "*"); + + if (request.method == HTTP_OPTIONS) { + reply.status = http::HTTPPayload::OK; + } + + matcher.second->handleRequest(request, reply, what); + return; + } + } else { + if (std::regex_match(request.uri, what, std::regex(matcher.first))) { + if (m_debug) { + ::LogDebug(LOG_REST, "regex endpoint, uri = %s, expression = %s", request.uri.c_str(), matcher.first.c_str()); + } + + matcher.second->handleRequest(request, reply, what); + return; + } + } + } + + ::LogError(LOG_REST, "unknown endpoint, uri = %s", request.uri.c_str()); + reply = http::HTTPPayload::statusPayload(http::HTTPPayload::BAD_REQUEST, "application/json"); + } + + private: + typedef std::shared_ptr MatcherTypePtr; + + std::string m_basePath; + std::map m_matchers; + + bool m_debug; + }; + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class implements a generic basic request dispatcher. + * @tparam Request HTTP request. + * @tparam Reply HTTP reply. + */ + template + class BasicRequestDispatcher { + public: + typedef std::function RequestHandlerType; + + /** + * @brief Initializes a new instance of the BasicRequestDispatcher class. + */ + BasicRequestDispatcher() { /* stub */ } + /** + * @brief Initializes a new instance of the BasicRequestDispatcher class. + * @param handler Instance of a RequestHandlerType for this dispatcher. + */ + BasicRequestDispatcher(RequestHandlerType handler) : m_handler(handler) { /* stub */ } + + /** + * @brief Helper to handle HTTP request. + * @param request HTTP request. + * @param reply HTTP reply. + */ + void handleRequest(const Request& request, Reply& reply) + { + if (m_handler) { + m_handler(request, reply); + } + } + + private: + RequestHandlerType m_handler; + }; + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class implements a generic debug request dispatcher. + * @tparam Request HTTP request. + * @tparam Reply HTTP reply. + */ + template + class DebugRequestDispatcher { + public: + /** + * @brief Initializes a new instance of the DebugRequestDispatcher class. + */ + DebugRequestDispatcher() { /* stub */ } + + /** + * @brief Helper to handle HTTP request. + * @param request HTTP request. + * @param reply HTTP reply. + */ + void handleRequest(const Request& request, Reply& reply) + { + for (auto header : request.headers.headers()) + ::LogDebugEx(LOG_REST, "DebugRequestDispatcher::handleRequest()", "header = %s, value = %s", header.name.c_str(), header.value.c_str()); + + ::LogDebugEx(LOG_REST, "DebugRequestDispatcher::handleRequest()", "content = %s", request.content.c_str()); + } + }; + + typedef RequestDispatcher DefaultRequestDispatcher; +} // namespace restapi + +#endif // __REST__DISPATCHER_H__ diff --git a/src/common/restapi/http/ClientConnection.h b/src/common/restapi/http/ClientConnection.h new file mode 100644 index 00000000..0b370cdf --- /dev/null +++ b/src/common/restapi/http/ClientConnection.h @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file ClientConnection.h + * @ingroup http + */ +#if !defined(__REST_HTTP__CLIENT_CONNECTION_H__) +#define __REST_HTTP__CLIENT_CONNECTION_H__ + +#include "common/Defines.h" +#include "common/restapi/http/HTTPLexer.h" +#include "common/restapi/http/HTTPPayload.h" +#include "common/Log.h" + +#include +#include +#include +#include + +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class represents a single connection from a client. + * @tparam RequestHandlerType Type representing a request handler. + * @ingroup http + */ + template + class ClientConnection { + public: + auto operator=(ClientConnection&) -> ClientConnection& = delete; + auto operator=(ClientConnection&&) -> ClientConnection& = delete; + ClientConnection(ClientConnection&) = delete; + + /** + * @brief Initializes a new instance of the ClientConnection class. + * @param socket TCP socket for this connection. + * @param handler Request handler for this connection. + */ + explicit ClientConnection(asio::ip::tcp::socket socket, RequestHandlerType& handler) : + m_socket(std::move(socket)), + m_requestHandler(handler), + m_lexer(HTTPLexer(true)) + { + /* stub */ + } + + /** + * @brief Start the first asynchronous operation for the connection. + */ + void start() { read(); } + /** + * @brief Stop all asynchronous operations associated with the connection. + */ + void stop() + { + try + { + ensureNoLinger(); + if (m_socket.is_open()) { + m_socket.close(); + } + } + catch(const std::exception&) { /* ignore */ } + } + + /** + * @brief Helper to enable the SO_LINGER socket option during shutdown. + */ + void ensureNoLinger() + { + try + { + // enable SO_LINGER timeout 0 + asio::socket_base::linger linger(true, 0); + m_socket.set_option(linger); + } + catch(const asio::system_error& e) + { + asio::error_code ec = e.code(); + if (ec) { + ::LogError(LOG_REST, "ClientConnection::ensureNoLinger(), %s, code = %u", ec.message().c_str(), ec.value()); + } + } + } + + /** + * @brief Perform an synchronous write operation. + * @param request HTTP request payload. + */ + void send(HTTPPayload request) + { + m_sizeToTransfer = m_bytesTransferred = 0U; + request.attachHostHeader(m_socket.remote_endpoint()); + write(request); + } + private: + /** + * @brief Perform an asynchronous read operation. + */ + void read() + { + m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t bytes_transferred) { + if (!ec) { + HTTPLexer::ResultType result; + char* content; + + try + { + if (m_sizeToTransfer > 0U && (m_bytesTransferred + bytes_transferred) < m_sizeToTransfer) { + ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); + m_bytesTransferred += bytes_transferred; + + read(); + } + else { + if (m_sizeToTransfer > 0U) { + // final copy + ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); + m_bytesTransferred += bytes_transferred; + + m_sizeToTransfer = 0U; + bytes_transferred = m_bytesTransferred; + + // reset lexer and re-parse the full content + m_lexer.reset(); + std::tie(result, content) = m_lexer.parse(m_request, m_fullBuffer.data(), m_fullBuffer.data() + bytes_transferred); + } else { + ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); + m_bytesTransferred += bytes_transferred; + + std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); + } + + // determine content length + std::string contentLength = m_request.headers.find("Content-Length"); + if (contentLength != "") { + size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); + + // setup a full read if necessary + if (length > bytes_transferred && m_sizeToTransfer == 0U) { + m_sizeToTransfer = length; + } + + if (m_sizeToTransfer > 0U) { + result = HTTPLexer::CONTINUE; + } else { + m_request.content = std::string(content, length); + } + } + + m_request.headers.add("RemoteHost", m_socket.remote_endpoint().address().to_string()); + + if (result == HTTPLexer::GOOD) { + m_sizeToTransfer = m_bytesTransferred = 0U; + m_requestHandler.handleRequest(m_request, m_reply); + } + else if (result == HTTPLexer::BAD) { + m_sizeToTransfer = m_bytesTransferred = 0U; + return; + } + else { + read(); + } + } + } + catch(const std::exception& e) { ::LogError(LOG_REST, "ClientConnection::read(), %s", ec.message().c_str()); } + } + else if (ec != asio::error::operation_aborted) { + if (ec) { + ::LogError(LOG_REST, "ClientConnection::read(), %s, code = %u", ec.message().c_str(), ec.value()); + } + stop(); + } + }); + } + + /** + * @brief Perform an synchronous write operation. + * @param request HTTP request payload. + */ + void write(HTTPPayload request) + { + try + { + auto buffers = request.toBuffers(); + asio::write(m_socket, buffers); + } + catch(const asio::system_error& e) + { + asio::error_code ec = e.code(); + if (ec) { + ::LogError(LOG_REST, "ClientConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); + + try + { + // initiate graceful connection closure + asio::error_code ignored_ec; + m_socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); + } + catch(const std::exception& e) { + ::LogError(LOG_REST, "ClientConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); + } + } + } + } + + asio::ip::tcp::socket m_socket; + + RequestHandlerType& m_requestHandler; + + std::size_t m_sizeToTransfer; + std::size_t m_bytesTransferred; + std::array m_fullBuffer; + + std::array m_buffer; + + HTTPPayload m_request; + HTTPLexer m_lexer; + HTTPPayload m_reply; + }; + } // namespace http +} // namespace restapi + +#endif // __REST_HTTP__CLIENT_CONNECTION_H__ diff --git a/src/common/restapi/http/HTTPClient.h b/src/common/restapi/http/HTTPClient.h new file mode 100644 index 00000000..7f2f76e8 --- /dev/null +++ b/src/common/restapi/http/HTTPClient.h @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2023 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file HTTPClient.h + * @ingroup http + */ +#if !defined(__REST_HTTP__HTTP_CLIENT_H__) +#define __REST_HTTP__HTTP_CLIENT_H__ + +#include "common/Defines.h" +#include "common/restapi/http/ClientConnection.h" +#include "common/restapi/http/HTTPRequestHandler.h" +#include "common/Thread.h" + +#include +#include +#include +#include +#include +#include + +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class implements top-level routines of the HTTP client. + * @tparam RequestHandlerType Type representing a request handler. + * @tparam ConnectionImpl Type representing the connection implementation. + * @ingroup http + */ + template class ConnectionImpl = ClientConnection> + class HTTPClient : private Thread { + public: + auto operator=(HTTPClient&) -> HTTPClient& = delete; + auto operator=(HTTPClient&&) -> HTTPClient& = delete; + HTTPClient(HTTPClient&) = delete; + + /** + * @brief Initializes a new instance of the HTTPClient class. + * @param address Hostname/IP Address. + * @param port Port. + */ + HTTPClient(const std::string& address, uint16_t port) : + m_address(address), + m_port(port), + m_connection(nullptr), + m_ioContext(), + m_socket(m_ioContext), + m_requestHandler() + { + /* stub */ + } + /** + * @brief Finalizes a instance of the HTTPClient class. + */ + ~HTTPClient() override + { + if (m_connection != nullptr) { + close(); + } + } + + /** + * @brief Helper to set the HTTP request handlers. + * @tparam Handler Type representing the request handler. + * @param handler Request handler. + */ + template + void setHandler(Handler&& handler) + { + m_requestHandler = RequestHandlerType(std::forward(handler)); + } + + /** + * @brief Send HTTP request to HTTP server. + * @param request HTTP request. + * @returns True, if request was completed, otherwise false. + */ + bool request(HTTPPayload& request) + { + if (m_completed) { + return false; + } + + asio::post(m_ioContext, [this, request]() { + std::lock_guard guard(m_lock); + { + if (m_connection != nullptr) { + m_connection->send(request); + } + } + }); + + return true; + } + + /** + * @brief Opens connection to the network. + */ + bool open() + { + if (m_completed) { + return false; + } + + return run(); + } + + /** + * @brief Closes connection to the network. + */ + void close() + { + if (m_completed) { + return; + } + + m_completed = true; + m_ioContext.stop(); + + wait(); + } + + private: + /** + * @brief Internal entry point for the ASIO IO context thread. + */ + void entry() override + { + if (m_completed) { + return; + } + + asio::ip::tcp::resolver resolver(m_ioContext); + auto endpoints = resolver.resolve(m_address, std::to_string(m_port)); + + try { + connect(endpoints); + + // the entry() call will block until all asynchronous operations + // have finished + m_ioContext.run(); + } + catch (std::exception&) { /* stub */ } + + if (m_connection != nullptr) { + m_connection->stop(); + } + } + + /** + * @brief Perform an asynchronous connect operation. + * @param endpoints TCP endpoint to connect to. + */ + void connect(asio::ip::basic_resolver_results& endpoints) + { + asio::connect(m_socket, endpoints); + + m_connection = std::make_unique(std::move(m_socket), m_requestHandler); + m_connection->start(); + } + + std::string m_address; + uint16_t m_port; + + typedef ConnectionImpl ConnectionType; + + std::unique_ptr m_connection; + + bool m_completed = false; + asio::io_context m_ioContext; + + asio::ip::tcp::socket m_socket; + + RequestHandlerType m_requestHandler; + + std::mutex m_lock; + }; + } // namespace http +} // namespace restapi + +#endif // __REST_HTTP__HTTP_CLIENT_H__ diff --git a/src/common/restapi/http/HTTPHeaders.h b/src/common/restapi/http/HTTPHeaders.h new file mode 100644 index 00000000..7dc6daf0 --- /dev/null +++ b/src/common/restapi/http/HTTPHeaders.h @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: BSL-1.0 +/* + * Digital Voice Modem - Common Library + * BSL-1.0 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (c) 2003-2013 Christopher M. Kohlhoff + * Copyright (C) 2023 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file HTTPClient.h + * @ingroup http + */ +#if !defined(__REST_HTTP__HTTP_HEADERS_H__) +#define __REST_HTTP__HTTP_HEADERS_H__ + +#include "common/Defines.h" +#include "common/Log.h" +#include "common/Utils.h" + +#include +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Prototypes + // --------------------------------------------------------------------------- + + struct HTTPPayload; + + // --------------------------------------------------------------------------- + // Structure Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents HTTP headers. + * @ingroup http + */ + struct HTTPHeaders { + /** + * @brief Structure representing an individual HTTP header. + * @ingroup http + */ + struct Header + { + /** + * @brief Header name. + */ + std::string name; + /** + * @brief Header value. + */ + std::string value; + + /** + * @brief Initializes a new instance of the Header class. + */ + Header() : name{}, value{} { /* stub */} + /** + * @brief Initializes a new instance of the Header class + * @param n Header name. + * @param v Header value. + */ + Header(std::string n, std::string v) : name{n}, value{v} { /* stub */ } + }; + + /** + * @brief Gets the list of HTTP headers. + * @returns std::vector
List of HTTP headers. + */ + std::vector
headers() const { return m_headers; } + /** + * @brief Returns true if the headers are empty. + * @returns bool True, if no HTTP headers are present, otherwise false. + */ + bool empty() const { return m_headers.empty(); } + /** + * @brief Returns the number of headers. + * @returns std::size_t Number of headers. + */ + std::size_t size() const { return m_headers.size(); } + /** + * @brief Clears the list of HTTP headers. + */ + void clearHeaders() { m_headers = std::vector
(); } + /** + * @brief Helper to add a HTTP header. + * @param name Header name. + * @param value Header value. + */ + void add(const std::string& name, const std::string& value) + { + //::LogDebugEx(LOG_REST, "HTTPHeaders::add()", "header = %s, value = %s", name.c_str(), value.c_str()); + for (auto& header : m_headers) { + if (::strtolower(header.name) == ::strtolower(name)) { + header.value = value; + return; + } + } + + m_headers.push_back(Header(name, value)); + //for (auto header : m_headers) + // ::LogDebugEx(LOG_REST, "HTTPHeaders::add()", "m_headers.header = %s, m_headers.value = %s", header.name.c_str(), header.value.c_str()); + } + /** + * @brief Helper to remove a HTTP header. + * @param headerName Header name. + */ + void remove(const std::string headerName) + { + auto header = std::find_if(m_headers.begin(), m_headers.end(), [&](const Header& h) { + return ::strtolower(h.name) == ::strtolower(headerName); + }); + + if (header != m_headers.end()) { + m_headers.erase(header); + } + } + /** + * @brief Helper to find the named HTTP header. + * @param headerName Header name. + * @returns std::string Value of named header (if any). + */ + std::string find(const std::string headerName) const + { + auto header = std::find_if(m_headers.begin(), m_headers.end(), [&](const Header& h) { + return ::strtolower(h.name) == ::strtolower(headerName); + }); + + if (header != m_headers.end()) { + return header->value; + } + else { + return ""; + } + } + + private: + friend struct HTTPPayload; + std::vector
m_headers; + }; + } // namespace http +} // namespace restapi + +#endif // __REST_HTTP__HTTP_HEADERS_H__ diff --git a/src/common/network/rest/http/HTTPLexer.cpp b/src/common/restapi/http/HTTPLexer.cpp similarity index 98% rename from src/common/network/rest/http/HTTPLexer.cpp rename to src/common/restapi/http/HTTPLexer.cpp index 668d1820..63594410 100644 --- a/src/common/network/rest/http/HTTPLexer.cpp +++ b/src/common/restapi/http/HTTPLexer.cpp @@ -9,11 +9,11 @@ * */ #include "Defines.h" -#include "network/rest/http/HTTPLexer.h" -#include "network/rest/http/HTTPPayload.h" +#include "restapi/http/HTTPLexer.h" +#include "restapi/http/HTTPPayload.h" #include "Log.h" -using namespace network::rest::http; +using namespace restapi::http; #include diff --git a/src/common/restapi/http/HTTPLexer.h b/src/common/restapi/http/HTTPLexer.h new file mode 100644 index 00000000..8f62d63c --- /dev/null +++ b/src/common/restapi/http/HTTPLexer.h @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: BSL-1.0 +/* + * Digital Voice Modem - Common Library + * BSL-1.0 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (c) 2003-2013 Christopher M. Kohlhoff + * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file HTTPLexer.h + * @ingroup http + * @file HTTPLexer.cpp + * @ingroup http + */ +#if !defined(__REST_HTTP__HTTP_LEXER_H__) +#define __REST_HTTP__HTTP_LEXER_H__ + +#include "common/Defines.h" + +#include +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Prototypes + // --------------------------------------------------------------------------- + + struct HTTPPayload; + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class implements the lexer for incoming payloads. + */ + class HTTPLexer { + public: + /** + * @brief Lexing result. + */ + enum ResultType { GOOD, BAD, INDETERMINATE, CONTINUE }; + + /** + * @brief Initializes a new instance of the HTTPLexer class. + * @param clientLexer Flag indicating this lexer is used for a HTTP client. + */ + HTTPLexer(bool clientLexer); + + /** + * @brief Reset to initial parser state. + */ + void reset(); + + /** + * @brief Parse some data. The enum return value is good when a complete request has + * been parsed, bad if the data is invalid, indeterminate when more data is + * required. The InputIterator return value indicates how much of the input + * has been consumed. + * @tparam InputIterator + * @param payload HTTP request payload. + * @param begin + * @param end + * @returns std::tuple + */ + template + std::tuple parse(HTTPPayload& payload, InputIterator begin, InputIterator end) + { + while (begin != end) { + ResultType result = consume(payload, *begin++); + if (result == GOOD || result == BAD) + return std::make_tuple(result, begin); + } + return std::make_tuple(INDETERMINATE, begin); + } + + /** + * @brief Returns flag indicating whether or not characters have been consumed from the payload. + * @returns True, if characters were consumed, otherwise false. + */ + uint32_t consumed() const { return m_consumed; } + + private: + /** + * @brief Handle the next character of input. + * @param payload HTTP request payload. + * @param input Character. + */ + ResultType consume(HTTPPayload& payload, char input); + + /** + * @brief Check if a byte is an HTTP character. + * @param c Character. + * @returns bool True, if character is an HTTP character, otherwise false. + */ + static bool isChar(int c); + /** + * @brief Check if a byte is an HTTP control character. + * @param c Character. + * @returns bool True, if character is an HTTP control character, otherwise false. + */ + static bool isControl(int c); + /** + * @brief Check if a byte is an HTTP special character. + * @param c Character. + * @returns bool True, if character is an HTTP special character, otherwise false. + */ + static bool isSpecial(int c); + /** + * @brief Check if a byte is an digit. + * @param c Character. + * @returns bool True, if character is a digit, otherwise false. + */ + static bool isDigit(int c); + + /** + * @brief Structure representing lexed HTTP headers. + */ + struct LexedHeader + { + /** + * @brief Header name. + */ + std::string name; + /** + * @brief Header value. + */ + std::string value; + + /** + * @brief Initializes a new instance of the LexedHeader class + */ + LexedHeader() { /* stub */ } + /** + * @brief Initializes a new instance of the LexedHeader class + * @param n Header name. + * @param v Header value. + */ + LexedHeader(const std::string& n, const std::string& v) : name(n), value(v) {} + }; + + std::vector m_headers; + uint16_t m_status; + bool m_clientLexer = false; + uint32_t m_consumed; + + /** + * @brief Lexer machine state. + */ + enum state + { + METHOD_START, //!< HTTP Method Start + METHOD, //!< HTTP Method + URI, //!< HTTP URI + + HTTP_VERSION_H, //!< HTTP Version: H + HTTP_VERSION_T_1, //!< HTTP Version: T + HTTP_VERSION_T_2, //!< HTTP Version: T + HTTP_VERSION_P, //!< HTTP Version: P + HTTP_VERSION_SLASH, //!< HTTP Version: / + HTTP_VERSION_MAJOR_START, //!< HTTP Version Major Start + HTTP_VERSION_MAJOR, //!< HTTP Version Major + HTTP_VERSION_MINOR_START, //!< HTTP Version Minor Start + HTTP_VERSION_MINOR, //!< HTTP Version Minor + + HTTP_STATUS_1, //!< Status Number 1 + HTTP_STATUS_2, //!< Status Number 2 + HTTP_STATUS_3, //!< Status Number 3 + HTTP_STATUS_END, //!< Status End + HTTP_STATUS_MESSAGE_START, //!< Status Message Start + HTTP_STATUS_MESSAGE, //!< Status Message End + + EXPECTING_NEWLINE_1, //!< + + HEADER_LINE_START, //!< Header Line Start + HEADER_LWS, //!< + HEADER_NAME, //!< Header Name + SPACE_BEFORE_HEADER_VALUE, //!< + HEADER_VALUE, //!< Header Value + + EXPECTING_NEWLINE_2, //!< + EXPECTING_NEWLINE_3 //!< + } m_state; + }; + } // namespace http +} // namespace restapi + +#endif // __REST_HTTP__HTTP_LEXER_H__ diff --git a/src/common/network/rest/http/HTTPPayload.cpp b/src/common/restapi/http/HTTPPayload.cpp similarity index 99% rename from src/common/network/rest/http/HTTPPayload.cpp rename to src/common/restapi/http/HTTPPayload.cpp index 088db94b..5107a601 100644 --- a/src/common/network/rest/http/HTTPPayload.cpp +++ b/src/common/restapi/http/HTTPPayload.cpp @@ -9,11 +9,11 @@ * */ #include "Defines.h" -#include "network/rest/http/HTTPPayload.h" +#include "restapi/http/HTTPPayload.h" #include "Log.h" #include "Utils.h" -using namespace network::rest::http; +using namespace restapi::http; #include diff --git a/src/common/restapi/http/HTTPPayload.h b/src/common/restapi/http/HTTPPayload.h new file mode 100644 index 00000000..ba493a1c --- /dev/null +++ b/src/common/restapi/http/HTTPPayload.h @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: BSL-1.0 +/* + * Digital Voice Modem - Common Library + * BSL-1.0 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (c) 2003-2013 Christopher M. Kohlhoff + * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file HTTPPayload.h + * @ingroup http + * @file HTTPPayload.cpp + * @ingroup http + */ +#if !defined(__REST_HTTP__HTTP_PAYLOAD_H__) +#define __REST_HTTP__HTTP_PAYLOAD_H__ + +#include "common/Defines.h" +#include "common/json/json.h" +#include "common/restapi/http/HTTPHeaders.h" + +#include +#include + +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + + #define HTTP_GET "GET" + #define HTTP_POST "POST" + #define HTTP_PUT "PUT" + #define HTTP_DELETE "DELETE" + #define HTTP_OPTIONS "OPTIONS" + + // --------------------------------------------------------------------------- + // Structure Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This struct implements a model of a payload to be sent to a + * HTTP client/server. + * @ingroup http + */ + struct HTTPPayload { + /** + * @brief HTTP Status/Response Codes + */ + enum StatusType { + OK = 200, //!< HTTP OK 200 + CREATED = 201, //!< HTTP Created 201 + ACCEPTED = 202, //!< HTTP Accepted 202 + NO_CONTENT = 204, //!< HTTP No Content 204 + + MULTIPLE_CHOICES = 300, //!< HTTP Multiple Choices 300 + MOVED_PERMANENTLY = 301, //!< HTTP Moved Permenantly 301 + MOVED_TEMPORARILY = 302, //!< HTTP Moved Temporarily 302 + NOT_MODIFIED = 304, //!< HTTP Not Modified 304 + + BAD_REQUEST = 400, //!< HTTP Bad Request 400 + UNAUTHORIZED = 401, //!< HTTP Unauthorized 401 + FORBIDDEN = 403, //!< HTTP Forbidden 403 + NOT_FOUND = 404, //!< HTTP Not Found 404 + + INTERNAL_SERVER_ERROR = 500, //!< HTTP Internal Server Error 500 + NOT_IMPLEMENTED = 501, //!< HTTP Not Implemented 501 + BAD_GATEWAY = 502, //!< HTTP Bad Gateway 502 + SERVICE_UNAVAILABLE = 503 //!< HTTP Service Unavailable 503 + } status; + + HTTPHeaders headers; + std::string content; + size_t contentLength; + + std::string method; + std::string uri; + + int httpVersionMajor; + int httpVersionMinor; + + bool isClientPayload = false; + + /** + * @brief Convert the payload into a vector of buffers. The buffers do not own the + * underlying memory blocks, therefore the payload object must remain valid and + * not be changed until the write operation has completed. + * @returns std::vector List of buffers representing the HTTP payload. + */ + std::vector toBuffers(); + + /** + * @brief Prepares payload for transmission by finalizing status and content type. + * @param obj + * @param status HTTP status. + */ + void payload(json::object& obj, StatusType status = OK); + /** + * @brief Prepares payload for transmission by finalizing status and content type. + * @param content + * @param status HTTP status. + * @param contentType HTTP content type. + */ + void payload(std::string& content, StatusType status = OK, const std::string& contentType = "text/html"); + + /** + * @brief Get a request payload. + * @param method HTTP method. + * @param uri HTTP uri. + */ + static HTTPPayload requestPayload(std::string method, std::string uri); + /** + * @brief Get a status payload. + * @param status HTTP status. + * @param contentType HTTP content type. + */ + static HTTPPayload statusPayload(StatusType status, const std::string& contentType = "text/html"); + + /** + * @brief Helper to attach a host TCP stream reader. + * @param remoteEndpoint Endpoint. + */ + void attachHostHeader(const asio::ip::tcp::endpoint remoteEndpoint); + + private: + /** + * @brief Internal helper to ensure the headers are of a default for the given content type. + * @param contentType HTTP content type. + */ + void ensureDefaultHeaders(const std::string& contentType = "text/html"); + }; + } // namespace http +} // namespace restapi + +#endif // __REST_HTTP__HTTP_PAYLOAD_H__ diff --git a/src/common/network/rest/http/HTTPRequestHandler.cpp b/src/common/restapi/http/HTTPRequestHandler.cpp similarity index 96% rename from src/common/network/rest/http/HTTPRequestHandler.cpp rename to src/common/restapi/http/HTTPRequestHandler.cpp index 9b04fac7..2bf96be5 100644 --- a/src/common/network/rest/http/HTTPRequestHandler.cpp +++ b/src/common/restapi/http/HTTPRequestHandler.cpp @@ -9,10 +9,10 @@ * */ #include "Defines.h" -#include "network/rest/http/HTTPRequestHandler.h" -#include "network/rest/http/HTTPPayload.h" +#include "restapi/http/HTTPRequestHandler.h" +#include "restapi/http/HTTPPayload.h" -using namespace network::rest::http; +using namespace restapi::http; #include #include diff --git a/src/common/restapi/http/HTTPRequestHandler.h b/src/common/restapi/http/HTTPRequestHandler.h new file mode 100644 index 00000000..6bbc73a9 --- /dev/null +++ b/src/common/restapi/http/HTTPRequestHandler.h @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: BSL-1.0 +/* + * Digital Voice Modem - Common Library + * BSL-1.0 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (c) 2003-2013 Christopher M. Kohlhoff + * Copyright (C) 2023 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file HTTPRequestHandler.h + * @ingroup http + * @file HTTPRequestHandler.cpp + * @ingroup http + */ +#if !defined(__REST_HTTP__HTTP_REQUEST_HANDLER_H__) +#define __REST_HTTP__HTTP_REQUEST_HANDLER_H__ + +#include "common/Defines.h" + +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Prototypes + // --------------------------------------------------------------------------- + + struct HTTPPayload; + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class implements the common handler for all incoming requests. + * @ingroup http + */ + class HTTPRequestHandler { + public: + auto operator=(HTTPRequestHandler&) -> HTTPRequestHandler& = delete; + HTTPRequestHandler(HTTPRequestHandler&) = delete; + + /** + * @brief Initializes a new instance of the HTTPRequestHandler class. + * @param docRoot Path to the document root to serve. + */ + explicit HTTPRequestHandler(const std::string& docRoot); + /** + * @brief + */ + HTTPRequestHandler(HTTPRequestHandler&&) = default; + + /** + * @brief + */ + HTTPRequestHandler& operator=(HTTPRequestHandler&&) = default; + + /** + * @brief Handle a request and produce a reply. + * @param req HTTP request. + * @param reply HTTP reply. + */ + void handleRequest(const HTTPPayload& req, HTTPPayload& reply); + + private: + /** + * @brief Internal helper to decode an incoming URL. + * @param[in] in Incoming URL string. + * @param[out] out Decoded URL string. + * @returns bool True, if URL decoded, otherwise false. + */ + static bool urlDecode(const std::string& in, std::string& out); + + std::string m_docRoot; + }; + } // namespace http +} // namespace restapi + +#endif // __REST_HTTP__HTTP_REQUEST_HANDLER_H__ diff --git a/src/common/restapi/http/HTTPServer.h b/src/common/restapi/http/HTTPServer.h new file mode 100644 index 00000000..b47c392b --- /dev/null +++ b/src/common/restapi/http/HTTPServer.h @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: BSL-1.0 +/* + * Digital Voice Modem - Common Library + * BSL-1.0 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (c) 2003-2013 Christopher M. Kohlhoff + * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file HTTPServer.h + * @ingroup http + */ +#if !defined(__REST_HTTP__HTTP_SERVER_H__) +#define __REST_HTTP__HTTP_SERVER_H__ + +#include "common/Defines.h" +#include "common/restapi/http/ServerConnection.h" +#include "common/restapi/http/ServerConnectionManager.h" +#include "common/restapi/http/HTTPRequestHandler.h" + +#include +#include +#include +#include +#include + +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class implements top-level routines of the HTTP server. + * @tparam RequestHandlerType Type representing a request handler. + * @tparam ConnectionImpl Type representing the connection implementation. + * @ingroup http + */ + template class ConnectionImpl = ServerConnection> + class HTTPServer { + public: + auto operator=(HTTPServer&) -> HTTPServer& = delete; + auto operator=(HTTPServer&&) -> HTTPServer& = delete; + HTTPServer(HTTPServer&) = delete; + + /** + * @brief Initializes a new instance of the HTTPServer class. + * @param address Hostname/IP Address. + * @param port Port. + * @param debug Flag indicating whether or not verbose logging should be enabled. + */ + explicit HTTPServer(const std::string& address, uint16_t port, bool debug) : + m_ioService(), + m_acceptor(m_ioService), + m_connectionManager(), + m_socket(m_ioService), + m_requestHandler(), + m_debug(debug) + { + // open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR) + asio::ip::address ipAddress = asio::ip::address::from_string(address); + m_endpoint = asio::ip::tcp::endpoint(ipAddress, port); + } + + /** + * @brief Helper to set the HTTP request handlers. + * @tparam Handler Type representing the request handler. + * @param handler Request handler. + */ + template + void setHandler(Handler&& handler) + { + m_requestHandler = RequestHandlerType(std::forward(handler)); + } + + /** + * @brief Open TCP acceptor. + */ + void open() + { + // open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR) + m_acceptor.open(m_endpoint.protocol()); + m_acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); + m_acceptor.set_option(asio::socket_base::keep_alive(true)); + m_acceptor.bind(m_endpoint); + m_acceptor.listen(); + + accept(); + } + + /** + * @brief Run the servers ASIO IO service loop. + */ + void run() + { + // the run() call will block until all asynchronous operations + // have finished; while the server is running, there is always at least one + // asynchronous operation outstanding: the asynchronous accept call waiting + // for new incoming connections + m_ioService.run(); + } + + /** + * @brief Helper to stop running ASIO IO services. + */ + void stop() + { + // the server is stopped by cancelling all outstanding asynchronous + // operations; once all operations have finished the m_ioService::run() + // call will exit + m_acceptor.close(); + m_connectionManager.stopAll(); + } + + private: + /** + * @brief Perform an asynchronous accept operation. + */ + void accept() + { + m_acceptor.async_accept(m_socket, [this](asio::error_code ec) { + // check whether the server was stopped by a signal before this + // completion handler had a chance to run + if (!m_acceptor.is_open()) { + return; + } + + if (!ec) { + m_connectionManager.start(std::make_shared(std::move(m_socket), m_connectionManager, m_requestHandler, false, m_debug)); + } + + accept(); + }); + } + + typedef ConnectionImpl ConnectionType; + typedef std::shared_ptr ConnectionTypePtr; + + asio::io_service m_ioService; + asio::ip::tcp::acceptor m_acceptor; + + asio::ip::tcp::endpoint m_endpoint; + + ServerConnectionManager m_connectionManager; + + asio::ip::tcp::socket m_socket; + + RequestHandlerType m_requestHandler; + bool m_debug; + }; + } // namespace http +} // namespace restapi + +#endif // __REST_HTTP__HTTP_SERVER_H__ diff --git a/src/common/restapi/http/SecureClientConnection.h b/src/common/restapi/http/SecureClientConnection.h new file mode 100644 index 00000000..16d24341 --- /dev/null +++ b/src/common/restapi/http/SecureClientConnection.h @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * 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 SecureClientConnection.h + * @ingroup http + */ +#if !defined(__REST_HTTP__SECURE_CLIENT_CONNECTION_H__) +#define __REST_HTTP__SECURE_CLIENT_CONNECTION_H__ + +#if defined(ENABLE_SSL) + +#include "common/Defines.h" +#include "common/restapi/http/HTTPLexer.h" +#include "common/restapi/http/HTTPPayload.h" +#include "common/Log.h" + +#include +#include +#include +#include + +#include +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class represents a single connection from a client. + * @tparam RequestHandlerType Type representing a request handler. + * @ingroup http + */ + template + class SecureClientConnection { + public: + auto operator=(SecureClientConnection&) -> SecureClientConnection& = delete; + auto operator=(SecureClientConnection&&) -> SecureClientConnection& = delete; + SecureClientConnection(SecureClientConnection&) = delete; + + /** + * @brief Initializes a new instance of the SecureClientConnection class. + * @param socket TCP socket for this connection. + * @param context SSL context for this connection. + * @param handler Request handler for this connection. + */ + explicit SecureClientConnection(asio::ip::tcp::socket socket, asio::ssl::context& context, RequestHandlerType& handler) : + m_socket(std::move(socket), context), + m_requestHandler(handler), + m_lexer(HTTPLexer(true)) + { + m_socket.set_verify_mode(asio::ssl::verify_none); + m_socket.set_verify_callback(std::bind(&SecureClientConnection::verify_certificate, this, std::placeholders::_1, std::placeholders::_2)); + } + + /** + * @brief Start the first asynchronous operation for the connection. + */ + void start() + { + m_socket.handshake(asio::ssl::stream_base::client); + read(); + } + /** + * @brief Stop all asynchronous operations associated with the connection. + */ + void stop() + { + try + { + ensureNoLinger(); + if (m_socket.lowest_layer().is_open()) { + m_socket.lowest_layer().close(); + } + } + catch(const std::exception&) { /* ignore */ } + } + + /** + * @brief Helper to enable the SO_LINGER socket option during shutdown. + */ + void ensureNoLinger() + { + try + { + // enable SO_LINGER timeout 0 + asio::socket_base::linger linger(true, 0); + m_socket.lowest_layer().set_option(linger); + } + catch(const asio::system_error& e) + { + asio::error_code ec = e.code(); + if (ec) { + ::LogError(LOG_REST, "SecureClientConnection::ensureNoLinger(), %s, code = %u", ec.message().c_str(), ec.value()); + } + } + } + + /** + * @brief Perform an synchronous write operation. + * @param request HTTP request. + */ + void send(HTTPPayload request) + { + request.attachHostHeader(m_socket.lowest_layer().remote_endpoint()); + write(request); + } + private: + /** + * @brief Perform an SSL certificate verification. + * @param preverified Flag indicating the SSL certificate was preverified. + * @param context SSL verification context. + * @returns True, if SSL certificate is valid, otherwise false. + */ + bool verify_certificate(bool preverified, asio::ssl::verify_context& context) + { + return true; // ignore always valid + } + + /** + * @brief Perform an asynchronous read operation. + */ + void read() + { + m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t bytes_transferred) { + if (!ec) { + HTTPLexer::ResultType result; + char* content; + + try + { + if (m_sizeToTransfer > 0U && (m_bytesTransferred + bytes_transferred) < m_sizeToTransfer) { + ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); + m_bytesTransferred += bytes_transferred; + + read(); + } + else { + if (m_sizeToTransfer > 0U) { + // final copy + ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); + m_bytesTransferred += bytes_transferred; + + m_sizeToTransfer = 0U; + bytes_transferred = m_bytesTransferred; + + // reset lexer and re-parse the full content + m_lexer.reset(); + std::tie(result, content) = m_lexer.parse(m_request, m_fullBuffer.data(), m_fullBuffer.data() + bytes_transferred); + } else { + ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); + m_bytesTransferred += bytes_transferred; + + std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); + } + + // determine content length + std::string contentLength = m_request.headers.find("Content-Length"); + if (contentLength != "") { + size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); + + // setup a full read if necessary + if (length > bytes_transferred && m_sizeToTransfer == 0U) { + m_sizeToTransfer = length; + } + + if (m_sizeToTransfer > 0U) { + result = HTTPLexer::CONTINUE; + } else { + m_request.content = std::string(content, length); + } + } + + m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string()); + if (result == HTTPLexer::GOOD) { + m_sizeToTransfer = m_bytesTransferred = 0U; + m_requestHandler.handleRequest(m_request, m_reply); + } + else if (result == HTTPLexer::BAD) { + m_sizeToTransfer = m_bytesTransferred = 0U; + return; + } + else { + read(); + } + } + } + catch(const std::exception& e) { ::LogError(LOG_REST, "SecureClientConnection::read(), %s", ec.message().c_str()); } + } + else if (ec != asio::error::operation_aborted) { + if (ec) { + ::LogError(LOG_REST, "SecureClientConnection::read(), %s, code = %u", ec.message().c_str(), ec.value()); + } + stop(); + } + }); + } + + /** + * @brief Perform an synchronous write operation. + * @param request HTTP request. + */ + void write(HTTPPayload request) + { + try + { + m_socket.handshake(asio::ssl::stream_base::client); + + auto buffers = request.toBuffers(); + asio::write(m_socket, buffers); + } + catch(const asio::system_error& e) + { + asio::error_code ec = e.code(); + if (ec) { + ::LogError(LOG_REST, "SecureClientConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); + + try + { + // initiate graceful connection closure + asio::error_code ignored_ec; + m_socket.lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); + } + catch(const std::exception& e) { + ::LogError(LOG_REST, "SecureClientConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); + } + } + } + } + + asio::ssl::stream m_socket; + + RequestHandlerType& m_requestHandler; + + std::size_t m_sizeToTransfer; + std::size_t m_bytesTransferred; + std::array m_fullBuffer; + + std::array m_buffer; + + HTTPPayload m_request; + HTTPLexer m_lexer; + HTTPPayload m_reply; + }; + } // namespace http +} // namespace restapi + +#endif // ENABLE_SSL + +#endif // __REST_HTTP__SECURE_CLIENT_CONNECTION_H__ diff --git a/src/common/restapi/http/SecureHTTPClient.h b/src/common/restapi/http/SecureHTTPClient.h new file mode 100644 index 00000000..e4a42bf9 --- /dev/null +++ b/src/common/restapi/http/SecureHTTPClient.h @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** +* Digital Voice Modem - Common Library +* 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 SecureHTTPClient.h + * @ingroup http + */ +#if !defined(__REST_HTTP__SECURE_HTTP_CLIENT_H__) +#define __REST_HTTP__SECURE_HTTP_CLIENT_H__ + +#if defined(ENABLE_SSL) + +#include "common/Defines.h" +#include "common/restapi/http/SecureClientConnection.h" +#include "common/restapi/http/HTTPRequestHandler.h" +#include "common/Thread.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class implements top-level routines of the secure HTTP client. + * @tparam RequestHandlerType Type representing a request handler. + * @tparam ConnectionImpl Type representing the connection implementation. + * @ingroup http + */ + template class ConnectionImpl = SecureClientConnection> + class SecureHTTPClient : private Thread { + public: + auto operator=(SecureHTTPClient&) -> SecureHTTPClient& = delete; + auto operator=(SecureHTTPClient&&) -> SecureHTTPClient& = delete; + SecureHTTPClient(SecureHTTPClient&) = delete; + + /** + * @brief Initializes a new instance of the SecureHTTPClient class. + * @param address Hostname/IP Address. + * @param port Port. + */ + SecureHTTPClient(const std::string& address, uint16_t port) : + m_address(address), + m_port(port), + m_connection(nullptr), + m_ioContext(), + m_context(asio::ssl::context::tlsv12), + m_socket(m_ioContext), + m_requestHandler() + { + /* stub */ + } + /** + * @brief Finalizes a instance of the SecureHTTPClient class. + */ + ~SecureHTTPClient() override + { + if (m_connection != nullptr) { + close(); + } + } + + /** + * @brief Helper to set the HTTP request handlers. + * @tparam Handler Type representing the request handler. + * @param handler Request handler. + */ + template + void setHandler(Handler&& handler) + { + m_requestHandler = RequestHandlerType(std::forward(handler)); + } + + /** + * @brief Send HTTP request to HTTP server. + * @param request HTTP request. + * @returns True, if request was completed, otherwise false. + */ + bool request(HTTPPayload& request) + { + if (m_completed) { + return false; + } + + asio::post(m_ioContext, [this, request]() { + std::lock_guard guard(m_lock); + { + if (m_connection != nullptr) { + m_connection->send(request); + } + } + }); + + return true; + } + + /** + * @brief Opens connection to the network. + */ + bool open() + { + if (m_completed) { + return false; + } + + return run(); + } + + /** + * @brief Closes connection to the network. + */ + void close() + { + if (m_completed) { + return; + } + + m_completed = true; + m_ioContext.stop(); + + wait(); + } + + private: + /** + * @brief Internal entry point for the ASIO IO context thread. + */ + void entry() override + { + if (m_completed) { + return; + } + + asio::ip::tcp::resolver resolver(m_ioContext); + auto endpoints = resolver.resolve(m_address, std::to_string(m_port)); + + try { + connect(endpoints); + + // the entry() call will block until all asynchronous operations + // have finished + m_ioContext.run(); + } + catch (std::exception&) { /* stub */ } + + if (m_connection != nullptr) { + m_connection->stop(); + } + } + + /** + * @brief Perform an asynchronous connect operation. + * @param endpoints TCP endpoint to connect to. + */ + void connect(asio::ip::basic_resolver_results& endpoints) + { + asio::connect(m_socket, endpoints); + + m_connection = std::make_unique(std::move(m_socket), m_context, m_requestHandler); + m_connection->start(); + } + + std::string m_address; + uint16_t m_port; + + typedef ConnectionImpl ConnectionType; + + std::unique_ptr m_connection; + + bool m_completed = false; + asio::io_context m_ioContext; + + asio::ssl::context m_context; + asio::ip::tcp::socket m_socket; + + RequestHandlerType m_requestHandler; + + std::mutex m_lock; + }; + } // namespace http +} // namespace restapi + +#endif // ENABLE_SSL + +#endif // __REST_HTTP__SECURE_HTTP_CLIENT_H__ diff --git a/src/common/restapi/http/SecureHTTPServer.h b/src/common/restapi/http/SecureHTTPServer.h new file mode 100644 index 00000000..25ff73d1 --- /dev/null +++ b/src/common/restapi/http/SecureHTTPServer.h @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: BSL-1.0 +/* + * Digital Voice Modem - Common Library + * BSL-1.0 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (c) 2003-2013 Christopher M. Kohlhoff + * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file HTTPServer.h + * @ingroup http + */ +#if !defined(__REST_HTTP__SECURE_HTTP_SERVER_H__) +#define __REST_HTTP__SECURE_HTTP_SERVER_H__ + +#if defined(ENABLE_SSL) + +#include "common/Defines.h" +#include "common/restapi/http/SecureServerConnection.h" +#include "common/restapi/http/ServerConnectionManager.h" +#include "common/restapi/http/HTTPRequestHandler.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class implements top-level routines of the secure HTTP server. + * @tparam RequestHandlerType Type representing a request handler. + * @tparam ConnectionImpl Type representing the connection implementation. + * @ingroup http + */ + template class ConnectionImpl = SecureServerConnection> + class SecureHTTPServer { + public: + auto operator=(SecureHTTPServer&) -> SecureHTTPServer& = delete; + auto operator=(SecureHTTPServer&&) -> SecureHTTPServer& = delete; + SecureHTTPServer(SecureHTTPServer&) = delete; + + /** + * @brief Initializes a new instance of the SecureHTTPServer class. + * @param address Hostname/IP Address. + * @param port Port. + * @param debug Flag indicating whether or not verbose logging should be enabled. + */ + explicit SecureHTTPServer(const std::string& address, uint16_t port, bool debug) : + m_ioService(), + m_acceptor(m_ioService), + m_connectionManager(), + m_context(asio::ssl::context::tlsv12), + m_socket(m_ioService), + m_requestHandler(), + m_debug(debug) + { + asio::ip::address ipAddress = asio::ip::address::from_string(address); + m_endpoint = asio::ip::tcp::endpoint(ipAddress, port); + } + + /** + * @brief Helper to set the SSL certificate and private key. + * @param keyFile SSL certificate private key. + * @param certFile SSL certificate. + */ + bool setCertAndKey(const std::string& keyFile, const std::string& certFile) + { + try + { + m_context.use_certificate_chain_file(certFile); + m_context.use_private_key_file(keyFile, asio::ssl::context::pem); + return true; + } + catch(const std::exception& e) { + ::LogError(LOG_REST, "%s", e.what()); + return false; + } + } + + /** + * @brief Helper to set the HTTP request handlers. + * @tparam Handler Type representing the request handler. + * @param handler Request handler. + */ + template + void setHandler(Handler&& handler) + { + m_requestHandler = RequestHandlerType(std::forward(handler)); + } + + /** + * @brief Open TCP acceptor. + */ + void open() + { + // open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR) + m_acceptor.open(m_endpoint.protocol()); + m_acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); + m_acceptor.set_option(asio::socket_base::keep_alive(true)); + m_acceptor.bind(m_endpoint); + m_acceptor.listen(); + + accept(); + } + + /** + * @brief Run the servers ASIO IO service loop. + */ + void run() + { + // the run() call will block until all asynchronous operations + // have finished; while the server is running, there is always at least one + // asynchronous operation outstanding: the asynchronous accept call waiting + // for new incoming connections + m_ioService.run(); + } + + /** + * @brief Helper to stop running ASIO IO services. + */ + void stop() + { + // the server is stopped by cancelling all outstanding asynchronous + // operations; once all operations have finished the m_ioService::run() + // call will exit + m_acceptor.close(); + m_connectionManager.stopAll(); + } + + private: + /** + * @brief Perform an asynchronous accept operation. + */ + void accept() + { + m_acceptor.async_accept(m_socket, [this](asio::error_code ec) { + // check whether the server was stopped by a signal before this + // completion handler had a chance to run + if (!m_acceptor.is_open()) { + return; + } + + if (!ec) { + m_connectionManager.start(std::make_shared(std::move(m_socket), m_context, m_connectionManager, m_requestHandler, false, m_debug)); + } + + accept(); + }); + } + + typedef ConnectionImpl ConnectionType; + typedef std::shared_ptr ConnectionTypePtr; + + asio::io_service m_ioService; + asio::ip::tcp::acceptor m_acceptor; + + asio::ip::tcp::endpoint m_endpoint; + + ServerConnectionManager m_connectionManager; + + asio::ssl::context m_context; + asio::ip::tcp::socket m_socket; + + std::string m_certFile; + std::string m_keyFile; + + RequestHandlerType m_requestHandler; + bool m_debug; + }; + } // namespace http +} // namespace restapi + +#endif // ENABLE_SSL + +#endif // __REST_HTTP__SECURE_HTTP_SERVER_H__ diff --git a/src/common/restapi/http/SecureServerConnection.h b/src/common/restapi/http/SecureServerConnection.h new file mode 100644 index 00000000..32dcd4a6 --- /dev/null +++ b/src/common/restapi/http/SecureServerConnection.h @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: BSL-1.0 +/* + * Digital Voice Modem - Common Library + * BSL-1.0 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (c) 2003-2013 Christopher M. Kohlhoff + * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file SecureServerConnection.h + * @ingroup http + */ +#if !defined(__REST_HTTP__SECURE_SERVER_CONNECTION_H__) +#define __REST_HTTP__SECURE_SERVER_CONNECTION_H__ + +#if defined(ENABLE_SSL) + +#include "common/Defines.h" +#include "common/restapi/http/HTTPLexer.h" +#include "common/restapi/http/HTTPPayload.h" +#include "common/Log.h" + +#include +#include +#include +#include + +#include +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Prototypes + // --------------------------------------------------------------------------- + + template class ServerConnectionManager; + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class represents a single connection from a client. + * @tparam RequestHandlerType Type representing a request handler. + * @ingroup http + */ + template + class SecureServerConnection : public std::enable_shared_from_this> { + typedef SecureServerConnection selfType; + typedef std::shared_ptr selfTypePtr; + typedef ServerConnectionManager ConnectionManagerType; + public: + auto operator=(SecureServerConnection&) -> SecureServerConnection& = delete; + auto operator=(SecureServerConnection&&) -> SecureServerConnection& = delete; + SecureServerConnection(SecureServerConnection&) = delete; + + /** + * @brief Initializes a new instance of the SecureServerConnection class. + * @param socket TCP socket for this connection. + * @param context SSL context. + * @param manager Connection manager for this connection. + * @param handler Request handler for this connection. + * @param persistent Flag indicating whether or not the connection is persistent. + * @param debug Flag indicating whether or not verbose logging should be enabled. + */ + explicit SecureServerConnection(asio::ip::tcp::socket socket, asio::ssl::context& context, ConnectionManagerType& manager, RequestHandlerType& handler, + bool persistent = false, bool debug = false) : + m_socket(std::move(socket), context), + m_connectionManager(manager), + m_requestHandler(handler), + m_lexer(HTTPLexer(false)), + m_continue(false), + m_contResult(HTTPLexer::INDETERMINATE), + m_persistent(persistent), + m_debug(debug) + { + /* stub */ + } + + /** + * @brief Start the first asynchronous operation for the connection. + */ + void start() { handshake(); } + /** + * @brief Stop all asynchronous operations associated with the connection. + */ + void stop() + { + try + { + if (m_socket.lowest_layer().is_open()) { + m_socket.lowest_layer().close(); + } + } + catch(const std::exception&) { /* ignore */ } + } + + private: + /** + * @brief Perform an asynchronous SSL handshake. + */ + void handshake() + { + if (!m_persistent) { + auto self(this->shared_from_this()); + } + + m_socket.async_handshake(asio::ssl::stream_base::server, [this](asio::error_code ec) { + if (!ec) { + read(); + } + }); + } + + /** + * @brief Perform an asynchronous read operation. + */ + void read() + { + if (!m_persistent) { + auto self(this->shared_from_this()); + } + + m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t recvLength) { + if (!ec) { + HTTPLexer::ResultType result = HTTPLexer::GOOD; + char* content; + + // catch exceptions here so we don't blatently crash the system + try + { + if (!m_continue) { + std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + recvLength); + + m_request.content = std::string(); + std::string contentLength = m_request.headers.find("Content-Length"); + if (contentLength != "" && (::strlen(content) != 0)) { + size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); + m_request.contentLength = length; + m_request.content = std::string(content, length); + } + + m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string()); + + uint32_t consumed = m_lexer.consumed(); + if (result == HTTPLexer::GOOD && consumed == recvLength && + ((m_request.method == HTTP_POST) || (m_request.method == HTTP_PUT))) { + if (m_debug) { + LogDebug(LOG_REST, "HTTPS Partial Request, recvLength = %u, consumed = %u, result = %u", recvLength, consumed, result); + Utils::dump(1U, "SecureServerConnection::read(), m_buffer", (uint8_t*)m_buffer.data(), recvLength); + } + + result = HTTPLexer::INDETERMINATE; + m_continue = true; + } + } else { + if (m_debug) { + LogDebug(LOG_REST, "HTTP Partial Request, recvLength = %u, result = %u", recvLength, result); + Utils::dump(1U, "SecureServerConnection::read(), m_buffer", (uint8_t*)m_buffer.data(), recvLength); + } + + if (m_contResult == HTTPLexer::INDETERMINATE) { + m_request.content = std::string(m_buffer.data(), recvLength); + } else { + m_request.content.append(std::string(m_buffer.data(), recvLength)); + } + + if (m_request.contentLength != 0 && recvLength < m_request.contentLength) { + m_contResult = result = HTTPLexer::CONTINUE; + m_continue = true; + } + } + + if (result == HTTPLexer::GOOD) { + if (m_debug) { + Utils::dump(1U, "SecureServerConnection::read(), HTTPS Request Content", (uint8_t*)m_request.content.c_str(), m_request.content.length()); + } + + m_continue = false; + m_contResult = HTTPLexer::INDETERMINATE; + m_requestHandler.handleRequest(m_request, m_reply); + + if (m_debug) { + Utils::dump(1U, "SecureServerConnection::read(), HTTPS Reply Content", (uint8_t*)m_reply.content.c_str(), m_reply.content.length()); + } + + write(); + } + else if (result == HTTPLexer::BAD) { + m_continue = false; + m_contResult = HTTPLexer::INDETERMINATE; + m_reply = HTTPPayload::statusPayload(HTTPPayload::BAD_REQUEST); + write(); + } + else { + read(); + } + } + catch(const std::exception& e) { + ::LogError(LOG_REST, "SecureServerConnection::read(), %s", ec.message().c_str()); + m_continue = false; + m_contResult = HTTPLexer::INDETERMINATE; + } + } + else if (ec != asio::error::operation_aborted) { + if (ec) { + ::LogError(LOG_REST, "SecureServerConnection::read(), %s, code = %u", ec.message().c_str(), ec.value()); + } + m_connectionManager.stop(this->shared_from_this()); + m_continue = false; + } + }); + } + + /** + * @brief Perform an asynchronous write operation. + */ + void write() + { + if (!m_persistent) { + auto self(this->shared_from_this()); + } else { + m_reply.headers.add("Connection", "keep-alive"); + } + + auto buffers = m_reply.toBuffers(); + asio::async_write(m_socket, buffers, [=](asio::error_code ec, std::size_t) { + if (m_persistent) { + m_lexer.reset(); + m_reply.headers = HTTPHeaders(); + m_reply.status = HTTPPayload::OK; + m_reply.content = ""; + m_request = HTTPPayload(); + read(); + } + else { + if (!ec) { + try + { + // initiate graceful connection closure + asio::error_code ignored_ec; + m_socket.lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); + } + catch(const std::exception& e) { ::LogError(LOG_REST, "%s", ec.message().c_str()); } + } + + if (ec != asio::error::operation_aborted) { + if (ec) { + ::LogError(LOG_REST, "SecureServerConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); + } + m_connectionManager.stop(this->shared_from_this()); + } + } + }); + } + + asio::ssl::stream m_socket; + + ConnectionManagerType& m_connectionManager; + RequestHandlerType& m_requestHandler; + + std::array m_buffer; + + HTTPPayload m_request; + HTTPLexer m_lexer; + HTTPPayload m_reply; + + bool m_continue; + HTTPLexer::ResultType m_contResult; + + bool m_persistent; + bool m_debug; + }; + } // namespace http +} // namespace restapi + +#endif // ENABLE_SSL + +#endif // __REST_HTTP__SECURE_SERVER_CONNECTION_H__ diff --git a/src/common/restapi/http/ServerConnection.h b/src/common/restapi/http/ServerConnection.h new file mode 100644 index 00000000..d5195341 --- /dev/null +++ b/src/common/restapi/http/ServerConnection.h @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: BSL-1.0 +/* + * Digital Voice Modem - Common Library + * BSL-1.0 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (c) 2003-2013 Christopher M. Kohlhoff + * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file ServerConnection.h + * @ingroup http + */ +#if !defined(__REST_HTTP__SERVER_CONNECTION_H__) +#define __REST_HTTP__SERVER_CONNECTION_H__ + +#include "common/Defines.h" +#include "common/restapi/http/HTTPLexer.h" +#include "common/restapi/http/HTTPPayload.h" +#include "common/Log.h" +#include "common/Utils.h" + +#include +#include +#include +#include + +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Prototypes + // --------------------------------------------------------------------------- + + template class ServerConnectionManager; + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief This class represents a single connection from a client. + * @tparam RequestHandlerType Type representing a request handler. + * @ingroup http + */ + template + class ServerConnection : public std::enable_shared_from_this> { + typedef ServerConnection selfType; + typedef std::shared_ptr selfTypePtr; + typedef ServerConnectionManager ConnectionManagerType; + public: + auto operator=(ServerConnection&) -> ServerConnection& = delete; + auto operator=(ServerConnection&&) -> ServerConnection& = delete; + ServerConnection(ServerConnection&) = delete; + + /** + * @brief Initializes a new instance of the ServerConnection class. + * @param socket TCP socket for this connection. + * @param manager Connection manager for this connection. + * @param handler Request handler for this connection. + * @param persistent Flag indicating whether or not the connection is persistent. + * @param debug Flag indicating whether or not verbose logging should be enabled. + */ + explicit ServerConnection(asio::ip::tcp::socket socket, ConnectionManagerType& manager, RequestHandlerType& handler, + bool persistent = false, bool debug = false) : + m_socket(std::move(socket)), + m_connectionManager(manager), + m_requestHandler(handler), + m_lexer(HTTPLexer(false)), + m_continue(false), + m_contResult(HTTPLexer::INDETERMINATE), + m_persistent(persistent), + m_debug(debug) + { + /* stub */ + } + + /** + * @brief Start the first asynchronous operation for the connection. + */ + void start() { read(); } + /** + * @brief Stop all asynchronous operations associated with the connection. + */ + void stop() + { + try + { + if (m_socket.is_open()) { + m_socket.close(); + } + } + catch(const std::exception&) { /* ignore */ } + } + + private: + /** + * @brief Perform an asynchronous read operation. + */ + void read() + { + if (!m_persistent) { + auto self(this->shared_from_this()); + } + + m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t recvLength) { + if (!ec) { + HTTPLexer::ResultType result = HTTPLexer::GOOD; + char* content; + + // catch exceptions here so we don't blatently crash the system + try + { + if (!m_continue) { + std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + recvLength); + + m_request.content = std::string(); + std::string contentLength = m_request.headers.find("Content-Length"); + if (contentLength != "" && (::strlen(content) != 0)) { + size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); + m_request.contentLength = length; + m_request.content = std::string(content, length); + } + + m_request.headers.add("RemoteHost", m_socket.remote_endpoint().address().to_string()); + + uint32_t consumed = m_lexer.consumed(); + if (result == HTTPLexer::GOOD && consumed == recvLength && + ((m_request.method == HTTP_POST) || (m_request.method == HTTP_PUT))) { + if (m_debug) { + LogDebug(LOG_REST, "HTTP Partial Request, recvLength = %u, consumed = %u, result = %u", recvLength, consumed, result); + Utils::dump(1U, "ServerConnection::read(), m_buffer", (uint8_t*)m_buffer.data(), recvLength); + } + + m_contResult = result = HTTPLexer::INDETERMINATE; + m_continue = true; + } + } else { + if (m_debug) { + LogDebug(LOG_REST, "HTTP Partial Request, recvLength = %u, result = %u", recvLength, result); + Utils::dump(1U, "ServerConnection::read(), m_buffer", (uint8_t*)m_buffer.data(), recvLength); + } + + if (m_contResult == HTTPLexer::INDETERMINATE) { + m_request.content = std::string(m_buffer.data(), recvLength); + } else { + m_request.content.append(std::string(m_buffer.data(), recvLength)); + } + + if (m_request.contentLength != 0 && recvLength < m_request.contentLength) { + m_contResult = result = HTTPLexer::CONTINUE; + m_continue = true; + } + } + + if (result == HTTPLexer::GOOD) { + if (m_debug) { + Utils::dump(1U, "ServerConnection::read(), HTTP Request Content", (uint8_t*)m_request.content.c_str(), m_request.content.length()); + } + + m_continue = false; + m_contResult = HTTPLexer::INDETERMINATE; + m_requestHandler.handleRequest(m_request, m_reply); + + if (m_debug) { + Utils::dump(1U, "ServerConnection::read(), HTTP Reply Content", (uint8_t*)m_reply.content.c_str(), m_reply.content.length()); + } + + write(); + } + else if (result == HTTPLexer::BAD) { + m_continue = false; + m_contResult = HTTPLexer::INDETERMINATE; + m_reply = HTTPPayload::statusPayload(HTTPPayload::BAD_REQUEST); + write(); + } + else { + read(); + } + } + catch(const std::exception& e) { + ::LogError(LOG_REST, "ServerConnection::read(), %s", ec.message().c_str()); + m_continue = false; + m_contResult = HTTPLexer::INDETERMINATE; + } + } + else if (ec != asio::error::operation_aborted) { + if (ec) { + ::LogError(LOG_REST, "ServerConnection::read(), %s, code = %u", ec.message().c_str(), ec.value()); + } + m_connectionManager.stop(this->shared_from_this()); + m_continue = false; + m_contResult = HTTPLexer::INDETERMINATE; + } + }); + } + + /** + * @brief Perform an asynchronous write operation. + */ + void write() + { + if (!m_persistent) { + auto self(this->shared_from_this()); + } else { + m_reply.headers.add("Connection", "keep-alive"); + } + + auto buffers = m_reply.toBuffers(); + asio::async_write(m_socket, buffers, [=](asio::error_code ec, std::size_t) { + if (m_persistent) { + m_lexer.reset(); + m_reply.headers = HTTPHeaders(); + m_reply.status = HTTPPayload::OK; + m_reply.content = ""; + m_request = HTTPPayload(); + read(); + } + else { + if (!ec) { + try + { + // initiate graceful connection closure + asio::error_code ignored_ec; + m_socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); + } + catch(const std::exception& e) { ::LogError(LOG_REST, "ServerConnection::write(), %s", ec.message().c_str()); } + } + + if (ec != asio::error::operation_aborted) { + if (ec) { + ::LogError(LOG_REST, "ServerConnection::write(), %s, code = %u", ec.message().c_str(), ec.value()); + } + m_connectionManager.stop(this->shared_from_this()); + } + } + }); + } + + asio::ip::tcp::socket m_socket; + + ConnectionManagerType& m_connectionManager; + RequestHandlerType& m_requestHandler; + + std::array m_buffer; + + HTTPPayload m_request; + HTTPLexer m_lexer; + HTTPPayload m_reply; + + bool m_continue; + HTTPLexer::ResultType m_contResult; + + bool m_persistent; + bool m_debug; + }; + } // namespace http +} // namespace restapi + +#endif // __REST_HTTP__SERVER_CONNECTION_H__ diff --git a/src/common/restapi/http/ServerConnectionManager.h b/src/common/restapi/http/ServerConnectionManager.h new file mode 100644 index 00000000..fe4e2e9f --- /dev/null +++ b/src/common/restapi/http/ServerConnectionManager.h @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: BSL-1.0 +/* + * Digital Voice Modem - Common Library + * BSL-1.0 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (c) 2003-2013 Christopher M. Kohlhoff + * Copyright (C) 2023 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file ServerConnection.h + * @ingroup http + */ +#if !defined(__REST_HTTP__SERVER_CONNECTION_MANAGER_H__) +#define __REST_HTTP__SERVER_CONNECTION_MANAGER_H__ + +#include "common/Defines.h" + +#include +#include + +namespace restapi +{ + namespace http + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Manages open connections so that they may be cleanly stopped when the server + * needs to shut down. + * @tparam ConnectionPtr + * @ingroup http + */ + template + class ServerConnectionManager { + public: + auto operator=(ServerConnectionManager&) -> ServerConnectionManager& = delete; + auto operator=(ServerConnectionManager&&) -> ServerConnectionManager& = delete; + ServerConnectionManager(ServerConnectionManager&) = delete; + + /** + * @brief Initializes a new instance of the ServerConnectionManager class. + */ + ServerConnectionManager() = default; + + /** + * @brief Add the specified connection to the manager and start it. + * @param c + */ + void start(ConnectionPtr c) + { + std::lock_guard guard(m_lock); + { + m_connections.insert(c); + } + c->start(); + } + + /** + * @brief Stop the specified connection. + * @param c + */ + void stop(ConnectionPtr c) + { + std::lock_guard guard(m_lock); + { + m_connections.erase(c); + } + c->stop(); + } + + /** + * @brief Stop all connections. + */ + void stopAll() + { + for (auto c : m_connections) + c->stop(); + + std::lock_guard guard(m_lock); + m_connections.clear(); + } + + private: + std::set m_connections; + std::mutex m_lock; + }; + } // namespace http +} // namespace restapi + +#endif // __REST_HTTP__SERVER_CONNECTION_MANAGER_H__ diff --git a/src/fne/CMakeLists.txt b/src/fne/CMakeLists.txt index 2425e6c0..2319fc23 100644 --- a/src/fne/CMakeLists.txt +++ b/src/fne/CMakeLists.txt @@ -18,6 +18,8 @@ file(GLOB dvmfne_SRC "src/fne/network/influxdb/*.cpp" "src/fne/network/*.h" "src/fne/network/*.cpp" + "src/fne/restapi/*.h" + "src/fne/restapi/*.cpp" "src/fne/xml/*.h" "src/fne/win32/*.h" "src/fne/*.h" diff --git a/src/fne/HostFNE.h b/src/fne/HostFNE.h index ce9d21dc..4c5f1b73 100644 --- a/src/fne/HostFNE.h +++ b/src/fne/HostFNE.h @@ -27,7 +27,7 @@ #include "network/FNENetwork.h" #include "network/DiagNetwork.h" #include "network/PeerNetwork.h" -#include "network/RESTAPI.h" +#include "restapi/RESTAPI.h" #include "CryptoContainer.h" #include diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 22320129..994510e6 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -9,8 +9,8 @@ */ #include "fne/Defines.h" #include "common/edac/SHA256.h" -#include "common/network/json/json.h" #include "common/p25/kmm/KMMFactory.h" +#include "common/json/json.h" #include "common/zlib/Compression.h" #include "common/Log.h" #include "common/StopWatch.h" diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 65952339..62a79927 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -27,12 +27,12 @@ #include "fne/Defines.h" #include "common/concurrent/unordered_map.h" #include "common/concurrent/shared_unordered_map.h" -#include "common/network/BaseNetwork.h" -#include "common/network/json/json.h" +#include "common/json/json.h" #include "common/lookups/RadioIdLookup.h" #include "common/lookups/TalkgroupRulesLookup.h" #include "common/lookups/PeerListLookup.h" #include "common/lookups/AdjSiteMapLookup.h" +#include "common/network/BaseNetwork.h" #include "common/network/Network.h" #include "common/network/PacketBuffer.h" #include "common/ThreadPool.h" diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 36236a01..0397ade3 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -8,7 +8,7 @@ * */ #include "fne/Defines.h" -#include "common/network/json/json.h" +#include "common/json/json.h" #include "common/zlib/Compression.h" #include "common/Log.h" #include "common/Utils.h" diff --git a/src/fne/network/SpanningTree.h b/src/fne/network/SpanningTree.h index 9fd0fc37..07e0de23 100644 --- a/src/fne/network/SpanningTree.h +++ b/src/fne/network/SpanningTree.h @@ -17,7 +17,7 @@ #define __SPANNING_TREE_H__ #include "fne/Defines.h" -#include "common/network/json/json.h" +#include "common/json/json.h" #include #include diff --git a/src/fne/network/RESTAPI.cpp b/src/fne/restapi/RESTAPI.cpp similarity index 99% rename from src/fne/network/RESTAPI.cpp rename to src/fne/restapi/RESTAPI.cpp index 003b9cbb..4ed1586e 100644 --- a/src/fne/network/RESTAPI.cpp +++ b/src/fne/restapi/RESTAPI.cpp @@ -10,20 +10,19 @@ */ #include "fne/Defines.h" #include "common/edac/SHA256.h" +#include "common/json/json.h" #include "common/lookups/AffiliationLookup.h" -#include "common/network/json/json.h" #include "common/Log.h" #include "common/Utils.h" #include "fne/network/callhandler/TagDMRData.h" #include "fne/network/callhandler/TagP25Data.h" -#include "fne/network/RESTAPI.h" #include "fne/network/SpanningTree.h" +#include "fne/restapi/RESTAPI.h" #include "HostFNE.h" using namespace network; -using namespace network::rest; -using namespace network::rest::http; - +using namespace restapi; +using namespace restapi::http; using namespace lookups; #include diff --git a/src/fne/network/RESTAPI.h b/src/fne/restapi/RESTAPI.h similarity index 84% rename from src/fne/network/RESTAPI.h rename to src/fne/restapi/RESTAPI.h index c64f4685..66684a71 100644 --- a/src/fne/network/RESTAPI.h +++ b/src/fne/restapi/RESTAPI.h @@ -17,14 +17,14 @@ #define __REST_API_H__ #include "fne/Defines.h" -#include "common/network/rest/RequestDispatcher.h" -#include "common/network/rest/http/HTTPServer.h" -#include "common/network/rest/http/SecureHTTPServer.h" +#include "common/restapi/RequestDispatcher.h" +#include "common/restapi/http/HTTPServer.h" +#include "common/restapi/http/SecureHTTPServer.h" #include "common/lookups/AdjSiteMapLookup.h" #include "common/lookups/RadioIdLookup.h" #include "common/lookups/TalkgroupRulesLookup.h" #include "common/Thread.h" -#include "fne/network/RESTDefines.h" +#include "fne/restapi/RESTDefines.h" #include #include @@ -92,12 +92,12 @@ class HOST_SW_API RESTAPI : private Thread { void close(); private: - typedef network::rest::RequestDispatcher RESTDispatcherType; - typedef network::rest::http::HTTPPayload HTTPPayload; + typedef restapi::RequestDispatcher RESTDispatcherType; + typedef restapi::http::HTTPPayload HTTPPayload; RESTDispatcherType m_dispatcher; - network::rest::http::HTTPServer m_restServer; + restapi::http::HTTPServer m_restServer; #if defined(ENABLE_SSL) - network::rest::http::SecureHTTPServer m_restSecureServer; + restapi::http::SecureHTTPServer m_restSecureServer; bool m_enableSSL; #endif // ENABLE_SSL @@ -148,7 +148,7 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutAuth(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutAuth(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get version request. @@ -156,14 +156,14 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetVersion(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetVersion(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get status request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetStatus(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetStatus(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get peer query request. @@ -171,28 +171,28 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetPeerQuery(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetPeerQuery(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get peer count request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetPeerCount(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetPeerCount(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get peer reset request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutPeerReset(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutPeerReset(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put reset upstream peer connection request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutPeerResetConn(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutPeerResetConn(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get radio ID query request. @@ -200,28 +200,28 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetRIDQuery(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetRIDQuery(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put radio ID add request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutRIDAdd(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutRIDAdd(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put radio ID delete request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutRIDDelete(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutRIDDelete(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put radio ID commit request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetRIDCommit(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetRIDCommit(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get talkgroup ID query request. @@ -229,28 +229,28 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetTGQuery(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetTGQuery(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put talkgroup ID add request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutTGAdd(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutTGAdd(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put talkgroup ID delete request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutTGDelete(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutTGDelete(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put talkgroup ID commit request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetTGCommit(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetTGCommit(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get peer list query request. @@ -258,28 +258,28 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetPeerList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetPeerList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put peer add request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutPeerAdd(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutPeerAdd(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put peer delete request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutPeerDelete(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutPeerDelete(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put peer list commit request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetPeerCommit(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetPeerCommit(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get adjacent site map list query request. @@ -287,28 +287,28 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetAdjMapList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetAdjMapList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put adjacent site map add request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutAdjMapAdd(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutAdjMapAdd(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put adjacent site map delete request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutAdjMapDelete(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutAdjMapDelete(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put adjacent site map commit request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetAdjMapCommit(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetAdjMapCommit(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief @@ -316,7 +316,7 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetForceUpdate(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetForceUpdate(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get reload talkgroup ID list request. @@ -324,14 +324,14 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetReloadTGs(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetReloadTGs(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get reload radio ID list request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetReloadRIDs(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetReloadRIDs(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get affiliation list request. @@ -339,7 +339,7 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get spanning tree list request. @@ -347,7 +347,7 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetSpanningTree(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetSpanningTree(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /* ** Digital Mobile Radio @@ -359,7 +359,7 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutDMRRID(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutDMRRID(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /* ** Project 25 @@ -371,7 +371,7 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutP25RID(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutP25RID(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); }; #endif // __REST_API_H__ diff --git a/src/fne/network/RESTDefines.h b/src/fne/restapi/RESTDefines.h similarity index 98% rename from src/fne/network/RESTDefines.h rename to src/fne/restapi/RESTDefines.h index 41c1f36f..52a8d149 100644 --- a/src/fne/network/RESTDefines.h +++ b/src/fne/restapi/RESTDefines.h @@ -19,7 +19,7 @@ #define __FNE_REST_DEFINES_H__ #include "fne/Defines.h" -#include "host/network/RESTDefines.h" +#include "host/restapi/RESTDefines.h" // --------------------------------------------------------------------------- // Constants diff --git a/src/host/CMakeLists.txt b/src/host/CMakeLists.txt index aba8f13e..7b94bba7 100644 --- a/src/host/CMakeLists.txt +++ b/src/host/CMakeLists.txt @@ -50,6 +50,8 @@ file(GLOB dvmhost_SRC "src/host/modem/port/specialized/*.cpp" "src/host/network/*.h" "src/host/network/*.cpp" + "src/host/restapi/*.h" + "src/host/restapi/*.cpp" "src/host/win32/*.h" ) diff --git a/src/host/Host.h b/src/host/Host.h index 5a65c426..774847ce 100644 --- a/src/host/Host.h +++ b/src/host/Host.h @@ -32,15 +32,15 @@ #include "common/lookups/IdenTableLookup.h" #include "common/lookups/RadioIdLookup.h" #include "common/lookups/TalkgroupRulesLookup.h" -#include "common/network/json/json.h" +#include "common/json/json.h" #include "common/network/Network.h" #include "common/network/NetRPC.h" #include "common/yaml/Yaml.h" #include "dmr/Control.h" #include "p25/Control.h" #include "nxdn/Control.h" -#include "network/RESTAPI.h" #include "network/RPCDefines.h" +#include "restapi/RESTAPI.h" #include "modem/Modem.h" #include "modem/ModemV24.h" diff --git a/src/host/modem/Modem.h b/src/host/modem/Modem.h index 8b085218..219a8cd5 100644 --- a/src/host/modem/Modem.h +++ b/src/host/modem/Modem.h @@ -30,7 +30,7 @@ #include "common/RingBuffer.h" #include "common/Timer.h" #include "modem/port/IModemPort.h" -#include "network/RESTAPI.h" +#include "restapi/RESTAPI.h" #include #include diff --git a/src/host/network/RESTAPI.cpp b/src/host/restapi/RESTAPI.cpp similarity index 99% rename from src/host/network/RESTAPI.cpp rename to src/host/restapi/RESTAPI.cpp index f82f2cae..c652b744 100644 --- a/src/host/network/RESTAPI.cpp +++ b/src/host/restapi/RESTAPI.cpp @@ -10,20 +10,20 @@ #include "Defines.h" #include "common/edac/SHA256.h" #include "common/lookups/AffiliationLookup.h" -#include "common/network/json/json.h" +#include "common/json/json.h" #include "common/Log.h" #include "common/Utils.h" #include "dmr/Control.h" #include "p25/Control.h" #include "nxdn/Control.h" #include "modem/Modem.h" -#include "network/RESTAPI.h" +#include "restapi/RESTAPI.h" #include "Host.h" #include "HostMain.h" using namespace network; -using namespace network::rest; -using namespace network::rest::http; +using namespace restapi; +using namespace restapi::http; using namespace modem; #include diff --git a/src/host/network/RESTAPI.h b/src/host/restapi/RESTAPI.h similarity index 84% rename from src/host/network/RESTAPI.h rename to src/host/restapi/RESTAPI.h index 2cffa9e7..40a36361 100644 --- a/src/host/network/RESTAPI.h +++ b/src/host/restapi/RESTAPI.h @@ -17,13 +17,13 @@ #define __REST_API_H__ #include "Defines.h" -#include "common/network/rest/RequestDispatcher.h" -#include "common/network/rest/http/HTTPServer.h" -#include "common/network/rest/http/SecureHTTPServer.h" +#include "common/restapi/RequestDispatcher.h" +#include "common/restapi/http/HTTPServer.h" +#include "common/restapi/http/SecureHTTPServer.h" #include "common/lookups/RadioIdLookup.h" #include "common/lookups/TalkgroupRulesLookup.h" #include "common/Thread.h" -#include "network/RESTDefines.h" +#include "restapi/RESTDefines.h" #include #include @@ -92,12 +92,12 @@ class HOST_SW_API RESTAPI : private Thread { void close(); private: - typedef network::rest::RequestDispatcher RESTDispatcherType; - typedef network::rest::http::HTTPPayload HTTPPayload; + typedef restapi::RequestDispatcher RESTDispatcherType; + typedef restapi::http::HTTPPayload HTTPPayload; RESTDispatcherType m_dispatcher; - network::rest::http::HTTPServer m_restServer; + restapi::http::HTTPServer m_restServer; #if defined(ENABLE_SSL) - network::rest::http::SecureHTTPServer m_restSecureServer; + restapi::http::SecureHTTPServer m_restSecureServer; bool m_enableSSL; #endif // ENABLE_SSL @@ -150,7 +150,7 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutAuth(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutAuth(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get version request. @@ -158,21 +158,21 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetVersion(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetVersion(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get status request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetStatus(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetStatus(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get voice channels request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetVoiceCh(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetVoiceCh(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put/set modem mode request. @@ -180,14 +180,14 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutModemMode(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutModemMode(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements put/request modem kill request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutModemKill(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutModemKill(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements set supervisory mode request. @@ -195,35 +195,35 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutSetSupervisor(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutSetSupervisor(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements permit TG request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutPermitTG(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutPermitTG(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements grant TG request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutGrantTG(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutGrantTG(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements release grants request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetReleaseGrants(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetReleaseGrants(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements release affiliations request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetReleaseAffs(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetReleaseAffs(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get RID whitelist request. @@ -231,14 +231,14 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetRIDWhitelist(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetRIDWhitelist(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get RID blacklist request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetRIDBlacklist(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetRIDBlacklist(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /* ** Digital Mobile Radio @@ -250,49 +250,49 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetDMRBeacon(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetDMRBeacon(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get DMR debug state request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetDMRDebug(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetDMRDebug(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get DMR dump CSBK state request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetDMRDumpCSBK(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetDMRDumpCSBK(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements DMR RID operations request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutDMRRID(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutDMRRID(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements toggle DMR CC enable request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetDMRCCEnable(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetDMRCCEnable(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements toggle DMR CC broadcast request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetDMRCCBroadcast(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetDMRCCBroadcast(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get DMR affiliations request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetDMRAffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetDMRAffList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /* ** Project 25 @@ -304,56 +304,56 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetP25CC(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetP25CC(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements P25 debug state request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetP25Debug(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetP25Debug(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements P25 dump TSBK state request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetP25DumpTSBK(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetP25DumpTSBK(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements P25 RID operation request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutP25RID(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutP25RID(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements toggle P25 CC request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetP25CCEnable(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetP25CCEnable(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements toggle P25 broadcast request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetP25CCBroadcast(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetP25CCBroadcast(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements transmitting raw TSBK request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutP25RawTSBK(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_PutP25RawTSBK(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get P25 affiliations request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetP25AffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetP25AffList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /* ** Next Generation Digital Narrowband @@ -365,35 +365,35 @@ class HOST_SW_API RESTAPI : private Thread { * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetNXDNCC(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetNXDNCC(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements NXDN debug state request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetNXDNDebug(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetNXDNDebug(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements NXDN dump RCCH state request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetNXDNDumpRCCH(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetNXDNDumpRCCH(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements toggle NXDN CC request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetNXDNCCEnable(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetNXDNCCEnable(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** * @brief REST API endpoint; implements get NXDN affiliations request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetNXDNAffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + void restAPI_GetNXDNAffList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); }; #endif // __REST_API_H__ diff --git a/src/host/network/RESTDefines.h b/src/host/restapi/RESTDefines.h similarity index 100% rename from src/host/network/RESTDefines.h rename to src/host/restapi/RESTDefines.h diff --git a/src/monitor/InhibitSubscriberWnd.h b/src/monitor/InhibitSubscriberWnd.h index e9c2a55a..76204faf 100644 --- a/src/monitor/InhibitSubscriberWnd.h +++ b/src/monitor/InhibitSubscriberWnd.h @@ -137,7 +137,7 @@ class HOST_SW_API InhibitSubscriberWnd final : public TransmitWndBase { // callback REST API int ret = RESTClient::send(m_selectedCh.address(), m_selectedCh.port(), m_selectedCh.password(), HTTP_PUT, method, req, m_selectedCh.ssl(), g_debug); - if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + if (ret != restapi::http::HTTPPayload::StatusType::OK) { ::LogError(LOG_HOST, "failed to send request %s to %s:%u", method.c_str(), m_selectedCh.address().c_str(), m_selectedCh.port()); } } diff --git a/src/monitor/NodeStatusWnd.h b/src/monitor/NodeStatusWnd.h index 9aeea5f4..a4884ca1 100644 --- a/src/monitor/NodeStatusWnd.h +++ b/src/monitor/NodeStatusWnd.h @@ -15,7 +15,7 @@ #define __NODE_STATUS_WND_H__ #include "common/lookups/AffiliationLookup.h" -#include "host/network/RESTDefines.h" +#include "host/restapi/RESTDefines.h" #include "host/modem/Modem.h" #include "remote/RESTClient.h" @@ -376,7 +376,7 @@ class HOST_SW_API NodeStatusWnd final : public finalcut::FDialog { int ret = RESTClient::send(m_chData.address(), m_chData.port(), m_chData.password(), HTTP_GET, GET_STATUS, req, rsp, m_chData.ssl(), g_debug); - if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + if (ret != restapi::http::HTTPPayload::StatusType::OK) { ::LogError(LOG_HOST, "failed to get status for %s:%u, chNo = %u", m_chData.address().c_str(), m_chData.port(), m_channelNo); ++m_failCnt; if (m_failCnt > NODE_UPDATE_FAIL_CNT) { @@ -506,7 +506,7 @@ class HOST_SW_API NodeStatusWnd final : public finalcut::FDialog { json::object req = json::object(); int ret = RESTClient::send(m_chData.address(), m_chData.port(), m_chData.password(), HTTP_GET, GET_STATUS, req, m_chData.ssl(), g_debug); - if (ret == network::rest::http::HTTPPayload::StatusType::OK) { + if (ret == restapi::http::HTTPPayload::StatusType::OK) { m_failed = false; m_failCnt = 0U; m_tbText = std::string("UNKNOWN"); diff --git a/src/monitor/PageSubscriberWnd.h b/src/monitor/PageSubscriberWnd.h index 2ef3c5f8..39ca03e1 100644 --- a/src/monitor/PageSubscriberWnd.h +++ b/src/monitor/PageSubscriberWnd.h @@ -137,7 +137,7 @@ class HOST_SW_API PageSubscriberWnd final : public TransmitWndBase { // callback REST API int ret = RESTClient::send(m_selectedCh.address(), m_selectedCh.port(), m_selectedCh.password(), HTTP_PUT, method, req, m_selectedCh.ssl(), g_debug); - if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + if (ret != restapi::http::HTTPPayload::StatusType::OK) { ::LogError(LOG_HOST, "failed to send request %s to %s:%u", method.c_str(), m_selectedCh.address().c_str(), m_selectedCh.port()); } } diff --git a/src/monitor/RadioCheckSubscriberWnd.h b/src/monitor/RadioCheckSubscriberWnd.h index c3afe1f3..8114d06d 100644 --- a/src/monitor/RadioCheckSubscriberWnd.h +++ b/src/monitor/RadioCheckSubscriberWnd.h @@ -137,7 +137,7 @@ class HOST_SW_API RadioCheckSubscriberWnd final : public TransmitWndBase { // callback REST API int ret = RESTClient::send(m_selectedCh.address(), m_selectedCh.port(), m_selectedCh.password(), HTTP_PUT, method, req, m_selectedCh.ssl(), g_debug); - if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + if (ret != restapi::http::HTTPPayload::StatusType::OK) { ::LogError(LOG_HOST, "failed to send request %s to %s:%u", method.c_str(), m_selectedCh.address().c_str(), m_selectedCh.port()); } } diff --git a/src/monitor/TransmitWndBase.h b/src/monitor/TransmitWndBase.h index 8796f9e9..4aeca58b 100644 --- a/src/monitor/TransmitWndBase.h +++ b/src/monitor/TransmitWndBase.h @@ -15,7 +15,7 @@ #define __TRANSMIT_WND_BASE_H__ #include "common/lookups/AffiliationLookup.h" -#include "host/network/RESTDefines.h" +#include "host/restapi/RESTDefines.h" #include "host/modem/Modem.h" #include "remote/RESTClient.h" #include "MonitorMain.h" @@ -107,7 +107,7 @@ class HOST_SW_API TransmitWndBase : public FDblDialog { int ret = RESTClient::send(m_selectedCh.address(), m_selectedCh.port(), m_selectedCh.password(), HTTP_GET, GET_STATUS, req, rsp, m_selectedCh.ssl(), g_debug); - if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + if (ret != restapi::http::HTTPPayload::StatusType::OK) { ::LogError(LOG_HOST, "failed to get status for %s:%u", m_selectedCh.address().c_str(), m_selectedCh.port()); } diff --git a/src/monitor/UninhibitSubscriberWnd.h b/src/monitor/UninhibitSubscriberWnd.h index 012bdb74..ea485137 100644 --- a/src/monitor/UninhibitSubscriberWnd.h +++ b/src/monitor/UninhibitSubscriberWnd.h @@ -137,7 +137,7 @@ class HOST_SW_API UninhibitSubscriberWnd final : public TransmitWndBase { // callback REST API int ret = RESTClient::send(m_selectedCh.address(), m_selectedCh.port(), m_selectedCh.password(), HTTP_PUT, method, req, m_selectedCh.ssl(), g_debug); - if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + if (ret != restapi::http::HTTPPayload::StatusType::OK) { ::LogError(LOG_HOST, "failed to send request %s to %s:%u", method.c_str(), m_selectedCh.address().c_str(), m_selectedCh.port()); } } diff --git a/src/patch/network/PeerNetwork.cpp b/src/patch/network/PeerNetwork.cpp index 6bbe6ad5..497760f1 100644 --- a/src/patch/network/PeerNetwork.cpp +++ b/src/patch/network/PeerNetwork.cpp @@ -11,9 +11,9 @@ #include "common/dmr/data/EMB.h" #include "common/dmr/lc/FullLC.h" #include "common/dmr/SlotType.h" -#include "common/network/json/json.h" #include "common/p25/dfsi/DFSIDefines.h" #include "common/p25/dfsi/LC.h" +#include "common/json/json.h" #include "common/Utils.h" #include "network/PeerNetwork.h" diff --git a/src/remote/RESTClient.cpp b/src/remote/RESTClient.cpp index 59d43a7b..4ec31f0c 100644 --- a/src/remote/RESTClient.cpp +++ b/src/remote/RESTClient.cpp @@ -9,16 +9,16 @@ */ #include "Defines.h" #include "common/edac/SHA256.h" -#include "common/network/json/json.h" -#include "common/network/rest/http/HTTPClient.h" -#include "common/network/rest/http/SecureHTTPClient.h" -#include "common/network/rest/RequestDispatcher.h" +#include "common/json/json.h" +#include "common/restapi/http/HTTPClient.h" +#include "common/restapi/http/SecureHTTPClient.h" +#include "common/restapi/RequestDispatcher.h" #include "common/Thread.h" #include "common/Log.h" #include "remote/RESTClient.h" -using namespace network; -using namespace network::rest::http; +using namespace restapi; +using namespace restapi::http; #include #include @@ -149,7 +149,7 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin s_enableSSL = enableSSL; s_debug = debug; - typedef network::rest::BasicRequestDispatcher RESTDispatcherType; + typedef restapi::BasicRequestDispatcher RESTDispatcherType; RESTDispatcherType m_dispatcher(RESTClient::responseHandler); HTTPClient* client = nullptr; #if defined(ENABLE_SSL) diff --git a/src/remote/RESTClient.h b/src/remote/RESTClient.h index db92d945..025af96e 100644 --- a/src/remote/RESTClient.h +++ b/src/remote/RESTClient.h @@ -21,8 +21,8 @@ #define __REST_CLIENT_H__ #include "Defines.h" -#include "common/network/json/json.h" -#include "common/network/rest/http/HTTPPayload.h" +#include "common/json/json.h" +#include "common/restapi/http/HTTPPayload.h" #include @@ -104,7 +104,7 @@ class HOST_SW_API RESTClient const std::string endpoint, json::object payload, json::object& response, bool enableSSL, int timeout, bool debug = false); private: - typedef network::rest::http::HTTPPayload HTTPPayload; + typedef restapi::http::HTTPPayload HTTPPayload; /** * @brief HTTP response handler. * @param request HTTP request. diff --git a/src/remote/RESTClientMain.cpp b/src/remote/RESTClientMain.cpp index c22c6c38..01c49e79 100644 --- a/src/remote/RESTClientMain.cpp +++ b/src/remote/RESTClientMain.cpp @@ -8,8 +8,8 @@ * */ #include "remote/RESTClient.h" -#include "host/network/RESTDefines.h" -#include "fne/network/RESTDefines.h" +#include "host/restapi/RESTDefines.h" +#include "fne/restapi/RESTDefines.h" #include "common/Thread.h" #include "common/Log.h" diff --git a/src/sysview/AffListWnd.h b/src/sysview/AffListWnd.h index 93a4a580..6a3cbb81 100644 --- a/src/sysview/AffListWnd.h +++ b/src/sysview/AffListWnd.h @@ -16,7 +16,7 @@ #include "common/lookups/AffiliationLookup.h" #include "common/Log.h" -#include "fne/network/RESTDefines.h" +#include "fne/restapi/RESTDefines.h" #include "remote/RESTClient.h" #include "FDblDialog.h" @@ -102,7 +102,7 @@ class HOST_SW_API AffListWnd final : public FDblDialog { int ret = RESTClient::send(fneRESTAddress, fneRESTPort, fnePassword, HTTP_GET, FNE_GET_AFF_LIST, req, rsp, fneSSL, g_debug); - if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + if (ret != restapi::http::HTTPPayload::StatusType::OK) { ::LogError(LOG_HOST, "[AFFVIEW] failed to get affiliations for %s:%u", fneRESTAddress.c_str(), fneRESTPort); } else { diff --git a/src/sysview/HostWS.cpp b/src/sysview/HostWS.cpp index cdabfbdb..1925955c 100644 --- a/src/sysview/HostWS.cpp +++ b/src/sysview/HostWS.cpp @@ -14,7 +14,7 @@ #include "common/StopWatch.h" #include "common/Thread.h" #include "common/Utils.h" -#include "fne/network/RESTDefines.h" +#include "fne/restapi/RESTDefines.h" #include "remote/RESTClient.h" #include "network/PeerNetwork.h" #include "HostWS.h" @@ -327,7 +327,7 @@ int HostWS::run() int ret = RESTClient::send(fneRESTAddress, fneRESTPort, fnePassword, HTTP_GET, FNE_GET_PEER_QUERY, req, rsp, fneSSL, g_debug); - if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + if (ret != restapi::http::HTTPPayload::StatusType::OK) { ::LogError(LOG_HOST, "[AFFVIEW] failed to query peers for %s:%u", fneRESTAddress.c_str(), fneRESTPort); } else { @@ -354,7 +354,7 @@ int HostWS::run() int ret = RESTClient::send(fneRESTAddress, fneRESTPort, fnePassword, HTTP_GET, FNE_GET_AFF_LIST, req, rsp, fneSSL, g_debug); - if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + if (ret != restapi::http::HTTPPayload::StatusType::OK) { ::LogError(LOG_HOST, "[AFFVIEW] failed to query peers for %s:%u", fneRESTAddress.c_str(), fneRESTPort); } else { diff --git a/src/sysview/PeerListWnd.h b/src/sysview/PeerListWnd.h index 6629cd62..83e43bee 100644 --- a/src/sysview/PeerListWnd.h +++ b/src/sysview/PeerListWnd.h @@ -16,7 +16,7 @@ #include "common/lookups/AffiliationLookup.h" #include "common/Log.h" -#include "fne/network/RESTDefines.h" +#include "fne/restapi/RESTDefines.h" #include "remote/RESTClient.h" #include "FDblDialog.h" @@ -102,7 +102,7 @@ class HOST_SW_API PeerListWnd final : public FDblDialog { int ret = RESTClient::send(fneRESTAddress, fneRESTPort, fnePassword, HTTP_GET, FNE_GET_PEER_QUERY, req, rsp, fneSSL, g_debug); - if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + if (ret != restapi::http::HTTPPayload::StatusType::OK) { ::LogError(LOG_HOST, "[AFFVIEW] failed to query peers for %s:%u", fneRESTAddress.c_str(), fneRESTPort); } else { diff --git a/src/sysview/network/PeerNetwork.cpp b/src/sysview/network/PeerNetwork.cpp index fbdd262f..a1069f3c 100644 --- a/src/sysview/network/PeerNetwork.cpp +++ b/src/sysview/network/PeerNetwork.cpp @@ -8,9 +8,9 @@ * */ #include "sysview/Defines.h" -#include "common/network/json/json.h" #include "common/p25/dfsi/DFSIDefines.h" #include "common/p25/dfsi/LC.h" +#include "common/json/json.h" #include "common/zlib/zlib.h" #include "common/Utils.h" #include "network/PeerNetwork.h" From f55f8a3467386db4a3f82699814c97eb901c6047 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 10 Nov 2025 23:33:13 -0500 Subject: [PATCH 150/200] add multi-block PDU for V.24; --- src/common/p25/dfsi/DFSIDefines.h | 2 +- src/host/modem/ModemV24.cpp | 118 ++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) diff --git a/src/common/p25/dfsi/DFSIDefines.h b/src/common/p25/dfsi/DFSIDefines.h index 5cde6888..4e0f781c 100644 --- a/src/common/p25/dfsi/DFSIDefines.h +++ b/src/common/p25/dfsi/DFSIDefines.h @@ -139,7 +139,7 @@ namespace p25 MOT_PDU_UNCONF_END = 0x85U, //!< Motorola/V.24 PDU (Unconfirmed Block End) MOT_PDU_SINGLE_UNCONF = 0x87U, //!< Motorola/V.24 PDU (Single Unconfirmed Block) - MOD_PDU_CONF_HEADER = 0x88U, //!< Motorola/V.24 PDU (Confirmed Block Header) + MOT_PDU_CONF_HEADER = 0x88U, //!< Motorola/V.24 PDU (Confirmed Block Header) MOT_PDU_CONF_BLOCK_1 = 0x89U, //!< Motorola/V.24 PDU (Confirmed Block 1) MOT_PDU_CONF_BLOCK_2 = 0x8AU, //!< Motorola/V.24 PDU (Confirmed Block 2) MOT_PDU_CONF_BLOCK_3 = 0x8BU, //!< Motorola/V.24 PDU (Confirmed Block 3) diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index b00b60c1..eafc2655 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -978,6 +978,65 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) } break; + case DFSIFrameType::MOT_PDU_UNCONF_HEADER: + { + m_rxCall->resetCallData(); + + uint8_t header[P25_PDU_HEADER_LENGTH_BYTES]; + ::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); + + ::memcpy(header, dfsiData + 1U, P25_PDU_HEADER_LENGTH_BYTES); + data::DataHeader pduHeader = data::DataHeader(); + bool ret = pduHeader.decode(header, true); + if (!ret) { + LogWarning(LOG_MODEM, P25_PDU_STR ", unfixable RF 1/2 rate header data"); + Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); + break; + } + + if (m_debug) { + ::LogDebugEx(LOG_MODEM, "ModemV24::convertToAirV24()", "V.24 RX, PDU ISP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", + pduHeader.getAckNeeded(), pduHeader.getOutbound(), pduHeader.getFormat(), pduHeader.getMFId(), pduHeader.getSAP(), pduHeader.getFullMessage(), + pduHeader.getBlocksToFollow(), pduHeader.getPadLength(), pduHeader.getPacketLength(), pduHeader.getSynchronize(), pduHeader.getNs(), pduHeader.getFSN(), pduHeader.getLastFragment(), + pduHeader.getHeaderOffset(), pduHeader.getLLId()); + } + + m_rxCall->dataCall = true; + m_rxCall->dataHeader = pduHeader; + + // PDU_UNCONF_HEADER only contains 3 blocks + for (uint8_t i = 0U; i < 3U; i++) { + uint8_t dataBlock[P25_PDU_UNCONFIRMED_LENGTH_BYTES]; + ::memset(dataBlock, 0x00U, P25_PDU_UNCONFIRMED_LENGTH_BYTES); + ::memcpy(dataBlock, dfsiData + 1U + P25_PDU_HEADER_LENGTH_BYTES + (i * P25_PDU_UNCONFIRMED_LENGTH_BYTES), P25_PDU_UNCONFIRMED_LENGTH_BYTES); + + ::memcpy(m_rxCall->pduUserData + m_rxCall->pduUserDataOffset, dataBlock, P25_PDU_UNCONFIRMED_LENGTH_BYTES); + m_rxCall->pduUserDataOffset += P25_PDU_UNCONFIRMED_LENGTH_BYTES; + } + } + break; + case DFSIFrameType::MOT_PDU_UNCONF_BLOCK_1: + case DFSIFrameType::MOT_PDU_UNCONF_BLOCK_2: + case DFSIFrameType::MOT_PDU_UNCONF_BLOCK_3: + case DFSIFrameType::MOT_PDU_UNCONF_BLOCK_4: + case DFSIFrameType::MOT_PDU_UNCONF_END: + { + // PDU_UNCONF_BLOCK_X and PDU_UNCONF_END only contains 4 blocks each + for (uint8_t i = 0U; i < 4U; i++) { + uint8_t dataBlock[P25_PDU_UNCONFIRMED_LENGTH_BYTES]; + ::memset(dataBlock, 0x00U, P25_PDU_UNCONFIRMED_LENGTH_BYTES); + ::memcpy(dataBlock, dfsiData + 1U + P25_PDU_HEADER_LENGTH_BYTES + (i * P25_PDU_UNCONFIRMED_LENGTH_BYTES), P25_PDU_UNCONFIRMED_LENGTH_BYTES); + + ::memcpy(m_rxCall->pduUserData + m_rxCall->pduUserDataOffset, dataBlock, P25_PDU_UNCONFIRMED_LENGTH_BYTES); + m_rxCall->pduUserDataOffset += P25_PDU_UNCONFIRMED_LENGTH_BYTES; + } + + if (frameType == DFSIFrameType::MOT_PDU_UNCONF_END) { + storeConvertedRxPDU(m_rxCall->dataHeader, m_rxCall->pduUserData); + } + } + break; + case DFSIFrameType::MOT_PDU_SINGLE_CONF: { m_rxCall->resetCallData(); @@ -1017,6 +1076,65 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) } break; + case DFSIFrameType::MOT_PDU_CONF_HEADER: + { + m_rxCall->resetCallData(); + + uint8_t header[P25_PDU_HEADER_LENGTH_BYTES]; + ::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); + + ::memcpy(header, dfsiData + 1U, P25_PDU_HEADER_LENGTH_BYTES); + data::DataHeader pduHeader = data::DataHeader(); + bool ret = pduHeader.decode(header, true); + if (!ret) { + LogWarning(LOG_MODEM, P25_PDU_STR ", unfixable RF 1/2 rate header data"); + Utils::dump(1U, "P25, Unfixable PDU Data", buffer, P25_PDU_FEC_LENGTH_BYTES); + break; + } + + if (m_debug) { + ::LogDebugEx(LOG_MODEM, "ModemV24::convertToAirV24()", "V.24 RX, PDU ISP, ack = %u, outbound = %u, fmt = $%02X, mfId = $%02X, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, S = %u, n = %u, seqNo = %u, lastFragment = %u, hdrOffset = %u, llId = %u", + pduHeader.getAckNeeded(), pduHeader.getOutbound(), pduHeader.getFormat(), pduHeader.getMFId(), pduHeader.getSAP(), pduHeader.getFullMessage(), + pduHeader.getBlocksToFollow(), pduHeader.getPadLength(), pduHeader.getPacketLength(), pduHeader.getSynchronize(), pduHeader.getNs(), pduHeader.getFSN(), pduHeader.getLastFragment(), + pduHeader.getHeaderOffset(), pduHeader.getLLId()); + } + + m_rxCall->dataCall = true; + m_rxCall->dataHeader = pduHeader; + + // PDU_CONF_HEADER only contains 3 blocks + for (uint8_t i = 0U; i < 3U; i++) { + uint8_t dataBlock[P25_PDU_CONFIRMED_LENGTH_BYTES]; + ::memset(dataBlock, 0x00U, P25_PDU_CONFIRMED_LENGTH_BYTES); + ::memcpy(dataBlock, dfsiData + 1U + P25_PDU_HEADER_LENGTH_BYTES + (i * P25_PDU_CONFIRMED_LENGTH_BYTES), P25_PDU_CONFIRMED_LENGTH_BYTES); + + ::memcpy(m_rxCall->pduUserData + m_rxCall->pduUserDataOffset, dataBlock, P25_PDU_CONFIRMED_LENGTH_BYTES); + m_rxCall->pduUserDataOffset += P25_PDU_CONFIRMED_LENGTH_BYTES; + } + } + break; + case DFSIFrameType::MOT_PDU_CONF_BLOCK_1: + case DFSIFrameType::MOT_PDU_CONF_BLOCK_2: + case DFSIFrameType::MOT_PDU_CONF_BLOCK_3: + case DFSIFrameType::MOT_PDU_CONF_BLOCK_4: + case DFSIFrameType::MOT_PDU_CONF_END: + { + // PDU_CONF_BLOCK_X only contains 4 blocks each + for (uint8_t i = 0U; i < 4U; i++) { + uint8_t dataBlock[P25_PDU_CONFIRMED_LENGTH_BYTES]; + ::memset(dataBlock, 0x00U, P25_PDU_CONFIRMED_LENGTH_BYTES); + ::memcpy(dataBlock, dfsiData + 1U + P25_PDU_HEADER_LENGTH_BYTES + (i * P25_PDU_CONFIRMED_LENGTH_BYTES), P25_PDU_CONFIRMED_LENGTH_BYTES); + + ::memcpy(m_rxCall->pduUserData + m_rxCall->pduUserDataOffset, dataBlock, P25_PDU_CONFIRMED_LENGTH_BYTES); + m_rxCall->pduUserDataOffset += P25_PDU_CONFIRMED_LENGTH_BYTES; + } + + if (frameType == DFSIFrameType::MOT_PDU_CONF_END) { + storeConvertedRxPDU(m_rxCall->dataHeader, m_rxCall->pduUserData); + } + } + break; + case DFSIFrameType::MOT_TSBK: { MotTSBKFrame tf = MotTSBKFrame(dfsiData); From 4a8be298e57533770af7fbd90a2bbf93707b674a Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 11 Nov 2025 08:51:50 -0500 Subject: [PATCH 151/200] add some guard rails on PDU reception so we dont overflow ourselves; --- src/host/modem/ModemV24.cpp | 28 ++++++++++++++++++++++++++++ src/host/modem/ModemV24.h | 6 ++++++ 2 files changed, 34 insertions(+) diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index eafc2655..fdfc9315 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -1001,6 +1001,14 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) pduHeader.getHeaderOffset(), pduHeader.getLLId()); } + // make sure we don't get a PDU with more blocks then we support + if (pduHeader.getBlocksToFollow() >= P25_MAX_PDU_BLOCKS) { + LogError(LOG_MODEM, P25_PDU_STR ", ISP, too many PDU blocks to process, %u > %u", pduHeader.getBlocksToFollow(), P25_MAX_PDU_BLOCKS); + + m_rxCall->resetCallData(); + break; + } + m_rxCall->dataCall = true; m_rxCall->dataHeader = pduHeader; @@ -1012,6 +1020,7 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) ::memcpy(m_rxCall->pduUserData + m_rxCall->pduUserDataOffset, dataBlock, P25_PDU_UNCONFIRMED_LENGTH_BYTES); m_rxCall->pduUserDataOffset += P25_PDU_UNCONFIRMED_LENGTH_BYTES; + m_rxCall->pduTotalBlocks++; } } break; @@ -1021,6 +1030,10 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) case DFSIFrameType::MOT_PDU_UNCONF_BLOCK_4: case DFSIFrameType::MOT_PDU_UNCONF_END: { + // only process blocks if we've received a header and started a data call + if (!m_rxCall->dataCall) + break; + // PDU_UNCONF_BLOCK_X and PDU_UNCONF_END only contains 4 blocks each for (uint8_t i = 0U; i < 4U; i++) { uint8_t dataBlock[P25_PDU_UNCONFIRMED_LENGTH_BYTES]; @@ -1029,6 +1042,7 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) ::memcpy(m_rxCall->pduUserData + m_rxCall->pduUserDataOffset, dataBlock, P25_PDU_UNCONFIRMED_LENGTH_BYTES); m_rxCall->pduUserDataOffset += P25_PDU_UNCONFIRMED_LENGTH_BYTES; + m_rxCall->pduTotalBlocks++; } if (frameType == DFSIFrameType::MOT_PDU_UNCONF_END) { @@ -1099,6 +1113,14 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) pduHeader.getHeaderOffset(), pduHeader.getLLId()); } + // make sure we don't get a PDU with more blocks then we support + if (pduHeader.getBlocksToFollow() >= P25_MAX_PDU_BLOCKS) { + LogError(LOG_MODEM, P25_PDU_STR ", ISP, too many PDU blocks to process, %u > %u", pduHeader.getBlocksToFollow(), P25_MAX_PDU_BLOCKS); + + m_rxCall->resetCallData(); + break; + } + m_rxCall->dataCall = true; m_rxCall->dataHeader = pduHeader; @@ -1110,6 +1132,7 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) ::memcpy(m_rxCall->pduUserData + m_rxCall->pduUserDataOffset, dataBlock, P25_PDU_CONFIRMED_LENGTH_BYTES); m_rxCall->pduUserDataOffset += P25_PDU_CONFIRMED_LENGTH_BYTES; + m_rxCall->pduTotalBlocks++; } } break; @@ -1119,6 +1142,10 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) case DFSIFrameType::MOT_PDU_CONF_BLOCK_4: case DFSIFrameType::MOT_PDU_CONF_END: { + // only process blocks if we've received a header and started a data call + if (!m_rxCall->dataCall) + break; + // PDU_CONF_BLOCK_X only contains 4 blocks each for (uint8_t i = 0U; i < 4U; i++) { uint8_t dataBlock[P25_PDU_CONFIRMED_LENGTH_BYTES]; @@ -1127,6 +1154,7 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) ::memcpy(m_rxCall->pduUserData + m_rxCall->pduUserDataOffset, dataBlock, P25_PDU_CONFIRMED_LENGTH_BYTES); m_rxCall->pduUserDataOffset += P25_PDU_CONFIRMED_LENGTH_BYTES; + m_rxCall->pduTotalBlocks++; } if (frameType == DFSIFrameType::MOT_PDU_CONF_END) { diff --git a/src/host/modem/ModemV24.h b/src/host/modem/ModemV24.h index ec705da9..25dfffe4 100644 --- a/src/host/modem/ModemV24.h +++ b/src/host/modem/ModemV24.h @@ -85,6 +85,7 @@ namespace modem dataHeader(), dataCall(false), pduUserDataOffset(0U), + pduTotalBlocks(0U), errors(0U) { MI = new uint8_t[P25DEF::MI_LENGTH_BYTES]; @@ -166,6 +167,7 @@ namespace modem dataCall = false; pduUserDataOffset = 0U; + pduTotalBlocks = 0U; errors = 0U; } @@ -265,6 +267,10 @@ namespace modem * @brief Offset index when populating the user data buffer. */ uint32_t pduUserDataOffset; + /** + * @brief Total count of PDU blocks. + */ + uint32_t pduTotalBlocks; /** * @brief Total errors for a given call sequence. From 7e00a0a1dbcbc2d095fbe1c2492245cf0246c5ef Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 11 Nov 2025 09:08:07 -0500 Subject: [PATCH 152/200] it seems as if PDU_(UN)CONF_END is variable length, it will always be at least 1 block containing the last PDU block, and PDU_(UN)CONF_BLOCK_X is always 4 blocks; --- src/common/p25/dfsi/DFSIDefines.h | 2 ++ src/host/modem/ModemV24.cpp | 26 ++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/common/p25/dfsi/DFSIDefines.h b/src/common/p25/dfsi/DFSIDefines.h index 4e0f781c..16d86f5a 100644 --- a/src/common/p25/dfsi/DFSIDefines.h +++ b/src/common/p25/dfsi/DFSIDefines.h @@ -49,6 +49,8 @@ namespace p25 const uint32_t DFSI_MOT_TSBK_LEN = 24U; const uint32_t DFSI_MOT_TDULC_LEN = 21U; + const uint32_t DFSI_PDU_BLOCK_CNT = 4U; + const uint32_t DFSI_TIA_VHDR_LEN = 22U; const uint32_t DFSI_MOT_ICW_LENGTH = 6U; diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index fdfc9315..8ffcdd07 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -1034,8 +1034,19 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) if (!m_rxCall->dataCall) break; + uint32_t blockCnt = DFSI_PDU_BLOCK_CNT; + + // PDU_UNCONF_END are variable length depending on the message + if (frameType == DFSIFrameType::MOT_PDU_UNCONF_END) { + blockCnt = m_rxCall->dataHeader.getBlocksToFollow() - m_rxCall->pduTotalBlocks; + + // bryanb: I wonder if there's a chance somehow the calculation will be less then zero...reasonably + // as far as I can tell that should never happen as PDU_UNCONF_BLOCK_X should *always* contain + // 4 blocks of user data, and the PDU_UNCONF_END is always variable with at least the last data block + } + // PDU_UNCONF_BLOCK_X and PDU_UNCONF_END only contains 4 blocks each - for (uint8_t i = 0U; i < 4U; i++) { + for (uint8_t i = 0U; i < blockCnt; i++) { uint8_t dataBlock[P25_PDU_UNCONFIRMED_LENGTH_BYTES]; ::memset(dataBlock, 0x00U, P25_PDU_UNCONFIRMED_LENGTH_BYTES); ::memcpy(dataBlock, dfsiData + 1U + P25_PDU_HEADER_LENGTH_BYTES + (i * P25_PDU_UNCONFIRMED_LENGTH_BYTES), P25_PDU_UNCONFIRMED_LENGTH_BYTES); @@ -1146,8 +1157,19 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) if (!m_rxCall->dataCall) break; + uint32_t blockCnt = DFSI_PDU_BLOCK_CNT; + + // PDU_CONF_END are variable length depending on the message + if (frameType == DFSIFrameType::MOT_PDU_CONF_END) { + blockCnt = m_rxCall->dataHeader.getBlocksToFollow() - m_rxCall->pduTotalBlocks; + + // bryanb: I wonder if there's a chance somehow the calculation will be less then zero...reasonably + // as far as I can tell that should never happen as PDU_CONF_BLOCK_X should *always* contain + // 4 blocks of user data, and the PDU_CONF_END is always variable with at least the last data block + } + // PDU_CONF_BLOCK_X only contains 4 blocks each - for (uint8_t i = 0U; i < 4U; i++) { + for (uint8_t i = 0U; i < blockCnt; i++) { uint8_t dataBlock[P25_PDU_CONFIRMED_LENGTH_BYTES]; ::memset(dataBlock, 0x00U, P25_PDU_CONFIRMED_LENGTH_BYTES); ::memcpy(dataBlock, dfsiData + 1U + P25_PDU_HEADER_LENGTH_BYTES + (i * P25_PDU_CONFIRMED_LENGTH_BYTES), P25_PDU_CONFIRMED_LENGTH_BYTES); From f257389e2a501b5004539c7822911c917c9c4dc2 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 11 Nov 2025 09:20:57 -0500 Subject: [PATCH 153/200] cleanup dataBlocks pointer array; --- src/host/modem/ModemV24.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index 8ffcdd07..41429a46 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -2870,6 +2870,8 @@ void ModemV24::convertFromAirV24(uint8_t* data, uint32_t length) else { // TODO: support for more then 3 blocks } + + delete[] dataBlocks; } } break; From 4aa2ead4f641656aea3d7c74f26518f674cd3fae Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 11 Nov 2025 09:58:32 -0500 Subject: [PATCH 154/200] preliminary code for sending PDU frames >3 blocks over V.24; --- src/host/modem/ModemV24.cpp | 101 +++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index 41429a46..16f1be51 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -2868,7 +2868,106 @@ void ModemV24::convertFromAirV24(uint8_t* data, uint32_t length) queueP25Frame(endBuf, DFSI_MOT_START_LEN, STT_NON_IMBE_NO_JITTER); } else { - // TODO: support for more then 3 blocks + int32_t remainderBlocks = (blocksToFollow - 3U) % DFSI_PDU_BLOCK_CNT; + int32_t baseBlockCnt = (blocksToFollow - 3U) / DFSI_PDU_BLOCK_CNT; + uint32_t currentBlock = 0U; + + DECLARE_UINT8_ARRAY(pduBuf, ((DFSI_PDU_BLOCK_CNT + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES)) + 1U); + uint32_t pduLen = ((DFSI_PDU_BLOCK_CNT + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES)) + 1U; + + MotStartOfStream start = MotStartOfStream(); + start.setOpcode(m_rtrt ? MotStartStreamOpcode::TRANSMIT : MotStartStreamOpcode::RECEIVE); + start.setParam1(DSFI_MOT_ICW_PARM_PAYLOAD); + start.setArgument1(MotStreamPayload::DATA); + + // assemble the first frame + pduBuf[0U] = (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? DFSIFrameType::MOT_PDU_CONF_HEADER : DFSIFrameType::MOT_PDU_UNCONF_HEADER; + + dataHeader.encode(pduBuf + 1U, true); + for (uint32_t i = 0U; i < DFSI_PDU_BLOCK_CNT - 1U; i++) { + dataBlocks[currentBlock].encode(pduBuf + 1U + ((i + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES)), true); + currentBlock++; + } + + // create buffer for bytes and encode + uint8_t startBuf[DFSI_MOT_START_LEN]; + ::memset(startBuf, 0x00U, DFSI_MOT_START_LEN); + start.encode(startBuf); + + if (m_trace) + Utils::dump(1U, "ModemV24::convertFromAirV24(), PDU StartOfStream", startBuf, DFSI_MOT_START_LEN); + + queueP25Frame(startBuf, DFSI_MOT_START_LEN, STT_NON_IMBE_NO_JITTER); + + if (m_trace) + Utils::dump(1U, "ModemV24::convertFromAirV24(), MotPDUFrame", pduBuf, pduLen); + + queueP25Frame(pduBuf, pduLen, STT_NON_IMBE_NO_JITTER); + + // iterate through the count of full 4 block buffers and send + for (int32_t i = 1; i < baseBlockCnt; i++) { + // reset buffer and set data + ::memset(pduBuf, 0x00U, pduLen); + for (uint32_t i = 0U; i < DFSI_PDU_BLOCK_CNT - 1U; i++) { + dataBlocks[currentBlock].encode(pduBuf + 1U + ((i + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES)), true); + currentBlock++; + } + + // create buffer for bytes and encode + uint8_t startBuf[DFSI_MOT_START_LEN]; + ::memset(startBuf, 0x00U, DFSI_MOT_START_LEN); + start.encode(startBuf); + + if (m_trace) + Utils::dump(1U, "ModemV24::convertFromAirV24(), PDU StartOfStream", startBuf, DFSI_MOT_START_LEN); + + queueP25Frame(startBuf, DFSI_MOT_START_LEN, STT_NON_IMBE_NO_JITTER); + + if (m_trace) + Utils::dump(1U, "ModemV24::convertFromAirV24(), MotPDUFrame", pduBuf, pduLen); + + queueP25Frame(pduBuf, pduLen, STT_NON_IMBE_NO_JITTER); + } + + // do we have any remaining blocks? + if (remainderBlocks > 0) { + // reset buffer and set data + ::memset(pduBuf, 0x00U, pduLen); + pduLen = 0U; + for (uint32_t i = 0U; i < remainderBlocks; i++) { + dataBlocks[currentBlock].encode(pduBuf + 1U + ((i + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES)), true); + pduLen += 1U + (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES; + currentBlock++; + } + + // create buffer for bytes and encode + uint8_t startBuf[DFSI_MOT_START_LEN]; + ::memset(startBuf, 0x00U, DFSI_MOT_START_LEN); + start.encode(startBuf); + + if (m_trace) + Utils::dump(1U, "ModemV24::convertFromAirV24(), PDU StartOfStream", startBuf, DFSI_MOT_START_LEN); + + queueP25Frame(startBuf, DFSI_MOT_START_LEN, STT_NON_IMBE_NO_JITTER); + + if (m_trace) + Utils::dump(1U, "ModemV24::convertFromAirV24(), MotPDUFrame", pduBuf, pduLen); + + queueP25Frame(pduBuf, pduLen, STT_NON_IMBE_NO_JITTER); + + MotStartOfStream end = MotStartOfStream(); + end.setOpcode(m_rtrt ? MotStartStreamOpcode::TRANSMIT : MotStartStreamOpcode::RECEIVE); + end.setParam1(DFSI_MOT_ICW_PARM_STOP); + end.setArgument1(MotStreamPayload::DATA); + + // create buffer and encode + uint8_t endBuf[DFSI_MOT_START_LEN]; + ::memset(endBuf, 0x00U, DFSI_MOT_START_LEN); + end.encode(endBuf); + + queueP25Frame(endBuf, DFSI_MOT_START_LEN, STT_NON_IMBE_NO_JITTER); + queueP25Frame(endBuf, DFSI_MOT_START_LEN, STT_NON_IMBE_NO_JITTER); + } } delete[] dataBlocks; From fcfec19eb9e109ada2a97128f9b19b3e2f38bd1f Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 11 Nov 2025 10:00:32 -0500 Subject: [PATCH 155/200] use unsigned numbers for these loops the values should never be negative anyway; --- src/host/modem/ModemV24.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index 16f1be51..a773f088 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -2868,8 +2868,8 @@ void ModemV24::convertFromAirV24(uint8_t* data, uint32_t length) queueP25Frame(endBuf, DFSI_MOT_START_LEN, STT_NON_IMBE_NO_JITTER); } else { - int32_t remainderBlocks = (blocksToFollow - 3U) % DFSI_PDU_BLOCK_CNT; - int32_t baseBlockCnt = (blocksToFollow - 3U) / DFSI_PDU_BLOCK_CNT; + uint32_t remainderBlocks = (blocksToFollow - 3U) % DFSI_PDU_BLOCK_CNT; + uint32_t baseBlockCnt = (blocksToFollow - 3U) / DFSI_PDU_BLOCK_CNT; uint32_t currentBlock = 0U; DECLARE_UINT8_ARRAY(pduBuf, ((DFSI_PDU_BLOCK_CNT + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES)) + 1U); @@ -2882,7 +2882,7 @@ void ModemV24::convertFromAirV24(uint8_t* data, uint32_t length) // assemble the first frame pduBuf[0U] = (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? DFSIFrameType::MOT_PDU_CONF_HEADER : DFSIFrameType::MOT_PDU_UNCONF_HEADER; - + dataHeader.encode(pduBuf + 1U, true); for (uint32_t i = 0U; i < DFSI_PDU_BLOCK_CNT - 1U; i++) { dataBlocks[currentBlock].encode(pduBuf + 1U + ((i + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES)), true); @@ -2905,7 +2905,7 @@ void ModemV24::convertFromAirV24(uint8_t* data, uint32_t length) queueP25Frame(pduBuf, pduLen, STT_NON_IMBE_NO_JITTER); // iterate through the count of full 4 block buffers and send - for (int32_t i = 1; i < baseBlockCnt; i++) { + for (uint32_t i = 1U; i < baseBlockCnt; i++) { // reset buffer and set data ::memset(pduBuf, 0x00U, pduLen); for (uint32_t i = 0U; i < DFSI_PDU_BLOCK_CNT - 1U; i++) { From 5a73e262e0920b0acb5e343795d39e791be0d040 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 11 Nov 2025 10:04:53 -0500 Subject: [PATCH 156/200] properly encode sequence opcode; --- src/host/modem/ModemV24.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index a773f088..f56eb78e 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -2905,14 +2905,21 @@ void ModemV24::convertFromAirV24(uint8_t* data, uint32_t length) queueP25Frame(pduBuf, pduLen, STT_NON_IMBE_NO_JITTER); // iterate through the count of full 4 block buffers and send + uint8_t currentOpcode = (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? DFSIFrameType::MOT_PDU_CONF_BLOCK_1 : DFSIFrameType::MOT_PDU_UNCONF_BLOCK_1; for (uint32_t i = 1U; i < baseBlockCnt; i++) { // reset buffer and set data ::memset(pduBuf, 0x00U, pduLen); + pduBuf[0U] = currentOpcode; + for (uint32_t i = 0U; i < DFSI_PDU_BLOCK_CNT - 1U; i++) { dataBlocks[currentBlock].encode(pduBuf + 1U + ((i + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES)), true); currentBlock++; } + currentOpcode++; + if (currentOpcode > (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? DFSIFrameType::MOT_PDU_CONF_BLOCK_4 : DFSIFrameType::MOT_PDU_UNCONF_BLOCK_4) + currentOpcode = (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? DFSIFrameType::MOT_PDU_CONF_BLOCK_1 : DFSIFrameType::MOT_PDU_UNCONF_BLOCK_1; + // create buffer for bytes and encode uint8_t startBuf[DFSI_MOT_START_LEN]; ::memset(startBuf, 0x00U, DFSI_MOT_START_LEN); @@ -2933,6 +2940,7 @@ void ModemV24::convertFromAirV24(uint8_t* data, uint32_t length) if (remainderBlocks > 0) { // reset buffer and set data ::memset(pduBuf, 0x00U, pduLen); + pduBuf[0U] = (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? DFSIFrameType::MOT_PDU_CONF_END : DFSIFrameType::MOT_PDU_UNCONF_END; pduLen = 0U; for (uint32_t i = 0U; i < remainderBlocks; i++) { dataBlocks[currentBlock].encode(pduBuf + 1U + ((i + 1U) * ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? P25_PDU_CONFIRMED_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES)), true); From 38e939e2cbf59defc7b68112cdf0d2f5f35b3501 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 11 Nov 2025 10:06:09 -0500 Subject: [PATCH 157/200] whoops missing parens; --- src/host/modem/ModemV24.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index f56eb78e..ea6ef12f 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -2917,7 +2917,7 @@ void ModemV24::convertFromAirV24(uint8_t* data, uint32_t length) } currentOpcode++; - if (currentOpcode > (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? DFSIFrameType::MOT_PDU_CONF_BLOCK_4 : DFSIFrameType::MOT_PDU_UNCONF_BLOCK_4) + if (currentOpcode > ((dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? DFSIFrameType::MOT_PDU_CONF_BLOCK_4 : DFSIFrameType::MOT_PDU_UNCONF_BLOCK_4)) currentOpcode = (dataHeader.getFormat() == PDUFormatType::CONFIRMED) ? DFSIFrameType::MOT_PDU_CONF_BLOCK_1 : DFSIFrameType::MOT_PDU_UNCONF_BLOCK_1; // create buffer for bytes and encode From 2acb0d4623c5622b7583de90b625718072b21f87 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 11 Nov 2025 13:50:36 -0500 Subject: [PATCH 158/200] initialize these unordered_maps to allow the max connection cap count number of entries (before it was zero and could result in many reallocations of these maps); --- src/fne/network/FNENetwork.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 994510e6..92888053 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -93,10 +93,10 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_adjSiteMapLookup(nullptr), m_cryptoLookup(nullptr), m_status(NET_STAT_INVALID), - m_peers(), - m_peerReplicaPeers(), - m_peerAffiliations(), - m_ccPeerMap(), + m_peers(MAX_HARD_CONN_CAP), + m_peerReplicaPeers(MAX_HARD_CONN_CAP), + m_peerAffiliations(MAX_HARD_CONN_CAP), + m_ccPeerMap(MAX_HARD_CONN_CAP), m_peerReplicaKeyQueue(), m_treeRoot(nullptr), m_treeLock(), From 268379b9120d9262f91f015615645f4b1ee8f899 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 11 Nov 2025 14:06:21 -0500 Subject: [PATCH 159/200] well that was a little naive of me, I didnt multiply the count by the size of the stored elements...; --- src/fne/network/FNENetwork.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 92888053..39a5cbad 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -93,10 +93,10 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_adjSiteMapLookup(nullptr), m_cryptoLookup(nullptr), m_status(NET_STAT_INVALID), - m_peers(MAX_HARD_CONN_CAP), - m_peerReplicaPeers(MAX_HARD_CONN_CAP), - m_peerAffiliations(MAX_HARD_CONN_CAP), - m_ccPeerMap(MAX_HARD_CONN_CAP), + m_peers(MAX_HARD_CONN_CAP * sizeof(ulong64_t)), + m_peerReplicaPeers(), + m_peerAffiliations(MAX_HARD_CONN_CAP * sizeof(ulong64_t)), + m_ccPeerMap(), m_peerReplicaKeyQueue(), m_treeRoot(nullptr), m_treeLock(), From 9042511eb10d96779411e5d3549f90f6e47f552a Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 11 Nov 2025 14:33:36 -0500 Subject: [PATCH 160/200] reverse course lets do this more intelligently and implement the passthru for the reserve() function on the concurrency classes; --- src/common/concurrent/shared_unordered_map.h | 10 ++++++++++ src/common/concurrent/shared_vector.h | 10 ++++++++++ src/common/concurrent/unordered_map.h | 10 ++++++++++ src/common/concurrent/vector.h | 10 ++++++++++ src/fne/network/FNENetwork.cpp | 9 +++++++-- 5 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/common/concurrent/shared_unordered_map.h b/src/common/concurrent/shared_unordered_map.h index 35737d6b..bbcaa8b9 100644 --- a/src/common/concurrent/shared_unordered_map.h +++ b/src/common/concurrent/shared_unordered_map.h @@ -351,6 +351,16 @@ namespace concurrent return m_map; } + /** + * @brief Prepare the underlying unordered_map for a specified number of + * elements. + * @param n Number of elements required. + */ + void reserve(size_t n) + { + m_map.reserve(n); + } + private: std::unordered_map m_map; }; diff --git a/src/common/concurrent/shared_vector.h b/src/common/concurrent/shared_vector.h index 5a4c6eea..34aac3ac 100644 --- a/src/common/concurrent/shared_vector.h +++ b/src/common/concurrent/shared_vector.h @@ -408,6 +408,16 @@ namespace concurrent return m_vector; } + /** + * @brief Prepare the underlying vector for a specified number of + * elements. + * @param n Number of elements required. + */ + void reserve(size_t n) + { + m_vector.reserve(n); + } + private: std::vector m_vector; }; diff --git a/src/common/concurrent/unordered_map.h b/src/common/concurrent/unordered_map.h index 0a131c38..715d8fc5 100644 --- a/src/common/concurrent/unordered_map.h +++ b/src/common/concurrent/unordered_map.h @@ -368,6 +368,16 @@ namespace concurrent return m_map; } + /** + * @brief Prepare the underlying unordered_map for a specified number of + * elements. + * @param n Number of elements required. + */ + void reserve(size_t n) + { + m_map.reserve(n); + } + private: std::unordered_map m_map; }; diff --git a/src/common/concurrent/vector.h b/src/common/concurrent/vector.h index a365bf33..d395cd18 100644 --- a/src/common/concurrent/vector.h +++ b/src/common/concurrent/vector.h @@ -430,6 +430,16 @@ namespace concurrent return m_vector; } + /** + * @brief Prepare the underlying vector for a specified number of + * elements. + * @param n Number of elements required. + */ + void reserve(size_t n) + { + m_vector.reserve(n); + } + private: std::vector m_vector; }; diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 39a5cbad..cedbf439 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -93,9 +93,9 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_adjSiteMapLookup(nullptr), m_cryptoLookup(nullptr), m_status(NET_STAT_INVALID), - m_peers(MAX_HARD_CONN_CAP * sizeof(ulong64_t)), + m_peers(), m_peerReplicaPeers(), - m_peerAffiliations(MAX_HARD_CONN_CAP * sizeof(ulong64_t)), + m_peerAffiliations(), m_ccPeerMap(), m_peerReplicaKeyQueue(), m_treeRoot(nullptr), @@ -147,6 +147,11 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, assert(port > 0U); assert(!password.empty()); + m_peers.reserve(MAX_HARD_CONN_CAP); + m_peerReplicaPeers.reserve(MAX_HARD_CONN_CAP); + m_peerAffiliations.reserve(MAX_HARD_CONN_CAP); + m_ccPeerMap.reserve(MAX_HARD_CONN_CAP); + m_tagDMR = new TagDMRData(this, debug); m_tagP25 = new TagP25Data(this, debug); m_tagNXDN = new TagNXDNData(this, debug); From 1b08fb14641df3299436a9ad60bfb66dd486b878 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 12 Nov 2025 09:29:40 -0500 Subject: [PATCH 161/200] add STP check to see if a downstream leaf is blown itself away on the tree; --- src/fne/network/FNENetwork.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index cedbf439..56be36ea 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1400,6 +1400,25 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogInfoEx(LOG_MASTER, "PEER %u (%s) ping, pingsReceived = %u, lastPing = %u, now = %u", peerId, connection->identWithQualifier().c_str(), connection->pingsReceived(), lastPing, now); } + + // ensure STP sanity, when we receive a ping from a downstream leaf + // this check ensures a STP entry for a downstream leaf isn't accidentally blown off + // the tree during a fast reconnect + if (network->m_enableSpanningTree && connection->isNeighborFNEPeer() && !connection->isSysView()) { + std::lock_guard guard(network->m_treeLock); + + if ((connection->masterId() != peerId) && (connection->masterId() != 0U)) { + // check if this peer is already connected via another peer + SpanningTree* tree = SpanningTree::findByMasterID(connection->masterId()); + if (tree == nullptr) { + LogWarning(LOG_STP, "PEER %u (%s) downstream server not announced in server tree, reinitializing STP entry, this is abnormal, peerId = %u, masterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), + peerId, connection->masterId(), connection->connectionState()); + SpanningTree* node = new SpanningTree(peerId, connection->masterId(), network->m_treeRoot); + node->identity(connection->identity()); + network->logSpanningTree(connection); + } + } + } } else { network->writePeerNAK(peerId, streamId, TAG_REPEATER_PING); From 2098fa0bc2a9768e63d7d92a6e6bdc7509fd82a6 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 12 Nov 2025 10:54:19 -0500 Subject: [PATCH 162/200] implement DMR data PDU assembler based on the P25 PDU assembler; --- src/common/dmr/data/Assembler.cpp | 172 ++++++++++++++++++++++++++++++ src/common/dmr/data/Assembler.h | 94 ++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 src/common/dmr/data/Assembler.cpp create mode 100644 src/common/dmr/data/Assembler.h diff --git a/src/common/dmr/data/Assembler.cpp b/src/common/dmr/data/Assembler.cpp new file mode 100644 index 00000000..a876237d --- /dev/null +++ b/src/common/dmr/data/Assembler.cpp @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "dmr/DMRDefines.h" +#include "dmr/data/Assembler.h" +#include "edac/CRC.h" +#include "Log.h" +#include "Utils.h" + +using namespace dmr; +using namespace dmr::defines; +using namespace dmr::data; + +#include +#include + +// --------------------------------------------------------------------------- +// Static Class Members +// --------------------------------------------------------------------------- + +bool Assembler::s_dumpPDUData = false; +bool Assembler::s_verbose = false; + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the Assembler class. */ + +Assembler::Assembler() : + m_blockWriter(nullptr) +{ + /* stub */ +} + +/* Finalizes a instance of the Assembler class. */ + +Assembler::~Assembler() = default; + +/* Helper to assemble user data as a DMR PDU packet. */ + +void Assembler::assemble(data::DataHeader& dataHeader, DataType::E dataType, const uint8_t* pduUserData, + uint32_t* assembledBitLength, void* userContext) +{ + assert(pduUserData != nullptr); + assert(m_blockWriter != nullptr); + + if (assembledBitLength != nullptr) + *assembledBitLength = 0U; + + uint32_t bitLength = ((dataHeader.getBlocksToFollow() + 1U) * DMR_FRAME_LENGTH_BITS); + if (dataHeader.getPadLength() > 0U) + bitLength += (dataHeader.getPadLength() * 8U); + + UInt8Array dataArray = std::make_unique((bitLength / 8U) + 1U); + uint8_t* data = dataArray.get(); + ::memset(data, 0x00U, (bitLength / 8U) + 1U); + + uint8_t block[DMR_FRAME_LENGTH_BYTES]; + ::memset(block, 0x00U, DMR_FRAME_LENGTH_BYTES); + + uint32_t blocksToFollow = dataHeader.getBlocksToFollow(); + + if (s_verbose) { + LogInfoEx(LOG_DMR, DMR_DT_DATA_HEADER ", dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", + dataHeader.getDPF(), dataHeader.getA(), dataHeader.getSAP(), dataHeader.getFullMesage(), dataHeader.getBlocksToFollow(), dataHeader.getPadLength(), dataHeader.getPacketLength(dataType), + dataHeader.getFSN(), dataHeader.getDstId(), dataHeader.getSrcId(), dataHeader.getGI()); + } + + // generate the PDU header + dataHeader.encode(block); + +#if DEBUG_DMR_PDU_DATA + Utils::dump(1U, "DMR, PDU Assembler Block", block, DMR_FRAME_LENGTH_BYTES); +#endif + + m_blockWriter(userContext, 0U, block, DMR_FRAME_LENGTH_BYTES, false); + + if (pduUserData != nullptr && blocksToFollow > 0U) { + uint32_t dataOffset = 0U; + uint32_t pduLength = dataHeader.getPDULength(dataType) + dataHeader.getPadLength(); + uint32_t dataBlockCnt = 1U; + uint32_t secondHeaderOffset = 0U; + + // we pad 20 bytes of extra space -- confirmed data will use various extra space in the PDU + DECLARE_UINT8_ARRAY(packetData, pduLength + 20U); + + uint32_t packetLength = dataHeader.getPacketLength(dataType); + uint32_t padLength = dataHeader.getPadLength(); +#if DEBUG_DMR_PDU_DATA + LogDebugEx(LOG_DMR, "Assembler::assemble()", "packetLength = %u, secondHeaderOffset = %u, padLength = %u, pduLength = %u", packetLength, secondHeaderOffset, padLength, pduLength); +#endif + ::memcpy(packetData + secondHeaderOffset, pduUserData, packetLength); + edac::CRC::addCRC32(packetData, packetLength + 4U); + + if (padLength > 0U) { + // move the CRC-32 to the end of the packet data after the padding + uint8_t crcBytes[4U]; + ::memcpy(crcBytes, packetData + packetLength, 4U); + ::memset(packetData + packetLength, 0x00U, 4U); + ::memcpy(packetData + (packetLength + padLength), crcBytes, 4U); + } + +#if DEBUG_DMR_PDU_DATA + Utils::dump(1U, "DMR, Assembled PDU User Data", packetData, packetLength + padLength + 4U); +#endif + + // generate the PDU data + for (uint32_t i = 0U; i < blocksToFollow; i++) { + DataBlock dataBlock = DataBlock(); + dataBlock.setFormat(dataHeader); + dataBlock.setSerialNo(i); + dataBlock.setData(packetData + dataOffset); + dataBlock.setLastBlock((i + 1U) == blocksToFollow); + + if (s_verbose) { + if (dataType == DataType::RATE_34_DATA) { + LogInfoEx(LOG_DMR, DMR_DT_RATE_34_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", + (dataHeader.getDPF() == DPF::CONFIRMED_DATA) ? dataBlock.getSerialNo() : i, dataBlock.getDataType(), dataBlock.getFormat()); + } else if (dataType == DataType::RATE_12_DATA) { + LogInfoEx(LOG_DMR, DMR_DT_RATE_12_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", + (dataHeader.getDPF() == DPF::CONFIRMED_DATA) ? dataBlock.getSerialNo() : i, dataBlock.getDataType(), dataBlock.getFormat()); + } + else { + LogInfoEx(LOG_DMR, DMR_DT_RATE_1_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", + (dataHeader.getDPF() == DPF::CONFIRMED_DATA) ? dataBlock.getSerialNo() : i, dataBlock.getDataType(), dataBlock.getFormat()); + } + } + + ::memset(block, 0x00U, DMR_FRAME_LENGTH_BYTES); + dataBlock.encode(block); + +#if DEBUG_P25_PDU_DATA + Utils::dump(1U, "DMR, PDU Assembler Block", block, DMR_FRAME_LENGTH_BYTES); +#endif + + m_blockWriter(userContext, dataBlockCnt, block, DMR_FRAME_LENGTH_BYTES, dataBlock.getLastBlock()); + + if (dataHeader.getDPF() == DPF::CONFIRMED_DATA) { + if (dataType == DataType::RATE_34_DATA) { + dataOffset += DMR_PDU_CONFIRMED_TQ_DATA_LENGTH_BYTES; + } else if (dataType == DataType::RATE_12_DATA) { + dataOffset += DMR_PDU_CONFIRMED_HR_DATA_LENGTH_BYTES; + } + else { + dataOffset += DMR_PDU_CONFIRMED_UNCODED_DATA_LENGTH_BYTES; + } + } else { + if (dataType == DataType::RATE_34_DATA) { + dataOffset += DMR_PDU_THREEQUARTER_LENGTH_BYTES; + } else if (dataType == DataType::RATE_12_DATA) { + dataOffset += DMR_PDU_HALFRATE_LENGTH_BYTES; + } + else { + dataOffset += DMR_PDU_UNCODED_LENGTH_BYTES; + } + } + + dataBlockCnt++; + } + } + + if (assembledBitLength != nullptr) + *assembledBitLength = bitLength; +} diff --git a/src/common/dmr/data/Assembler.h b/src/common/dmr/data/Assembler.h new file mode 100644 index 00000000..b2103808 --- /dev/null +++ b/src/common/dmr/data/Assembler.h @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file Assembler.h + * @ingroup dmr_pdu + * @file Assembler.cpp + * @ingroup dmr_pdu + */ +#if !defined(__DMR_DATA__ASSEMBLER_H__) +#define __DMR_DATA__ASSEMBLER_H__ + +#include "common/Defines.h" +#include "common/dmr/data/DataBlock.h" +#include "common/dmr/data/DataHeader.h" + +#include +#include + +namespace dmr +{ + namespace data + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Implements a packet assembler for DMR PDU packet streams. + * @ingroup dmr_pdu + */ + class HOST_SW_API Assembler { + public: + /** + * @brief Initializes a new instance of the Assembler class. + */ + Assembler(); + /** + * @brief Finalizes a instance of the Assembler class. + */ + ~Assembler(); + + /** + * @brief Helper to assemble user data as a DMR PDU packet. + * @note When using a custom block writer, this will return null. + * @param dataHeader Instance of a PDU data header. + * @param dataType DMR packet data type. + * @param[in] pduUserData Buffer containing user data to assemble. + * @param[out] assembledBitLength Length of assembled packet in bits. + * @param[in] userContext User supplied context data to pass to custom block writer. + * @returns UInt8Array Assembled PDU buffer. + */ + void assemble(data::DataHeader& dataHeader, DMRDEF::DataType::E dataType, const uint8_t* pduUserData, + uint32_t* assembledBitLength, void* userContext = nullptr); + + /** + * @brief Helper to set the custom block writer callback. + * @param callback + */ + void setBlockWriter(std::function&& callback) + { + m_blockWriter = callback; + } + + /** + * @brief Sets the flag indicating whether or not the assembler will dump PDU data. + * @param dumpPDUData Flag indicating PDU log dumping. + */ + static void setDumpPDUData(bool dumpPDUData) { s_dumpPDUData = dumpPDUData; } + /** + * @brief Sets the flag indicating verbose log output. + * @param verbose Flag indicating verbose log output. + */ + static void setVerbose(bool verbose) { s_verbose = verbose; } + + private: + static bool s_dumpPDUData; + static bool s_verbose; + + /** + * @brief Custom block writing callback. + */ + std::function m_blockWriter; + }; + } // namespace data +} // namespace dmr + +#endif // __DMR_DATA__ASSEMBLER_H__ From 401e6127ad72bacebe33b1b33be3b2eb9021cd80 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 12 Nov 2025 11:12:27 -0500 Subject: [PATCH 163/200] expand on the files related to an FNE instance for clarity; --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b244d52..1e0b76bb 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This project suite generates a few executables: ### Core Applications - `dvmhost` host software that connects to the DVM modems (both air interface for repeater and hotspot or P25 DFSI for commerical P25 hardware) and is the primary data processing application for digital modes. [See configuration](#dvmhost-configuration) to configure and calibrate. -- `dvmfne` a network "core", this provides a central server for `dvmhost` instances to connect to and be networked with, allowing relay of traffic and other data between `dvmhost` instances and other `dvmfne` instances. [See configuration](#dvmfne-configuration) to configure. +- `dvmfne` a network core, this provides a central server for `dvmhost` instances (and other instances like consoles or `dvmbridge`s) to connect to and be networked with other instances, allowing switching of traffic and other data between `dvmhost` instances, as well as other peered `dvmfne` instances. [See configuration](#dvmfne-configuration) to configure. - `dvmbridge` a analog/PCM audio bridge, this provides the capability for analog or PCM audio resources to be connected to a `dvmfne` instance, allowing realtime vocoding of traffic. [See configuration](#dvmbridge-configuration) to configure. - `dvmpatch` a talkgroup patching utility, this provides the capability to manually patch talkgroups of the same digital mode together. [See configuration](#dvmpatch-configuration) to configure. - `dvmcmd` a simple command-line utility to send remote control commands to a `dvmhost` or `dvmfne` instance with REST API configured. @@ -206,6 +206,19 @@ for step 4 to observe frequency error.) This source repository contains configuration example files within the configs folder, please review `fne-config.example.yml` for the `dvmfne` for details on various configurable options. When first setting up a FNE instance, it is important to properly configure a `talkgroup_rules.example.yml` file, this file defines all the various rules for valid talkgroups and other settings. +The other configurables on a `dvmfne` instance are within the `fne-config.example.yml`. Some of these are Radio ID (RID) access control listings, Peer ID (PID) access control listings, adjacent site maps for systems with trunked `dvmhost` instances connected, annd many other parameters. + +Here is a listing of files in the configs folder in this repo that pertain to FNE configuration: +- `adj_site_map.example.yml` - This is an example configuration file configuring adjacent site mappings for trunked `dvmhost` instances. +- `fne-config.example.yml` - This is the main/primary example configuration file for an FNE instance. +- `peer_list.example.dat` - This is a simple CSV-style file containing access control permissions for peers allowed to connect to the FNE (this includes both downstream peers (like `dvmhost` or `dvmbridge`) and other `dvmfne` instances connecting *to* the FNE instance). +- `rid_acl.example.dat` - This is a simple CSV-style file containing the access control permissions for radio ID (RID)s allowed to use a configured system/network. +- `talkgroup_rules.example.yml` - This is the second most important configuration file for an FNE, this file describes all the talkgroups and their related access control and configuration parameters. + +There is another file that is attributed to the FNE that an example is not provided for and that is the `key-container.ekc` file. This file provides cryptographic material needed for providing keyloading functionality across a configured system/network. + +Most parameters within the `fne-config.example.yml` should be set to reasonable defaults for simply just starting up a FNE, the only parameters in the configuration that *must* be reviewed before starting up an instance are proper file paths for the ACL and other files used by the FNE. + There is no other real configuration for a `dvmfne` instance other then setting the appropriate parameters within the configuration files. ## dvmbridge Configuration From 2524ceea95febd6e56cd1ac280d63f4a02af5fca Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 12 Nov 2025 13:47:00 -0500 Subject: [PATCH 164/200] add preliminary support for enabling peers with call priority, this will give those peers the ability to override any current call in progress; --- configs/fne-config.example.yml | 2 +- configs/peer_list.example.dat | 18 ++++++++----- src/common/lookups/PeerListLookup.cpp | 19 +++++++++++-- src/common/lookups/PeerListLookup.h | 9 ++++++- src/fne/network/FNENetwork.cpp | 18 +++++++++---- src/fne/network/FNENetwork.h | 5 ++-- src/fne/network/FNEPeerConnection.h | 7 +++++ src/fne/network/callhandler/TagAnalogData.cpp | 27 +++++++++++++++++++ src/fne/network/callhandler/TagDMRData.cpp | 27 +++++++++++++++++++ src/fne/network/callhandler/TagNXDNData.cpp | 27 +++++++++++++++++++ src/fne/network/callhandler/TagP25Data.cpp | 27 +++++++++++++++++++ src/peered/PeerEditWnd.h | 14 ++++++++-- src/peered/PeerListWnd.h | 7 +++-- 13 files changed, 186 insertions(+), 21 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index d6beb41e..67757e94 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -140,7 +140,7 @@ master: allowConvSiteAffOverride: true # Flag indicating whether or not In-Call Control feedback is enabled. # (This will enforce RID ACLs network wide, regardless of local peer RID ACL setting.) - enableInCallCtrl: false + enableRIDInCallCtrl: false # Flag indicating whether or not unknown/undefined RIDs will be rejected by the FNE. # (This is a strict rejection, any unknown or undefined RID not in the RID ACL list will be hard rejected.) rejectUnknownRID: false diff --git a/configs/peer_list.example.dat b/configs/peer_list.example.dat index 2f7c0af3..b2a7fd96 100644 --- a/configs/peer_list.example.dat +++ b/configs/peer_list.example.dat @@ -10,17 +10,23 @@ # full configuration from this FNE. When peer replication is set, and the connection is # another FNE, that FNE will receive all the talkgroups, radio ID lists, and # peer lists from this FNE, it will also receive all system traffic. +# * PEER ALIAS [OPTIONAL] - Textual name alias for the peer. # * CAN REQUEST KEYS [OPTIONAL] - Flag indicating the peer connection is allowed to request encryption keys. # If this flag is disabled (0), and the connected peer requests and encryption key # the encryption key request will be dropped and ignored. # * CAN ISSUE INHIBIT [OPTIONAL] - Flag indicating the peer connection is capable of transmitting inhibit packets. # If this flag is disabled (0), and the connected peer issues an inhibit to the network # this FNE will drop the packet and ignore it. +# * HAS CALL PRIORITY [OPTIONAL] - Flag indicating the peer connection has call priority. +# If this flag is disabled (0), and the connected peer tries to transmit over an on going +# call, normal call collision rules are applied to the traffic being transmitted. +# If this flag is enabled (1), and the connected peer tries to transmit over an on going + call, call collision rules are ignored, and the peer is given priority. # -# Entry Format: "Peer ID,Peer Password,Peer Replication (1 = Enabled / 0 = Disabled),Peer Alias (optional),Can Request Keys (1 = Enabled / 0 = Disabled),Can Issue Inhibit (1 = Enabled / 0 = Disabled)" +# Entry Format: "Peer ID,Peer Password,Peer Replication (1 = Enabled / 0 = Disabled),Peer Alias (optional),Can Request Keys (1 = Enabled / 0 = Disabled),Can Issue Inhibit (1 = Enabled / 0 = Disabled),Has Call Priority (1 = Enabled / 0 = Disabled)" # Examples: -#1234,,0,,1,0, -#5678,MYSECUREPASSWORD,0,,0,0, -#9876,MYSECUREPASSWORD,1,,0,0, -#5432,MYSECUREPASSWORD,,Peer Alias 1,0,0, -#1012,MYSECUREPASSWORD,1,Peer Alias 2,1,0, +#1234,,0,,1,0,0, +#5678,MYSECUREPASSWORD,0,,0,0,0, +#9876,MYSECUREPASSWORD,1,,0,0,0, +#5432,MYSECUREPASSWORD,,Peer Alias 1,0,0,0, +#1012,MYSECUREPASSWORD,1,Peer Alias 2,1,0,0, diff --git a/src/common/lookups/PeerListLookup.cpp b/src/common/lookups/PeerListLookup.cpp index 29744a1b..674c72b3 100644 --- a/src/common/lookups/PeerListLookup.cpp +++ b/src/common/lookups/PeerListLookup.cpp @@ -237,6 +237,11 @@ bool PeerListLookup::load() if (parsed.size() >= 6) canIssueInhibit = ::atoi(parsed[5].c_str()) == 1; + // parse can issue inhibit flag + bool hasCallPriority = false; + if (parsed.size() >= 7) + hasCallPriority = ::atoi(parsed[6].c_str()) == 1; + // parse optional password std::string password = ""; if (parsed.size() >= 2) @@ -247,16 +252,18 @@ bool PeerListLookup::load() entry.peerReplica(peerReplica); entry.canRequestKeys(canRequestKeys); entry.canIssueInhibit(canIssueInhibit); + entry.hasCallPriority(hasCallPriority); m_table[id] = entry; // log depending on what was loaded - LogInfoEx(LOG_HOST, "Loaded peer ID %u%s into peer ID lookup table, %s%s%s", id, + LogInfoEx(LOG_HOST, "Loaded peer ID %u%s into peer ID lookup table, %s%s%s%s", id, (!alias.empty() ? (" (" + alias + ")").c_str() : ""), (!password.empty() ? "using unique peer password" : "using master password"), (peerReplica) ? ", Replication Enabled" : "", (canRequestKeys) ? ", Can Request Keys" : "", - (canIssueInhibit) ? ", Can Issue Inhibit" : ""); + (canIssueInhibit) ? ", Can Issue Inhibit" : "", + (hasCallPriority) ? ", Has Call Priority" : ""); } } @@ -343,6 +350,14 @@ bool PeerListLookup::save(bool quiet) line += "0,"; } + // add hasCallPriority flag + bool hasCallPriority = entry.second.hasCallPriority(); + if (hasCallPriority) { + line += "1,"; + } else { + line += "0,"; + } + line += "\n"; file << line; lines++; diff --git a/src/common/lookups/PeerListLookup.h b/src/common/lookups/PeerListLookup.h index 13bb3f0e..9d9563c4 100644 --- a/src/common/lookups/PeerListLookup.h +++ b/src/common/lookups/PeerListLookup.h @@ -52,6 +52,7 @@ namespace lookups m_peerReplica(false), m_canRequestKeys(false), m_canIssueInhibit(false), + m_hasCallPriority(false), m_peerDefault(false) { /* stub */ @@ -71,6 +72,7 @@ namespace lookups m_peerReplica(false), m_canRequestKeys(false), m_canIssueInhibit(false), + m_hasCallPriority(false), m_peerDefault(peerDefault) { /* stub */ @@ -89,6 +91,7 @@ namespace lookups m_peerReplica = data.m_peerReplica; m_canRequestKeys = data.m_canRequestKeys; m_canIssueInhibit = data.m_canIssueInhibit; + m_hasCallPriority = data.m_hasCallPriority; m_peerDefault = data.m_peerDefault; } @@ -117,7 +120,7 @@ namespace lookups */ DECLARE_PROPERTY_PLAIN(uint32_t, peerId); /** - * @breif Peer Alias + * @brief Peer Alias */ DECLARE_PROPERTY_PLAIN(std::string, peerAlias); /** @@ -136,6 +139,10 @@ namespace lookups * @brief Flag indicating if the peer can issue inhibit/uninhibit packets. */ DECLARE_PROPERTY_PLAIN(bool, canIssueInhibit); + /** + * @brief Flag indicating if the peer has call transmit priority. + */ + DECLARE_PROPERTY_PLAIN(bool, hasCallPriority); /** * @brief Flag indicating if the peer is default. */ diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 56be36ea..2f56563d 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -118,7 +118,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_disallowCallTerm(false), m_restrictGrantToAffOnly(false), m_restrictPVCallToRegOnly(false), - m_enableInCallCtrl(true), + m_enableRIDInCallCtrl(true), m_rejectUnknownRID(false), m_maskOutboundPeerID(false), m_maskOutboundPeerIDForNonPL(false), @@ -193,7 +193,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) m_disallowAdjStsBcast = conf["disallowAdjStsBcast"].as(false); m_disallowExtAdjStsBcast = conf["disallowExtAdjStsBcast"].as(true); m_allowConvSiteAffOverride = conf["allowConvSiteAffOverride"].as(true); - m_enableInCallCtrl = conf["enableInCallCtrl"].as(false); + m_enableRIDInCallCtrl = conf["enableRIDInCallCtrl"].as(false); m_rejectUnknownRID = conf["rejectUnknownRID"].as(false); m_maskOutboundPeerID = conf["maskOutboundPeerID"].as(false); m_maskOutboundPeerIDForNonPL = conf["maskOutboundPeerIDForNonPeerLink"].as(false); @@ -301,7 +301,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogInfo(" Disable P25 ADJ_STS_BCAST to neighbor peers: %s", m_disallowExtAdjStsBcast ? "yes" : "no"); LogInfo(" Disable P25 TDULC call termination broadcasts to any peers: %s", m_disallowCallTerm ? "yes" : "no"); LogInfo(" Allow conventional sites to override affiliation and receive all traffic: %s", m_allowConvSiteAffOverride ? "yes" : "no"); - LogInfo(" Enable In-Call Control: %s", m_enableInCallCtrl ? "yes" : "no"); + LogInfo(" Enable RID In-Call Control: %s", m_enableRIDInCallCtrl ? "yes" : "no"); LogInfo(" Reject Unknown RIDs: %s", m_rejectUnknownRID ? "yes" : "no"); LogInfo(" Log Traffic Denials: %s", m_logDenials ? "yes" : "no"); LogInfo(" Log Upstream Call Start/End Events: %s", m_logUpstreamCallStartEnd ? "yes" : "no"); @@ -1206,6 +1206,13 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) connection->pingsReceived(0U); connection->lastPing(now); connection->missedMetadataUpdates(0U); + + lookups::PeerId peerEntry = network->m_peerListLookup->find(peerId); + if (!peerEntry.peerDefault()) { + connection->hasCallPriority(peerEntry.hasCallPriority()); + LogInfoEx(LOG_MASTER, "PEER %u >> Has Call Priority", peerId); + } + network->m_peers[peerId] = connection; // attach extra notification data to the RPTC ACK to notify the peer of @@ -2769,11 +2776,12 @@ void FNENetwork::writeTreeDisconnect(uint32_t peerId, uint32_t offendingPeerId) /* Helper to send a In-Call Control command to the specified peer. */ -bool FNENetwork::writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::ENUM subFunc, NET_ICC::ENUM command, uint32_t dstId, uint8_t slotNo) +bool FNENetwork::writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::ENUM subFunc, NET_ICC::ENUM command, uint32_t dstId, uint8_t slotNo, + bool systemReq) { if (peerId == 0) return false; - if (!m_enableInCallCtrl) + if (!m_enableRIDInCallCtrl && !systemReq) return false; if (dstId == 0U) return false; diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 62a79927..61887da2 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -357,7 +357,7 @@ namespace network bool m_disallowCallTerm; bool m_restrictGrantToAffOnly; bool m_restrictPVCallToRegOnly; - bool m_enableInCallCtrl; + bool m_enableRIDInCallCtrl; bool m_rejectUnknownRID; bool m_maskOutboundPeerID; @@ -650,9 +650,10 @@ namespace network * @param command In-Call Control Command. * @param dstId Destination ID. * @param slotNo DMR slot. + * @param systemReq Flag indicating the ICC request is a system generated one not a automatic RID rule generated one. */ bool writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::ENUM subFunc = NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, - NET_ICC::ENUM command = NET_ICC::NOP, uint32_t dstId = 0U, uint8_t slotNo = 0U); + NET_ICC::ENUM command = NET_ICC::NOP, uint32_t dstId = 0U, uint8_t slotNo = 0U, bool systemReq = false); /* ** Generic Message Writing diff --git a/src/fne/network/FNEPeerConnection.h b/src/fne/network/FNEPeerConnection.h index d4b21a56..55888717 100644 --- a/src/fne/network/FNEPeerConnection.h +++ b/src/fne/network/FNEPeerConnection.h @@ -54,6 +54,7 @@ namespace network m_pingsReceived(0U), m_lastPing(0U), m_missedMetadataUpdates(0U), + m_hasCallPriority(false), m_isNeighborFNEPeer(false), m_isReplica(false), m_isConventionalPeer(false), @@ -84,6 +85,7 @@ namespace network m_pingsReceived(0U), m_lastPing(0U), m_missedMetadataUpdates(0U), + m_hasCallPriority(false), m_isNeighborFNEPeer(false), m_isReplica(false), m_isConventionalPeer(false), @@ -186,6 +188,11 @@ namespace network */ DECLARE_PROPERTY_PLAIN(uint32_t, missedMetadataUpdates); + /** + * @brief Flag indicating this connection has call priority. + */ + DECLARE_PROPERTY_PLAIN(bool, hasCallPriority); + /** * @brief Flag indicating this connection is from an downstream neighbor FNE peer. */ diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 0a73cb1a..10419c04 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -163,7 +163,26 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee RxStatus status = it->second; if (streamId != status.streamId) { if (status.srcId != 0U && status.srcId != srcId) { + bool hasCallPriority = false; + + // determine if the peer trying to transmit has call priority if (m_network->m_callCollisionTimeout > 0U) { + m_network->m_peers.shared_lock(); + for (auto peer : m_network->m_peers) { + if (peerId == peer.first) { + FNEPeerConnection* conn = peer.second; + if (conn != nullptr) { + hasCallPriority = conn->hasCallPriority(); + break; + } + } + } + m_network->m_peers.shared_unlock(); + } + + // perform standard call collision if the call collision timeout is set *and* + // the peer doesn't have call priority + if (m_network->m_callCollisionTimeout > 0U && !hasCallPriority) { uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); @@ -179,6 +198,14 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return false; } } else { + if (hasCallPriority) { + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call Source Switched (Priority), peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); + + // since we're gonna switch over the stream and interrupt the current call inprogress lets try to ICC the transmitting peer + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true); + } + m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 4b60b417..4735b1e9 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -261,7 +261,26 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId } else { if (status.srcId != 0U && status.srcId != srcId) { + bool hasCallPriority = false; + + // determine if the peer trying to transmit has call priority if (m_network->m_callCollisionTimeout > 0U) { + m_network->m_peers.shared_lock(); + for (auto peer : m_network->m_peers) { + if (peerId == peer.first) { + FNEPeerConnection* conn = peer.second; + if (conn != nullptr) { + hasCallPriority = conn->hasCallPriority(); + break; + } + } + } + m_network->m_peers.shared_unlock(); + } + + // perform standard call collision if the call collision timeout is set *and* + // the peer doesn't have call priority + if (m_network->m_callCollisionTimeout > 0U && !hasCallPriority) { uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); @@ -276,6 +295,14 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId return false; } } else { + if (hasCallPriority) { + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Source Switched (Priority), peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, fromUpstream); + + // since we're gonna switch over the stream and interrupt the current call inprogress lets try to ICC the transmitting peer + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, dstId, slotNo, true); + } + m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index c0af7cc8..93da0ea4 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -299,7 +299,26 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI } else { if (status.srcId != 0U && status.srcId != srcId) { + bool hasCallPriority = false; + + // determine if the peer trying to transmit has call priority if (m_network->m_callCollisionTimeout > 0U) { + m_network->m_peers.shared_lock(); + for (auto peer : m_network->m_peers) { + if (peerId == peer.first) { + FNEPeerConnection* conn = peer.second; + if (conn != nullptr) { + hasCallPriority = conn->hasCallPriority(); + break; + } + } + } + m_network->m_peers.shared_unlock(); + } + + // perform standard call collision if the call collision timeout is set *and* + // the peer doesn't have call priority + if (m_network->m_callCollisionTimeout > 0U && !hasCallPriority) { uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); @@ -314,6 +333,14 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI return false; } } else { + if (hasCallPriority) { + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Source Switched (Priority), peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); + + // since we're gonna switch over the stream and interrupt the current call inprogress lets try to ICC the transmitting peer + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true); + } + m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 1819f703..772dabb1 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -341,7 +341,26 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_status.unlock(); } else { if (status.srcId != 0U && status.srcId != srcId) { + bool hasCallPriority = false; + + // determine if the peer trying to transmit has call priority if (m_network->m_callCollisionTimeout > 0U) { + m_network->m_peers.shared_lock(); + for (auto peer : m_network->m_peers) { + if (peerId == peer.first) { + FNEPeerConnection* conn = peer.second; + if (conn != nullptr) { + hasCallPriority = conn->hasCallPriority(); + break; + } + } + } + m_network->m_peers.shared_unlock(); + } + + // perform standard call collision if the call collision timeout is set *and* + // the peer doesn't have call priority + if (m_network->m_callCollisionTimeout > 0U && !hasCallPriority) { uint64_t lastPktDuration = hrc::diff(hrc::now(), status.lastPacket); if ((lastPktDuration / 1000) > m_network->m_callCollisionTimeout) { LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Collision, lasted more then %us with no further updates, resetting call source", m_network->m_callCollisionTimeout); @@ -357,6 +376,14 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId return false; } } else { + if (hasCallPriority) { + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Source Switched (Priority), peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); + + // since we're gonna switch over the stream and interrupt the current call inprogress lets try to ICC the transmitting peer + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true); + } + m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; diff --git a/src/peered/PeerEditWnd.h b/src/peered/PeerEditWnd.h index 4b7de34c..6133b47e 100644 --- a/src/peered/PeerEditWnd.h +++ b/src/peered/PeerEditWnd.h @@ -153,6 +153,7 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { FCheckBox m_peerReplicaEnabled{"Peer Replica", &m_configGroup}; FCheckBox m_canReqKeysEnabled{"Request Keys", &m_configGroup}; FCheckBox m_canInhibitEnabled{"Issue Inhibit", &m_configGroup}; + FCheckBox m_callPriorityEnabled{"Call Priority", &m_configGroup}; /** * @brief Initializes the window layout. @@ -263,7 +264,7 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { // configuration { - m_configGroup.setGeometry(FPoint(39, 5), FSize(23, 5)); + m_configGroup.setGeometry(FPoint(39, 5), FSize(23, 6)); m_peerReplicaEnabled.setGeometry(FPoint(2, 1), FSize(10, 1)); m_peerReplicaEnabled.setChecked(m_rule.peerReplica()); @@ -282,6 +283,12 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { m_canInhibitEnabled.addCallback("toggled", [&]() { m_rule.canIssueInhibit(m_canInhibitEnabled.isChecked()); }); + + m_callPriorityEnabled.setGeometry(FPoint(2, 4), FSize(10, 1)); + m_callPriorityEnabled.setChecked(m_rule.hasCallPriority()); + m_callPriorityEnabled.addCallback("toggled", [&]() { + m_rule.hasCallPriority(m_callPriorityEnabled.isChecked()); + }); } CloseWndBase::initControls(); @@ -297,8 +304,9 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { bool peerReplica = m_rule.peerReplica(); bool canRequestKeys = m_rule.canRequestKeys(); bool canIssueInhibit = m_rule.canIssueInhibit(); + bool hasCallPriority = m_rule.hasCallPriority(); - ::LogInfoEx(LOG_HOST, "Peer ALIAS: %s PEERID: %u REPLICA: %u CAN REQUEST KEYS: %u CAN ISSUE INHIBIT: %u", peerAlias.c_str(), peerId, peerReplica, canRequestKeys, canIssueInhibit); + ::LogInfoEx(LOG_HOST, "Peer ALIAS: %s PEERID: %u REPLICA: %u CAN REQUEST KEYS: %u CAN ISSUE INHIBIT: %u HAS CALL PRIORITY: %u", peerAlias.c_str(), peerId, peerReplica, canRequestKeys, canIssueInhibit, hasCallPriority); } /* @@ -368,6 +376,7 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { entry.peerReplica(m_rule.peerReplica()); entry.canRequestKeys(m_rule.canRequestKeys()); entry.canIssueInhibit(m_rule.canIssueInhibit()); + entry.hasCallPriority(m_rule.hasCallPriority()); g_pidLookups->addEntry(m_rule.peerId(), entry); @@ -404,6 +413,7 @@ class HOST_SW_API PeerEditWnd final : public CloseWndBase { entry.peerReplica(m_rule.peerReplica()); entry.canRequestKeys(m_rule.canRequestKeys()); entry.canIssueInhibit(m_rule.canIssueInhibit()); + entry.hasCallPriority(m_rule.hasCallPriority()); g_pidLookups->addEntry(m_rule.peerId(), entry); diff --git a/src/peered/PeerListWnd.h b/src/peered/PeerListWnd.h index e96cd2d1..e7ba7426 100644 --- a/src/peered/PeerListWnd.h +++ b/src/peered/PeerListWnd.h @@ -119,12 +119,13 @@ class HOST_SW_API PeerListWnd final : public FDblDialog { bool masterPassword = (entry.peerPassword().size() == 0U); // build list view entry - const std::array columns = { + const std::array columns = { oss.str(), (masterPassword) ? "X" : "", (entry.peerReplica()) ? "X" : "", (entry.canRequestKeys()) ? "X" : "", (entry.canIssueInhibit()) ? "X" : "", + (entry.hasCallPriority()) ? "X" : "", entry.peerAlias() }; @@ -213,6 +214,7 @@ class HOST_SW_API PeerListWnd final : public FDblDialog { m_listView.addColumn("Peer Replica", 12); m_listView.addColumn("Request Keys", 12); m_listView.addColumn("Can Inhibit", 12); + m_listView.addColumn("Call Priority", 12); m_listView.addColumn("Alias", 40); // set right alignment for peer ID @@ -220,7 +222,8 @@ class HOST_SW_API PeerListWnd final : public FDblDialog { m_listView.setColumnAlignment(3, finalcut::Align::Center); m_listView.setColumnAlignment(4, finalcut::Align::Center); m_listView.setColumnAlignment(5, finalcut::Align::Center); - m_listView.setColumnAlignment(6, finalcut::Align::Left); + m_listView.setColumnAlignment(6, finalcut::Align::Center); + m_listView.setColumnAlignment(7, finalcut::Align::Left); // set type of sorting m_listView.setColumnSortType(1, finalcut::SortType::Name); From fbd264a569d094f0f86731a79a32ecf0d043ee07 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 13 Nov 2025 09:32:44 -0500 Subject: [PATCH 165/200] implement a more solidified peer call priority mechanism using ICC; --- configs/fne-config.example.yml | 2 + src/bridge/HostBridge.cpp | 9 +- src/common/network/BaseNetwork.h | 2 + src/common/network/Network.cpp | 20 +- src/common/network/Network.h | 20 +- src/fne/HostFNE.cpp | 68 ++++++ src/fne/HostFNE.h | 41 ++++ src/fne/network/FNENetwork.cpp | 194 +++++++++++++++++- src/fne/network/FNENetwork.h | 38 +++- src/fne/network/callhandler/TagAnalogData.cpp | 46 ++++- src/fne/network/callhandler/TagAnalogData.h | 15 ++ src/fne/network/callhandler/TagDMRData.cpp | 48 ++++- src/fne/network/callhandler/TagDMRData.h | 15 ++ src/fne/network/callhandler/TagNXDNData.cpp | 48 ++++- src/fne/network/callhandler/TagNXDNData.h | 15 ++ src/fne/network/callhandler/TagP25Data.cpp | 52 ++++- src/fne/network/callhandler/TagP25Data.h | 15 ++ src/host/dmr/Control.cpp | 3 +- src/host/nxdn/Control.cpp | 3 +- src/host/p25/Control.cpp | 3 +- 20 files changed, 620 insertions(+), 37 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 67757e94..ae37309a 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -139,6 +139,8 @@ master: # Flag indicating whether or not a conventional site can override affiliation rules. allowConvSiteAffOverride: true # Flag indicating whether or not In-Call Control feedback is enabled. + disallowInCallCtrl: false + # Flag indicating whether or not RID ACL In-Call Control feedback is enabled. # (This will enforce RID ACLs network wide, regardless of local peer RID ACL setting.) enableRIDInCallCtrl: false # Flag indicating whether or not unknown/undefined RIDs will be rejected by the FNE. diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 1ced0b1d..7c03e2c2 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -529,15 +529,18 @@ int HostBridge::run() // set the In-Call Control function callback if (m_network != nullptr) { if (m_txMode == TX_MODE_DMR) { - m_network->setDMRICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, uint8_t slotNo) { processInCallCtrl(command, dstId, slotNo); }); + m_network->setDMRICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, + uint8_t slotNo, uint32_t peerId, uint32_t ssrc, uint32_t streamId) { processInCallCtrl(command, dstId, slotNo); }); } if (m_txMode == TX_MODE_P25) { - m_network->setP25ICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId) { processInCallCtrl(command, dstId, 0U); }); + m_network->setP25ICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) { processInCallCtrl(command, dstId, 0U); }); } if (m_txMode == TX_MODE_ANALOG) { - m_network->setAnalogICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId) { processInCallCtrl(command, dstId, 0U); }); + m_network->setAnalogICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) { processInCallCtrl(command, dstId, 0U); }); } } diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index 9fae95c5..1a056772 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -63,6 +63,8 @@ #define TAG_REPEATER_GRANT "RPTG" #define TAG_REPEATER_KEY "RKEY" +#define TAG_INCALL_CTRL "ICC " + #define TAG_TRANSFER "TRNS" #define TAG_TRANSFER_ACT_LOG "TRNSLOG" #define TAG_TRANSFER_DIAG_LOG "TRNSDIAG" diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index 1bac4a9a..8814fdb6 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -312,7 +312,7 @@ void Network::clock(uint32_t ms) break; } - // process incomfing message subfunction opcodes + // process incoming message subfunction opcodes switch (fneHeader.getSubFunction()) { case NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR: // Encapsulated DMR data frame { @@ -679,7 +679,7 @@ void Network::clock(uint32_t ms) case NET_FUNC::MASTER: // Master { - // process incomfing message subfunction opcodes + // process incoming message subfunction opcodes switch (fneHeader.getSubFunction()) { case NET_SUBFUNC::MASTER_SUBFUNC_WL_RID: // Radio ID Whitelist { @@ -867,7 +867,13 @@ void Network::clock(uint32_t ms) case NET_FUNC::INCALL_CTRL: // In-Call Control { - // process incomfing message subfunction opcodes + uint32_t ssrc = rtpHeader.getSSRC(); + if (!m_promiscuousPeer && ssrc != peerId) { + LogWarning(LOG_NET, "PEER %u, ignoring in-call control not destined for this peer SSRC %u", m_peerId, ssrc); + break; + } + + // process incoming message subfunction opcodes switch (fneHeader.getSubFunction()) { case NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR: // DMR In-Call Control { @@ -878,7 +884,7 @@ void Network::clock(uint32_t ms) // fire off DMR in-call callback if we have one if (m_dmrInCallCallback != nullptr) { - m_dmrInCallCallback(command, dstId, slot); + m_dmrInCallCallback(command, dstId, slot, peerId, ssrc, streamId); } } } @@ -891,7 +897,7 @@ void Network::clock(uint32_t ms) // fire off P25 in-call callback if we have one if (m_p25InCallCallback != nullptr) { - m_p25InCallCallback(command, dstId); + m_p25InCallCallback(command, dstId, peerId, ssrc, streamId); } } } @@ -904,7 +910,7 @@ void Network::clock(uint32_t ms) // fire off NXDN in-call callback if we have one if (m_nxdnInCallCallback != nullptr) { - m_nxdnInCallCallback(command, dstId); + m_nxdnInCallCallback(command, dstId, peerId, ssrc, streamId); } } } @@ -917,7 +923,7 @@ void Network::clock(uint32_t ms) // fire off analog in-call callback if we have one if (m_analogInCallCallback != nullptr) { - m_analogInCallCallback(command, dstId); + m_analogInCallCallback(command, dstId, peerId, ssrc, streamId); } } } diff --git a/src/common/network/Network.h b/src/common/network/Network.h index d9b61794..7f14e917 100644 --- a/src/common/network/Network.h +++ b/src/common/network/Network.h @@ -264,22 +264,22 @@ namespace network * @brief Helper to set the DMR In-Call Control callback. * @param callback */ - void setDMRICCCallback(std::function&& callback) { m_dmrInCallCallback = callback; } + void setDMRICCCallback(std::function&& callback) { m_dmrInCallCallback = callback; } /** * @brief Helper to set the P25 In-Call Control callback. * @param callback */ - void setP25ICCCallback(std::function&& callback) { m_p25InCallCallback = callback; } + void setP25ICCCallback(std::function&& callback) { m_p25InCallCallback = callback; } /** * @brief Helper to set the NXDN In-Call Control callback. * @param callback */ - void setNXDNICCCallback(std::function&& callback) { m_nxdnInCallCallback = callback; } + void setNXDNICCCallback(std::function&& callback) { m_nxdnInCallCallback = callback; } /** * @brief Helper to set the analog In-Call Control callback. * @param callback */ - void setAnalogICCCallback(std::function&& callback) { m_analogInCallCallback = callback; } + void setAnalogICCCallback(std::function&& callback) { m_analogInCallCallback = callback; } /** * @brief Helper to set the enc. key response callback. @@ -370,22 +370,26 @@ namespace network * @brief DMR In-Call Control Function Callback. * (This is called when the master sends a In-Call Control request.) */ - std::function m_dmrInCallCallback; + std::function m_dmrInCallCallback; /** * @brief P25 In-Call Control Function Callback. * (This is called once the master sends a In-Call Control request.) */ - std::function m_p25InCallCallback; + std::function m_p25InCallCallback; /** * @brief NXDN In-Call Control Function Callback. * (This is called once the master sends a In-Call Control request.) */ - std::function m_nxdnInCallCallback; + std::function m_nxdnInCallCallback; /** * @brief Analog In-Call Control Function Callback. * (This is called once the master sends a In-Call Control request.) */ - std::function m_analogInCallCallback; + std::function m_analogInCallCallback; /** * @brief Encryption Key Response Function Callback. diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index af17a68f..50bb47fd 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -883,9 +883,17 @@ bool HostFNE::createPeerNetworks() } network->setDMRCallback(std::bind(&HostFNE::processPeerDMR, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); + network->setDMRICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, uint8_t slot, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) { processPeerDMRInCallCtrl(command, dstId, slot, peerId, ssrc, streamId); }); network->setP25Callback(std::bind(&HostFNE::processPeerP25, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); + network->setP25ICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) { processPeerP25InCallCtrl(command, dstId, peerId, ssrc, streamId); }); network->setNXDNCallback(std::bind(&HostFNE::processPeerNXDN, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); + network->setNXDNICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) { processPeerNXDNInCallCtrl(command, dstId, peerId, ssrc, streamId); }); network->setAnalogCallback(std::bind(&HostFNE::processPeerAnalog, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); + network->setAnalogICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) { processPeerAnalogInCallCtrl(command, dstId, peerId, ssrc, streamId); }); network->setNetTreeDiscCallback(std::bind(&HostFNE::processNetworkTreeDisconnect, this, std::placeholders::_1, std::placeholders::_2)); network->setNotifyPeerReplicaCallback(std::bind(&HostFNE::processPeerReplicaNotify, this, std::placeholders::_1)); @@ -1041,6 +1049,21 @@ void HostFNE::processPeerDMR(network::PeerNetwork* peerNetwork, const uint8_t* d } } +/* Helper to process an DMR In-Call Control message. */ + +void HostFNE::processPeerDMRInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId, uint8_t slot, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) +{ + switch (command) { + case network::NET_ICC::REJECT_TRAFFIC: + m_network->processDownstreamInCallCtrl(command, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, dstId, slot, peerId, ssrc, streamId); + break; + + default: + break; + } +} + /* Processes P25 peer network traffic. */ void HostFNE::processPeerP25(network::PeerNetwork* peerNetwork, const uint8_t* data, uint32_t length, uint32_t streamId, @@ -1063,6 +1086,21 @@ void HostFNE::processPeerP25(network::PeerNetwork* peerNetwork, const uint8_t* d } } +/* Helper to process an P25 In-Call Control message. */ + +void HostFNE::processPeerP25InCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) +{ + switch (command) { + case network::NET_ICC::REJECT_TRAFFIC: + m_network->processDownstreamInCallCtrl(command, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, dstId, 0U, peerId, ssrc, streamId); + break; + + default: + break; + } +} + /* Processes NXDN peer network traffic. */ void HostFNE::processPeerNXDN(network::PeerNetwork* peerNetwork, const uint8_t* data, uint32_t length, uint32_t streamId, @@ -1085,6 +1123,21 @@ void HostFNE::processPeerNXDN(network::PeerNetwork* peerNetwork, const uint8_t* } } +/* Helper to process an NXDN In-Call Control message. */ + +void HostFNE::processPeerNXDNInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) +{ + switch (command) { + case network::NET_ICC::REJECT_TRAFFIC: + m_network->processDownstreamInCallCtrl(command, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, dstId, 0U, peerId, ssrc, streamId); + break; + + default: + break; + } +} + /* Processes analog peer network traffic. */ void HostFNE::processPeerAnalog(network::PeerNetwork* peerNetwork, const uint8_t* data, uint32_t length, uint32_t streamId, @@ -1107,6 +1160,21 @@ void HostFNE::processPeerAnalog(network::PeerNetwork* peerNetwork, const uint8_t } } +/* Helper to process an analog In-Call Control message. */ + +void HostFNE::processPeerAnalogInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) +{ + switch (command) { + case network::NET_ICC::REJECT_TRAFFIC: + m_network->processDownstreamInCallCtrl(command, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG, dstId, 0U, peerId, ssrc, streamId); + break; + + default: + break; + } +} + /* Processes network tree disconnect notification. */ void HostFNE::processNetworkTreeDisconnect(network::PeerNetwork* peerNetwork, const uint32_t offendingPeerId) diff --git a/src/fne/HostFNE.h b/src/fne/HostFNE.h index 4c5f1b73..9537ed0a 100644 --- a/src/fne/HostFNE.h +++ b/src/fne/HostFNE.h @@ -184,6 +184,17 @@ class HOST_SW_API HostFNE { */ void processPeerDMR(network::PeerNetwork* peerNetwork, const uint8_t* data, uint32_t length, uint32_t streamId, const network::frame::RTPFNEHeader& fneHeader, const network::frame::RTPHeader& rtpHeader); + /** + * @brief Helper to process an DMR In-Call Control message. + * @param command In-Call Control Command. + * @param dstId Destination ID. + * @param slot Slot Number. + * @param peerId Peer ID. + * @param ssrc Synchronization Source. + * @param streamId Stream ID. + */ + void processPeerDMRInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId, uint8_t slot, + uint32_t peerId, uint32_t ssrc, uint32_t streamId); /** * @brief Processes P25 peer network traffic. * @param data Buffer containing P25 data. @@ -194,6 +205,16 @@ class HOST_SW_API HostFNE { */ void processPeerP25(network::PeerNetwork* peerNetwork, const uint8_t* data, uint32_t length, uint32_t streamId, const network::frame::RTPFNEHeader& fneHeader, const network::frame::RTPHeader& rtpHeader); + /** + * @brief Helper to process an P25 In-Call Control message. + * @param command In-Call Control Command. + * @param dstId Destination ID. + * @param peerId Peer ID. + * @param ssrc Synchronization Source. + * @param streamId Stream ID. + */ + void processPeerP25InCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId); /** * @brief Processes NXDN peer network traffic. * @param data Buffer containing NXDN data. @@ -204,6 +225,16 @@ class HOST_SW_API HostFNE { */ void processPeerNXDN(network::PeerNetwork* peerNetwork, const uint8_t* data, uint32_t length, uint32_t streamId, const network::frame::RTPFNEHeader& fneHeader, const network::frame::RTPHeader& rtpHeader); + /** + * @brief Helper to process an NXDN In-Call Control message. + * @param command In-Call Control Command. + * @param dstId Destination ID. + * @param peerId Peer ID. + * @param ssrc Synchronization Source. + * @param streamId Stream ID. + */ + void processPeerNXDNInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId); /** * @brief Processes analog peer network traffic. * @param data Buffer containing analog data. @@ -214,6 +245,16 @@ class HOST_SW_API HostFNE { */ void processPeerAnalog(network::PeerNetwork* peerNetwork, const uint8_t* data, uint32_t length, uint32_t streamId, const network::frame::RTPFNEHeader& fneHeader, const network::frame::RTPHeader& rtpHeader); + /** + * @brief Helper to process an analog In-Call Control message. + * @param command In-Call Control Command. + * @param dstId Destination ID. + * @param peerId Peer ID. + * @param ssrc Synchronization Source. + * @param streamId Stream ID. + */ + void processPeerAnalogInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId); /** * @brief Processes network tree disconnect notification. diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 2f56563d..588e926a 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -119,6 +119,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_restrictGrantToAffOnly(false), m_restrictPVCallToRegOnly(false), m_enableRIDInCallCtrl(true), + m_disallowInCallCtrl(false), m_rejectUnknownRID(false), m_maskOutboundPeerID(false), m_maskOutboundPeerIDForNonPL(false), @@ -194,6 +195,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) m_disallowExtAdjStsBcast = conf["disallowExtAdjStsBcast"].as(true); m_allowConvSiteAffOverride = conf["allowConvSiteAffOverride"].as(true); m_enableRIDInCallCtrl = conf["enableRIDInCallCtrl"].as(false); + m_disallowInCallCtrl = conf["disallowInCallCtrl"].as(false); m_rejectUnknownRID = conf["rejectUnknownRID"].as(false); m_maskOutboundPeerID = conf["maskOutboundPeerID"].as(false); m_maskOutboundPeerIDForNonPL = conf["maskOutboundPeerIDForNonPeerLink"].as(false); @@ -302,6 +304,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogInfo(" Disable P25 TDULC call termination broadcasts to any peers: %s", m_disallowCallTerm ? "yes" : "no"); LogInfo(" Allow conventional sites to override affiliation and receive all traffic: %s", m_allowConvSiteAffOverride ? "yes" : "no"); LogInfo(" Enable RID In-Call Control: %s", m_enableRIDInCallCtrl ? "yes" : "no"); + LogInfo(" Disallow In-Call Control Requests: %s", m_disallowInCallCtrl ? "yes" : "no"); LogInfo(" Reject Unknown RIDs: %s", m_rejectUnknownRID ? "yes" : "no"); LogInfo(" Log Traffic Denials: %s", m_logDenials ? "yes" : "no"); LogInfo(" Log Upstream Call Start/End Events: %s", m_logUpstreamCallStartEnd ? "yes" : "no"); @@ -449,6 +452,17 @@ void FNENetwork::processNetworkTreeDisconnect(uint32_t peerId, uint32_t offendin } } +/* Helper to process an downstream peer In-Call Control message. */ + +void FNENetwork::processDownstreamInCallCtrl(network::NET_ICC::ENUM command, network::NET_SUBFUNC::ENUM subFunc, uint32_t dstId, + uint8_t slotNo, uint32_t peerId, uint32_t ssrc, uint32_t streamId) +{ + if (m_disallowInCallCtrl) + return; + + processInCallCtrl(command, subFunc, dstId, slotNo, peerId, ssrc, streamId); +} + /* Updates the timer by the passed number of milliseconds. */ void FNENetwork::clock(uint32_t ms) @@ -1496,8 +1510,27 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) case NET_FUNC::INCALL_CTRL: // In-Call Control { - // FNEs are god-like entities, and we don't recognize the authority of foreign FNEs telling us what - // to do... + if (network->m_disallowInCallCtrl) + break; + + if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) { + FNEPeerConnection* connection = network->m_peers[peerId]; + if (connection != nullptr) { + std::string ip = udp::Socket::address(req->address); + + // validate peer (simple validation really) + if (connection->connected() && connection->address() == ip) { + NET_ICC::ENUM command = (NET_ICC::ENUM)req->buffer[10U]; + uint32_t dstId = GET_UINT24(req->buffer, 11U); + uint8_t slot = req->buffer[14U]; + + network->processInCallCtrl(command, req->fneHeader.getSubFunction(), dstId, slot, peerId, ssrc, streamId); + } + else { + network->writePeerNAK(peerId, streamId, TAG_INCALL_CTRL, NET_CONN_NAK_FNE_UNAUTHORIZED); + } + } + } } break; @@ -2046,6 +2079,21 @@ void FNENetwork::erasePeer(uint32_t peerId) erasePeerAffiliations(peerId); } +/* Helper to determine if the peer is local to this master. */ + +bool FNENetwork::isPeerLocal(uint32_t peerId) +{ + m_peers.shared_lock(); + auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; }); + if (it != m_peers.end()) { + m_peers.shared_unlock(); + return true; + } + m_peers.shared_unlock(); + + return false; +} + /* Helper to find the unit registration for the given source ID. */ uint32_t FNENetwork::findPeerUnitReg(uint32_t srcId) @@ -2180,6 +2228,116 @@ void FNENetwork::setupRepeaterLogin(uint32_t peerId, uint32_t streamId, FNEPeerC LogInfoEx(LOG_MASTER, "PEER %u RPTL ACK, challenge response sent for login", peerId); } +/* Helper to process an In-Call Control message. */ + +void FNENetwork::processInCallCtrl(network::NET_ICC::ENUM command, network::NET_SUBFUNC::ENUM subFunc, uint32_t dstId, + uint8_t slotNo, uint32_t peerId, uint32_t ssrc, uint32_t streamId) +{ + if (m_debug) + LogDebugEx(LOG_HOST, "FNENetwork::processInCallCtrl()", "peerId = %u, command = $%02X, subFunc = $%02X, dstId = %u, slot = %u, ssrc = %u, streamId = %u", + peerId, command, subFunc, dstId, slotNo, ssrc, streamId); + + if (m_disallowInCallCtrl) { + LogWarning(LOG_MASTER, "PEER %u In-Call Control disabled, ignoring ICC request, dstId = %u, slot = %u, ssrc = %u, streamId = %u", + peerId, dstId, slotNo, ssrc, streamId); + return; + } + + switch (command) { + case network::NET_ICC::REJECT_TRAFFIC: + { + // is this a local peer? + if (ssrc > 0 && (m_peers.find(ssrc) != m_peers.end())) { + FNEPeerConnection* connection = m_peers[ssrc]; + if (connection != nullptr) { + // validate peer (simple validation really) + if (connection->connected()) { + LogInfoEx(LOG_MASTER, "PEER %u In-Call Control Request to Local Peer, dstId = %u, slot = %u, ssrc = %u, streamId = %u", peerId, dstId, slotNo, ssrc, streamId); + + // send ICC request to local peer + writePeerICC(ssrc, streamId, subFunc, command, dstId, slotNo, true); + + // flag the protocol call handler to allow call takeover on the next audio frame + switch (subFunc) { + case NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR: // Encapsulated DMR data frame + m_tagDMR->triggerCallTakeover(dstId); + break; + + case NET_SUBFUNC::PROTOCOL_SUBFUNC_P25: // Encapsulated P25 data frame + m_tagP25->triggerCallTakeover(dstId); + break; + + case NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN: // Encapsulated NXDN data frame + m_tagNXDN->triggerCallTakeover(dstId); + break; + + case NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG: // Encapsulated analog data frame + m_tagAnalog->triggerCallTakeover(dstId); + break; + + default: + break; + } + } + } + } else { + LogInfoEx(LOG_MASTER, "PEER %u In-Call Control Request to Neighbors, dstId = %u, slot = %u, ssrc = %u, streamId = %u", peerId, dstId, slotNo, ssrc, streamId); + + // send ICC request to any peers connected to us that are neighbor FNEs + m_peers.shared_lock(); + for (auto peer : m_peers) { + if (peer.second == nullptr) + continue; + if (peerId != peer.first) { + FNEPeerConnection* conn = peer.second; + if (peerId == peer.first) { + // skip the peer if it is the source peer + continue; + } + + if (conn->isNeighborFNEPeer()) { + // send ICC request to local peer + writePeerICC(peer.first, streamId, subFunc, command, dstId, slotNo, true, false, ssrc); + } + } + } + m_peers.shared_unlock(); + + // flag the protocol call handler to allow call takeover on the next audio frame + switch (subFunc) { + case NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR: // Encapsulated DMR data frame + m_tagDMR->triggerCallTakeover(dstId); + break; + + case NET_SUBFUNC::PROTOCOL_SUBFUNC_P25: // Encapsulated P25 data frame + m_tagP25->triggerCallTakeover(dstId); + break; + + case NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN: // Encapsulated NXDN data frame + m_tagNXDN->triggerCallTakeover(dstId); + break; + + case NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG: // Encapsulated analog data frame + m_tagAnalog->triggerCallTakeover(dstId); + break; + + default: + break; + } + + // send further up the network tree + if (m_host->m_peerNetworks.size() > 0) { + writePeerICC(peerId, streamId, subFunc, command, dstId, slotNo, true, true, ssrc); + } + } + } + break; + + default: + break; + } +} + /* Helper to send the network metadata to the specified peer in a separate thread. */ void FNENetwork::peerMetadataUpdate(uint32_t peerId) @@ -2777,7 +2935,7 @@ void FNENetwork::writeTreeDisconnect(uint32_t peerId, uint32_t offendingPeerId) /* Helper to send a In-Call Control command to the specified peer. */ bool FNENetwork::writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::ENUM subFunc, NET_ICC::ENUM command, uint32_t dstId, uint8_t slotNo, - bool systemReq) + bool systemReq, bool toUpstream, uint32_t ssrc) { if (peerId == 0) return false; @@ -2786,15 +2944,41 @@ bool FNENetwork::writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::E if (dstId == 0U) return false; + if (systemReq && ssrc == 0U) + ssrc = peerId; + + if (m_debug) + LogDebugEx(LOG_HOST, "FNENetwork::writePeerICC()", "peerId = %u, command = $%02X, subFunc = $%02X, dstId = %u, slot = %u, ssrc = %u, streamId = %u", + peerId, command, subFunc, dstId, slotNo, ssrc, streamId); + uint8_t buffer[DATA_PACKET_LENGTH]; ::memset(buffer, 0x00U, DATA_PACKET_LENGTH); - SET_UINT32(peerId, buffer, 6U); // Peer ID + if (systemReq) { + SET_UINT32(ssrc, buffer, 6U); // Peer ID + } else { + SET_UINT32(peerId, buffer, 6U); // Peer ID + } buffer[10U] = (uint8_t)command; // In-Call Control Command SET_UINT24(dstId, buffer, 11U); // Destination ID buffer[14U] = slotNo; // DMR Slot No - return writePeer(peerId, m_peerId, { NET_FUNC::INCALL_CTRL, subFunc }, buffer, 15U, RTP_END_OF_CALL_SEQ, streamId); + // are we sending this ICC request upstream? + if (toUpstream && systemReq) { + if (m_host->m_peerNetworks.size() > 0U) { + for (auto peer : m_host->m_peerNetworks) { + if (peer.second != nullptr) { + if (peer.second->isEnabled()) { + peer.second->writeMaster({ NET_FUNC::INCALL_CTRL, subFunc }, buffer, 15U, RTP_END_OF_CALL_SEQ, streamId, false, 0U, ssrc); + } + } + } + } + + return true; + } + else + return writePeer(peerId, ssrc, { NET_FUNC::INCALL_CTRL, subFunc }, buffer, 15U, RTP_END_OF_CALL_SEQ, streamId); } /* diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 61887da2..04783234 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -237,6 +237,19 @@ namespace network */ void processNetworkTreeDisconnect(uint32_t peerId, uint32_t offendingPeerId); + /** + * @brief Helper to process an downstream peer In-Call Control message. + * @param command In-Call Control Command. + * @param subFunc Network Sub-Function. + * @param dstId Destination ID. + * @param slotNo Slot Number. + * @param peerId Peer ID. + * @param ssrc RTP synchronization source ID. + * @param streamId Stream ID. + */ + void processDownstreamInCallCtrl(network::NET_ICC::ENUM command, network::NET_SUBFUNC::ENUM subFunc, uint32_t dstId, + uint8_t slotNo, uint32_t peerId, uint32_t ssrc, uint32_t streamId); + /** * @brief Updates the timer by the passed number of milliseconds. * @param ms Number of milliseconds. @@ -358,6 +371,7 @@ namespace network bool m_restrictGrantToAffOnly; bool m_restrictPVCallToRegOnly; bool m_enableRIDInCallCtrl; + bool m_disallowInCallCtrl; bool m_rejectUnknownRID; bool m_maskOutboundPeerID; @@ -447,6 +461,12 @@ namespace network * @returns bool True, if peer was deleted, otherwise false. */ void erasePeer(uint32_t peerId); + /** + * @brief Helper to determine if the peer is local to this master. + * @param peerId Peer ID. + * @returns bool True, if peer is local, otherwise false. + */ + bool isPeerLocal(uint32_t peerId); /** * @brief Helper to find the unit registration for the given source ID. @@ -470,6 +490,19 @@ namespace network */ void setupRepeaterLogin(uint32_t peerId, uint32_t streamId, FNEPeerConnection* connection); + /** + * @brief Helper to process an In-Call Control message. + * @param command In-Call Control Command. + * @param subFunc Network Sub-Function. + * @param dstId Destination ID. + * @param slotNo Slot Number. + * @param peerId Peer ID. + * @param ssrc RTP synchronization source ID. + * @param streamId Stream ID for this message. + */ + void processInCallCtrl(network::NET_ICC::ENUM command, network::NET_SUBFUNC::ENUM subFunc, uint32_t dstId, + uint8_t slotNo, uint32_t peerId, uint32_t ssrc, uint32_t streamId); + /** * @brief Helper to send the network metadata to the specified peer in a separate thread. * @param peerId Peer ID. @@ -651,9 +684,12 @@ namespace network * @param dstId Destination ID. * @param slotNo DMR slot. * @param systemReq Flag indicating the ICC request is a system generated one not a automatic RID rule generated one. + * @param toUpstream Flag indicating the ICC request is directed at an upstream peer. + * @param ssrc RTP synchronization source ID. */ bool writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::ENUM subFunc = NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, - NET_ICC::ENUM command = NET_ICC::NOP, uint32_t dstId = 0U, uint8_t slotNo = 0U, bool systemReq = false); + NET_ICC::ENUM command = NET_ICC::NOP, uint32_t dstId = 0U, uint8_t slotNo = 0U, bool systemReq = false, bool toUpstream = false, + uint32_t ssrc = 0U); /* ** Generic Message Writing diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 10419c04..8933c2cd 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -161,6 +161,22 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee }); if (it != m_status.end()) { RxStatus status = it->second; + + // is the call being taken over? + if (status.callTakeover) { + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call Source Switched (Takeover), peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); + + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; + m_status[dstId].callTakeover = false; // reset takeover flag + m_status.unlock(); + + status = m_status[dstId]; + } + if (streamId != status.streamId) { if (status.srcId != 0U && status.srcId != srcId) { bool hasCallPriority = false; @@ -190,6 +206,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; m_status.unlock(); } else { @@ -198,17 +215,23 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return false; } } else { - if (hasCallPriority) { + if (hasCallPriority && !m_network->m_disallowInCallCtrl) { LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "Analog, Call Source Switched (Priority), peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); // since we're gonna switch over the stream and interrupt the current call inprogress lets try to ICC the transmitting peer - m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true); + if (m_network->isPeerLocal(m_status[dstId].ssrc)) + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true, false, + m_status[dstId].ssrc); + else + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true, true, + m_status[dstId].ssrc); } m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; m_status.unlock(); } } @@ -238,6 +261,7 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee m_status[dstId].dstId = dstId; m_status[dstId].streamId = streamId; m_status[dstId].peerId = peerId; + m_status[dstId].ssrc = ssrc; m_status[dstId].activeCall = true; m_status.unlock(); @@ -377,6 +401,24 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee return false; } +/* Helper to trigger a call takeover from a In-Call control event. */ + +void TagAnalogData::triggerCallTakeover(uint32_t dstId) +{ + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { + if (x.second.dstId == dstId) { + if (x.second.activeCall) + return true; + } + return false; + }); + if (it != m_status.end()) { + m_status.lock(false); + m_status[dstId].callTakeover = true; + m_status.unlock(); + } +} + /* Helper to playback a parrot frame to the network. */ void TagAnalogData::playbackParrot() diff --git a/src/fne/network/callhandler/TagAnalogData.h b/src/fne/network/callhandler/TagAnalogData.h index 43dd059d..f01a841b 100644 --- a/src/fne/network/callhandler/TagAnalogData.h +++ b/src/fne/network/callhandler/TagAnalogData.h @@ -64,6 +64,11 @@ namespace network */ bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint32_t ssrc, uint16_t pktSeq, uint32_t streamId, bool fromUpstream = false); + /** + * @brief Helper to trigger a call takeover from a In-Call control event. + */ + void triggerCallTakeover(uint32_t dstId); + /** * @brief Helper to playback a parrot frame to the network. */ @@ -169,10 +174,18 @@ namespace network * @brief Peer ID. */ uint32_t peerId; + /** + * @brief Synchronization Source. + */ + uint32_t ssrc; /** * @brief Flag indicating this call is active with traffic currently in progress. */ bool activeCall; + /** + * @brief Flag indicating the metadata for the call on the next frame will be overwritten. + */ + bool callTakeover; /** * @brief Helper to reset call status. @@ -183,7 +196,9 @@ namespace network dstId = 0U; streamId = 0U; peerId = 0U; + ssrc = 0U; activeCall = false; + callTakeover = false; } }; typedef std::pair StatusMapPair; diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 4735b1e9..f32b87b2 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -244,12 +244,29 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_status.end()) { RxStatus status = it->second; + + // is the call being taken over? + if (status.callTakeover) { + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Source Switched (Takeover), peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, fromUpstream); + + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; + m_status[dstId].callTakeover = false; // reset takeover flag + m_status.unlock(); + + status = m_status[dstId]; + } + if (streamId != status.streamId) { // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver && status.slotNo == slotNo) { m_status.lock(false); m_status[dstId].streamId = streamId; + m_status[dstId].ssrc = ssrc; if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { @@ -288,6 +305,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; m_status.unlock(); } else { LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", @@ -295,17 +313,23 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId return false; } } else { - if (hasCallPriority) { + if (hasCallPriority && !m_network->m_disallowInCallCtrl) { LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "DMR, Call Source Switched (Priority), peer = %u, ssrc = %u, srcId = %u, dstId = %u, slotNo = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxSlotNo = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, streamId, status.peerId, status.srcId, status.dstId, status.slotNo, status.streamId, fromUpstream); // since we're gonna switch over the stream and interrupt the current call inprogress lets try to ICC the transmitting peer - m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, dstId, slotNo, true); + if (m_network->isPeerLocal(m_status[dstId].ssrc)) + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true, false, + m_status[dstId].ssrc); + else + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true, true, + m_status[dstId].ssrc); } m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; m_status.unlock(); } } @@ -338,6 +362,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_status[dstId].slotNo = slotNo; m_status[dstId].streamId = streamId; m_status[dstId].peerId = peerId; + m_status[dstId].ssrc = ssrc; m_status[dstId].activeCall = true; m_status.unlock(); @@ -350,6 +375,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_statusPVCall[dstId].slotNo = slotNo; m_statusPVCall[dstId].streamId = streamId; m_statusPVCall[dstId].peerId = peerId; + m_statusPVCall[dstId].ssrc = ssrc; m_statusPVCall[dstId].activeCall = true; // find the SSRC of the peer that registered this unit @@ -626,6 +652,24 @@ bool TagDMRData::processGrantReq(uint32_t srcId, uint32_t dstId, uint8_t slot, b return true; } +/* Helper to trigger a call takeover from a In-Call control event. */ + +void TagDMRData::triggerCallTakeover(uint32_t dstId) +{ + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { + if (x.second.dstId == dstId) { + if (x.second.activeCall) + return true; + } + return false; + }); + if (it != m_status.end()) { + m_status.lock(false); + m_status[dstId].callTakeover = true; + m_status.unlock(); + } +} + /* Helper to playback a parrot frame to the network. */ void TagDMRData::playbackParrot() diff --git a/src/fne/network/callhandler/TagDMRData.h b/src/fne/network/callhandler/TagDMRData.h index ae2ff484..1786531a 100644 --- a/src/fne/network/callhandler/TagDMRData.h +++ b/src/fne/network/callhandler/TagDMRData.h @@ -76,6 +76,11 @@ namespace network */ bool processGrantReq(uint32_t srcId, uint32_t dstId, uint8_t slot, bool unitToUnit, uint32_t peerId, uint16_t pktSeq, uint32_t streamId); + /** + * @brief Helper to trigger a call takeover from a In-Call control event. + */ + void triggerCallTakeover(uint32_t dstId); + /** * @brief Helper to playback a parrot frame to the network. */ @@ -214,6 +219,10 @@ namespace network * @brief Peer ID. */ uint32_t peerId; + /** + * @brief Synchronization Source. + */ + uint32_t ssrc; /** * @brief Destination Peer ID. */ @@ -222,6 +231,10 @@ namespace network * @brief Flag indicating this call is active with traffic currently in progress. */ bool activeCall; + /** + * @brief Flag indicating the metadata for the call on the next frame will be overwritten. + */ + bool callTakeover; /** * @brief Helper to reset call status. @@ -233,7 +246,9 @@ namespace network slotNo = 0U; streamId = 0U; peerId = 0U; + ssrc = 0U; activeCall = false; + callTakeover = false; } }; typedef std::pair StatusMapPair; diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 93da0ea4..48701672 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -282,12 +282,29 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI }); if (it != m_status.end()) { RxStatus status = m_status[dstId]; + + // is the call being taken over? + if (status.callTakeover) { + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Source Switched (Takeover), peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); + + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; + m_status[dstId].callTakeover = false; // reset takeover flag + m_status.unlock(); + + status = m_status[dstId]; + } + if (streamId != status.streamId) { // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver) { m_status.lock(false); m_status[dstId].streamId = streamId; + m_status[dstId].ssrc = ssrc; if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { @@ -326,6 +343,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; m_status.unlock(); } else { LogWarning((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Collision, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", @@ -333,17 +351,23 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI return false; } } else { - if (hasCallPriority) { + if (hasCallPriority && !m_network->m_disallowInCallCtrl) { LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "NXDN, Call Source Switched (Priority), peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); // since we're gonna switch over the stream and interrupt the current call inprogress lets try to ICC the transmitting peer - m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true); + if (m_network->isPeerLocal(m_status[dstId].ssrc)) + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true, false, + m_status[dstId].ssrc); + else + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true, true, + m_status[dstId].ssrc); } m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; m_status.unlock(); } } @@ -374,6 +398,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI m_status[dstId].dstId = dstId; m_status[dstId].streamId = streamId; m_status[dstId].peerId = peerId; + m_status[dstId].ssrc = ssrc; m_status[dstId].activeCall = true; m_status.unlock(); @@ -385,6 +410,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI m_statusPVCall[dstId].dstId = dstId; m_statusPVCall[dstId].streamId = streamId; m_statusPVCall[dstId].peerId = peerId; + m_statusPVCall[dstId].ssrc = ssrc; m_statusPVCall[dstId].activeCall = true; // find the SSRC of the peer that registered this unit @@ -655,6 +681,24 @@ bool TagNXDNData::processGrantReq(uint32_t srcId, uint32_t dstId, bool unitToUni return true; } +/* Helper to trigger a call takeover from a In-Call control event. */ + +void TagNXDNData::triggerCallTakeover(uint32_t dstId) +{ + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { + if (x.second.dstId == dstId) { + if (x.second.activeCall) + return true; + } + return false; + }); + if (it != m_status.end()) { + m_status.lock(false); + m_status[dstId].callTakeover = true; + m_status.unlock(); + } +} + /* Helper to playback a parrot frame to the network. */ void TagNXDNData::playbackParrot() diff --git a/src/fne/network/callhandler/TagNXDNData.h b/src/fne/network/callhandler/TagNXDNData.h index aa70f7b6..427cf5ba 100644 --- a/src/fne/network/callhandler/TagNXDNData.h +++ b/src/fne/network/callhandler/TagNXDNData.h @@ -74,6 +74,11 @@ namespace network */ bool processGrantReq(uint32_t srcId, uint32_t dstId, bool unitToUnit, uint32_t peerId, uint16_t pktSeq, uint32_t streamId); + /** + * @brief Helper to trigger a call takeover from a In-Call control event. + */ + void triggerCallTakeover(uint32_t dstId); + /** * @brief Helper to playback a parrot frame to the network. */ @@ -179,6 +184,10 @@ namespace network * @brief Peer ID. */ uint32_t peerId; + /** + * @brief Synchronization Source. + */ + uint32_t ssrc; /** * @brief Destination Peer ID. */ @@ -187,6 +196,10 @@ namespace network * @brief Flag indicating this call is active with traffic currently in progress. */ bool activeCall; + /** + * @brief Flag indicating the metadata for the call on the next frame will be overwritten. + */ + bool callTakeover; /** * @brief Helper to reset call status. @@ -197,7 +210,9 @@ namespace network dstId = 0U; streamId = 0U; peerId = 0U; + ssrc = 0U; activeCall = false; + callTakeover = false; } }; typedef std::pair StatusMapPair; diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 772dabb1..af1f09ea 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -325,12 +325,29 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId }); if (it != m_status.end()) { RxStatus status = m_status[dstId]; + + // is the call being taken over? + if (status.callTakeover) { + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Source Switched (Takeover), peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSsrc = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.ssrc, status.srcId, status.dstId, status.streamId, fromUpstream); + + m_status.lock(false); + m_status[dstId].streamId = streamId; + m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; + m_status[dstId].callTakeover = false; // reset takeover flag + m_status.unlock(); + + status = m_status[dstId]; + } + if (streamId != status.streamId && ((duid != DUID::TDU) && (duid != DUID::TDULC))) { // perform TG switch over -- this can happen in special conditions where a TG may rapidly switch // from one source to another (primarily from bridge resources) if (switchOver) { m_status.lock(false); m_status[dstId].streamId = streamId; + m_status[dstId].ssrc = ssrc; if (status.srcId == 0U) m_status[dstId].srcId = srcId; if (status.srcId != srcId) { @@ -368,6 +385,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; m_status.unlock(); } else { @@ -376,17 +394,23 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId return false; } } else { - if (hasCallPriority) { - LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Source Switched (Priority), peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", - peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.srcId, status.dstId, status.streamId, fromUpstream); + if (hasCallPriority && !m_network->m_disallowInCallCtrl) { + LogInfoEx((fromUpstream) ? LOG_PEER : LOG_MASTER, "P25, Call Source Switched (Priority), peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, rxPeer = %u, rxSsrc = %u, rxSrcId = %u, rxDstId = %u, rxStreamId = %u, fromUpstream = %u", + peerId, ssrc, sysId, netId, srcId, dstId, streamId, status.peerId, status.ssrc, status.srcId, status.dstId, status.streamId, fromUpstream); // since we're gonna switch over the stream and interrupt the current call inprogress lets try to ICC the transmitting peer - m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true); + if (m_network->isPeerLocal(m_status[dstId].ssrc)) + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true, false, + m_status[dstId].ssrc); + else + m_network->writePeerICC(m_status[dstId].peerId, m_status[dstId].streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, dstId, 0U, true, true, + m_status[dstId].ssrc); } m_status.lock(false); m_status[dstId].streamId = streamId; m_status[dstId].srcId = srcId; + m_status[dstId].ssrc = ssrc; m_status.unlock(); } } @@ -417,6 +441,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_status[dstId].dstId = dstId; m_status[dstId].streamId = streamId; m_status[dstId].peerId = peerId; + m_status[dstId].ssrc = ssrc; m_status[dstId].activeCall = true; m_status.unlock(); @@ -428,6 +453,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_statusPVCall[dstId].dstId = dstId; m_statusPVCall[dstId].streamId = streamId; m_statusPVCall[dstId].peerId = peerId; + m_statusPVCall[dstId].ssrc = ssrc; m_statusPVCall[dstId].activeCall = true; // find the SSRC of the peer that registered this unit @@ -725,6 +751,24 @@ bool TagP25Data::processGrantReq(uint32_t srcId, uint32_t dstId, bool unitToUnit return true; } +/* Helper to trigger a call takeover from a In-Call control event. */ + +void TagP25Data::triggerCallTakeover(uint32_t dstId) +{ + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair& x) { + if (x.second.dstId == dstId) { + if (x.second.activeCall) + return true; + } + return false; + }); + if (it != m_status.end()) { + m_status.lock(false); + m_status[dstId].callTakeover = true; + m_status.unlock(); + } +} + /* Helper to playback a parrot frame to the network. */ void TagP25Data::playbackParrot() diff --git a/src/fne/network/callhandler/TagP25Data.h b/src/fne/network/callhandler/TagP25Data.h index e8e0910d..53fa700e 100644 --- a/src/fne/network/callhandler/TagP25Data.h +++ b/src/fne/network/callhandler/TagP25Data.h @@ -80,6 +80,11 @@ namespace network */ bool processGrantReq(uint32_t srcId, uint32_t dstId, bool unitToUnit, uint32_t peerId, uint16_t pktSeq, uint32_t streamId); + /** + * @brief Helper to trigger a call takeover from a In-Call control event. + */ + void triggerCallTakeover(uint32_t dstId); + /** * @brief Helper to playback a parrot frame to the network. */ @@ -228,6 +233,10 @@ namespace network * @brief Peer ID. */ uint32_t peerId; + /** + * @brief Synchronization Source. + */ + uint32_t ssrc; /** * @brief Destination Peer ID. */ @@ -236,6 +245,10 @@ namespace network * @brief Flag indicating this call is active with traffic currently in progress. */ bool activeCall; + /** + * @brief Flag indicating the metadata for the call on the next frame will be overwritten. + */ + bool callTakeover; /** * @brief Helper to reset call status. @@ -246,7 +259,9 @@ namespace network dstId = 0U; streamId = 0U; peerId = 0U; + ssrc = 0U; activeCall = false; + callTakeover = false; } }; typedef std::pair StatusMapPair; diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index 903cfd9e..5f92964f 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -196,7 +196,8 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa // set the In-Call Control function callback if (m_network != nullptr) { - m_network->setDMRICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, uint8_t slotNo) { processInCallCtrl(command, dstId, slotNo); }); + m_network->setDMRICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, + uint8_t slotNo, uint32_t peerId, uint32_t ssrc, uint32_t streamId) { processInCallCtrl(command, dstId, slotNo); }); } /* diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index 6fea2cf5..5c058529 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -315,7 +315,8 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw // set the In-Call Control function callback if (m_network != nullptr) { - m_network->setNXDNICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId) { processInCallCtrl(command, dstId); }); + m_network->setNXDNICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) { processInCallCtrl(command, dstId); }); } if (printOptions) { diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index 7e43d9a6..c5db58f1 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -483,7 +483,8 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw // set the In-Call Control function callback if (m_network != nullptr) { - m_network->setP25ICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId) { processInCallCtrl(command, dstId); }); + m_network->setP25ICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, + uint32_t peerId, uint32_t ssrc, uint32_t streamId) { processInCallCtrl(command, dstId); }); } // throw a warning if we are notifying a CC of our presence (this indicates we're a VC) *AND* we have the control From 41f2539169d22895eee9aca6a568b26bb05121a7 Mon Sep 17 00:00:00 2001 From: Natalie Date: Thu, 13 Nov 2025 10:47:04 -0600 Subject: [PATCH 166/200] bump WebSocketPP version to conform to new CMake minimum version; (#108) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0f95c56..d2f35f61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,7 +222,7 @@ if (NOT DISABLE_WEBSOCKETS) FetchContent_Declare( WEBSOCKETPP GIT_REPOSITORY https://github.com/zaphoyd/websocketpp.git - GIT_TAG 56123c87598f8b1dd471be83ca841ceae07f95ba # 0.8.2 + GIT_TAG 4dfe1be74e684acca19ac1cf96cce0df9eac2a2d # 0.8.2 with modified CMake version ) FetchContent_MakeAvailable(WEBSOCKETPP) add_subdirectory(${websocketpp_SOURCE_DIR} EXCLUDE_FROM_ALL) From 00fcc65c0bac881a19fb2791321863b6ebb1e3e6 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 16 Nov 2025 21:40:32 -0500 Subject: [PATCH 167/200] fix typo; --- configs/peer_list.example.dat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/peer_list.example.dat b/configs/peer_list.example.dat index b2a7fd96..922ec695 100644 --- a/configs/peer_list.example.dat +++ b/configs/peer_list.example.dat @@ -21,7 +21,7 @@ # If this flag is disabled (0), and the connected peer tries to transmit over an on going # call, normal call collision rules are applied to the traffic being transmitted. # If this flag is enabled (1), and the connected peer tries to transmit over an on going - call, call collision rules are ignored, and the peer is given priority. +# call, call collision rules are ignored, and the peer is given priority. # # Entry Format: "Peer ID,Peer Password,Peer Replication (1 = Enabled / 0 = Disabled),Peer Alias (optional),Can Request Keys (1 = Enabled / 0 = Disabled),Can Issue Inhibit (1 = Enabled / 0 = Disabled),Has Call Priority (1 = Enabled / 0 = Disabled)" # Examples: From ab5f1692d9225bb38f74d0e4b197a8d59991bba5 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 17 Nov 2025 09:37:03 -0500 Subject: [PATCH 168/200] add some documentation around newer protocol additions; --- docs/TN.1000 - FNE Network.adoc | 74 ++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/docs/TN.1000 - FNE Network.adoc b/docs/TN.1000 - FNE Network.adoc index f6552601..477c429b 100644 --- a/docs/TN.1000 - FNE Network.adoc +++ b/docs/TN.1000 - FNE Network.adoc @@ -31,6 +31,8 @@ This document describes, in high-level, the general concepts and procedures for * DMR: Digital Mobile Radio. (ETSI TS-102) * P25: Project 25. (TIA-102) * NXDN: Next Generation Digital Narrowband. +* ANALOG: Analog Audio. +* STP: Spanning Tree Protocol. === 2.2 General Concept The DVM FNE Network Protocol defines a common and standard communications protocol between the, CFNE server application, and DVM end-point applications. @@ -166,9 +168,13 @@ The function parameter of the extension header defines the major operation opcod |$91 |This function is used to announce status related to Group Affiliation, Unit Registration and Voice Channel registration. -|Peer-Link +|Peer Replication |$92 |This function is used from both Master -> Peer and Peer -> Master for linked CFNEs operating as a single network. + +|Network Tree +|$93 +|This function is used from both Master -> Peer and Peer -> Master for linked CFNEs operating as a single network to transfer spanning tree data. |=== ===== 2.3.1.2.2 Sub-Function @@ -193,6 +199,11 @@ The sub-function parameter of the extension header defines the minor operation o |Protocol |This sub-function is used for NXDN traffic. +|Analog +|$0F +|Protocol +|This sub-function is used for analog audio traffic. + |Whitelist RIDs |$00 |Master @@ -258,25 +269,40 @@ The sub-function parameter of the extension header defines the minor operation o |Announce |This sub-function is used for a peer to announce its list of registered voice channels to the master. -|Peer-Link Talkgroup Transfer +|Peer Replication Talkgroup Transfer |$00 -|Peer-Link -|This sub-function is used for a CFNE master to transfer the entire certified talkgroup rules configuration to a CFNE linked peer. +|Peer Replication +|This sub-function is used for a CFNE master to transfer the entire certified talkgroup rules configuration to a CFNE peer replica. -|Peer-Link Radio ID Transfer +|Peer Replication Radio ID Transfer |$01 -|Peer-Link -|This sub-function is used for a CFNE master to transfer the entire certified radio ID lookup configuration to a CFNE linked peer. +|Peer Replication +|This sub-function is used for a CFNE master to transfer the entire certified radio ID lookup configuration to a CFNE peer replica. -|Peer-Link Peer ID Transfer +|Peer Replication Peer ID Transfer |$02 -|Peer-Link -|This sub-function is used for a CFNE master to transfer the entire certified peer ID lookup configuration to a CFNE linked peer. +|Peer Replication +|This sub-function is used for a CFNE master to transfer the entire certified peer ID lookup configuration to a CFNE peer replica. -|Peer-Link Active Peer List Transfer +|Peer Replication Active Peer List Transfer |$A2 -|Peer-Link -|This sub-function is used for a CFNE linked peer to transfer the internal list of active peers to the CFNE master. +|Peer Replication +|This sub-function is used for a CFNE peer replica to transfer the internal list of active peers to the CFNE master. + +|Peer Replication HA Parameters +|$A3 +|Peer Replication +|This sub-function is used for a CFNE peer replica to transfer the configured HA parameters to the CFNE master. + +|Network Tree List +|$00 +|Spanning Tree +|This sub-function is used for a CFNE peer to transfer the network tree list to/from the CFNE master. + +|Network Tree Disconnect +|$01 +|Spanning Tree +|This sub-function is used for a CFNE master to command a disconnect of a duplicated CFNE connection. |=== === 2.3 NACK Types @@ -321,6 +347,10 @@ This is the basic description of the various packet NACKs that may occur. |FNE Max Connections |8 |General failure of the CFNE having reached its maximum allowable connected peers. + +|FNE Duplicate Connection +|9 +|Fatal failure where a downstream CFNE peer is already connected to the network. |=== === 2.4 TAG Types @@ -342,6 +372,10 @@ Some protocol commands (documented in the procedures below) utilize textual mark |NXDD |Marker for NXDN data packets. +|Analog Audio Data +|ANOD +|Marker for analog audio packets. + |Repeater/Peer Login |RPTL | @@ -358,10 +392,18 @@ Some protocol commands (documented in the procedures below) utilize textual mark |RPTP | -|Ping Keep-Alive Response +|Repeater Grant Request |RPTG | +|Repeater Key Request +|RKEY +| + +|ICC +|In-Call Control Request +| + |Transfer Message |TRNS | @@ -382,6 +424,10 @@ Some protocol commands (documented in the procedures below) utilize textual mark |ANNC | +|Replication +|REPL +| + |=== == 3. Procedures From 67ef2dc7fb2b0902f90ee660d45aa6af0fd0e2e1 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 17 Nov 2025 09:38:50 -0500 Subject: [PATCH 169/200] whoops typo; --- docs/TN.1000 - FNE Network.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/TN.1000 - FNE Network.adoc b/docs/TN.1000 - FNE Network.adoc index 477c429b..106af41f 100644 --- a/docs/TN.1000 - FNE Network.adoc +++ b/docs/TN.1000 - FNE Network.adoc @@ -400,8 +400,8 @@ Some protocol commands (documented in the procedures below) utilize textual mark |RKEY | -|ICC |In-Call Control Request +|ICC | |Transfer Message From 36e70f0b2831e61c07cf59716ee809081d243f1c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 18 Nov 2025 15:59:10 -0500 Subject: [PATCH 170/200] start adding foundational work for future P25 Phase 2; --- src/common/edac/RS634717.cpp | 275 +++++++++++++++++++++++++++++- src/common/edac/RS634717.h | 51 +++++- src/common/network/RTPFNEHeader.h | 3 +- src/common/p25/P25Defines.h | 53 ++++++ src/common/p25/dfsi/DFSIDefines.h | 11 +- src/common/p25/dfsi/LC.cpp | 229 ++++++++++++++++++++++++- src/common/p25/dfsi/LC.h | 40 ++++- src/common/p25/lc/LC.cpp | 3 + src/common/p25/lc/LC.h | 7 + 9 files changed, 664 insertions(+), 8 deletions(-) diff --git a/src/common/edac/RS634717.cpp b/src/common/edac/RS634717.cpp index 4ad9b2cb..0eb3b42d 100644 --- a/src/common/edac/RS634717.cpp +++ b/src/common/edac/RS634717.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2016 Jonathan Naylor, G4KLX - * Copyright (C) 2017,2023 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017,2023,2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" @@ -77,6 +77,43 @@ const uint8_t ENCODE_MATRIX_362017[20U][36U] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 002, 001, 053, 074, 002, 014, 052, 074, 012, 057, 024, 063, 015, 042, 052, 033 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 034, 035, 002, 023, 021, 027, 022, 033, 064, 042, 005, 073, 051, 046, 073, 060 } }; +const uint8_t ENCODE_MATRIX_633529[35U][63U] = { + { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 013, 014, 023, 076, 015, 077, 050, 062, 015, 014, 012, 007, 074, 045, 023, 071, 050, 064, 010, 016, 022, 071, 077, 020, 051, 061, 032, 006 }, + { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 072, 043, 045, 021, 020, 011, 012, 002, 034, 045, 060, 030, 011, 047, 014, 003, 014, 026, 004, 054, 041, 002, 075, 034, 043, 011, 056, 016 }, + { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 041, 061, 024, 036, 024, 045, 063, 072, 007, 027, 012, 032, 077, 066, 020, 035, 071, 030, 045, 023, 025, 060, 067, 030, 050, 001, 003, 012 }, + { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 015, 032, 011, 022, 057, 030, 071, 016, 013, 074, 020, 074, 010, 022, 016, 040, 001, 070, 013, 012, 041, 045, 074, 021, 016, 013, 040, 077 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 063, 005, 071, 034, 045, 005, 050, 044, 071, 003, 060, 053, 024, 017, 061, 040, 020, 030, 011, 076, 026, 017, 017, 035, 036, 021, 046, 044 }, + { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 003, 011, 061, 045, 002, 035, 037, 016, 072, 003, 044, 011, 074, 020, 073, 024, 072, 053, 064, 070, 056, 063, 067, 024, 043, 027, 055, 073 }, + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 037, 073, 045, 031, 046, 021, 013, 017, 015, 002, 047, 003, 024, 051, 074, 064, 002, 066, 072, 071, 057, 041, 040, 025, 071, 075, 021, 061 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 022, 034, 057, 013, 053, 071, 033, 046, 075, 016, 041, 066, 014, 054, 075, 003, 076, 017, 064, 030, 034, 020, 076, 044, 056, 004, 032, 061 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 022, 021, 010, 001, 071, 064, 063, 066, 024, 076, 055, 060, 071, 064, 070, 002, 011, 063, 015, 026, 075, 043, 017, 072, 037, 023, 043, 072 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 024, 046, 056, 036, 017, 025, 012, 021, 070, 040, 020, 015, 021, 011, 013, 016, 074, 061, 052, 016, 023, 013, 017, 075, 076, 060, 017, 071 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 011, 064, 054, 071, 007, 041, 020, 075, 010, 030, 020, 071, 053, 015, 003, 065, 013, 033, 060, 073, 075, 055, 045, 015, 001, 001, 002, 037 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 034, 013, 054, 030, 044, 054, 055, 046, 040, 012, 033, 016, 063, 072, 025, 051, 071, 074, 046, 014, 074, 027, 006, 034, 036, 026, 073, 003 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 035, 010, 076, 055, 017, 046, 027, 070, 061, 064, 024, 022, 011, 037, 017, 035, 022, 046, 044, 064, 072, 064, 025, 066, 044, 016, 070, 061 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 022, 036, 034, 020, 037, 020, 054, 072, 012, 062, 027, 005, 035, 061, 013, 060, 027, 037, 044, 006, 021, 005, 053, 021, 015, 031, 051, 030 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 055, 064, 074, 024, 056, 017, 001, 002, 004, 054, 007, 034, 075, 062, 023, 010, 041, 052, 032, 062, 074, 022, 025, 041, 030, 013, 046, 072 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 024, 031, 013, 052, 032, 002, 061, 043, 014, 060, 002, 047, 075, 015, 015, 045, 066, 031, 063, 031, 067, 012, 076, 047, 045, 067, 027, 074 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 056, 010, 017, 037, 012, 062, 011, 071, 003, 020, 042, 060, 010, 026, 033, 053, 056, 060, 060, 024, 063, 021, 042, 057, 020, 052, 064, 031 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 046, 004, 071, 071, 054, 045, 013, 025, 012, 051, 057, 056, 064, 002, 047, 041, 022, 047, 075, 050, 074, 011, 076, 070, 017, 047, 017, 041 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 044, 040, 054, 046, 036, 022, 061, 022, 062, 014, 054, 015, 060, 007, 052, 032, 065, 010, 043, 072, 041, 001, 067, 066, 015, 066, 052, 014 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 067, 067, 061, 050, 071, 026, 073, 046, 015, 041, 067, 010, 021, 006, 026, 012, 063, 012, 053, 050, 047, 001, 011, 062, 023, 016, 010, 002 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 026, 057, 021, 016, 062, 004, 005, 034, 074, 025, 065, 071, 063, 030, 040, 047, 031, 030, 032, 067, 014, 026, 074, 051, 043, 062, 072, 004 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 054, 046, 040, 054, 072, 013, 042, 010, 050, 014, 075, 051, 014, 041, 027, 001, 001, 014, 070, 042, 074, 055, 057, 077, 013, 042, 031, 042 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 071, 076, 073, 076, 034, 006, 044, 056, 070, 072, 027, 026, 060, 023, 074, 042, 056, 004, 020, 055, 035, 011, 021, 027, 062, 042, 001, 020 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 066, 074, 002, 012, 053, 075, 030, 020, 073, 075, 034, 044, 007, 073, 057, 076, 074, 071, 002, 065, 001, 037, 050, 035, 031, 066, 010, 042 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 071, 044, 041, 034, 072, 027, 022, 024, 040, 051, 046, 067, 075, 030, 046, 032, 021, 071, 045, 027, 012, 064, 043, 020, 020, 060, 025, 001 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 013, 065, 067, 037, 021, 005, 077, 040, 031, 054, 043, 041, 013, 030, 013, 037, 062, 045, 061, 053, 005, 063, 013, 063, 071, 041, 052, 023 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 053, 032, 074, 007, 035, 062, 040, 036, 042, 010, 024, 031, 067, 054, 021, 001, 072, 072, 073, 006, 061, 017, 020, 067, 005, 055, 045, 003 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 035, 077, 057, 075, 020, 037, 011, 065, 011, 066, 026, 035, 036, 033, 031, 031, 072, 045, 042, 051, 060, 071, 015, 040, 017, 025, 003, 057 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 005, 020, 040, 013, 037, 033, 061, 040, 027, 004, 034, 036, 044, 022, 004, 065, 067, 064, 022, 062, 031, 034, 062, 040, 041, 024, 022, 044 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 003, 077, 044, 074, 025, 047, 001, 027, 076, 055, 043, 045, 011, 040, 046, 041, 057, 014, 030, 043, 042, 074, 044, 051, 036, 050, 050, 017 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 052, 004, 033, 041, 064, 037, 065, 003, 037, 071, 010, 016, 076, 023, 004, 016, 063, 017, 067, 001, 010, 012, 066, 021, 064, 015, 070, 012 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 015, 021, 074, 035, 020, 070, 003, 010, 062, 044, 076, 076, 034, 023, 053, 064, 022, 062, 034, 030, 063, 070, 006, 020, 007, 027, 054, 004 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 054, 075, 036, 001, 051, 051, 036, 016, 074, 002, 014, 042, 013, 016, 034, 012, 022, 007, 022, 044, 023, 022, 001, 005, 062, 006, 074, 064 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 065, 023, 065, 063, 012, 060, 055, 014, 005, 003, 003, 006, 044, 004, 006, 073, 016, 076, 055, 006, 030, 064, 013, 026, 065, 077, 020, 002 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 026, 055, 065, 012, 051, 067, 043, 012, 026, 035, 027, 015, 075, 055, 042, 067, 050, 045, 056, 061, 042, 051, 011, 053, 007, 024, 013, 034 } }; + /** * @brief Define a reed-solomon codec. * @param TYPE Data type primitive @@ -129,6 +166,42 @@ class RS6355 : public __RS_63(55) { }; RS6355 rs24169; // 8 bit / 4 bit corrections max / 2 bytes total +/** + * @brief Implements Reed-Solomon (52,30,23) + */ +class RS6341 : public __RS_63(41) { +public: + RS6341() : __RS_63(41)() { /* stub */ } +}; +RS6341 rs523023; + +/** + * @brief Implements Reed-Solomon (46,26,21) + */ +class RS6343 : public __RS_63(43) { +public: + RS6343() : __RS_63(43)() { /* stub */ } +}; +RS6343 rs462621; + +/** + * @brief Implements Reed-Solomon (45,26,20) + */ +class RS6344 : public __RS_63(44) { +public: + RS6344() : __RS_63(44)() { /* stub */ } +}; +RS6344 rs452620; + +/** + * @brief Implements Reed-Solomon (44,16,29) + */ +class RS6335 : public __RS_63(35) { +public: + RS6335() : __RS_63(35)() { /* stub */ } +}; +RS6335 rs441629; + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -291,6 +364,206 @@ void RS634717::encode362017(uint8_t* data) Utils::hex2Bin(codeword[i], data, offset); } +/* Decode RS (52,30,23) FEC. */ + +bool RS634717::decode523023(uint8_t* data) +{ + assert(data != nullptr); + + std::vector codeword(63, 0); + + uint32_t offset = 0U; + for (uint32_t i = 0U; i < 52U; i++, offset += 6) + codeword[11 + i] = Utils::bin2Hex(data, offset); + + int ec = rs523023.decode(codeword); +#if DEBUG_RS + LogDebugEx(LOG_HOST, "RS634717::decode523023()", "errors = %d\n", ec); +#endif + offset = 0U; + for (uint32_t i = 0U; i < 30U; i++, offset += 6) + Utils::hex2Bin(codeword[11 + i], data, offset); + + if ((ec == -1) || (ec >= 8)) { + return false; + } + + return true; +} + +/* Encode RS (52,30,23) FEC. */ + +void RS634717::encode523023(uint8_t* data) +{ + assert(data != nullptr); + + uint8_t codeword[52U]; + + for (uint32_t i = 0U; i < 52U; i++) { + codeword[i] = 0x00U; + + uint32_t offset = 0U; + for (uint32_t j = 0U; j < 30U; j++, offset += 6U) { + uint8_t hexbit = Utils::bin2Hex(data, offset); + codeword[i] ^= gf6Mult(hexbit, ENCODE_MATRIX_633529[j][i]); + } + } + + uint32_t offset = 0U; + for (uint32_t i = 0U; i < 52U; i++, offset += 6U) + Utils::hex2Bin(codeword[i], data, offset); +} + +/* Decode RS (46,26,21) FEC. */ + +bool RS634717::decode462621(uint8_t* data) +{ + assert(data != nullptr); + + std::vector codeword(63, 0); + + uint32_t offset = 0U; + for (uint32_t i = 0U; i < 46U; i++, offset += 6) + codeword[17 + i] = Utils::bin2Hex(data, offset); + + int ec = rs462621.decode(codeword); +#if DEBUG_RS + LogDebugEx(LOG_HOST, "RS634717::decode462621()", "errors = %d\n", ec); +#endif + offset = 0U; + for (uint32_t i = 0U; i < 26U; i++, offset += 6) + Utils::hex2Bin(codeword[17 + i], data, offset); + + if ((ec == -1) || (ec >= 8)) { + return false; + } + + return true; +} + +/* Encode RS (46,26,21) FEC. */ + +void RS634717::encode462621(uint8_t* data) +{ + assert(data != nullptr); + + uint8_t codeword[46U]; + + for (uint32_t i = 0U; i < 46U; i++) { + codeword[i] = 0x00U; + + uint32_t offset = 0U; + for (uint32_t j = 0U; j < 26U; j++, offset += 6U) { + uint8_t hexbit = Utils::bin2Hex(data, offset); + codeword[i] ^= gf6Mult(hexbit, ENCODE_MATRIX_633529[j][i]); + } + } + + uint32_t offset = 0U; + for (uint32_t i = 0U; i < 46U; i++, offset += 6U) + Utils::hex2Bin(codeword[i], data, offset); +} + +/* Decode RS (45,26,20) FEC. */ + +bool RS634717::decode452620(uint8_t* data) +{ + assert(data != nullptr); + + std::vector codeword(63, 0); + + uint32_t offset = 0U; + for (uint32_t i = 0U; i < 45U; i++, offset += 6) + codeword[18 + i] = Utils::bin2Hex(data, offset); + + int ec = rs452620.decode(codeword); +#if DEBUG_RS + LogDebugEx(LOG_HOST, "RS634717::decode462620()", "errors = %d\n", ec); +#endif + offset = 0U; + for (uint32_t i = 0U; i < 26U; i++, offset += 6) + Utils::hex2Bin(codeword[18 + i], data, offset); + + if ((ec == -1) || (ec >= 8)) { + return false; + } + + return true; +} + +/* Encode RS (45,26,20) FEC. */ + +void RS634717::encode452620(uint8_t* data) +{ + assert(data != nullptr); + + uint8_t codeword[45U]; + + for (uint32_t i = 0U; i < 45U; i++) { + codeword[i] = 0x00U; + + uint32_t offset = 0U; + for (uint32_t j = 0U; j < 26U; j++, offset += 6U) { + uint8_t hexbit = Utils::bin2Hex(data, offset); + codeword[i] ^= gf6Mult(hexbit, ENCODE_MATRIX_633529[j][i]); + } + } + + uint32_t offset = 0U; + for (uint32_t i = 0U; i < 45U; i++, offset += 6U) + Utils::hex2Bin(codeword[i], data, offset); +} + +/* Decode RS (44,16,29) FEC. */ + +bool RS634717::decode441629(uint8_t* data) +{ + assert(data != nullptr); + + std::vector codeword(63, 0); + + uint32_t offset = 0U; + for (uint32_t i = 0U; i < 44U; i++, offset += 6) + codeword[19 + i] = Utils::bin2Hex(data, offset); + + int ec = rs452620.decode(codeword); +#if DEBUG_RS + LogDebugEx(LOG_HOST, "RS634717::decode462620()", "errors = %d\n", ec); +#endif + offset = 0U; + for (uint32_t i = 0U; i < 16U; i++, offset += 6) + Utils::hex2Bin(codeword[19 + i], data, offset); + + if ((ec == -1) || (ec >= 8)) { + return false; + } + + return true; +} + +/* Encode RS (44,16,29) FEC. */ + +void RS634717::encode441629(uint8_t* data) +{ + assert(data != nullptr); + + uint8_t codeword[44U]; + + for (uint32_t i = 0U; i < 44U; i++) { + codeword[i] = 0x00U; + + uint32_t offset = 0U; + for (uint32_t j = 0U; j < 16U; j++, offset += 6U) { + uint8_t hexbit = Utils::bin2Hex(data, offset); + codeword[i] ^= gf6Mult(hexbit, ENCODE_MATRIX_633529[j][i]); + } + } + + uint32_t offset = 0U; + for (uint32_t i = 0U; i < 44U; i++, offset += 6U) + Utils::hex2Bin(codeword[i], data, offset); +} + // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- diff --git a/src/common/edac/RS634717.h b/src/common/edac/RS634717.h index bfdb654e..c9e0d2b0 100644 --- a/src/common/edac/RS634717.h +++ b/src/common/edac/RS634717.h @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2016 Jonathan Naylor, G4KLX - * Copyright (C) 2017,2023 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017,2023,2025 Bryan Biedenkapp, N2PLL * */ /** @@ -27,8 +27,8 @@ namespace edac /** * @brief Implements Reed-Solomon (63,47,17). Which is also used to implement - * Reed-Solomon (24,12,13), (24,16,9) and (36,20,17) forward - * error correction. + * Reed-Solomon (24,12,13), (24,16,9), (36,20,17), (52,30,23), (46,26,21), + * (45,26,20). (44,16,29) forward error correction. * @ingroup edac */ class HOST_SW_API RS634717 { @@ -78,6 +78,51 @@ namespace edac */ void encode362017(uint8_t* data); + /** + * @brief Decode RS (52,30,23) FEC. + * @param data Reed-Solomon FEC encoded data to decode. + * @returns bool True, if data was decoded, otherwise false. + */ + bool decode523023(uint8_t* data); + /** + * @brief Encode RS (52,30,23) FEC. + * @param data Raw data to encode with Reed-Solomon FEC. + */ + void encode523023(uint8_t* data); + /** + * @brief Decode RS (46,26,21) FEC. + * @param data Reed-Solomon FEC encoded data to decode. + * @returns bool True, if data was decoded, otherwise false. + */ + bool decode462621(uint8_t* data); + /** + * @brief Encode RS (46,26,21) FEC. + * @param data Raw data to encode with Reed-Solomon FEC. + */ + void encode462621(uint8_t* data); + /** + * @brief Decode RS (45,26,20) FEC. + * @param data Reed-Solomon FEC encoded data to decode. + * @returns bool True, if data was decoded, otherwise false. + */ + bool decode452620(uint8_t* data); + /** + * @brief Encode RS (45,26,20) FEC. + * @param data Raw data to encode with Reed-Solomon FEC. + */ + void encode452620(uint8_t* data); + /** + * @brief Decode RS (44,16,29) FEC. + * @param data Reed-Solomon FEC encoded data to decode. + * @returns bool True, if data was decoded, otherwise false. + */ + bool decode441629(uint8_t* data); + /** + * @brief Encode RS (44,16,29) FEC. + * @param data Raw data to encode with Reed-Solomon FEC. + */ + void encode441629(uint8_t* data); + private: /** * @brief GF(2 ^ 6) multiply (for Reed-Solomon encoder). diff --git a/src/common/network/RTPFNEHeader.h b/src/common/network/RTPFNEHeader.h index c42abe97..3b842276 100644 --- a/src/common/network/RTPFNEHeader.h +++ b/src/common/network/RTPFNEHeader.h @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2023,2024,2025 Bryan Biedenkapp, N2PLL * */ /** @@ -88,6 +88,7 @@ namespace network PROTOCOL_SUBFUNC_DMR = 0x00U, //!< DMR PROTOCOL_SUBFUNC_P25 = 0x01U, //!< P25 PROTOCOL_SUBFUNC_NXDN = 0x02U, //!< NXDN + PROTOCOL_SUBFUNC_P25_P2 = 0x03U, //!< P25 Phase 2 PROTOCOL_SUBFUNC_ANALOG = 0x0FU, //!< Analog MASTER_SUBFUNC_WL_RID = 0x00U, //!< Whitelist RIDs diff --git a/src/common/p25/P25Defines.h b/src/common/p25/P25Defines.h index 0dab98b6..fa8ad957 100644 --- a/src/common/p25/P25Defines.h +++ b/src/common/p25/P25Defines.h @@ -63,14 +63,45 @@ namespace p25 const uint32_t P25_TDULC_FRAME_LENGTH_BYTES = 54U; const uint32_t P25_TDULC_FRAME_LENGTH_BITS = P25_TDULC_FRAME_LENGTH_BYTES * 8U; + const uint32_t P25_P2_FRAME_LENGTH_BYTES = 45U; + const uint32_t P25_P2_FRAME_LENGTH_BITS = P25_P2_FRAME_LENGTH_BYTES * 8U; + const uint32_t P25_NID_LENGTH_BYTES = 8U; const uint32_t P25_NID_LENGTH_BITS = P25_NID_LENGTH_BYTES * 8U; + // TIA-102.BAAA-B Section 7.3 + // 5 5 7 5 F 5 F F 7 7 F F + // 01 01 01 01 01 11 01 01 11 11 01 01 11 11 11 11 01 11 01 11 11 11 11 11 + // +3 +3 +3 +3 +3 -3 +3 +3 -3 -3 +3 +3 -3 -3 -3 -3 +3 -3 +3 -3 -3 -3 -3 -3 const uint8_t P25_SYNC_BYTES[] = { 0x55U, 0x75U, 0xF5U, 0xFFU, 0x77U, 0xFFU }; const uint32_t P25_SYNC_LENGTH_BYTES = 6U; const uint32_t P25_SYNC_LENGTH_BITS = P25_SYNC_LENGTH_BYTES * 8U; const uint8_t P25_START_SYNC = 0x5FU; + // TIA-102.BBAC-A Section 5.1 + // 5 F 5 7 7 D 5 7 7 F F + // 01 01 11 11 01 01 01 11 01 11 11 01 01 01 01 11 01 11 11 11 11 11 + // +3 +3 -3 -3 +3 +3 +3 -3 +3 -3 -3 +3 +3 +3 +3 -3 +3 -3 -3 -3 -3 -3 + const uint8_t P25_P2_IEMI_SYNC_BYTES[] = { 0x05U, 0xF5U, 0x77U, 0xD5U, 0x77U, 0xFFU }; + const uint8_t P25_P2_IEMI_SYNC_LENGTH_BYTES = 6U; + const uint8_t P25_P2_IEMI_SYNC_LENGTH_BITS = 44U; + + // TIA-102.BBAC-A Section 5.1 + // 3 F D 5 5 D D F 5 F 5 + // 11 11 11 11 01 01 01 01 01 11 01 11 01 11 11 01 01 11 11 01 01 + // -3 -3 -3 -3 +3 +3 +3 +3 +3 -3 +3 -3 +3 -3 -3 +3 +3 -3 -3 +3 +3 + const uint8_t P25_P2_OEMI_SYNC_BYTES[] = { 0x03U, 0xFDU, 0x55U, 0xDDU, 0xF5U, 0xF5U }; + const uint8_t P25_P2_OEMI_SYNC_LENGTH_BYTES = 6U; + const uint8_t P25_P2_OEMI_SYNC_LENGTH_BITS = 42U; + + // TIA-102.BBAC-A Section 5.1 + // 5 7 5 D 5 7 F 7 F F + // 01 01 01 11 01 01 11 01 01 01 01 11 11 11 01 11 11 11 11 11 + // +3 +3 +3 -3 +3 +3 -3 +3 +3 +3 +3 -3 -3 -3 +3 -3 -3 -3 -3 -3 + const uint8_t P25_P2_ISCH_SYNC_BYTES[] = { 0x57U, 0x5DU, 0x57U, 0xF7U, 0xFFU }; + const uint8_t P25_P2_ISCH_SYNC_LENGTH_BYTES = 5U; + const uint8_t P25_P2_ISCH_SYNC_LENGTH_BITS = 38U; + const uint32_t P25_PREAMBLE_LENGTH_BYTES = P25_SYNC_LENGTH_BYTES + P25_NID_LENGTH_BYTES; const uint32_t P25_PREAMBLE_LENGTH_BITS = P25_SYNC_LENGTH_BITS + P25_NID_LENGTH_BITS; @@ -103,6 +134,7 @@ namespace p25 const uint32_t MI_LENGTH_BYTES = 9U; const uint32_t RAW_IMBE_LENGTH_BYTES = 11U; + const uint32_t RAW_AMBE_LENGTH_BYTES = 7U; const uint32_t P25_SS0_START = 70U; const uint32_t P25_SS1_START = 71U; @@ -805,6 +837,7 @@ namespace p25 }; } + // TIA-102.BAAC-D Section 2.11 /** @brief Data Unit ID(s) */ namespace DUID { /** @brief Data Unit ID(s) */ @@ -821,6 +854,19 @@ namespace p25 }; } + /** @brief Phase 2 Data Unit ID(s) */ + namespace P2_DUID { + /** @brief Data Unit ID(s) */ + enum E : uint8_t { + VTCH_4V = 0x00U, //!< Inbound/Outbound 4V + SACCH_SCRAMBLED = 0x03U, //!< SACCH Scrambled + VTCH_2V = 0x06U, //!< Inbound/Outbound 2V + FACCH_SCRAMBLED = 0x09U, //!< FACCH Scrambled + SACCH_UNSCRAMBLED = 0x0CU, //!< SACCH Unscrambled + FACCH_UNSCRAMBLED = 0x0FU //!< FACCH Unscrambled + }; + } + /** @} */ #define P25_HDU_STR "P25, HDU (Header Data Unit)" @@ -834,6 +880,13 @@ namespace p25 #define P25_TDULC_STR "P25, TDULC (Terminator Data Unit with Link Control)" #define P25_KMM_STR "P25, KMM (Key Management Message)" + + #define P25_P2_VTCH_4V_STR "P25 Phase 2, VTCH 4V (Voice Traffic Channel 4Vx)" + #define P25_P2_SACCH_SCRAMBLED_STR "P25 Phase 2, SACCH (Slow Associated Control Channel Scrambled)" + #define P25_P2_VTCH_2V_STR "P25 Phase 2, VTCH 2V (Voice Traffic Channel 2V)" + #define P25_P2_FACCH_SCRAMBLED_STR "P25 Phase 2, FACCH (Fast Associated Control Channel Scrambled)" + #define P25_P2_SACCH_UNSCRAMBLED_STR "P25 Phase 2, SACCH (Slow Associated Control Channel Unscrambled)" + #define P25_P2_FACCH_UNSCRAMBLED_STR "P25 Phase 2, FACCH (Fast Associated Control Channel Unscrambled)" } // namespace defines } // namespace p25 diff --git a/src/common/p25/dfsi/DFSIDefines.h b/src/common/p25/dfsi/DFSIDefines.h index 16d86f5a..29a73283 100644 --- a/src/common/p25/dfsi/DFSIDefines.h +++ b/src/common/p25/dfsi/DFSIDefines.h @@ -75,6 +75,9 @@ namespace p25 const uint32_t DFSI_LDU2_VOICE17_FRAME_LENGTH_BYTES = 17U; const uint32_t DFSI_LDU2_VOICE18_FRAME_LENGTH_BYTES = 16U; + const uint32_t DFSI_P2_4V_FRAME_LENGTH_BYTES = 39U; + const uint32_t DFSI_P2_2V_FRAME_LENGTH_BYTES = 32U; + /** * @addtogroup p25_dfsi * @{ @@ -149,7 +152,13 @@ namespace p25 MOT_PDU_CONF_END = 0x8DU, //!< Motorola/V.24 PDU (Confirmed Block End) MOT_PDU_SINGLE_CONF = 0x8FU, //!< Motorola/V.24 PDU (Single Confirmed Block) - MOT_TSBK = 0xA1U //!< Motorola/V.24 TSBK (Single Block) + MOT_TSBK = 0xA1U, //!< Motorola/V.24 TSBK (Single Block) + + P2_4VA = 0xF0U, //!< AMBE - Inbound/Outbound 4Va (P25 Phase 2) + P2_4VB = 0xF1U, //!< AMBE - Inbound/Outbound 4Vb (P25 Phase 2) + P2_4VC = 0xF2U, //!< AMBE - Inbound/Outbound 4Vc (P25 Phase 2) + P2_4VD = 0xF3U, //!< AMBE - Inbound/Outbound 4Vd (P25 Phase 2) + P2_2V = 0xF4U, //!< AMBE - Inbound/Outbound 2V (P25 Phase 2) }; } diff --git a/src/common/p25/dfsi/LC.cpp b/src/common/p25/dfsi/LC.cpp index eaa3ee3a..3e688e66 100644 --- a/src/common/p25/dfsi/LC.cpp +++ b/src/common/p25/dfsi/LC.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2022,2024,2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" @@ -651,6 +651,233 @@ void LC::encodeLDU2(uint8_t* data, const uint8_t* imbe) ::memcpy(data, dfsiFrame, frameLength); } +/* Decode a Phase 2 4Vx voice frame. */ + +bool LC::decodeP2_4V(const uint8_t* data, uint8_t* ambe1, uint8_t* ambe2, + uint8_t* ambe3, uint8_t* ambe4) +{ + assert(data != nullptr); + assert(ambe1 != nullptr); + assert(ambe2 != nullptr); + assert(ambe3 != nullptr); + assert(ambe4 != nullptr); + + m_frameType = (DFSIFrameType::E)data[0U]; // Frame Type + + ::memcpy(ambe1, data + 1U, RAW_AMBE_LENGTH_BYTES); // AMBE1 + ::memcpy(ambe2, data + 8U, RAW_AMBE_LENGTH_BYTES); // AMBE2 + ::memcpy(ambe3, data + 15U, RAW_AMBE_LENGTH_BYTES); // AMBE3 + ::memcpy(ambe4, data + 22U, RAW_AMBE_LENGTH_BYTES); // AMBE4 + + uint8_t slotNo = data[38U] & 0x0FU; // Slot Number + m_control->setSlotNo(slotNo); + + // different frame types mean different things + switch (m_frameType) + { + case DFSIFrameType::P2_4VA: + { + m_control->setAlgId(data[34U]); // Algorithm ID + uint32_t kid = (data[35U] << 8) | (data[36U] << 0); // Key ID + m_control->setKId(kid); + } + break; + case DFSIFrameType::P2_4VB: + { + m_mi[0U] = data[34U]; // Message Indicator + m_mi[1U] = data[35U]; + m_mi[2U] = data[36U]; + } + break; + case DFSIFrameType::P2_4VC: + { + m_mi[3U] = data[34U]; // Message Indicator + m_mi[4U] = data[35U]; + m_mi[5U] = data[36U]; + } + break; + case DFSIFrameType::P2_4VD: + { + m_mi[6U] = data[34U]; // Message Indicator + m_mi[7U] = data[35U]; + m_mi[8U] = data[36U]; + m_control->setMI(m_mi); + } + break; + + default: + { + LogError(LOG_P25, "LC::decodeP2_4V(), invalid frame type, frameType = $%02X", m_frameType); + return false; + } + break; + } + + return true; +} + +/* Encode a Phase 2 4Vx voice frame. */ + +void LC::encodeP2_4V(uint8_t* data, const uint8_t* ambe1, const uint8_t* ambe2, const uint8_t* ambe3, const uint8_t* ambe4) +{ + assert(data != nullptr); + assert(ambe1 != nullptr); + assert(ambe2 != nullptr); + assert(ambe3 != nullptr); + assert(ambe4 != nullptr); + + // generate MI data + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + m_control->getMI(mi); + + DECLARE_UINT8_ARRAY(dfsiFrame, DFSI_P2_4V_FRAME_LENGTH_BYTES); + + dfsiFrame[0U] = m_frameType; // Frame Type + + ::memcpy(dfsiFrame + 1U, ambe1, RAW_AMBE_LENGTH_BYTES); // AMBE1 + ::memcpy(dfsiFrame + 8U, ambe2, RAW_AMBE_LENGTH_BYTES); // AMBE2 + ::memcpy(dfsiFrame + 15U, ambe3, RAW_AMBE_LENGTH_BYTES); // AMBE3 + ::memcpy(dfsiFrame + 22U, ambe4, RAW_AMBE_LENGTH_BYTES); // AMBE4 + + uint8_t slotNo = m_control->getSlotNo(); + dfsiFrame[38U] = slotNo & 0x0FU; // Slot Number + + uint8_t rs[P25_LDU_LC_FEC_LENGTH_BYTES]; + ::memset(rs, 0x00U, P25_LDU_LC_FEC_LENGTH_BYTES); + + for (uint32_t i = 0; i < MI_LENGTH_BYTES; i++) + rs[i] = mi[i]; // Message Indicator + + rs[9U] = m_control->getAlgId(); // Algorithm ID + rs[10U] = (m_control->getKId() >> 8) & 0xFFU; // Key ID + rs[11U] = (m_control->getKId() >> 0) & 0xFFU; // ... + + // different frame types mean different things + switch (m_frameType) + { + case DFSIFrameType::P2_4VA: + { + dfsiFrame[34U] = rs[9U]; // Algorithm ID + dfsiFrame[35U] = rs[10U]; // Key ID + dfsiFrame[36U] = rs[11U]; + } + break; + case DFSIFrameType::P2_4VB: + { + dfsiFrame[34U] = rs[0U]; // Message Indicator + dfsiFrame[35U] = rs[1U]; + dfsiFrame[36U] = rs[2U]; + } + break; + case DFSIFrameType::P2_4VC: + { + dfsiFrame[34U] = rs[3U]; // Message Indicator + dfsiFrame[35U] = rs[4U]; + dfsiFrame[36U] = rs[5U]; + } + break; + case DFSIFrameType::P2_4VD: + { + dfsiFrame[34U] = rs[6U]; // Message Indicator + dfsiFrame[35U] = rs[7U]; + dfsiFrame[36U] = rs[8U]; + } + break; + + default: + { + LogError(LOG_P25, "LC::encodeP2_4V(), invalid frame type, frameType = $%02X", m_frameType); + return; + } + break; + } + +#if DEBUG_P25_DFSI + LogDebugEx(LOG_P25, "dfsi::LC::encodeP2_4V()", "frameType = $%02X", m_frameType); + Utils::dump(2U, "P25, dfsi::LC::encodeP2_4V(), DFSI Phase 2 4Vx Frame", dfsiFrame, DFSI_P2_4V_FRAME_LENGTH_BYTES); +#endif + + ::memcpy(data, dfsiFrame, DFSI_P2_4V_FRAME_LENGTH_BYTES); +} + +/* Decode a Phase 2 2V voice frame.*/ + +bool LC::decodeP2_2V(const uint8_t* data, uint8_t* ambe1, uint8_t* ambe2) +{ + assert(data != nullptr); + assert(ambe1 != nullptr); + assert(ambe2 != nullptr); + + m_frameType = (DFSIFrameType::E)data[0U]; // Frame Type + + ::memcpy(ambe1, data + 1U, RAW_AMBE_LENGTH_BYTES); // AMBE1 + ::memcpy(ambe2, data + 8U, RAW_AMBE_LENGTH_BYTES); // AMBE2 + + uint8_t essErrorCnt = data[17U]; + + if (essErrorCnt == 0U) { + m_control->setAlgId(data[18U]); // Algorithm ID + uint32_t kid = (data[19U] << 8) | (data[20U] << 0); // Key ID + m_control->setKId(kid); + + ::memcpy(m_mi, data + 21U, MI_LENGTH_BYTES); // Message Indicator + m_control->setMI(m_mi); + } + + uint8_t slotNo = data[31U] & 0x0FU; // Slot Number + m_control->setSlotNo(slotNo); + + return false; +} + +/* Encode a Phase 2 2V voice frame.*/ + +void LC::encodeP2_2V(uint8_t* data, const uint8_t* ambe1, const uint8_t* ambe2) +{ + assert(data != nullptr); + assert(ambe1 != nullptr); + assert(ambe2 != nullptr); + + // generate MI data + uint8_t mi[MI_LENGTH_BYTES]; + ::memset(mi, 0x00U, MI_LENGTH_BYTES); + m_control->getMI(mi); + + DECLARE_UINT8_ARRAY(dfsiFrame, DFSI_P2_2V_FRAME_LENGTH_BYTES); + + dfsiFrame[0U] = m_frameType; // Frame Type + + ::memcpy(dfsiFrame + 1U, ambe1, RAW_AMBE_LENGTH_BYTES); // AMBE1 + ::memcpy(dfsiFrame + 8U, ambe2, RAW_AMBE_LENGTH_BYTES); // AMBE2 + + uint8_t slotNo = m_control->getSlotNo(); + dfsiFrame[31U] = slotNo & 0x0FU; // Slot Number + + uint8_t rs[P25_LDU_LC_FEC_LENGTH_BYTES]; + ::memset(rs, 0x00U, P25_LDU_LC_FEC_LENGTH_BYTES); + + for (uint32_t i = 0; i < MI_LENGTH_BYTES; i++) + rs[i] = mi[i]; // Message Indicator + + rs[9U] = m_control->getAlgId(); // Algorithm ID + rs[10U] = (m_control->getKId() >> 8) & 0xFFU; // Key ID + rs[11U] = (m_control->getKId() >> 0) & 0xFFU; // ... + + dfsiFrame[18U] = rs[9U]; // Algorithm ID + dfsiFrame[19U] = rs[10U]; // Key ID + dfsiFrame[20U] = rs[11U]; + + ::memcpy(dfsiFrame + 21U, mi, MI_LENGTH_BYTES); // Message Indicator + +#if DEBUG_P25_DFSI + LogDebugEx(LOG_P25, "dfsi::LC::encodeP2_2V()", "frameType = $%02X", m_frameType); + Utils::dump(2U, "P25, dfsi::LC::encodeP2_2V(), DFSI Phase 2 2V Frame", dfsiFrame, DFSI_P2_2V_FRAME_LENGTH_BYTES); +#endif + + ::memcpy(data, dfsiFrame, DFSI_P2_2V_FRAME_LENGTH_BYTES); +} + // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- diff --git a/src/common/p25/dfsi/LC.h b/src/common/p25/dfsi/LC.h index 7af0492b..b1f498d2 100644 --- a/src/common/p25/dfsi/LC.h +++ b/src/common/p25/dfsi/LC.h @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2022,2024,2025 Bryan Biedenkapp, N2PLL * */ /** @@ -98,6 +98,44 @@ namespace p25 */ void encodeLDU2(uint8_t* data, const uint8_t* imbe); + /** + * @brief Decode a Phase 2 4Vx voice frame. + * @param data Buffer containing the P2 4V frame to decode. + * @param ambe1 Raw AMBE from P2 4V frame. + * @param ambe2 Raw AMBE from P2 4V frame. + * @param ambe3 Raw AMBE from P2 4V frame. + * @param ambe4 Raw AMBE from P2 4V frame. + * @return bool True, if P2 4V frame decoded, otherwise false. + */ + bool decodeP2_4V(const uint8_t* data, uint8_t* ambe1, uint8_t* ambe2, + uint8_t* ambe3, uint8_t* ambe4); + /** + * @brief Encode a Phase 2 4Vx voice frame. + * @param data Buffer to encode a P2 4V frame. + * @param ambe1 Raw AMBE from P2 4V frame. + * @param ambe2 Raw AMBE from P2 4V frame. + * @param ambe3 Raw AMBE from P2 4V frame. + * @param ambe4 Raw AMBE from P2 4V frame. + */ + void encodeP2_4V(uint8_t* data, const uint8_t* ambe1, const uint8_t* ambe2, + const uint8_t* ambe3, const uint8_t* ambe4); + + /** + * @brief Decode a Phase 2 2V voice frame. + * @param data Buffer containing the P2 2V frame to decode. + * @param ambe1 Raw AMBE from P2 2V frame. + * @param ambe2 Raw AMBE from P2 2V frame. + * @return bool True, if P2 2V frame decoded, otherwise false. + */ + bool decodeP2_2V(const uint8_t* data, uint8_t* ambe1, uint8_t* ambe2); + /** + * @brief Encode a Phase 2 2V voice frame. + * @param data Buffer to encode a P2 2V frame. + * @param ambe1 Raw AMBE from P2 2V frame. + * @param ambe2 Raw AMBE from P2 2V frame. + */ + void encodeP2_2V(uint8_t* data, const uint8_t* ambe1, const uint8_t* ambe2); + public: // Common Data /** diff --git a/src/common/p25/lc/LC.cpp b/src/common/p25/lc/LC.cpp index df443fb7..931f7e0e 100644 --- a/src/common/p25/lc/LC.cpp +++ b/src/common/p25/lc/LC.cpp @@ -62,6 +62,7 @@ LC::LC() : m_group(true), m_algId(ALGO_UNENCRYPT), m_kId(0U), + m_slotNo(0U), m_rsValue(0U), m_rs(), m_encryptOverride(false), @@ -838,6 +839,8 @@ void LC::copy(const LC& data) m_callTimer = data.m_callTimer; + m_slotNo = data.m_slotNo; + m_rsValue = data.m_rsValue; m_algId = data.m_algId; diff --git a/src/common/p25/lc/LC.h b/src/common/p25/lc/LC.h index 37960a84..d92d9dcb 100644 --- a/src/common/p25/lc/LC.h +++ b/src/common/p25/lc/LC.h @@ -249,6 +249,13 @@ namespace p25 DECLARE_PROPERTY(uint32_t, kId, KId); /** @} */ + /** @name Phase 2 Data */ + /** + * @brief Slot Number. + */ + DECLARE_PROPERTY(uint8_t, slotNo, SlotNo); + /** @} */ + /** @name Packed RS Data */ /** * @brief Packed RS Data. From 6e8cb203951b3ab5a7277bb85bd97f4a5f9e7461 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 18 Nov 2025 17:15:41 -0500 Subject: [PATCH 171/200] decomplicate foundational changes to RS coders for Phase 2; --- src/common/edac/RS634717.cpp | 207 +++-------------------------------- src/common/edac/RS634717.h | 41 +------ 2 files changed, 19 insertions(+), 229 deletions(-) diff --git a/src/common/edac/RS634717.cpp b/src/common/edac/RS634717.cpp index 0eb3b42d..6c346cef 100644 --- a/src/common/edac/RS634717.cpp +++ b/src/common/edac/RS634717.cpp @@ -167,40 +167,13 @@ class RS6355 : public __RS_63(55) { RS6355 rs24169; // 8 bit / 4 bit corrections max / 2 bytes total /** - * @brief Implements Reed-Solomon (52,30,23) - */ -class RS6341 : public __RS_63(41) { -public: - RS6341() : __RS_63(41)() { /* stub */ } -}; -RS6341 rs523023; - -/** - * @brief Implements Reed-Solomon (46,26,21) - */ -class RS6343 : public __RS_63(43) { -public: - RS6343() : __RS_63(43)() { /* stub */ } -}; -RS6343 rs462621; - -/** - * @brief Implements Reed-Solomon (45,26,20) - */ -class RS6344 : public __RS_63(44) { -public: - RS6344() : __RS_63(44)() { /* stub */ } -}; -RS6344 rs452620; - -/** - * @brief Implements Reed-Solomon (44,16,29) + * @brief Implements Reed-Solomon (63,35,29) */ class RS6335 : public __RS_63(35) { public: RS6335() : __RS_63(35)() { /* stub */ } }; -RS6335 rs441629; +RS6355 rs633529; // 28 bit / 14 bit corrections max / 4 bytes total // --------------------------------------------------------------------------- // Public Class Members @@ -364,175 +337,25 @@ void RS634717::encode362017(uint8_t* data) Utils::hex2Bin(codeword[i], data, offset); } -/* Decode RS (52,30,23) FEC. */ - -bool RS634717::decode523023(uint8_t* data) -{ - assert(data != nullptr); - - std::vector codeword(63, 0); - - uint32_t offset = 0U; - for (uint32_t i = 0U; i < 52U; i++, offset += 6) - codeword[11 + i] = Utils::bin2Hex(data, offset); - - int ec = rs523023.decode(codeword); -#if DEBUG_RS - LogDebugEx(LOG_HOST, "RS634717::decode523023()", "errors = %d\n", ec); -#endif - offset = 0U; - for (uint32_t i = 0U; i < 30U; i++, offset += 6) - Utils::hex2Bin(codeword[11 + i], data, offset); - - if ((ec == -1) || (ec >= 8)) { - return false; - } - - return true; -} - -/* Encode RS (52,30,23) FEC. */ - -void RS634717::encode523023(uint8_t* data) -{ - assert(data != nullptr); - - uint8_t codeword[52U]; - - for (uint32_t i = 0U; i < 52U; i++) { - codeword[i] = 0x00U; - - uint32_t offset = 0U; - for (uint32_t j = 0U; j < 30U; j++, offset += 6U) { - uint8_t hexbit = Utils::bin2Hex(data, offset); - codeword[i] ^= gf6Mult(hexbit, ENCODE_MATRIX_633529[j][i]); - } - } - - uint32_t offset = 0U; - for (uint32_t i = 0U; i < 52U; i++, offset += 6U) - Utils::hex2Bin(codeword[i], data, offset); -} - -/* Decode RS (46,26,21) FEC. */ - -bool RS634717::decode462621(uint8_t* data) -{ - assert(data != nullptr); - - std::vector codeword(63, 0); - - uint32_t offset = 0U; - for (uint32_t i = 0U; i < 46U; i++, offset += 6) - codeword[17 + i] = Utils::bin2Hex(data, offset); - - int ec = rs462621.decode(codeword); -#if DEBUG_RS - LogDebugEx(LOG_HOST, "RS634717::decode462621()", "errors = %d\n", ec); -#endif - offset = 0U; - for (uint32_t i = 0U; i < 26U; i++, offset += 6) - Utils::hex2Bin(codeword[17 + i], data, offset); - - if ((ec == -1) || (ec >= 8)) { - return false; - } - - return true; -} - -/* Encode RS (46,26,21) FEC. */ - -void RS634717::encode462621(uint8_t* data) -{ - assert(data != nullptr); - - uint8_t codeword[46U]; - - for (uint32_t i = 0U; i < 46U; i++) { - codeword[i] = 0x00U; - - uint32_t offset = 0U; - for (uint32_t j = 0U; j < 26U; j++, offset += 6U) { - uint8_t hexbit = Utils::bin2Hex(data, offset); - codeword[i] ^= gf6Mult(hexbit, ENCODE_MATRIX_633529[j][i]); - } - } - - uint32_t offset = 0U; - for (uint32_t i = 0U; i < 46U; i++, offset += 6U) - Utils::hex2Bin(codeword[i], data, offset); -} - -/* Decode RS (45,26,20) FEC. */ +/* Decode RS (63,35,29) FEC. */ -bool RS634717::decode452620(uint8_t* data) +bool RS634717::decode633529(uint8_t* data) { assert(data != nullptr); std::vector codeword(63, 0); uint32_t offset = 0U; - for (uint32_t i = 0U; i < 45U; i++, offset += 6) - codeword[18 + i] = Utils::bin2Hex(data, offset); + for (uint32_t i = 0U; i < 63U; i++, offset += 6) + codeword[i] = Utils::bin2Hex(data, offset); - int ec = rs452620.decode(codeword); + int ec = rs633529.decode(codeword); #if DEBUG_RS - LogDebugEx(LOG_HOST, "RS634717::decode462620()", "errors = %d\n", ec); + LogDebugEx(LOG_HOST, "RS634717::decode633529()", "errors = %d\n", ec); #endif offset = 0U; - for (uint32_t i = 0U; i < 26U; i++, offset += 6) - Utils::hex2Bin(codeword[18 + i], data, offset); - - if ((ec == -1) || (ec >= 8)) { - return false; - } - - return true; -} - -/* Encode RS (45,26,20) FEC. */ - -void RS634717::encode452620(uint8_t* data) -{ - assert(data != nullptr); - - uint8_t codeword[45U]; - - for (uint32_t i = 0U; i < 45U; i++) { - codeword[i] = 0x00U; - - uint32_t offset = 0U; - for (uint32_t j = 0U; j < 26U; j++, offset += 6U) { - uint8_t hexbit = Utils::bin2Hex(data, offset); - codeword[i] ^= gf6Mult(hexbit, ENCODE_MATRIX_633529[j][i]); - } - } - - uint32_t offset = 0U; - for (uint32_t i = 0U; i < 45U; i++, offset += 6U) + for (uint32_t i = 0U; i < 35U; i++, offset += 6) Utils::hex2Bin(codeword[i], data, offset); -} - -/* Decode RS (44,16,29) FEC. */ - -bool RS634717::decode441629(uint8_t* data) -{ - assert(data != nullptr); - - std::vector codeword(63, 0); - - uint32_t offset = 0U; - for (uint32_t i = 0U; i < 44U; i++, offset += 6) - codeword[19 + i] = Utils::bin2Hex(data, offset); - - int ec = rs452620.decode(codeword); -#if DEBUG_RS - LogDebugEx(LOG_HOST, "RS634717::decode462620()", "errors = %d\n", ec); -#endif - offset = 0U; - for (uint32_t i = 0U; i < 16U; i++, offset += 6) - Utils::hex2Bin(codeword[19 + i], data, offset); if ((ec == -1) || (ec >= 8)) { return false; @@ -541,26 +364,26 @@ bool RS634717::decode441629(uint8_t* data) return true; } -/* Encode RS (44,16,29) FEC. */ +/* Encode RS (63,35,29) FEC. */ -void RS634717::encode441629(uint8_t* data) +void RS634717::encode633529(uint8_t* data) { assert(data != nullptr); - uint8_t codeword[44U]; + uint8_t codeword[63U]; - for (uint32_t i = 0U; i < 44U; i++) { + for (uint32_t i = 0U; i < 63U; i++) { codeword[i] = 0x00U; uint32_t offset = 0U; - for (uint32_t j = 0U; j < 16U; j++, offset += 6U) { + for (uint32_t j = 0U; j < 35U; j++, offset += 6U) { uint8_t hexbit = Utils::bin2Hex(data, offset); codeword[i] ^= gf6Mult(hexbit, ENCODE_MATRIX_633529[j][i]); } } uint32_t offset = 0U; - for (uint32_t i = 0U; i < 44U; i++, offset += 6U) + for (uint32_t i = 0U; i < 63U; i++, offset += 6U) Utils::hex2Bin(codeword[i], data, offset); } diff --git a/src/common/edac/RS634717.h b/src/common/edac/RS634717.h index c9e0d2b0..00c2f5ec 100644 --- a/src/common/edac/RS634717.h +++ b/src/common/edac/RS634717.h @@ -79,49 +79,16 @@ namespace edac void encode362017(uint8_t* data); /** - * @brief Decode RS (52,30,23) FEC. + * @brief Decode RS (63,35,29) FEC. * @param data Reed-Solomon FEC encoded data to decode. * @returns bool True, if data was decoded, otherwise false. */ - bool decode523023(uint8_t* data); + bool decode633529(uint8_t* data); /** - * @brief Encode RS (52,30,23) FEC. + * @brief Encode RS (63,35,29) FEC. * @param data Raw data to encode with Reed-Solomon FEC. */ - void encode523023(uint8_t* data); - /** - * @brief Decode RS (46,26,21) FEC. - * @param data Reed-Solomon FEC encoded data to decode. - * @returns bool True, if data was decoded, otherwise false. - */ - bool decode462621(uint8_t* data); - /** - * @brief Encode RS (46,26,21) FEC. - * @param data Raw data to encode with Reed-Solomon FEC. - */ - void encode462621(uint8_t* data); - /** - * @brief Decode RS (45,26,20) FEC. - * @param data Reed-Solomon FEC encoded data to decode. - * @returns bool True, if data was decoded, otherwise false. - */ - bool decode452620(uint8_t* data); - /** - * @brief Encode RS (45,26,20) FEC. - * @param data Raw data to encode with Reed-Solomon FEC. - */ - void encode452620(uint8_t* data); - /** - * @brief Decode RS (44,16,29) FEC. - * @param data Reed-Solomon FEC encoded data to decode. - * @returns bool True, if data was decoded, otherwise false. - */ - bool decode441629(uint8_t* data); - /** - * @brief Encode RS (44,16,29) FEC. - * @param data Raw data to encode with Reed-Solomon FEC. - */ - void encode441629(uint8_t* data); + void encode633529(uint8_t* data); private: /** From caabdbe848de8a0307a5615fe5a82e70a3dae0b3 Mon Sep 17 00:00:00 2001 From: W3AXL <29879554+W3AXL@users.noreply.github.com> Date: Tue, 18 Nov 2025 23:31:02 -0500 Subject: [PATCH 172/200] added new peer config options to FNE REST api --- src/fne/restapi/RESTAPI.cpp | 63 ++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/src/fne/restapi/RESTAPI.cpp b/src/fne/restapi/RESTAPI.cpp index 4ed1586e..17d715e4 100644 --- a/src/fne/restapi/RESTAPI.cpp +++ b/src/fne/restapi/RESTAPI.cpp @@ -1263,12 +1263,18 @@ void RESTAPI::restAPI_GetPeerList(const HTTPPayload& request, HTTPPayload& reply uint32_t peerId = entry.first; std::string peerAlias = entry.second.peerAlias(); - bool peerReplica = entry.second.peerReplica(); bool peerPassword = !entry.second.peerPassword().empty(); // True if password is not empty, otherwise false + bool peerReplica = entry.second.peerReplica(); + bool canRequestKeys = entry.second.canRequestKeys(); + bool canIssueInhibit = entry.second.canIssueInhibit(); + bool hasCallPriority = entry.second.hasCallPriority(); peerObj["peerId"].set(peerId); peerObj["peerAlias"].set(peerAlias); - peerObj["peerReplica"].set(peerReplica); peerObj["peerPassword"].set(peerPassword); + peerObj["peerReplica"].set(peerReplica); + peerObj["canRequestKeys"].set(canRequestKeys); + peerObj["canIssueInhibit"].set(canIssueInhibit); + peerObj["hasCallPriority"].set(hasCallPriority); peers.push_back(json::value(peerObj)); } } @@ -1313,6 +1319,18 @@ void RESTAPI::restAPI_PutPeerAdd(const HTTPPayload& request, HTTPPayload& reply, peerAlias = req["peerAlias"].get(); } + // Get peer password (optional) + std::string peerPassword = ""; + if (req.find("peerPassword") != req.end()) { + // Validate + if (!req["peerPassword"].is()) { + errorPayload(reply, "peerPassword was not a valid string"); + return; + } + // Get + peerPassword = req["peerPassword"].get(); + } + // Get peer link setting (optional) bool peerReplica = false; if (req.find("peerReplica") != req.end()) { @@ -1325,20 +1343,49 @@ void RESTAPI::restAPI_PutPeerAdd(const HTTPPayload& request, HTTPPayload& reply, peerReplica = req["peerReplica"].get(); } - // Get peer password (optional) - std::string peerPassword = ""; - if (req.find("peerPassword") != req.end()) { + // Get canRequestKeys + bool canRequestKeys = false; + if (req.find("canRequestKeys") != req.end()) { // Validate - if (!req["peerPassword"].is()) { - errorPayload(reply, "peerPassword was not a valid string"); + if (!req["canRequestKeys"].is()) { + errorPayload(reply, "canRequestKeys was not a valid boolean"); return; } // Get - peerPassword = req["peerPassword"].get(); + canRequestKeys = req["canRequestKeys"].get(); + } + + // Get canIssueInhibit + bool canIssueInhibit = false; + if (req.find("canIssueInhibit") != req.end()) { + // Validate + if (!req["canIssueInhibit"].is()) { + errorPayload(reply, "canIssueInhibit was not a valid boolean"); + return; + } + // Get + canIssueInhibit = req["canIssueInhibit"].get(); + } + + // Get hasCallPriority + bool hasCallPriority = false; + if (req.find("hasCallPriority") != req.end()) { + // Validate + if (!req["hasCallPriority"].is()) { + errorPayload(reply, "hasCallPriority was not a valid boolean"); + return; + } + // Get + hasCallPriority = req["hasCallPriority"].get(); } PeerId entry = PeerId(peerId, peerAlias, peerPassword, false); + + // Set additional values entry.peerReplica(peerReplica); + entry.canRequestKeys(canRequestKeys); + entry.canIssueInhibit(canIssueInhibit); + entry.hasCallPriority(hasCallPriority); m_peerListLookup->addEntry(peerId, entry); } From 7e7ff91b311294a75e97fde656a04350030586b4 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 20 Nov 2025 09:40:07 -0500 Subject: [PATCH 173/200] add some locks around pkt maps in DiagNetwork; --- src/fne/network/DiagNetwork.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index c5190383..dc05e90a 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -432,6 +432,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) uint32_t decompressedLen = 0U; uint8_t* decompressed = nullptr; + diagNetwork->m_peerReplicaActPkt.lock(); if (pkt.buffer->decode(rawPayload, &decompressed, &decompressedLen)) { std::string payload(decompressed + 8U, decompressed + decompressedLen); @@ -445,6 +446,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } + diagNetwork->m_peerReplicaActPkt.unlock(); diagNetwork->m_peerReplicaActPkt.erase(peerId); break; } @@ -458,6 +460,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } + diagNetwork->m_peerReplicaActPkt.unlock(); diagNetwork->m_peerReplicaActPkt.erase(peerId); break; } @@ -474,6 +477,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } + diagNetwork->m_peerReplicaActPkt.unlock(); diagNetwork->m_peerReplicaActPkt.erase(peerId); } else { pkt.locked = false; @@ -617,6 +621,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) uint32_t decompressedLen = 0U; uint8_t* decompressed = nullptr; + diagNetwork->m_peerTreeListPkt.lock(); if (pkt.buffer->decode(rawPayload, &decompressed, &decompressedLen)) { std::string payload(decompressed + 8U, decompressed + decompressedLen); @@ -630,6 +635,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } + diagNetwork->m_peerTreeListPkt.unlock(); diagNetwork->m_peerTreeListPkt.erase(peerId); break; } @@ -643,6 +649,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } + diagNetwork->m_peerTreeListPkt.unlock(); diagNetwork->m_peerTreeListPkt.erase(peerId); break; } @@ -673,6 +680,7 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) if (decompressed != nullptr) { delete[] decompressed; } + diagNetwork->m_peerTreeListPkt.unlock(); diagNetwork->m_peerTreeListPkt.erase(peerId); } else { pkt.locked = false; From 988c81bc532111e1bbebbedcf17148922b38a62d Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 08:07:36 -0500 Subject: [PATCH 174/200] better document the master peerId importance; --- configs/fne-config.example.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index ae37309a..ea9d1852 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -34,6 +34,8 @@ log: # master: # Network Peer ID + # NOTE: This ID is a uniquely identifying number. It *MUST* be a unique number across any networks this FNE + # instance connects to. Failure to use a unique number *WILL* cause network issues. peerId: 9000100 # Hostname/IP address to listen on (blank for all). address: 0.0.0.0 From 41f0ce525f30c29e19ac162d269083a30eba6b7a Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 09:01:20 -0500 Subject: [PATCH 175/200] ensure buffers are set to nullptr after deleting to prevent double frees; --- src/common/network/udp/Socket.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index 9c1fdeac..461b07b3 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -608,8 +608,10 @@ bool Socket::write(BufferQueue* buffers, ssize_t* lenWritten) noexcept delete[] cryptoBuffer; if (crypted == nullptr) { - if (iov_buffer != nullptr) + if (iov_buffer != nullptr) { delete[] iov_buffer; + iov_buffer = nullptr; + } continue; } @@ -622,7 +624,9 @@ bool Socket::write(BufferQueue* buffers, ssize_t* lenWritten) noexcept // cleanup buffers and replace with new delete[] crypted; + crypted = nullptr; delete[] iov_buffer; + iov_buffer = nullptr; iov_buffer = new uint8_t[cryptedLen + 2U]; ::memcpy(iov_buffer, out, cryptedLen + 2U); iov_length = cryptedLen + 2U; From 9aa605a309d123c59ae1273683df919d85aa4215 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 09:03:54 -0500 Subject: [PATCH 176/200] skip trying to transmit any buffer that is null; --- src/common/network/udp/Socket.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index 461b07b3..6beab0b1 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -632,6 +632,11 @@ bool Socket::write(BufferQueue* buffers, ssize_t* lenWritten) noexcept iov_length = cryptedLen + 2U; } + // skip if no IOV buffer + if (iov_buffer == nullptr) { + continue; + } + addresses[i] = new sockaddr_storage; ::memcpy(addresses[i], &address, sizeof(sockaddr_storage)); From bf1b07ba60520d5ec61f805933003d0ceeda8a48 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 09:06:41 -0500 Subject: [PATCH 177/200] add thread safties to SpanningTree; --- src/fne/network/SpanningTree.cpp | 92 ++++++++++++++++++++------------ src/fne/network/SpanningTree.h | 15 ++++++ 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/src/fne/network/SpanningTree.cpp b/src/fne/network/SpanningTree.cpp index 67e5cee7..6b84dc6e 100644 --- a/src/fne/network/SpanningTree.cpp +++ b/src/fne/network/SpanningTree.cpp @@ -17,6 +17,7 @@ using namespace network; // Static Class Members // --------------------------------------------------------------------------- +std::mutex SpanningTree::s_mutex; std::unordered_map SpanningTree::s_spanningTrees = std::unordered_map(); uint8_t SpanningTree::s_maxUpdatesBeforeReparent = 5U; @@ -102,6 +103,8 @@ uint32_t SpanningTree::countChildren(SpanningTree* node) void SpanningTree::erasePeer(const uint32_t peerId) { + std::lock_guard guard(s_mutex); + auto it = s_spanningTrees.find(peerId); if (it != s_spanningTrees.end()) { SpanningTree* tree = it->second; @@ -149,6 +152,57 @@ void SpanningTree::serializeTree(SpanningTree* node, json::array& jsonArray) /* Helper to recursively deserialize tree node from JSON array. */ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers) +{ + std::lock_guard guard(s_mutex); + internalDeserializeTree(jsonArray, parent, duplicatePeers); +} + +/* Helper to move the tree node to a different parent tree node. */ + +void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) +{ + if (node == nullptr) + return; + if (parent == nullptr) + return; + + std::lock_guard guard(s_mutex); + internalMoveParent(node, parent); +} + +/* Debug helper to visualize the tree structure in the log. */ + +void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) +{ + if (node == nullptr) + return; + if (node->m_children.size() == 0) { + return; + } + + if (level == 0U) + LogInfoEx(LOG_STP, "Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", + node->id(), node->masterId(), node->identity().c_str(), node->m_children.size(), node->isRoot()); + + std::string indent; + for (uint32_t i = 0U; i < level; i++) { + indent += " "; + } + + for (auto child : node->m_children) { + LogInfoEx(LOG_STP, "%s- Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", + indent.c_str(), child->id(), child->masterId(), child->identity().c_str(), child->m_children.size(), child->isRoot()); + visualizeTreeToLog(child, level + 1U); + } +} + +// --------------------------------------------------------------------------- +// Private Static Class Members +// --------------------------------------------------------------------------- + +/* Helper to recursively deserialize tree node from JSON array. */ + +void SpanningTree::internalDeserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers) { for (auto& v : jsonArray) { if (!v.is()) @@ -193,7 +247,7 @@ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, existingNode->m_updatesBeforeReparent = 0U; // reparent the node if necessary - moveParent(existingNode, parent); + internalMoveParent(existingNode, parent); } else { existingNode->m_updatesBeforeReparent++; } @@ -202,10 +256,10 @@ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, // process children json::array childArray = obj["children"].get(); - //LogDebugEx(LOG_STP, "SpanningTree::deserializeTree()", "peerId = %u, masterId = %u, parent = %u, childrenCnt = %u", + //LogDebugEx(LOG_STP, "SpanningTree::internalDeserializeTree()", "peerId = %u, masterId = %u, parent = %u, childrenCnt = %u", // existingNode->id(), existingNode->masterId(), parent->id(), childArray.size()); if (childArray.size() > 0U) { - deserializeTree(childArray, existingNode, duplicatePeers); + internalDeserializeTree(childArray, existingNode, duplicatePeers); // does the announced array disagree with our current count of children? if (childArray.size() < existingNode->m_children.size()) { @@ -268,7 +322,7 @@ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, /* Helper to move the tree node to a different parent tree node. */ -void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) +void SpanningTree::internalMoveParent(SpanningTree* node, SpanningTree* parent) { if (node == nullptr) return; @@ -319,36 +373,6 @@ void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) } } -/* Debug helper to visualize the tree structure in the log. */ - -void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) -{ - if (node == nullptr) - return; - if (node->m_children.size() == 0) { - return; - } - - if (level == 0U) - LogInfoEx(LOG_STP, "Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", - node->id(), node->masterId(), node->identity().c_str(), node->m_children.size(), node->isRoot()); - - std::string indent; - for (uint32_t i = 0U; i < level; i++) { - indent += " "; - } - - for (auto child : node->m_children) { - LogInfoEx(LOG_STP, "%s- Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", - indent.c_str(), child->id(), child->masterId(), child->identity().c_str(), child->m_children.size(), child->isRoot()); - visualizeTreeToLog(child, level + 1U); - } -} - -// --------------------------------------------------------------------------- -// Private Static Class Members -// --------------------------------------------------------------------------- - /* Erase all children of a spanning tree node. */ void SpanningTree::eraseChildren(SpanningTree* node) diff --git a/src/fne/network/SpanningTree.h b/src/fne/network/SpanningTree.h index 07e0de23..7906c4f9 100644 --- a/src/fne/network/SpanningTree.h +++ b/src/fne/network/SpanningTree.h @@ -20,6 +20,7 @@ #include "common/json/json.h" #include +#include #include #include @@ -149,6 +150,7 @@ namespace network SpanningTree* m_parent; //!< Parent tree node. (i.e. master FNE above this) std::vector m_children; //!< Child tree nodes. (i.e. peer FNEs below this) + static std::mutex s_mutex; static std::unordered_map s_spanningTrees; //!< Static map of all trees by peer ID. static uint8_t s_maxUpdatesBeforeReparent; //!< Maximum count of updates before allowing node reparenting. @@ -168,6 +170,19 @@ namespace network private: uint8_t m_updatesBeforeReparent; + /** + * @brief Helper to recursively deserialize tree node from JSON array. + * @param jsonArray JSON array to read node from. + * @param parent Pointer to parent SpanningTree node. + * @param duplicatePeers Pointer to vector to receive duplicate peer IDs found during deserialization. + */ + static void internalDeserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers); + /** + * @brief Helper to move the tree node to a different parent tree node. + * @param node Pointer to a SpanningTree node. + * @param parent Pointer to new parent SpanningTree Node. + */ + static void internalMoveParent(SpanningTree* node, SpanningTree* parent); /** * @brief Helper to erase all children of a spanning tree node. * @param node Pointer to SpanningTree node. From e3a5faf4ece489267c2ccfa512b76f49a099111d Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 09:14:03 -0500 Subject: [PATCH 178/200] correct bad memory allocation; --- src/common/network/udp/Socket.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index 6beab0b1..748f1226 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -590,7 +590,8 @@ bool Socket::write(BufferQueue* buffers, ssize_t* lenWritten) noexcept // are we crypto wrapped? if (m_isCryptoWrapped && m_presharedKey != nullptr) { uint32_t cryptedLen = length * sizeof(uint8_t); - uint8_t *cryptoBuffer = iov_buffer; + uint8_t* cryptoBuffer = new uint8_t[iov_length]; + ::memcpy(cryptoBuffer, iov_buffer, iov_length); // do we need to pad the original buffer to be block aligned? if (cryptedLen % crypto::AES::BLOCK_BYTES_LEN != 0) { From edcb4e39fc479ca3f1a0e98cc8fa43dfef123dc2 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 10:30:34 -0500 Subject: [PATCH 179/200] revert previous change to add locking to SpanningTree (this is handled externally in FNENetwork and DiagNetwork); fix issue in FNENetwork which could cause a STP deadlock; fix issue incorrectly labeling a peer as allowing call priority in the log when infact the peer was not configured that way; --- src/fne/network/DiagNetwork.cpp | 4 +- src/fne/network/FNENetwork.cpp | 13 +++-- src/fne/network/SpanningTree.cpp | 90 ++++++++++++-------------------- src/fne/network/SpanningTree.h | 14 ----- 4 files changed, 45 insertions(+), 76 deletions(-) diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index dc05e90a..73f0eb01 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -432,8 +432,8 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) uint32_t decompressedLen = 0U; uint8_t* decompressed = nullptr; - diagNetwork->m_peerReplicaActPkt.lock(); if (pkt.buffer->decode(rawPayload, &decompressed, &decompressedLen)) { + diagNetwork->m_peerReplicaActPkt.lock(); std::string payload(decompressed + 8U, decompressed + decompressedLen); // parse JSON body @@ -621,8 +621,8 @@ void DiagNetwork::taskNetworkRx(NetPacketRequest* req) uint32_t decompressedLen = 0U; uint8_t* decompressed = nullptr; - diagNetwork->m_peerTreeListPkt.lock(); if (pkt.buffer->decode(rawPayload, &decompressed, &decompressedLen)) { + diagNetwork->m_peerTreeListPkt.lock(); std::string payload(decompressed + 8U, decompressed + decompressedLen); // parse JSON body diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 588e926a..b9f3da81 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1223,8 +1223,10 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) lookups::PeerId peerEntry = network->m_peerListLookup->find(peerId); if (!peerEntry.peerDefault()) { - connection->hasCallPriority(peerEntry.hasCallPriority()); - LogInfoEx(LOG_MASTER, "PEER %u >> Has Call Priority", peerId); + if (peerEntry.hasCallPriority()) { + connection->hasCallPriority(peerEntry.hasCallPriority()); + LogInfoEx(LOG_MASTER, "PEER %u >> Has Call Priority", peerId); + } } network->m_peers[peerId] = connection; @@ -1299,7 +1301,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) } if (network->m_enableSpanningTree && !connection->isSysView()) { - std::lock_guard guard(network->m_treeLock); + network->m_treeLock.lock(); // check if this peer is already connected via another peer SpanningTree* tree = SpanningTree::findByMasterID(masterPeerId); @@ -1320,6 +1322,7 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) LogWarning(LOG_STP, "PEER %u (%s) RPTC NAK, server already connected via PEER %u, duplicate connection denied, peerId = %u, masterId = %u, treePeerId = %u, treeMasterId = %u, connectionState = %u", peerId, connection->identWithQualifier().c_str(), peerId, masterPeerId, tree->id(), tree->masterId(), tree->id(), connection->connectionState()); network->writePeerNAK(peerId, TAG_REPEATER_CONFIG, NET_CONN_NAK_FNE_DUPLICATE_CONN, req->address, req->addrLen); + network->m_treeLock.unlock(); network->disconnectPeer(peerId, connection); break; } @@ -1328,6 +1331,8 @@ void FNENetwork::taskNetworkRx(NetPacketRequest* req) node->identity(identity); network->logSpanningTree(connection); } + + network->m_treeLock.unlock(); } } @@ -2057,6 +2062,8 @@ void FNENetwork::erasePeer(uint32_t peerId) } if (neighborFNE && m_enableSpanningTree) { + std::lock_guard guard(m_treeLock); + // erase this peer from the master tree SpanningTree* tree = SpanningTree::findByPeerID(peerId); if (tree != nullptr) { diff --git a/src/fne/network/SpanningTree.cpp b/src/fne/network/SpanningTree.cpp index 6b84dc6e..a85c4a5e 100644 --- a/src/fne/network/SpanningTree.cpp +++ b/src/fne/network/SpanningTree.cpp @@ -17,7 +17,6 @@ using namespace network; // Static Class Members // --------------------------------------------------------------------------- -std::mutex SpanningTree::s_mutex; std::unordered_map SpanningTree::s_spanningTrees = std::unordered_map(); uint8_t SpanningTree::s_maxUpdatesBeforeReparent = 5U; @@ -103,8 +102,6 @@ uint32_t SpanningTree::countChildren(SpanningTree* node) void SpanningTree::erasePeer(const uint32_t peerId) { - std::lock_guard guard(s_mutex); - auto it = s_spanningTrees.find(peerId); if (it != s_spanningTrees.end()) { SpanningTree* tree = it->second; @@ -152,57 +149,6 @@ void SpanningTree::serializeTree(SpanningTree* node, json::array& jsonArray) /* Helper to recursively deserialize tree node from JSON array. */ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers) -{ - std::lock_guard guard(s_mutex); - internalDeserializeTree(jsonArray, parent, duplicatePeers); -} - -/* Helper to move the tree node to a different parent tree node. */ - -void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) -{ - if (node == nullptr) - return; - if (parent == nullptr) - return; - - std::lock_guard guard(s_mutex); - internalMoveParent(node, parent); -} - -/* Debug helper to visualize the tree structure in the log. */ - -void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) -{ - if (node == nullptr) - return; - if (node->m_children.size() == 0) { - return; - } - - if (level == 0U) - LogInfoEx(LOG_STP, "Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", - node->id(), node->masterId(), node->identity().c_str(), node->m_children.size(), node->isRoot()); - - std::string indent; - for (uint32_t i = 0U; i < level; i++) { - indent += " "; - } - - for (auto child : node->m_children) { - LogInfoEx(LOG_STP, "%s- Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", - indent.c_str(), child->id(), child->masterId(), child->identity().c_str(), child->m_children.size(), child->isRoot()); - visualizeTreeToLog(child, level + 1U); - } -} - -// --------------------------------------------------------------------------- -// Private Static Class Members -// --------------------------------------------------------------------------- - -/* Helper to recursively deserialize tree node from JSON array. */ - -void SpanningTree::internalDeserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers) { for (auto& v : jsonArray) { if (!v.is()) @@ -247,7 +193,7 @@ void SpanningTree::internalDeserializeTree(json::array& jsonArray, SpanningTree* existingNode->m_updatesBeforeReparent = 0U; // reparent the node if necessary - internalMoveParent(existingNode, parent); + moveParent(existingNode, parent); } else { existingNode->m_updatesBeforeReparent++; } @@ -259,7 +205,7 @@ void SpanningTree::internalDeserializeTree(json::array& jsonArray, SpanningTree* //LogDebugEx(LOG_STP, "SpanningTree::internalDeserializeTree()", "peerId = %u, masterId = %u, parent = %u, childrenCnt = %u", // existingNode->id(), existingNode->masterId(), parent->id(), childArray.size()); if (childArray.size() > 0U) { - internalDeserializeTree(childArray, existingNode, duplicatePeers); + deserializeTree(childArray, existingNode, duplicatePeers); // does the announced array disagree with our current count of children? if (childArray.size() < existingNode->m_children.size()) { @@ -322,7 +268,7 @@ void SpanningTree::internalDeserializeTree(json::array& jsonArray, SpanningTree* /* Helper to move the tree node to a different parent tree node. */ -void SpanningTree::internalMoveParent(SpanningTree* node, SpanningTree* parent) +void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) { if (node == nullptr) return; @@ -373,6 +319,36 @@ void SpanningTree::internalMoveParent(SpanningTree* node, SpanningTree* parent) } } +/* Debug helper to visualize the tree structure in the log. */ + +void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) +{ + if (node == nullptr) + return; + if (node->m_children.size() == 0) { + return; + } + + if (level == 0U) + LogInfoEx(LOG_STP, "Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", + node->id(), node->masterId(), node->identity().c_str(), node->m_children.size(), node->isRoot()); + + std::string indent; + for (uint32_t i = 0U; i < level; i++) { + indent += " "; + } + + for (auto child : node->m_children) { + LogInfoEx(LOG_STP, "%s- Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", + indent.c_str(), child->id(), child->masterId(), child->identity().c_str(), child->m_children.size(), child->isRoot()); + visualizeTreeToLog(child, level + 1U); + } +} + +// --------------------------------------------------------------------------- +// Private Static Class Members +// --------------------------------------------------------------------------- + /* Erase all children of a spanning tree node. */ void SpanningTree::eraseChildren(SpanningTree* node) diff --git a/src/fne/network/SpanningTree.h b/src/fne/network/SpanningTree.h index 7906c4f9..ee976646 100644 --- a/src/fne/network/SpanningTree.h +++ b/src/fne/network/SpanningTree.h @@ -150,7 +150,6 @@ namespace network SpanningTree* m_parent; //!< Parent tree node. (i.e. master FNE above this) std::vector m_children; //!< Child tree nodes. (i.e. peer FNEs below this) - static std::mutex s_mutex; static std::unordered_map s_spanningTrees; //!< Static map of all trees by peer ID. static uint8_t s_maxUpdatesBeforeReparent; //!< Maximum count of updates before allowing node reparenting. @@ -170,19 +169,6 @@ namespace network private: uint8_t m_updatesBeforeReparent; - /** - * @brief Helper to recursively deserialize tree node from JSON array. - * @param jsonArray JSON array to read node from. - * @param parent Pointer to parent SpanningTree node. - * @param duplicatePeers Pointer to vector to receive duplicate peer IDs found during deserialization. - */ - static void internalDeserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers); - /** - * @brief Helper to move the tree node to a different parent tree node. - * @param node Pointer to a SpanningTree node. - * @param parent Pointer to new parent SpanningTree Node. - */ - static void internalMoveParent(SpanningTree* node, SpanningTree* parent); /** * @brief Helper to erase all children of a spanning tree node. * @param node Pointer to SpanningTree node. From e7a1b4f0a5aca6bd189193993dfe7df17b5d5aa4 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 11:12:43 -0500 Subject: [PATCH 180/200] make sure during resetPeer() we lock the connection; --- 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 b9f3da81..fde70d6d 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -2170,7 +2170,9 @@ bool FNENetwork::resetPeer(uint32_t peerId) LogInfoEx(LOG_MASTER, "PEER %u (%s) resetting peer connection", peerId, connection->identWithQualifier().c_str()); writePeerNAK(peerId, TAG_REPEATER_LOGIN, NET_CONN_NAK_PEER_RESET, addr, addrLen); + connection->lock(); erasePeer(peerId); + connection->unlock(); delete connection; return true; From 7e59e14e9bcc965ba86ce262ca198da814f1ea47 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 11:33:46 -0500 Subject: [PATCH 181/200] readd SpanningTree internal locking mechanisms; --- src/fne/network/SpanningTree.cpp | 90 ++++++++++++++++++++------------ src/fne/network/SpanningTree.h | 14 +++++ 2 files changed, 71 insertions(+), 33 deletions(-) diff --git a/src/fne/network/SpanningTree.cpp b/src/fne/network/SpanningTree.cpp index a85c4a5e..6b84dc6e 100644 --- a/src/fne/network/SpanningTree.cpp +++ b/src/fne/network/SpanningTree.cpp @@ -17,6 +17,7 @@ using namespace network; // Static Class Members // --------------------------------------------------------------------------- +std::mutex SpanningTree::s_mutex; std::unordered_map SpanningTree::s_spanningTrees = std::unordered_map(); uint8_t SpanningTree::s_maxUpdatesBeforeReparent = 5U; @@ -102,6 +103,8 @@ uint32_t SpanningTree::countChildren(SpanningTree* node) void SpanningTree::erasePeer(const uint32_t peerId) { + std::lock_guard guard(s_mutex); + auto it = s_spanningTrees.find(peerId); if (it != s_spanningTrees.end()) { SpanningTree* tree = it->second; @@ -149,6 +152,57 @@ void SpanningTree::serializeTree(SpanningTree* node, json::array& jsonArray) /* Helper to recursively deserialize tree node from JSON array. */ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers) +{ + std::lock_guard guard(s_mutex); + internalDeserializeTree(jsonArray, parent, duplicatePeers); +} + +/* Helper to move the tree node to a different parent tree node. */ + +void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) +{ + if (node == nullptr) + return; + if (parent == nullptr) + return; + + std::lock_guard guard(s_mutex); + internalMoveParent(node, parent); +} + +/* Debug helper to visualize the tree structure in the log. */ + +void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) +{ + if (node == nullptr) + return; + if (node->m_children.size() == 0) { + return; + } + + if (level == 0U) + LogInfoEx(LOG_STP, "Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", + node->id(), node->masterId(), node->identity().c_str(), node->m_children.size(), node->isRoot()); + + std::string indent; + for (uint32_t i = 0U; i < level; i++) { + indent += " "; + } + + for (auto child : node->m_children) { + LogInfoEx(LOG_STP, "%s- Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", + indent.c_str(), child->id(), child->masterId(), child->identity().c_str(), child->m_children.size(), child->isRoot()); + visualizeTreeToLog(child, level + 1U); + } +} + +// --------------------------------------------------------------------------- +// Private Static Class Members +// --------------------------------------------------------------------------- + +/* Helper to recursively deserialize tree node from JSON array. */ + +void SpanningTree::internalDeserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers) { for (auto& v : jsonArray) { if (!v.is()) @@ -193,7 +247,7 @@ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, existingNode->m_updatesBeforeReparent = 0U; // reparent the node if necessary - moveParent(existingNode, parent); + internalMoveParent(existingNode, parent); } else { existingNode->m_updatesBeforeReparent++; } @@ -205,7 +259,7 @@ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, //LogDebugEx(LOG_STP, "SpanningTree::internalDeserializeTree()", "peerId = %u, masterId = %u, parent = %u, childrenCnt = %u", // existingNode->id(), existingNode->masterId(), parent->id(), childArray.size()); if (childArray.size() > 0U) { - deserializeTree(childArray, existingNode, duplicatePeers); + internalDeserializeTree(childArray, existingNode, duplicatePeers); // does the announced array disagree with our current count of children? if (childArray.size() < existingNode->m_children.size()) { @@ -268,7 +322,7 @@ void SpanningTree::deserializeTree(json::array& jsonArray, SpanningTree* parent, /* Helper to move the tree node to a different parent tree node. */ -void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) +void SpanningTree::internalMoveParent(SpanningTree* node, SpanningTree* parent) { if (node == nullptr) return; @@ -319,36 +373,6 @@ void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) } } -/* Debug helper to visualize the tree structure in the log. */ - -void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) -{ - if (node == nullptr) - return; - if (node->m_children.size() == 0) { - return; - } - - if (level == 0U) - LogInfoEx(LOG_STP, "Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", - node->id(), node->masterId(), node->identity().c_str(), node->m_children.size(), node->isRoot()); - - std::string indent; - for (uint32_t i = 0U; i < level; i++) { - indent += " "; - } - - for (auto child : node->m_children) { - LogInfoEx(LOG_STP, "%s- Peer ID: %u, Master Peer ID: %u (%s), Children: %u, IsRoot: %u", - indent.c_str(), child->id(), child->masterId(), child->identity().c_str(), child->m_children.size(), child->isRoot()); - visualizeTreeToLog(child, level + 1U); - } -} - -// --------------------------------------------------------------------------- -// Private Static Class Members -// --------------------------------------------------------------------------- - /* Erase all children of a spanning tree node. */ void SpanningTree::eraseChildren(SpanningTree* node) diff --git a/src/fne/network/SpanningTree.h b/src/fne/network/SpanningTree.h index ee976646..7906c4f9 100644 --- a/src/fne/network/SpanningTree.h +++ b/src/fne/network/SpanningTree.h @@ -150,6 +150,7 @@ namespace network SpanningTree* m_parent; //!< Parent tree node. (i.e. master FNE above this) std::vector m_children; //!< Child tree nodes. (i.e. peer FNEs below this) + static std::mutex s_mutex; static std::unordered_map s_spanningTrees; //!< Static map of all trees by peer ID. static uint8_t s_maxUpdatesBeforeReparent; //!< Maximum count of updates before allowing node reparenting. @@ -169,6 +170,19 @@ namespace network private: uint8_t m_updatesBeforeReparent; + /** + * @brief Helper to recursively deserialize tree node from JSON array. + * @param jsonArray JSON array to read node from. + * @param parent Pointer to parent SpanningTree node. + * @param duplicatePeers Pointer to vector to receive duplicate peer IDs found during deserialization. + */ + static void internalDeserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers); + /** + * @brief Helper to move the tree node to a different parent tree node. + * @param node Pointer to a SpanningTree node. + * @param parent Pointer to new parent SpanningTree Node. + */ + static void internalMoveParent(SpanningTree* node, SpanningTree* parent); /** * @brief Helper to erase all children of a spanning tree node. * @param node Pointer to SpanningTree node. From 896953c3076b3d4e0aced1315ea57f842c02a791 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 13:42:59 -0500 Subject: [PATCH 182/200] make sure pointers are set to null after delete; --- src/fne/network/SpanningTree.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fne/network/SpanningTree.cpp b/src/fne/network/SpanningTree.cpp index 6b84dc6e..22a77b31 100644 --- a/src/fne/network/SpanningTree.cpp +++ b/src/fne/network/SpanningTree.cpp @@ -119,6 +119,7 @@ void SpanningTree::erasePeer(const uint32_t peerId) } delete tree; + tree = nullptr; } s_spanningTrees.erase(it); @@ -384,6 +385,7 @@ void SpanningTree::eraseChildren(SpanningTree* node) if (child != nullptr) { eraseChildren(child); delete child; + child = nullptr; } } From f89a60f92656e341ff72f4b1ee5b8d20f81a7fc0 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 14:28:18 -0500 Subject: [PATCH 183/200] ensure when a FNE loses all its downstream leaves, that it will properly notify upstream FNEs; fix issue where dangling tree nodes were being incorrectly left in the flat peer node list for the spanning tree; --- src/fne/network/PeerNetwork.cpp | 10 ++-- src/fne/network/PeerNetwork.h | 2 + src/fne/network/SpanningTree.cpp | 86 ++++++++++++++++++-------------- src/fne/network/SpanningTree.h | 5 ++ 4 files changed, 62 insertions(+), 41 deletions(-) diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 0397ade3..f7c4c010 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -54,7 +54,8 @@ PeerNetwork::PeerNetwork(const std::string& address, uint16_t port, uint16_t loc m_tgidPkt(true, "Peer Replication, TGID List"), m_ridPkt(true, "Peer Replication, RID List"), m_pidPkt(true, "Peer Replication, PID List"), - m_threadPool(WORKER_CNT, "peer") + m_threadPool(WORKER_CNT, "peer"), + m_prevSpanningTreeChildren(0U) { assert(!address.empty()); assert(port > 0U); @@ -151,10 +152,10 @@ bool PeerNetwork::writeSpanningTree(SpanningTree* treeRoot) { if (treeRoot == nullptr) return false; - if (treeRoot->m_children.size() == 0) + if (treeRoot->m_children.size() == 0 && m_prevSpanningTreeChildren == 0U) return false; - if (treeRoot->m_children.size() > 0) { + if ((treeRoot->m_children.size() > 0) || (m_prevSpanningTreeChildren > 0U)) { json::array jsonArray; SpanningTree::serializeTree(treeRoot, jsonArray); @@ -181,9 +182,12 @@ bool PeerNetwork::writeSpanningTree(SpanningTree* treeRoot) } pkt.clear(); + + m_prevSpanningTreeChildren = treeRoot->m_children.size(); return true; } + m_prevSpanningTreeChildren = treeRoot->m_children.size(); return false; } diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index c974017a..48e74525 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -323,6 +323,8 @@ namespace network ThreadPool m_threadPool; + uint32_t m_prevSpanningTreeChildren; + /** * @brief Entry point to process a given network packet. * @param req Instance of the PeerPacketRequest structure. diff --git a/src/fne/network/SpanningTree.cpp b/src/fne/network/SpanningTree.cpp index 22a77b31..2a03bcc7 100644 --- a/src/fne/network/SpanningTree.cpp +++ b/src/fne/network/SpanningTree.cpp @@ -104,26 +104,7 @@ uint32_t SpanningTree::countChildren(SpanningTree* node) void SpanningTree::erasePeer(const uint32_t peerId) { std::lock_guard guard(s_mutex); - - auto it = s_spanningTrees.find(peerId); - if (it != s_spanningTrees.end()) { - SpanningTree* tree = it->second; - if (tree != nullptr) { - if (tree->m_parent != nullptr) { - auto& siblings = tree->m_parent->m_children; - siblings.erase(std::remove(siblings.begin(), siblings.end(), tree), siblings.end()); - } - - if (tree->hasChildren()) { - eraseChildren(tree); - } - - delete tree; - tree = nullptr; - } - - s_spanningTrees.erase(it); - } + internalErasePeer(peerId); } /* Helper to recursively serialize tree node to JSON array. */ @@ -201,6 +182,31 @@ void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) // Private Static Class Members // --------------------------------------------------------------------------- +/* Erase a peer from the tree. */ + +void SpanningTree::internalErasePeer(const uint32_t peerId) +{ + auto it = s_spanningTrees.find(peerId); + if (it != s_spanningTrees.end()) { + SpanningTree* tree = it->second; + if (tree != nullptr) { + if (tree->m_parent != nullptr) { + auto& siblings = tree->m_parent->m_children; + siblings.erase(std::remove(siblings.begin(), siblings.end(), tree), siblings.end()); + } + + if (tree->hasChildren()) { + eraseChildren(tree); + } + + delete tree; + tree = nullptr; + } + + s_spanningTrees.erase(it); + } +} + /* Helper to recursively deserialize tree node from JSON array. */ void SpanningTree::internalDeserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers) @@ -284,39 +290,43 @@ void SpanningTree::internalDeserializeTree(json::array& jsonArray, SpanningTree* } // iterate over the nodes children and remove entries - std::vector childrenToErase; + std::vector childrenToErase; for (auto child : existingNode->m_children) { if (child != nullptr) { auto it = std::find(announcedChildren.begin(), announcedChildren.end(), child->id()); if (it == announcedChildren.end()) { - childrenToErase.push_back(child); + childrenToErase.push_back(child->id()); } } } if (childrenToErase.size() > 0) { for (auto child : childrenToErase) { - if (child != nullptr) { - auto it = std::find_if(existingNode->m_children.begin(), existingNode->m_children.end(), - [&](SpanningTree* x) { - if (x != nullptr) { - return x->id() == child->id(); - } - - return false; - }); - if (it != existingNode->m_children.end()) { - existingNode->m_children.erase(it); - } - } + internalErasePeer(child); } } } } else { - // did the node report children atsome point and is no longer reporting them? - if (childArray.size() == 0U && existingNode->hasChildren()) - eraseChildren(existingNode); + // did the node report children at some point and is no longer reporting them? + if (childArray.size() == 0U && existingNode->hasChildren()) { + //LogDebugEx(LOG_STP, "SpanningTree::internalDeserializeTree()", "peerId = %u, masterId = %u, parent = %u, childrenCnt = %u, existingChildrenCnt = %u - erasing children", + // existingNode->id(), existingNode->masterId(), parent->id(), childArray.size(), existingNode->m_children.size()); + + // iterate over the nodes children and remove entries + std::vector childrenToErase; + for (auto child : existingNode->m_children) { + if (child != nullptr) { + childrenToErase.push_back(child->id()); + } + } + + if (childrenToErase.size() > 0) { + for (auto child : childrenToErase) { + internalErasePeer(child); + } + } + } } } } diff --git a/src/fne/network/SpanningTree.h b/src/fne/network/SpanningTree.h index 7906c4f9..0196a9d8 100644 --- a/src/fne/network/SpanningTree.h +++ b/src/fne/network/SpanningTree.h @@ -170,6 +170,11 @@ namespace network private: uint8_t m_updatesBeforeReparent; + /** + * @brief Erase a peer from the tree. + * @param peerId Peer ID. + */ + static void internalErasePeer(const uint32_t peerId); /** * @brief Helper to recursively deserialize tree node from JSON array. * @param jsonArray JSON array to read node from. From 2cb428f994f1de5632d682380e4da1d6b3078bda Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 17:52:01 -0500 Subject: [PATCH 184/200] fix defaultNetIdleTalkgroup being treated as a hex value instead of dec; enhance P25 defaultNetIdleTalkgroup slightly to better pass traffic after RF traffic; enhance P25 defaultNetIdleTalkgroup to pass traffic if there are affiliations to the group; --- src/host/dmr/Control.cpp | 4 ++-- src/host/nxdn/Control.cpp | 4 ++-- src/host/p25/Control.cpp | 4 ++-- src/host/p25/packet/Voice.cpp | 24 ++++++++++++++++++------ 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index 5f92964f..ce07af57 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -143,7 +143,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa m_enableTSCC = enableTSCC; yaml::Node rfssConfig = systemConf["config"]; - uint32_t defaultNetIdleTalkgroup = (uint32_t)::strtoul(rfssConfig["defaultNetIdleTalkgroup"].as("0").c_str(), NULL, 16); + uint32_t defaultNetIdleTalkgroup = (uint32_t)rfssConfig["defaultNetIdleTalkgroup"].as(0U); m_slot1->setDefaultNetIdleTG(defaultNetIdleTalkgroup); m_slot2->setDefaultNetIdleTG(defaultNetIdleTalkgroup); @@ -228,7 +228,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa } if (defaultNetIdleTalkgroup != 0U) { - LogInfo(" Default Network Idle Talkgroup: $%04X", defaultNetIdleTalkgroup); + LogInfo(" Default Network Idle Talkgroup: %u", defaultNetIdleTalkgroup); } LogInfo(" Ignore Affiliation Check: %s", ignoreAffiliationCheck ? "yes" : "no"); diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index 5c058529..9df6acfd 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -222,7 +222,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw m_legacyGroupReg = nxdnProtocol["legacyGroupReg"].as(false); yaml::Node rfssConfig = systemConf["config"]; - m_defaultNetIdleTalkgroup = (uint32_t)::strtoul(rfssConfig["defaultNetIdleTalkgroup"].as("0").c_str(), NULL, 16); + m_defaultNetIdleTalkgroup = (uint32_t)rfssConfig["defaultNetIdleTalkgroup"].as(0U); yaml::Node controlCh = rfssConfig["controlCh"]; m_notifyCC = controlCh["notifyEnable"].as(false); @@ -333,7 +333,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw } if (m_defaultNetIdleTalkgroup != 0U) { - LogInfo(" Default Network Idle Talkgroup: $%04X", m_defaultNetIdleTalkgroup); + LogInfo(" Default Network Idle Talkgroup: %u", m_defaultNetIdleTalkgroup); } LogInfo(" Ignore Affiliation Check: %s", m_ignoreAffiliationCheck ? "yes" : "no"); diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index c5db58f1..ea65bea6 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -248,7 +248,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw yaml::Node rfssConfig = systemConf["config"]; m_control->m_patchSuperGroup = (uint32_t)::strtoul(rfssConfig["pSuperGroup"].as("FFFE").c_str(), NULL, 16); m_control->m_announcementGroup = (uint32_t)::strtoul(rfssConfig["announcementGroup"].as("FFFE").c_str(), NULL, 16); - m_defaultNetIdleTalkgroup = (uint32_t)::strtoul(rfssConfig["defaultNetIdleTalkgroup"].as("0").c_str(), NULL, 16); + m_defaultNetIdleTalkgroup = (uint32_t)rfssConfig["defaultNetIdleTalkgroup"].as(0U); yaml::Node secureConfig = rfssConfig["secure"]; std::string key = secureConfig["key"].as(); @@ -521,7 +521,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw LogInfo(" Patch Super Group: $%04X", m_control->m_patchSuperGroup); LogInfo(" Announcement Group: $%04X", m_control->m_announcementGroup); if (m_defaultNetIdleTalkgroup != 0U) { - LogInfo(" Default Network Idle Talkgroup: $%04X", m_defaultNetIdleTalkgroup); + LogInfo(" Default Network Idle Talkgroup: %u", m_defaultNetIdleTalkgroup); } LogInfo(" Notify Control: %s", m_notifyCC ? "yes" : "no"); diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index d1576e9b..965136fb 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -1571,16 +1571,19 @@ bool Voice::checkNetTrafficCollision(uint32_t srcId, uint32_t dstId, defines::DU } } - // bryanb: possible fix for a "tail ride" condition where network traffic immediately follows RF traffic *while* - // the RF TG hangtimer is running - if (m_p25->m_rfTGHang.isRunning() && !m_p25->m_rfTGHang.hasExpired()) { - m_p25->m_rfTGHang.stop(); - } + LogDebugEx(LOG_NET, "Voice::checkNetTrafficCollision()", "rfLastDstId = %u, dstId = %u, defaultNetIdleTalkgroup = %u, rfTGHangRunning = %u", m_p25->m_rfLastDstId, dstId, + m_p25->m_defaultNetIdleTalkgroup, m_p25->m_rfTGHang.isRunning()); // don't process network frames if the RF TG hang timer isn't running, the default net idle talkgroup is set and // the destination ID doesn't match the default net idle talkgroup if (m_p25->m_defaultNetIdleTalkgroup != 0U && dstId != 0U && !m_p25->m_rfTGHang.isRunning()) { - if (m_p25->m_defaultNetIdleTalkgroup != dstId) { + if (m_p25->m_defaultNetIdleTalkgroup != dstId && !m_p25->m_affiliations->hasGroupAff(dstId)) { + if (!m_p25->m_dedicatedControl) { + if (m_p25->m_affiliations->isGranted(dstId)) { + m_p25->m_affiliations->releaseGrant(dstId, false); + } + } + resetNet(); if (m_p25->m_network != nullptr) m_p25->m_network->resetP25(); @@ -1588,6 +1591,15 @@ bool Voice::checkNetTrafficCollision(uint32_t srcId, uint32_t dstId, defines::DU } } + // bryanb: only perform tail ride fix if default net idle talkgroup is not set + if (m_p25->m_defaultNetIdleTalkgroup == 0U) { + // bryanb: possible fix for a "tail ride" condition where network traffic immediately follows RF traffic *while* + // the RF TG hangtimer is running + if (m_p25->m_rfTGHang.isRunning() && !m_p25->m_rfTGHang.hasExpired()) { + m_p25->m_rfTGHang.stop(); + } + } + // perform authoritative network TG hangtimer and traffic preemption if (m_p25->m_authoritative) { // don't process network frames if the destination ID's don't match and the network TG hang timer is running From 753d246480b80a4dfdea45ccc06d64d81e8133aa Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 21 Nov 2025 18:15:02 -0500 Subject: [PATCH 185/200] how about we dont blatently leave debug messages enabled...; --- src/host/p25/packet/Voice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index 965136fb..ae4e4597 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -1571,8 +1571,8 @@ bool Voice::checkNetTrafficCollision(uint32_t srcId, uint32_t dstId, defines::DU } } - LogDebugEx(LOG_NET, "Voice::checkNetTrafficCollision()", "rfLastDstId = %u, dstId = %u, defaultNetIdleTalkgroup = %u, rfTGHangRunning = %u", m_p25->m_rfLastDstId, dstId, - m_p25->m_defaultNetIdleTalkgroup, m_p25->m_rfTGHang.isRunning()); + //LogDebugEx(LOG_NET, "Voice::checkNetTrafficCollision()", "rfLastDstId = %u, dstId = %u, defaultNetIdleTalkgroup = %u, rfTGHangRunning = %u", m_p25->m_rfLastDstId, dstId, + // m_p25->m_defaultNetIdleTalkgroup, m_p25->m_rfTGHang.isRunning()); // don't process network frames if the RF TG hang timer isn't running, the default net idle talkgroup is set and // the destination ID doesn't match the default net idle talkgroup From 7618380a5e98b4864569eb05683af5d3f3faedc9 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 24 Nov 2025 21:00:36 -0500 Subject: [PATCH 186/200] BUGFIX: hopefully correct crash condition when trying to erase child nodes; --- src/fne/network/SpanningTree.cpp | 67 +++++++++++++------------------- src/fne/network/SpanningTree.h | 10 ++--- 2 files changed, 33 insertions(+), 44 deletions(-) diff --git a/src/fne/network/SpanningTree.cpp b/src/fne/network/SpanningTree.cpp index 2a03bcc7..1438b091 100644 --- a/src/fne/network/SpanningTree.cpp +++ b/src/fne/network/SpanningTree.cpp @@ -42,16 +42,7 @@ SpanningTree::SpanningTree(uint32_t id, uint32_t masterId, SpanningTree* parent) /* Finalizes a instance of the SpanningTree class. */ -SpanningTree::~SpanningTree() -{ - for (auto child : m_children) { - if (child != nullptr) { - delete child; - child = nullptr; - } - } - m_children.clear(); -} +SpanningTree::~SpanningTree() = default; /* Find a peer tree by peer ID. */ @@ -152,7 +143,7 @@ void SpanningTree::moveParent(SpanningTree* node, SpanningTree* parent) internalMoveParent(node, parent); } -/* Debug helper to visualize the tree structure in the log. */ +/* Helper to visualize the tree structure in the log. */ void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) { @@ -182,31 +173,6 @@ void SpanningTree::visualizeTreeToLog(SpanningTree* node, uint32_t level) // Private Static Class Members // --------------------------------------------------------------------------- -/* Erase a peer from the tree. */ - -void SpanningTree::internalErasePeer(const uint32_t peerId) -{ - auto it = s_spanningTrees.find(peerId); - if (it != s_spanningTrees.end()) { - SpanningTree* tree = it->second; - if (tree != nullptr) { - if (tree->m_parent != nullptr) { - auto& siblings = tree->m_parent->m_children; - siblings.erase(std::remove(siblings.begin(), siblings.end(), tree), siblings.end()); - } - - if (tree->hasChildren()) { - eraseChildren(tree); - } - - delete tree; - tree = nullptr; - } - - s_spanningTrees.erase(it); - } -} - /* Helper to recursively deserialize tree node from JSON array. */ void SpanningTree::internalDeserializeTree(json::array& jsonArray, SpanningTree* parent, std::vector* duplicatePeers) @@ -384,6 +350,31 @@ void SpanningTree::internalMoveParent(SpanningTree* node, SpanningTree* parent) } } +/* Erase a peer from the tree. */ + +void SpanningTree::internalErasePeer(const uint32_t peerId) +{ + auto it = s_spanningTrees.find(peerId); + if (it != s_spanningTrees.end()) { + SpanningTree* tree = it->second; + if (tree != nullptr) { + if (tree->m_parent != nullptr) { + auto& siblings = tree->m_parent->m_children; + siblings.erase(std::remove(siblings.begin(), siblings.end(), tree), siblings.end()); + } + + if (tree->hasChildren()) { + eraseChildren(tree); + } + + delete tree; + tree = nullptr; + } + + s_spanningTrees.erase(it); + } +} + /* Erase all children of a spanning tree node. */ void SpanningTree::eraseChildren(SpanningTree* node) @@ -393,9 +384,7 @@ void SpanningTree::eraseChildren(SpanningTree* node) for (auto child : node->m_children) { if (child != nullptr) { - eraseChildren(child); - delete child; - child = nullptr; + internalErasePeer(child->id()); } } diff --git a/src/fne/network/SpanningTree.h b/src/fne/network/SpanningTree.h index 0196a9d8..37aacc55 100644 --- a/src/fne/network/SpanningTree.h +++ b/src/fne/network/SpanningTree.h @@ -170,11 +170,6 @@ namespace network private: uint8_t m_updatesBeforeReparent; - /** - * @brief Erase a peer from the tree. - * @param peerId Peer ID. - */ - static void internalErasePeer(const uint32_t peerId); /** * @brief Helper to recursively deserialize tree node from JSON array. * @param jsonArray JSON array to read node from. @@ -188,6 +183,11 @@ namespace network * @param parent Pointer to new parent SpanningTree Node. */ static void internalMoveParent(SpanningTree* node, SpanningTree* parent); + /** + * @brief Erase a peer from the tree. + * @param peerId Peer ID. + */ + static void internalErasePeer(const uint32_t peerId); /** * @brief Helper to erase all children of a spanning tree node. * @param node Pointer to SpanningTree node. From 9c21855d234be793655c07f8d5542fb2e3b0ed17 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 24 Nov 2025 21:13:42 -0500 Subject: [PATCH 187/200] followup for last commit, simplify implementation; --- src/fne/network/SpanningTree.cpp | 24 +++++++----------------- src/fne/network/SpanningTree.h | 5 ----- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/src/fne/network/SpanningTree.cpp b/src/fne/network/SpanningTree.cpp index 1438b091..88abde59 100644 --- a/src/fne/network/SpanningTree.cpp +++ b/src/fne/network/SpanningTree.cpp @@ -364,7 +364,13 @@ void SpanningTree::internalErasePeer(const uint32_t peerId) } if (tree->hasChildren()) { - eraseChildren(tree); + for (auto child : tree->m_children) { + if (child != nullptr) { + internalErasePeer(child->id()); + } + } + + tree->m_children.clear(); } delete tree; @@ -374,19 +380,3 @@ void SpanningTree::internalErasePeer(const uint32_t peerId) s_spanningTrees.erase(it); } } - -/* Erase all children of a spanning tree node. */ - -void SpanningTree::eraseChildren(SpanningTree* node) -{ - if (node == nullptr) - return; - - for (auto child : node->m_children) { - if (child != nullptr) { - internalErasePeer(child->id()); - } - } - - node->m_children.clear(); -} diff --git a/src/fne/network/SpanningTree.h b/src/fne/network/SpanningTree.h index 37aacc55..b0407523 100644 --- a/src/fne/network/SpanningTree.h +++ b/src/fne/network/SpanningTree.h @@ -188,11 +188,6 @@ namespace network * @param peerId Peer ID. */ static void internalErasePeer(const uint32_t peerId); - /** - * @brief Helper to erase all children of a spanning tree node. - * @param node Pointer to SpanningTree node. - */ - static void eraseChildren(SpanningTree* node); }; } // namespace network From a0b1e81b79ad9db9fc6734642ec583dd74587fa0 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 29 Nov 2025 10:18:53 -0500 Subject: [PATCH 188/200] this will be unpopular, but I am deprecating support for cross-compiling for armhf/legacy RPi, maintaining this is already causing problems with OpenSSL and will ultimately handcuff us in the future if we upgrade C++ versions because the legacy toolchain uses GCC 4.9; --- .github/workflows/build.yml | 12 +++---- .github/workflows/release.yml | 12 +++---- CMakeLists.txt | 60 ----------------------------------- Makefile | 8 ----- 4 files changed, 8 insertions(+), 84 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a22ae44c..4cf4da48 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,7 +52,7 @@ jobs: needs: [setup] strategy: matrix: - arch: ["amd64", "arm", "aarch64", "armhf"] + arch: ["amd64", "arm", "aarch64"] runs-on: ubuntu-22.04 env: PACKAGENAME: ${{ needs.setup.outputs.APPNAME }}-${{ needs.setup.outputs.DATE }}-${{ matrix.arch }} @@ -105,12 +105,8 @@ jobs: build_args="$build_args -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS='-s' -DCMAKE_CXX_FLAGS='-s'" fi - if [[ "${{ matrix.arch }}" == 'armhf' ]]; then - cmake $(echo $build_args) -DCROSS_COMPILE_RPI_ARM=1 . - else - cmake $(echo $build_args) \ - -D "CROSS_COMPILE_$(echo '${{ matrix.arch }}' | tr '[:lower:]' '[:upper:]')=1" . - fi + cmake $(echo $build_args) \ + -D "CROSS_COMPILE_$(echo '${{ matrix.arch }}' | tr '[:lower:]' '[:upper:]')=1" . make -j $(nproc) make tarball @@ -146,7 +142,7 @@ jobs: needs: [setup, build, create-release] strategy: matrix: - arch: ["amd64", "arm", "aarch64", "armhf"] + arch: ["amd64", "arm", "aarch64"] runs-on: ubuntu-22.04 env: PACKAGENAME: ${{ needs.setup.outputs.APPNAME }}-${{ needs.setup.outputs.DATE }}-${{ matrix.arch }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5d22595b..6badffa1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,7 +30,7 @@ jobs: needs: [setup] strategy: matrix: - arch: ["amd64", "arm", "aarch64", "armhf"] + arch: ["amd64", "arm", "aarch64"] runs-on: ubuntu-22.04 env: PACKAGENAME: ${{ needs.setup.outputs.APPNAME }}-${{ needs.setup.outputs.VERSION }}-${{ matrix.arch }} @@ -77,12 +77,8 @@ jobs: build_args="$build_args -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS='-s' -DCMAKE_CXX_FLAGS='-s'" - if [[ "${{ matrix.arch }}" == 'armhf' ]]; then - cmake $(echo $build_args) -DCROSS_COMPILE_RPI_ARM=1 . - else - cmake $(echo $build_args) \ - -D "CROSS_COMPILE_$(echo '${{ matrix.arch }}' | tr '[:lower:]' '[:upper:]')=1" . - fi + cmake $(echo $build_args) \ + -D "CROSS_COMPILE_$(echo '${{ matrix.arch }}' | tr '[:lower:]' '[:upper:]')=1" . make -j $(nproc) make strip @@ -118,7 +114,7 @@ jobs: needs: [setup, build, create-release] strategy: matrix: - arch: ["amd64", "arm", "aarch64", "armhf"] + arch: ["amd64", "arm", "aarch64"] runs-on: ubuntu-22.04 env: PACKAGENAME: ${{ needs.setup.outputs.APPNAME }}-${{ needs.setup.outputs.VERSION }}-${{ matrix.arch }} diff --git a/CMakeLists.txt b/CMakeLists.txt index d2f35f61..84f3c28e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,6 @@ endif (DISABLE_WEBSOCKETS) # Cross-compile options option(CROSS_COMPILE_ARM "Cross-compile for 32-bit ARM" off) option(CROSS_COMPILE_AARCH64 "Cross-compile for 64-bit ARM" off) -option(CROSS_COMPILE_RPI_ARM "Cross-compile for (old RPi) 32-bit ARM" off) option(COMPILE_WIN32 "Compile for Win32" off) set(ENABLE_LIBDW_SUPPORT ON) @@ -101,44 +100,6 @@ if (WITH_RPI_ARM_TOOLS) message(CHECK_START "With RPi 1 Tools: ${RPI_ARM_TOOLS}") endif (WITH_RPI_ARM_TOOLS) -if (CROSS_COMPILE_RPI_ARM) - if (NOT WITH_RPI_ARM_TOOLS) - message("-- Cloning legacy Raspberry Pi compilation toolchain") - include(FetchContent) - FetchContent_Declare( - RPiTools - GIT_REPOSITORY https://github.com/raspberrypi/tools.git - ) - FetchContent_MakeAvailable(RPiTools) - set(CMAKE_C_COMPILER ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc CACHE STRING "C compiler" FORCE) - set(CMAKE_CXX_COMPILER ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ CACHE STRING "C++ compiler" FORCE) - - message(CHECK_START "Apply OpenSSL library binaries for cross compling (old RPi) 32-bit ARM - ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src") - execute_process(COMMAND tar xzf contrib/openssl_cross_patch.RPI_ARM.tar.gz -C ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) - - set(OPENSSL_ROOT_DIR ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/arm-linux-gnueabihf/sysroot/usr/lib) - else() - set(CMAKE_C_COMPILER ${RPI_ARM_TOOLS}/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc CACHE STRING "C compiler" FORCE) - set(CMAKE_CXX_COMPILER ${RPI_ARM_TOOLS}/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ CACHE STRING "C++ compiler" FORCE) - - message(CHECK_START "Apply OpenSSL library binaries for cross compling (old RPi) 32-bit ARM - ${RPI_ARM_TOOLS}") - execute_process(COMMAND tar xzf contrib/openssl_cross_patch.RPI_ARM.tar.gz -C ${RPI_ARM_TOOLS} WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) - - set(OPENSSL_ROOT_DIR ${RPI_ARM_TOOLS}/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/arm-linux-gnueabihf/sysroot/usr/lib) - endif () - - set(ARCH armhf) - set(CMAKE_SYSTEM_PROCESSOR armhf) - set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE armhf) - message(CHECK_START "Target Architecture - ${ARCH}") - message(CHECK_START "Cross compiling for (old RPi) 32-bit ARM - ${CMAKE_C_COMPILER}") - - # No TUI for this - set(ENABLE_TUI_SUPPORT OFF) - set(ENABLE_LIBDW_SUPPORT OFF) - message(CHECK_START "Enable TUI support - no; for simplicity RPI_ARM cross-compiling does not support TUI.") -endif (CROSS_COMPILE_RPI_ARM) - # search for programs in the build host directories set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) @@ -177,11 +138,6 @@ if (CROSS_COMPILE_ARM) set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wno-psabi") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-psabi") endif (CROSS_COMPILE_ARM) -if (CROSS_COMPILE_RPI_ARM) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c99") -endif (CROSS_COMPILE_RPI_ARM) if (COMPILE_WIN32) set(CMAKE_C_FLAGS "/EHsc /D_WIN32 /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR") set(CMAKE_CXX_FLAGS "/EHsc /D_WIN32 /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR") @@ -374,22 +330,6 @@ if (NOT TARGET strip) COMMAND aarch64-linux-gnu-strip -s dvmbridge COMMAND aarch64-linux-gnu-strip -s dvmpatch) endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) - elseif (CROSS_COMPILE_RPI_ARM) - if (NOT WITH_RPI_ARM_TOOLS) - add_custom_target(strip - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmhost - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmfne - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmcmd - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmbridge - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmpatch) - else() - add_custom_target(strip - COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmhost - COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmfne - COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmcmd - COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmbridge - COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmpatch) - endif () else() if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS)) add_custom_target(strip diff --git a/Makefile b/Makefile index 0b859b8b..f382d818 100644 --- a/Makefile +++ b/Makefile @@ -32,14 +32,6 @@ aarch64: && make -j $(nproc) @echo 'Successfully compiled for AARCH64' -armhf: - @echo 'Cross-Compiling for ARMHF' - mkdir -p "build/$@" && cd "build/$@" \ - && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-s" -DCMAKE_CXX_FLAGS="-s" \ - -DCROSS_COMPILE_RPI_ARM=1 ../.. \ - && make -j $(nproc) - @echo 'Successfully compiled for ARMHF' - clean: @echo 'Removing all temporary files' git clean -ffxd From 3bac5120495a10f6316e1568ff0534035ed51771 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 29 Nov 2025 10:22:21 -0500 Subject: [PATCH 189/200] remove deprecated patch; --- contrib/openssl_cross_patch.RPI_ARM.tar.gz | Bin 5054684 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 contrib/openssl_cross_patch.RPI_ARM.tar.gz diff --git a/contrib/openssl_cross_patch.RPI_ARM.tar.gz b/contrib/openssl_cross_patch.RPI_ARM.tar.gz deleted file mode 100644 index 29c5aac51a840eb72c1672ea2526de2a742a3378..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5054684 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�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 z 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 +Y=TpLRJzQfvO>rSP7_EXaKs>s6Yx}$6 z{3&x|Av9O+3a!vbkWmj=RFXy`8%e4*yt58g*lyc24Wf#^I-e^^?$tClq zVV`orA8V`mxk7d+fPF^sxivE(Gw^~!=>;9mqz>8I;>yNS4zIElpLgqmYYZ>f#_CdG z`DwmeafO4^8{4mR)JrTAgOw^EXVs+B{dB6J~+VO{&ag8r>R8jpMv# zw~2A%>*Wr*;j_(Z6K}wAznUha$rj67<%*@6QmDMTRUIIdEHAgc0F}7B#9d)|donne zu$eBVgQQ&xRlbenH=tgK1(E$KeSoT4pd(G*um*ma4)OV)8Lu1ATVl z>XJ{I+|@nwty$ff9BW_3pxbhK21QX&s^ht}0c&I1t&Kj!WWgDfmRA{`%YMg0rs@P4 zFH&0dIxJ51JZ)kUbG}+)FPX5ElcC&}$Ccqwl?R5334?+w3>Ari9vA2ir|WnK!*B(T zf~;vM)`nWl@Nz!4re}>14pU62K}FZ(4P5zjc5w6%j|M=6y~o+va}6Byb(7>iva2=j z$~wE%p+#xUc0&_jY}Ik55oK|OUtIsyn&T`)+L$@U%o&qUGC!)<%k`@ceZ0j9D_=aM z;ISuY1DOFl?k)s5Z{(=}vf`uW))o%Yjcr6TVJ_6JSq*l=Hm-CbhAaUbB^d*ZlHoc z-h>)RkXCU+vEiLraZ-g9B>S~4x3{@lBKWJ;|MDnNfvoj$_9v8FxUH|}p=8|{h>bxu zn8X-FgL5r2UZVg49q*zvtewzdhlP_}g?CpYDsHD(an#ANo5?Ayip}{OC$|1t@)3h) z1JT2L0VENvNR`#MkB}F%uMZ{6=#X;TE-73f7Zw~WmawzjU{QqK0Ky(4Jev?J+U3Dn zQ}8S_aZs{I*T*q0EpaNFj7w^5`bQ8`As6w<|9;X$J2xyP2IVBnM$jgkwL1DY#m&@Q ze!j3w#t9b|YPC+2s7lbSqVHE-}`k z*jp^HS)Ulh>|%FIxY-@L73n#gW^G23ccJ-cFBIc1v`RKk7`B=`#tAx)N}q$C1CL8~ zt@@<@Db2jR=TnpXd8;L6cOO$AvKyTzDgKF`g?8ndjsWB6Rd7(VWZ zNZhBD?ry+~{^e-3b;W44LCwR`VNXE{&*{X5FuHpyY>*VKba0|VJ(?P|v;#v1_9vzd zI&H@oRvNd&!`lFfO8abUO1TyCBa$y&%FZnomNl06Y2WOE9XHGtG@GCv5Y4AFaI3|Y z%g>iqvy!ov05|0yui1HdpIa8{M1`H(4RAHXPpPn!FRiWzTqI&QD*TlC0I4U2stte^ z=zUDUDH2Po~63p#rYuf4QSE*1s=QYWL#AcMP;XC7_*KA^QGmDOJteowxcz*R{pmtD{L;qYLaMajdMjb#j3)7BX)FnI}A zbCW)KS?FK4lRkOjy^Wk=Ev&2;P)*K0zb;4syWFhnZ6q!N;F?HDD=#V~??n#@@mPD4pf? zyvkpC+BbSJgQzTS3_i1w*WI*FPy1&z{Ad=)1vSI8PfC`NOe!Eh2l7eDvhPgR59E`Q zWvYcRQt4@*Hd^+b>-vFw+KB9)L+dZ?vw-3ghR`v^Z~4Jzgs>0`fzpaZAD|iSgk}xK zpfDh<;^+r7sV!9gaw%I3b_g<~)g1kRro-m^0m>~sqpA*@xt?P#nC)>~U%I)S_l}2V zdpt!M{KqmL2LEpTwv0i&wt@s2po|im;|)JBndEPd%PWoXL?by)0Z2olQKO;as<~C( zg*Cp#&U~4p-Elz)@*QIyK9oIUJG$_QSr{%iJJu7zX5^l*8wr2edv?d#b#GI08m8^} z65iDuuV*h6mYy#aR-X-aTA^B>crfPzVa^4?WS0YP^H0t&x3Ox4_?V`xB6#gCK(WOz zZn?gpxFCYaXin++eiCelB$*%z$sks+u7N6kC4*(gVI}=wtPqtzR`3Mgvhl%2jg}dB&p!WTr3)WfBcid_=Q>yki7+|hMB?&XfllWDrvwl=^^DVDnYmV zin_~BiM=gJeK&v-TOPe`otJZ>bl)&eUXQ-Jt=5TK`-6(1Q%zy06&W8AwO+s}dalCr z(5UsYO{n+XFFER|B!w_~G;Ew7V0hPyaf1w=n527MCQNuH5#`q<*dBWFRQc2sL7kDe z8Bjn?PN(C2a8Es$U?PbSMif;ZsK_}p(|SGbN2cv=3nC&0py!~mWecYAszDHf4pQU|ctLiSn`dw$mV5d$#jQrK$gOy)ScRx*@T z2VF`;*OFPTN_8Aq0_!TEZf?O+Fo9>YK6>s}C1!Fvk$?hC>m2b3L4n(GqVV9k86Av~ zQHf1=ndoLZ4PE0DX=9h%?rd7{e}@c1nr6G%?Vuqsd~ffVWyIp%_q3b1a6~UR)g~I0 z*ySy&zQ?Ud*gZFxldY{&NS?WM&O~9$+-r8()$S%P8gZeBrh@ENm5pY{thm*26K`#A zT9q>1x)O0gKE&1Pw(*innRG4G_!+!tV_vOytJ@TslHJH;gEPBHN!O;BgP^nSAv()$n15K>2sE-7?(MPiDf zcTlzX&e+_cp>JvU-9@Z@3vGPuq07XN=Nx!pLc{euN~?9PHL>H|TUJ=w#rec;HDnX( zQV!O@Zo@;?q<{W}wYO{Svb|PVSGNGIKvKUn+fa@-5mAg!-x* zBv6#^&0U>%BQ{tvI&1|@ioFB*VfP*eD2&zIa?Ck5$FJ#b59st}P?3eh&+%(Qi_dfO zY|<^<0|e?8oqH5oEl>`U`Z_$fL${M;o;kWb=Zsc#m7v+c;h~Q12yjrj zibK}*opQ&W_ObEhi8KD`akbkjVUQ*)C>q%68io5^5ql_RRZmXnu9~z<6EIzN-52TQ z1-gZTB?J!$eX!J#iyL8K6o|$3mIEbB28{z8Te2@tt_L4IXDxOmLf+2`!o_{m=rLW_ zpbEtFoQeu$X<}l6qi{S?(UpN(1Lm0RW~*J}mnZ1X2Ui%}lG!2Q7H8J6l2tQY_vMfZ zLEe7`6&vy*q^}HYB4%CdSVlv`Z?*!asH(S}eQqtACX(<=o}Uqs568e+D~1k305Y`x z=8DFdwYd5_p!E8Rjzbif zt=W7+JDp&SvfVAW%dmv9biApp;o8y!cXS#`@di0mWN787#x^cWRZHo34OD497G~93 z1eA?N#DI)g>6po>NZiO~uCRR9S`ihq>UQ^4$MIMMiXEV`0#%;mi3@d}H%&!^>V+Vr z?3@XbKP{KCKv*wo0e-tk>OzUKlC~|hh=+wxl~zJS5%hbU5@cON3xktB2rbzuRX8o@ zNNYb8>G<#&v<~eDptUw6Em~4_x~1kdtG(mDhn^6lIC^v;m$sCmn;oG(C1;`Bj>mH$ zP#-s2<1it?Fw||RvNVDQyTC-Ml zL(bz{=XTSWz_T6-a=M|?F*EZ*$S!I-nFI2m;_6rpF`GCbZ^hk`TI3 z>s<0iv8ET|R=TrkD{rvVx#E+0gEjIsFC%Rw$Q$_> zOkAw;E)!Z>5z#fLq<3RT1WWjt$LWMniC9Wjb7l1&pw~;vPKRoB+P?lIWK%KJ8nup# zfqEG{5QuNfc~o#`N+qGdhYAWMpNQs~Ts{X17TqAc{^vqpAD6=RVuq?0wAaZiUdGf! z`K{93Rp;s{0?0Kx}fV0LM~Pk>D?;7Zd@<7x5wpsjvkz}f)j}m zJY$4i`W)M8Nh0f-sf3UfNx6GYp=?q)8Km3)TDY!ucw?eQCr47AX3LwBx>D;NLAV0y z0&Jlz0o{!iOO5Lo-m!)5f1{(pO%^lWE6y1{5rIRTRd}8%q)sTgkr2i~iLf@4b4!~e zb$P(8F%YpD&sn1g*AB@B_NJ78gI}SQrLiFm-eM+36n6lH+|*tABNaPhFjh~5s@7(A zTfH#ul$#`JKiBm2*Yg91t6gi?8Lfh{*j7czB@I8$-oL0n-10J=6KHuINRHWU^kK?b za`nl^jXroTXDkdHyQ}^1)tlS>vfw65pDc|obmGuFTLb0cyH0&_;dawtImu)W-R5`| z=5IV&4g?m>waMWaoZi8fBaLRGv;{fpC35nDs~zU#rF;)Q^Lg(;=^~#p~r-r?k~Lo@N!H-?=Hu-T0>Up0FXYGyx87|ZO6L7 z<9f5=HM<@EMEiQJ+S&1W7@=0vwGXWwP#THv;7)_|*A?jp%W5OI;Qy_TxyM~Ct{H*p+k&=KeJQLA06_z_^-r&?wI z@U3!ZCm2|sjXEAkJ#hTRmLj@bo8_8P&y-)=D4`Ijy=7-Y{b5?|W~W(c)<48C2>7T5CVfqJF1xU#lBu0CAL z7oSDfGVYST+=dB{gHzhEavr*C8+_pY;9?V~=tKFHo!B)75wE0vkp}#t2FQNW+(Vld5imZ%}S^4PNZoi%1AS{$M!@N7`pc(&b|3ws6?1H z28HE`I~HYE3lvH%yKcUVJWHo3l3D;egDUCTGF;s!;h z0`vH)RWI+kMP?kL2}X(bCq{Gm)na}wyPnS#mP={)FO}f#pRg4%Xo$0gg1V$6okh(?C{MwqRKU^q} zCry_LBEqCz&+!<3vx$``qVhc?M9$rH`Z7IxuH?8GcfEG{GCiHouV$AD&pOUK)hpwa zX?bONc4=kqX$R8ZaW}uMdn5h=8^jgbL&uRo^EPv^S&2jnvKVRytrR;et&1Kqi5Xd%MQ& zR?w;ERu=@$@xGj z68g#UH0elu!dwHkAY&IVL{?egg;B>t5xXNG_`h$q_GrS(cLm&C3tkd;!+3jVv%wA> zoE>84d|{k58b1$_Exwy*$BxxqDt9{VQYmqRg%fpb&J(55!t%yksgyJydrWu;%iFmU zOr_QeE6ezj9oIvalU|grM+UeYV%7C&R=EWfwmdDCF7so(p!8+K$IN8ZYgxm&JU zbh!x+LD0abf`&`x+{KII4mNhRiW}FmQeaSUt5jlkQLVOx`ORt#)~OY0HUd1$wh2wO zDCXCG4MtSIm`c;!fNpSX`KpOt1yrHT1?40@mp}8%M+nURNuI8fb?gG_81Vo(UvxJA$HRRJ69}tAIqb-wk z9QOucll&tOhLAPL@Vng3K8?`e#EWY`>#K!88mu$~6jtm&O8o487-E~D)OekEWz<(W zotyTr?tU%j*hmbF$;Y0iVw^MGfhvVDg&=yig9r>&@ov@Y=?qxsWMVOti0hbl3^j(R z9Rvv=py!mVR`wws5~Ggk%MgqrNMJJx4(VM+X%5ww(Hd@=Be=6^p<~yv0U3y0&I`hX zkSZUR}Hm1?{c~|7wm+|G#|NB4X z_U@U@%I@U()P=M7ce_=yM4CS zsMNbv>uj@SHDCgGwpp=T6FW!)_eRf6Pvdv`{7g#xhR@W@`E=SyPtTm2nm#{uZgR#* zO--JgoHWeTm!bl?b_bNWX>5aHx@uXipnUz_|4;g1>;AcBYp+d?NG730&P=A#7bz;v z3bZBqW?yYL--B_Cvfa*3vu#h9*?Qe9B7kicEj%b-?mZCKQmq04U{#sB0$PnbXzJ{h znZSpqc*ax73DbPO*>ycu!rGe!eX8-ulNc?h8iy?*`r1Q+Xx zW_$ZA13Jr#GqKazbwVESLuM==4p;%d2-r`DJh%XZCP!o0fjX)F)b6dY@2Yc zY^}KQxiQ;}lFf}#w%DwDC==(TY;9||dCi)KGRzf4*JX(dN%MV5xKwJvV*c5cr}H@; zvj7A`8@%95x)0pOGKJQgFXWXVy?x-G&OeU_WW|+Bg*DOZZ$El-SuL13*#|B=KVMiX zWY-HT%X5p_{)^nh@umf2RBKnO|EYYAf^5_looP=-32c7l!RSa&?uCSo@ znV-7hlP}<8Fj5r#0Dg z`K9~X zftPuOJi`Jn>vH44dl`LUx!JMhLFW|ndH|JEeyjRPU*E!ZSb>?Ar)uX=YtC#CQa&fT`qzviG8F5l*`<83xZ;Y9W(<*`){D84XxG#J7(q=~-6vg_V_@+V=ij9G}$I{#+Z&PcN@Lw+w9?+)L59P1lxs zDuWK{5smG-MU@USW3gW>FMvQn#RPRrFGpa-ahz!ADzzm#`SFJ}B%sZ*U90%nnFSR+ z$GOoKy)Z9vl*~TT9hIM^hMKn%G0!Z;L z+Lso~H!edd)Oa2rIjjxlxC&5;oFT~@%uS3`uVYCBAx<2)gmbZ;AO0oAl8I!}SJZ%( za^2hjL5=|6ZzO@X7sDcqT~;(dzr0y>4EN!eS5}au49zk31EZQ`LXqt#`p4RT3O!>1rX0gGza3s=Y@5w!TLo zWH(yAf%h2s5sgTlnGMVQv@4w(Vpf+mA*)jn=pMsvjhk8wnqsnS9v=a`Z-=syT z>nlDC2E;~A3> zDf+NJYpH=Is)oxb16{}h*ceK?tW(w7=mQKM% z#T>HI-JwlWqR`%@5zDbwtr`v_D4Vesw~h5NmEI3iL$ae|MBxM-1Zmq@St)R}U$#49 zVfgkN6Kp|Ah%&|UNWOr=$5*{X0q8Y!`nWGxG*9TZ0_!Oh7H?nxtgj8pwUSuqK`L;J z0faF!U_I@>miAvqgVzw75WsYyR;_jR5_-VC6!l=79KStkTNVBntdZb{c7(nLle4Wo z-O#Qd8Us0^9<{cEl6lM3OyRK~_n@PcM_XcB2x-QM5%(}mDaUA4>VXTGG zKtyUVoo3V6Qf0R#>K8{6%g&5mdb%k#9^e=n*k9*O7{p*{Eet=ptpe=~nS**`klbc8 z1UU$#6@>uk&afQ{O?j693dyqJ1x~$Q#ihhz+1}yn)s@+&@^kC>W;ak~^@v~+M|i@3 z+=T?RDJ>gA6gg+Gm<&pdlszcAz0909K-sX_TeT3{mQXP3u|Yfa zgRfe3i|(q0qF|R^RKjU0len*}Z;vr5)j)MP#+I|b3MA!=)WrRAKQ+cD?XL>;*@tpp zJ>#;VuO_vAEvf1}L~Qb-lYM7eBo$rBE=sRt?)OnP|J9= zxI#plL#q<4MIZ>YM^usLpjsaD%A<-a$K{t+9j^TdYadzl=xfVAsHD0BzD`($*??4b zKaeQ0B2<5=)#7pmSH`tpl^v+GwZvrs_m^fc6{jVv6=ok3L4-p(goi`z6G3Q%ZXq$C z^o)?k98lNmwY^8v--7~Zik)bn5r%c%KK)2yB&ga3?M>ctP1l{o#hg?b8Zfa#NpSsp zlpdSgr4(%Y?^DCkmwT0_NJDk2LHd=Sk3iZXJxjo%cCA1}Lf?{^P&!vw!w%fLwB1V} z>+fGdWQXctgZD6*m%!=^?_-+%FHQZbM-FO_8_nhCma@e>GYy;HSf0a6DQkYy)UbS> z4N!PK!P+-GqpNHB;bTW){d1vJa=$#dHPt^C*-wXOa}4eKRat+gXr3LO$u+$1i>rUN zleupna;|xA(m63@>s@(E&$XUzC)nn=E3iUt+0HsNCj~{r?KC-~f1yIYW4xFb@_MB_ z(7pv`+xf4#hLy4e-IEQDoIi%Fygk$73g^7M)fT8 zEONleZ`xZNQ=4IGb4*`WB&s6`9--3jO2U1Cx4<3UV|S|c+9tb&?LNd)NOtque=*xC zHSI~~WkB!=@j^Ecs5KdWFUCoq>)=7YF0KW=mhP$d^OT{ib;oLRCl3B<+}quOO0?Zl4CIBpPi;G`Hs~e% zVZEP2HVaDU`|$pYlW7}c6g9fLJ}Bp9Ae0P|&pz+^TV39`G)vca9aiNRfq()j8PE?g zH@}pXZuL75?1dj-cB8PgUJ__H0L}|P$m&vd9WOomK%5tTkX%8z9q)j^3rBoc>QxrX z8v{+3>vY`{Wwwg`;cFG#i~un+mz%T{0ga1(UM9>9oB5b&Sl8hjZ}cHD5CRY~5cYDF zIT==NmNJLuxQ42BcX#)m9DSb?eiz6gCAw4UbM`FA7OFu9Dec~H_~LWuJRHEK-{u(q zxxPrjzUve*Ne&Dn=b*cZm@T0os(VoeWC(WMZYYp$BP6?6Dv@ov!J`KeNt8RyU1rch z1~;B1eR&u6WnJ?~_$vUJ>s|;%0_5R3N&}92+{X|4n66qK9Qb#w-DZ2wtXt))wlm_v z7}GAd_a@B37NS82pveLikZ#;1Av4Kv4^WRvam_hPm-3fN%LI75XWTD)u-!uO<&pWrheHGGRbrnu(B? zaZsNV+D33AU*p_L`lLehl2MHNNr&fM%ddI5b}~3G*_?=BNQ?ATZqoZO(xALxkQkk+u!ao0FZ+t{)I{x8E? z;Y_nd{ldAE%SR)oz*rm@UHleN8{;iO*I_=7CgU+GV@zOtR^djmgTj3|?gC(>Ht*LG zv~tpq^}S(O-@9L|FhCL{izlH+?#nYI4T^!LU}@H?CBmd9*$-r29MF&(R#08GAt|}Q z8uS9

TrK;T>#>qGRhsioM4SNysrIc7Wp8HK@Ip!((l@kPSU%A;YlPtqOWG3hijV zJo}>&+U4nD@cwk1j!sS{I26Ii~;h2oJq8+&|WtU*_0AH6&%lW1JrL_cwdpFHQ zB9Y#B*Siu}*ya`vf61{6XVS?eq_g}XWu<6Z;$fF&H$V;>%}Xg~a|1>spY(I-ThOQc zd3;Owv_Fq;)jsLZ<6EXr`SbYJ<H-R;RmNb0dapm&JK<6JaKJ ztj}_r0zMV$?Ec0dP; z!)#gYGwhw?AfI_rF1Ls#;7yJyCBfR{Yjl4(SPPMLv0tR+t)MCe+YG>?5z(MF3YJ`~ zO|cs~ZLmMqdBZGNowJ~LC@8Z@ryv0hqG=BkdVZ%?>DJ3_$5HeRGtsu{tNSV6$#RCDG^(uP3?&UapOr-0_5a^&xnYPY$}-?d+Z5*^;x%La{z= z6M7uTn0bm*JwIKQ-QAC!T4gE7RjM=xP_=%O^n=GL#N#3igl)f)k)sI^?Nov zu4oCcxW&`1QN2w*^e>HDf7nV6&oG)RdIW8^S*zDN zbP~2(uC$x>1gik}-|;ox;dDIwRt=9g07DlZIm<8&s{j#4SFlFG*2K1l8Oo;B=|Bm$827s<-dp8bouB<-)!}&|9l+5( zJVQ&TK2ZCO>bS(Pte+anZai)(+IUeDeXftQ<4wzjfo5rxw=Jv`q2PQ=7Z2oEtwS|I zalHC)T2pZ(?&!hpF6~R(AYZ~Kkxu&aVt3^QapRdJXbAD=-PYyxNF`-pAIgPT%+ppvF^m1cwm#%kjLWe@o zN|W68mphN!V!mBEKes!zdY$j9lUN0`jvGnU`K0}MHkOrKlm0w-{2<|(_UC!7SXh@l zC10TJHPQiWZyVNY*ehhI)f^A;EpT?8#N3nypBS;T5p-XQCq=lKj&-8EB)Rx{9w`0= zzTf?gs1l*IumRm}rK?2=3rCV5yI~km`ef&{C_AlB!?U|m=oX%2*QDqMA+Ta#BDp7q z^%fT#meM#GPvF@>NUq&hq6`x1!_SDE(e-%`7}nqzVF?z#ZCz({Gwzh_9b`m5&q<^< zxNFI}*E)#7IPS~ePC83!FP2|lxWw3&02UbX zQQ^93ZFaX=WMvjhSzK6b_b=MeW!rDz`j<{#2k_lqCq^o6TS`$-pif7)*ARk?Y1IVwb`(oT|%i`@`=a( z>P9}%7!HT##9}`}<3EJZ%g#W~HUsGSE0^0!SyibE<5VxCDQbLQ>%Eq{Z_61BKHU%z zYcu0)+T8_a?tFz~OjsCd>Vs|lYb`q+6P8?lc4I;GN$s63vhkqQ>f(&8vDMU{F{-)x zod|^xYPJ&LG%$mD@Gg)S+m^91Xlu(f#9kyWCaH1h;;YTfu68S;wUzi|i+->jHHv9ezx{4kQK5#0oVeR#hs=?5nqt_xm(g@N`JKC3lQMeYys9+|S9XrMTBnKfLFV3#CfoTl zCDjJ`rP7vd8-wOG)iC#JEe>n6%+DEfKyGWiK3+~r93;xg@x&ISdeU7T_|NhfZwbp$ zVN8uA@WI!aDf0<4A>usc7KJtpt?CozDf3Y)RC|t_s1SSygcQQX9UAcMd|`F1goa3> z39S1Nl?;hf+c%WZ+=Ih7X7kRwwdXPBPT{f)0cz!HsnxddyiZb`at1mL41QnPsnx3< zVJF_C9d~c<;$AA33H%d7n&UV20xx^pvb%NH;gtkiDp%i=5VCf7&u?yR(L%Sp z$B*Oq1Lg!$g9ljLV#7d-fY_htKr*oBF}4NbboS_f+-Z7eF<#fI?6&y!xNW}IYB!;M zwn!Vl03H7YlcGeK<&OD6`2{jvePQzj+K05h33N4WKNmWroH39dw-iQ8Q7nz=hVw-0 zT|&q(L##TpJQnKHY_`~-E}qvo5>r?%U|7P^(n>bx z%)FRG$2GKhW}oy@0H6-aG(Et{Ai!)cSIRCemF5boi}|8!_TU0e_XEro7V>NBe!vSs zfKWyqQ3o)c8URov>Htm#0ebQnb)tOIx<5HP#;{PLW?Z*#zODiDwv zdz6Z~Vs<&_1Dxy&n0tOXd#Ny|0#5e@|{zybT+#NEwQ|$ z3*5;7r~44aKC+&_tVZ%A*b7>sY}L@lfX0w*YioITD?g_Vz*+{k-=N=Ig24_<=$Ew`xeWkrPo2Jj_47A|K6-#Qi9Cn!2WYc{rOu*!lNGxL`k zUJC;{UJ}<53I(-tbexpPb;oPOiy7)7FMx}3tM%Gdho&|-eqnO%uM`%1s;^YCwHcqe zx%t`o;9Amr>@oAC^U^sxq&=!6KNWOdmYyptPffDZIpHy?+4bjF^MLAw7o^O&{CsIK ziIOZBbFdAIrgv%fI!t(**YV0N9{sbqD7AC3CW>*{F2>6H{LiR(XDUfls^*1@UJ2mQNZ~Oi7m|x7!GFeYt$P z9QxvgB+&SVx#p>y24GtP0aNn(-(3FIG+UCgk zIFsZXQ?ABWF3xhRoDhuKi>`KGFxf=UkHIl$>q~r8Z3@$00&eY4@{Fc61kt9gZDAfC z#`3tY!OczXmnpz4(`(_4=}Z54?fk#gYnx~FG;n@+er6`n{}X(e|CgE6Ikht3)yE&1bNKH&4jWGW|Gc)I>XW(6WdJ;t7{N(wWDfIu9 zKL2I^KVOp1U&$}cMhe;4kxQM~rFs9{IC3!#~EsX~Q($0Po&mydB@Se&E|5 zF`N{Ge3(XfiYPRG6!Ks?NWUTs|HmJJ^kz!$D@BYy|MrM+h`*1*zkl9%Go-griU9zo zLjsNC5igxKSaRMy^Lw4DuB-?jhg5|3BKbv}E4Z!CM>21Wt$#Rb9ElkBjZE+8+V~HAz%Y-*{vf(ctYol|_n`VtJao|5 zGB#h1Mva$&!;#5FeE$VtGxF%joe?7zMS4*)f9vfdk++x&5)5vEIF-hgzvS zk=~)q$;ilRHu_QqYqFMswZ@~y$6orE(c_1HK9)H(8!;DOiW+kx#_UUxjFEsIe&l4t zI1pd$#qMNpKbAQ%@|Uj>+$k9yo zv0GQn!>eYd_YpI9&^$2r_?hFzC!#<2UB^E1#fUMIf|fab_nk+gsnJ+3-g|zr_nlDY zW0^Z<^lj0|>BnvrN2+G*{rMlud}|rR*UT8V5=VbHbLiCV+oo~Eh#ijJijGc1B3H~~nb^okCi6ujGJo@O>@HMR^a_X&@b}J7 zjxgDM4C?P-#>_;H=3=wG(a((j$xE5%?>vmJZb$EZ=Pe@&*+&i}9z6}5-`VUv2EXDr zNB{N9#xE|$P3)1sa#%pju@Xbig!*J566t# zFB>mH{!g43IrMI$chESVF`~C)FO9?wm}6NZ6W{C_X4J?(A2Cjj7^a!%O5eTdLYXnxJMpZj7N`r-E8s>Fp)KkyZ4V9caOz0A2AOch>t{~ zS>r8{M|%%_4K({hrtuV@G-5Ghp%*o7-d!{!r(^N#?VH~|b~Qlfv59Y9%&jM)C%*G;KXY|4ny|V{h(73SW3C`;nya&yFn4-8p$@5fsFdk$Lg2gVHoNPsGg8 zO#EkhiC8vs(}<70{Nst}JOB1%+=zEZjJvTr$(zw5AIU@y8~1J=dgs41uE3x&eH6OY zAtSaKy%2+Z#*Z2qdI&(OBVr21F;GIeZgD z?q&GvvU%_}=%bjKIcdzk&3JX>=HdrldT4Z~_qj}F_07g)^p!iT&qSk3e`=&=Kl|T5 zbMlIjje-87?*ja-f%I$?a*T@ba*-I1?~j2(;rfq`27u<_W7 zkxwR!tof#RG!M;)QgP*x_~Sq>l6VN%Hu9TBW#E-2eag2|C<>6n8 zoa${Ek9_@njj zVv$(%Nc{E)^t0LC;>gHt80k%#U}U1%-1F}+F84+cS5sF{jYR&MF*XuQ+%ck`d()wp z%@OmJY{a-zG(H+h+%)bV`$X&(_SQGd_I~BjPrdQo(b&z@-`%`@JNlC!I&IuHe-4JL zN51bDE~nm_`t|6c;|HQ6w{B&k7tWnP9rn3DK7L_s_0`2wQ-AYd>|p%PGjGTCyRwRX z)rh5vhZjc{GZ7e4Upj_jdhiGUJ$&=V$jg7}(0@r_r;Xe-W8d(TZ^(XT3Py~-Y__f( zeB}@nA_i?6#j?bD$1|(3E8pG1*7oIpEKe#i-Ol?{O*hK$OlbO4FB+*=4!?$?wH1_vA=i;y8Y2LU%+!jUyk7o`Ci{WsKyFop(NZibQK9 z9)INaGjIOM@|}@y_|8nxe8))iI7}g8rV;t2`0pB->J^we9K^aZ*b}aXC(Z?=B?|Et3_+<1=Cg_z<0ImDcclN&LyWV#1<&z_s zM?e|DsCUJ9f9_M|XwEc7kG<7=ESfv~i1AJn2I*|O_VDcKX!L&M-a(u^821w9y<3NF zC40S_v17L~BQVH*G?E!RvH0V6jfZnF7`LahC_1Kj(HM>0eGy@LKb|-mzi=dD#9)Z| zs!Ti*kB&Tj>)>}Bg_)`OAK#oXPOKVF#qU7pe{uBjmq+5|`={b3Ka*L!m3sR_AN%kz zV|L_Td@%m>@6OR-JXr$=zH>m2F#-}2U|S@ zro59eTd*WViZS=~WEB6i_I1`VahQ&+lBcoT0=&TWe#y@xvRej2gpNbxRTjE9I&i&qT zGqZH;oyH+t0i1Xfu55bdt*@Z*Aac_fxe}QJfia9bw~u`ahKB=2ECKCd#_s;}qxX#~ z33Jsr@!`uGpS%^j9Wmbe(U)Up6b6rxQP2sC(a1x;am>h)%i8g~29Fed~dTf8u9e{)(g#xet%;9UqCkav0ZhQR7#7SMH2NPJCe`b_)i= z58jL!iFoAn(nj6QmQq>3GbH z-^_dpm-5D;)2R_aVopCzD$Y0^H{N=140SjR1vrUXy%W#B@eiU0Ve$6W?~hL#E9QG} z%9b6!XZ-Z~fw%RrtK5TZnd8x?J~|RL51%-G;@-dcTrA#7;9fu%(ltnXkiHqx3y|Ii z>02QE6-XaoYKcgFD~brDk3sq_NZ$9-*L3rPP8((gd}T}b~1((glp$*=K;kp9^4KK~xlpF;XGNdFh4{{-pJA^kT< ze*x+LCM9RkoM(jZ=^Taxiz(xckZ?+a-TGljk3c#B>1~kS4(T0`z7o<`LHbLOPI4Xp zDE|h{YrHeUmd#_3z8cavq%)9GkR~C`K)L|w2}tjP^mQB-=84!D=#zu*JfuZPPeXz+ z-dKjT62W;KOrUtab@+Z3(q%~B2x${i1(F463%5QXy$8}&NV|}lkZedDNL@(RA?-nW zFQjjQgk#n%NbiUA0Z!wC{QJZ3{jHF`4bpc&x(n%JkUkFSyCHo)&-a7y{Z}FVkgx%F zkN^Ei`2G~6zYgigApJO`pMdl?ApK29KLzP!NIwJV(~y1+($7Qs1xWWHeFoAikbV); zFG2eIkUqW%UJMjB=A$^fQ|2_Er zLr8xF>EA;7V@UrF(w{*3Q%HXX=|4gGb4dRg(tm;U7ZJ7;KL7~^eB%(L7^G21Z-n#^ z?FJaeF-Q+bX)G`vi86X`f$z6LdOM`A#EBuK(~us6lz=n_X`JUh1K(*#laQt$%|N;U z=^~^jd7iI>@42W~zIpijDgOIu_+Emv3h5b08<3uZ^gN`mhxCn*u0Yy^RDo25WI@`7 zRO2|V!uKx!yUD+8`0hfw2I)Ga_wwi84Bzj=MGvI+LxLgK_%Ni8@b}*e-`@rabh7bL zNZ-Yue>Z%80@C;M-#-A~KLqJhkbVTxk3;$iNI%Kn{}lgznScK@eE%$@Peb}UkbWN0 z--YxGkp3Q|S0KF#>6ajV7SgYBxPJuSpM&%pkUkITHzEB~NWTT?p9^)0pWlYxzXRzD zkiH1%UqkwRNPh(Bk0JegNPhz9KS27Akp53df6j6IXZZdvkp3&A{|4#5L;4Fykpt}g z0r)-$=`f^+ARUABFr?#<-VEt&klq35DFGKoi4*Rq4{b@*l7t($H>=pQamH++A@cmgxzruh2D*ygR{QKA8 z`yUH@{|5j2^Zfgt!uM}M`WKLX8`8gm^gED#7t-%R`h7@$$n*XYeE%Pi{yn5Wf%G3B z{VAkBgY@T+{xhWi3h6H}Kdh)}XO(YLy#c-tLwXaW&iGHu!!!q<292su8vt z#}96Cord&INRL5!98!|M8;9?+kkXK5Af1QwB&4r}l!Y`4DFJU<2BKMm>A{P*AD z-+u?be-6_B2I(`9UV-!~fA>r9{mYR40i=J(pZz*~|C13x4&d+4L;6kO*+1uh|2BO8 zOGsaU^hM$Q{|Hb>GvW1KWu#qSWV6Q_KIqE*d0Yhh(Z#QA|#b438hd8Q3z2f zgj6adArwU=L?IOEKoL?&2q6@eBy>Whl>G1Iw_7u>@9VnmYwc<6z1AF_^E@;2EPaJr zlkfXBjYz2oh?I(eNK5BXM5IMJ2U61A+Xw|jN<_Lr8b&vxOS(so957AR z!u6bSAIEjy&v_DmC2A#fl#?1GyCP}Ae?Buw`d#5>tK$eyP;M=NMxTOJ&sI+N`^9Lc z^Z+;r68K__^~t?L0{I~GV028o4$W8&1VELEA=(UYKoQ0j;z-V=X_H8j@bcSL&FIYV zhe(k3TLIL1_NaQ%^Ogq{q;!PQ?TEn9B8cJ{O#|5m zfp=hYu+36eE#51lpG3F*-*+vth}GfZ_nuHt z;7{RG%F#R_j>PZ14=IT#VG+>$lg>{Vg{OrtZ}!xTC<#v}&>)al!06CO#dwQM)`%Q> zQY6XSzNHYzwnB9GD^;LKyJS1_Azlkn+flD4c?IPx)%pFKHbT9CXLz$@L?5+ zgx~KEm%`mxMHA%&(a2FeAWb89RESRR*>cyGc$mqLM;~Ol@_S`j#JHSX)@H?z zrl0l?`aaaR0ZB+ab#njc0{`%V4}~;-2L5Q!=PU{z!WP2fKx#S4!-wZYApBN>s9+mN zS1s*VLdigcoS1(M=_USq-&-6DwH}$==T8ZpI#k<)uLuU(@ys4%krWa>4df7@d%}61 zp0QFKv-{HQz8Rh>p=uyQpo9Q<1s)Rrgt)D;W13DMZx)}b{Y(2p{a!Zk#`jUac4ae> z3KADWQ9PDF&J|(@{5nD@9|t)WVSFO|j&?pX>Ie6~@ zCLd~Te35p=6`D_^Ad*%Bj2z+EOCO}Su~AT_9O)tXwl%tPGJJ0;a`v{B^JmGEXJjAo zs{*C7o;4uo^%O*35WFJ4DG1(uW>%3UO{6Cx_(m3WF0J}$=7I5?60=&z)_8**;X0m) z8G*pVG16J05Zl0qW)w;HAwqh1SCZgp3ip1Kgv z;O*jjw-X)`wFL48Qm>Hj;SmHqd!~OFX%f8aWQ@go-fr4)OSR&6Pn_o!YX|0)3&jtD zXu`^NrIp7nPsK@J;?oC4$lZ6LZMb_NnBgw(GTe)ya-nY^-5?~Edw7R4ku-^N;8wf+ z3gst!ZG!1`(k!ZT0uZfq{X@8Q7V%g5t8zj(fG6sQd_CTaz*o4vf|$6%T#+peK0tZ|6w#jLe=_+;bsHz#GKlG;3V(A8Ba9 z`ycbo2v_(T&_N~iE0lLkAXvZ$JPS6`FM7PaByK2uE`|%I(uz|Q>9JLFWLYmYRuRIVg zCviyi9~ElbIah=ZiNE5d1hL-P8@>mDNxSpe+@moeIcGVF6~Z@x(qp9C@vYuU z+6Q!aK7batf0CnoLiH7oEl4v^RDflS1_Weq{nF{u|iS$_^OR_X@(Xq63AHb_7H##`0Og9c}5_LZxp!SPKA<^I+A#B zk03Z`X$tlLL`Cz5dd2VK;e`Muo$Z{7jEOKOaJ`+Z<67TXgTN+`sN*g&BZVl9ln*a4 z=<&+yUsRTOVnN0$&=oP?VEfh;!#jstA^Y^=93f<^5|sEK{R{Dy@zFuAv!0&c1JPX} z$)W!V;GVEj#Rd16K`^c?c$dGU_9U})|J?;O4xltf`;6icPw?OE5n^PfaD43_NTVgz|qU#V@u-#Jd*Z1Kqy>A z2>!njsJJ+bLK6aM%_xUR>F~($NzKSA9_v$;;u#aQ8HMlKnK7^Moo8}Fm)ou1&XL0R zwPmgk=UDGtPwN&DGMX*-#05{Bhzb8ipoQFH7lI!&SIC$DIX;TPhA5N9MUCip;7L3C z9RpTU?i&+B@onWuei1L?-FZTw^s`JqCwy4|9t(a<(4!TJ!~3rY2ZHW*U`vSLcE!W1UGM> zA{E9{``_T)4drYNsI_9!ZNj*~|LAZ}06(A5`i>4P|D!`-Nc)TbjZ4Bqs*5+$uG}H@ zpY-*~8^~_z@%8=_|NnSU*6!6#Ka0OdDRqG+zI}*v#;F$MRJFGV5%II3wZlBt zPsi+@0L2shNRnPWiy+rPK7ofxM5_1{fklDYD+ZG;{~@~*$R$9SM3zKI7sxI^pG3`r zM~8pb{-r~R$<^dO4`~|ltr1rP^pUZNs|nkA3k#ewP-=zD2R{{$wd1<1;gVby{n(h$ zmGVE@x3fIcc0uDoNrSrNUjFBCIYH}kJP)YT2-L|Tae=!ME7C4+58Et_%klJrC^~N6 zxDJ!J1sZ*R&$8INWxT@Y!upDc1y3OuE!~ZIc=-MkUk8Q(gZm8PBQzy+4ybSP+ zz_3AU+v&4dz7n+JBZBC%n74_V@SgA7UVnI^r};nG%duvaue82^-W_hXNI*dGD{B0y zAoKQnW{Y^vF(D-?*G7F3-nnLb|Lvc z{|`o(c4FTAdhrU_BSQI+^vxY7L|w@HiH`#bI=mx@;9oj0f>8E$_qP|6&`mkwL-oTp zY=bVrbg&yGl$AJhmk3_oAWDD;)Xk#Y#)IIS$cZf|qUm(WV2?G8Dlz#!?`uC?cG;NH zL_s3oP`Huv&+dA+ocQ$wCE81qGJzNwrRBaf`5yWny`?%ShJ~ zG6>`!XnH2^(JouBNPws>YICk^a>ohKfT`B|QtIsEeo_7J2AqJiYHjea>wEirwHK&p zlhOm$_L%3dD$kv?N!2fym6WkX9wHxflRAK&JX{1 zQm|XlC|84I|Nb-Zm4RgaXGe6tn)|D3iCdqUA9?HfIxUmH33E2OUC%{sQ9)xPclF*e z+`kXsMp7fi8J>FUjB|sP{VsNLy{j@%nXEO37j6O5Z2Y)-1}Fw15U^Lb&Q_j+mzAFyLm7s$1moFmLJ zUC6#fHeS{L6^<+4=*;T4UL?8`9!4*%VSpgM{9;$Lfh=uM6t^PqgF6n|-p z^XBzu$Ckf0&@Lm6?<4?0Ee)P({lp5Nqc8mYgX*=39frG|rkTnC#@?}w=p*oSS~%a+ z>iu1^adclxILkeK()*ClO4^KDG0?Yhxf z%B<=c^{wdFCgeBXh#A%90^Zg(@5M0S{WdtoL%M{rAudByl2O|aZ+oNhcCIol=Ry_1 zmd>A@vPNBbrmR{T_Cng)P41jmmyr#ytKFlS)QrcNoe-QBjZW~-4{d|MCrdf5CV34D zd5(ul0fLyulcD+~C#XP^_L!9Do`RQhK__F0scdiZ7VFh(e;WSJ1(Q`8yoVoq_jsId zmw|L?^UtrxR@buG+-_){!+y%fOKNT+&!wjKE9|e^Te}`3cR_7fb0TL)m#aTxSrV6O z&YV1~AfCG*N0kq;SjY3MlX53XbOutp1?-5~TQ{X+RLwDoqgE~SD`S=LECGgSsEzE$ z>Bc3V$~^L(ah@vNzW`nc9{hvVzZ#8uGw1D@U7+I{5t3rss&b&)<(7Tr+Sze!=)T($ zcdAT#ta!D2VKrss*^0|Gum&z)f0R9a;oeK1xoTlVAC>}6XqmKYu0Vd*azkaJo36-> zTBDh+=bTzH-_JxzAi#R;r>TicyOxuu>!!&%Tx#^NVcDTKBZJBOYhd_S_#+XeFti=F zci8o(}d>+iMD(aaf?PB<;!M zRQFq-s5Q&agnom>uI(lD5s$d3C^}AP#5aS#>?xW2O9#+%&2Ix3%nXkBW}H`gr%6Vs zo+O0vN?gBy!$80MRfaHnxdAa5r*eD!GP@j(H~2-lu`!ZX>mB1GhNWO)LZ36zD+cB*;{gt<;ugz^TaA;Kl)g{&$Lo<}J(%2b&rQ*ns>43b z%f24~J6vyc{iBP0Sd0_s%k1<`@^(De=equ8-$jIDMfqz1LPn#IRnvH0K!&N2RiXIz z08{Wq4N=ICZvFHl>Te?a?@6_vE(g@zj?Ou2w$)xUlE2k9?-di)Mp@5h1pqt`#8vf$wEOqMQwp2g<9 z868>CT_ihdGhJM>Z?f#0^Y!a)k$b?Rd~5nLNTpI(w%;+f@t=?RuEEK@`G{fiR4-oE{E{{6(_b9Y!8B+6vQB*gc{MVOZp?V#V)g{klDxQXda3mEC# zC(=8ORVHt0xoU3)1K<4oJUxv#m8t@H?faDs@#ig-+e3HiW_PjW;(opXNk?K}uF2PV z5V0;E_N`WXmgBR!n$YA=wr$tr<0P)8jYTx?07F{7`@X!Vv)5z=BJq&WVSy(!2I+;c zX0P@_LGpt1Qbr2{8hcG{4^YHmr*6TaS9+R1@IVdl$qJ0|&+Uxv{~azFc0jW91orCV zmD71st|r%+b)qv=bT+`ZmvypTgN016WVp+1nR=wHx~|dTZJR;N@GufAIH2Fw39`@1 z{JTgV(SM|sOXw}K^cW%x!j@-=X7aGbHfsju!+d{UK0hAH7c7Zu{-$P;;2^W{PZi9N zDBDnvMm79Hj}n)n01g)zcYs&ue)&bs&{b#X=sV32m{6H%bRF2GdBJM*`T7~QSo5`* zae9E#W1iA|cs9pibINCtgY=Ij;5jU?76;MbnnQa5oFf`H*X$j)0ZNsGwYrQGB+CZP zTPLkmKVaiF32+9lrH@rjd6hUrP4iRT9)H5J0}4Oaor{xkcPt}|%3z{Ba+pygXT)@zRGV!>tjZ1$rZG)O76y6MIfUb}0mG7&*RIWF^g@CB>js&TS>KgKK3{O|4T5 zh+bW2{>c{>vN0V(-P^|#@LJfUL}=E{)bRXfjUTtP_2WEBvtNLJ$!`{QQjDU%U;NIt zx~-rExA8dT@3H4@M+~tI#>;``k;btaytOb_G~4^)2bZLIS5D@Z?ag_S$K;m+O(1{U zJpPRe)T*;m?O*XD19GVyqYmTy{X)tkS9${M910OeGKom9f)_UY$>R zc`NZE1(W!l-k-$UXi8nyM|T{GcpH=KcWlSaXL1M5$;`xB}ZE-;K&a=WF4VEVe)4%U+6Vto;vd- z+Ib2-z%J^67I}6+4Z7IVg=v;Ixh=xFLtM*#mVTZ+RfHB|3Qq<>?uqt3mu8uxuO?xA zLzY8EHJti9s;!`*26Nbk!hRHYg44CDdCFN(T*1q@NCisPpOnf^&0iM*U6+n)Ki2LZ zA@QX`=#`0nKo_6#oqpiK%mj%o?VIeAdWzCt@H3T3>>+s<_?`f-TfR^Be|6a7|LqC#EjN4JH61=gq?zsaA!6- z8U=fUpr$yNFJ*Pp=;zn6!~yL#gUyI84&dviS^uNib;3y5nxUlzRex1v!e!s8f|fRf zXEE2s1LLYt-$!d6Ud=LROkR5V!v?c(zODM(QNPPiC>92@tBw&K;;mY`aCHgpZfAt* z9fSBorJ2u-G&arjP3u)HOmIogMP46%+SSSBLN&6+EX z`dYy78MmUzqXzhdN#52B;**|t8DMy!+Pkw%g@c+GA;L8BWYbeo*35o4I5OAKKh3Lz zi`}}m?Um~d%|wG$^vBdrPzY;}fmoedOvTIyL*N#l)9u zHl@dcyAUgh^|!f>}DC1%<<$B8~Vx+SAu8#)CniIpcPD5f|r1IT5U zvR685e7WRlhx_v{+Xjks=2;Z^ra81{^7{9_keU2}ZmesIWwU-*vah)s;hSidOM^c% z1umV?cM(yQxp^VoqK)7xflHLWjl|WDpuZMx!K)(&t~o}e9y}Bd_4egJt8A+(DWjw6 znb4OogGcE2&X_tX{kFyPV;hKP(pfb(!)Sd#{FgQ?2^c$A;P?S>LeWzu&Trt(eUl&nM32|`k;3|MX zA|@ViE-ch5{B$x5JpdVsADvZc#nD7^^bqly0=;$Kv%Sw|-!wgAEJ)m~{hg`$xa1#& zeiI1dh)pbk`dQTMb&{XfI$?aZHBVfz&bL?Aw3UG`ao#Fjy(T+6RDg*BK6l4*+l~+U zi6ZOQ`Mgm{oPgI3pE{ZV-+HHc{Jc; z;wBNkRqFcynW-bbr8^U}Gihz?PWx1RrDe9qWO9<+kw%hADioC#`;$ecmFMV^v(-7f zxD^w4%9Cx|*pd?T^tt9r2$(XTL7_I;NZB#!_~Pe%ZVP4)ns+a;YWg5wb#QR6b$sf) zd-Pt!G&#m_%5SGVg<%~3%mCv|rL8j@jc~K--P7qBA>lcxGOc*k&u@X)&SmG+0wgOX zoB||w%u?J=>RDA3uzHbK)jC7HNAe@#j`S*~UQkoX_(btm)Pg9DedgP<`VWRwwEJ&T zv@F5nU&>sTByCdhk8>)sJ58E@%Q88gA0v_Q?()#_wsN|yMHx8a6S%Ou;*>JQloT12 ziL#=t&ev}%7439uuh{f*mga=po~lDyT`O1D-$?v3O7^$3mQkUUnr@Un?@hqz;ePH~ z<{2si3_%u_BZv94&@+A{Ura!IDp+&N$G(58Im|TqVxyUXyKYvsm=EAHW7`K#CTvXo zZ+>RcCTs9iQKLl@5ZEd_y?o+Ps=xKCl-T{7mEPHIy>(Hpy_)vN zHd(39HhU&aS*0A-2hSENRZsk_wYN+>J$IIxX;PXwV&*LOp9F*BIyQ?SSozV=jkoh=ff`h(iM#2`#kr}7=4&ckbO8XC%FSO} zF?%>%M+Q|vaP<0;B5V7XRmUtnb~*m+$gosr-DZzZAQOPN;oHuDKZ`Yf$iw@M(`sSg z2zl5-0~SyS`|!53?i`vsi)G`qGT%|{<>*U+Wza4 zu1vV3#b{lm33VCUc}cy)vA*%7a4X+erQ1+j=VC4X{cv60hM5=s9_PbH*J1VgHTv9k zHAej$krbJ=R^j!HOt;?_vfH7*M8A2e*@?YptT%0;VOw9O+bMhcfWeBrvchq>)+!FD z{uttCy_wP-90^LTCbk)3UsxYev0Wekh#g~*Zf$jGVzB}#q+oML7%qHi(;Mo_%^8%?+F)b)p1vuw4PX3 zudZJB>)AoNy<1yTPF@@z|A41^+elFQN%6aOGzQxuk2Bm78L3&HH1NT49dXlcq|X<^ zi?esz$~D^PT6@N7bPQtdYJj19ml&OTUGZbFJTT^fsBz(2@knv1_gN zBl-FJsT%4g6BAFgZ|v<)&l(+v-Fx>oMyDKsH1m%xK0AKz*=Ce@1GkzHF}4GXh~L~L z$SVXoSz^QM2;eI9-LiDv(P}%O3a3vQK{K&CuV-MPth1IEt(d(P)k(28v7~$pB&_wa z*652l*|KY@m|G8@==tnl-h&+ZV!a#(w!G!sZ=`R;*A}O4Sa35LyZTn2W*a>FgAQDk z^NNM2s1qnJ<`k_VA3fUWU{dDc$CcC?g-|D75!*{cb2VI#MLFdqQ%O&~tTfQ8XJ9Ad zlIa(3vY<`3^5z_4--l4=62OajH9_6f^C-SPuODf+)B9g?`Es=*eY$JX>oAWlHWGRQ=u`K1~_NBuZ zl#%TlouY4hJzXSES~8j%y_1rVKd9!GXcYBhppA^nHv;}XK}wS`3mLV^iZwn`^&VHV za}t8b11aaf{-LA8KrGi1QF8{~u0=&xO$|c?HkIT9`&~bQfLDxQC*77`v1@vO7=;At z$Xy6nd{Yn^tuX`>>Zq0FOphwJ`x_PC=y&KlQ5}L)oi?S;G~w6H{DXVZbzQTkFnGpw ze~E-QmHc3oPphqUwSED)KiB1S*T93am%5Z&K$4DnEi+s%c-g@MY`@uVW60vO>6x8&~;U+~llG z29S$%@L-=_H8qyi{UoI4UaYK=yFaI$s%7tm2qONVF!A1X{8zJ- zScBM|Y)%cJp@;AAzgCgfxeoSBJ^PSat0;{rscg)f4KO@~_)$4D@rk$;N(Go?e7h2X zTkqI?=bN7>3lKq^AI(=vNTPdjNZOCSXc$O2Z$VY2qL>_Q@Cw|0xR=Uz{gtvZvQtWz z-+5UI`h1A7u^#54eZ!r63D#gbCuLLPJaA!3^7N5E&M(q{lx>pTUI}xn9O(824vVSV#V)GJRD&GNJT`Q_)o zTWOkf>R50*n_JJUZ5O`K4c(wsR|0|AcB`!G)#c*RLMGQ^?@f)=FCx{htKET{qf3;m zE0-4G3uHp+az)o_noff~dL(IwnJ@Y@n49u7Do4XZEs!P(u$gpkDxeg{$e~+i2dl4R z*sdp%lozq>Vm^E+6?WDnEnVq6f!bn0sE#HYCm1G+!dFnWrivy|Lk?Me%%GM`SKXS; zFD<_x=(ARZ_|K0lw480YQG-+v(^6OSm69Gec~UXqQOqE%yWW*!Tb2%AQL}WJi&u9R zERHSJ`MYFam@cU;qi(n}>mu&#&zQUpt&RHT=oyRS5>}o}1F{uimP)Uv>NQIWEG=6o z7j=m+)m7T9`x-)4;H|F{nl$Vm;QwSa4d5pa`_mV(C7-&y94oL8ckPPUWw^@I0H>LZ ziikcv1-R8ECiX$cy5_W{lBO5Q~EB+=;*O zwMb+yMJ1Bo?wdWQQ@Jh6%>qi-BX@R}P!qrLEC|Ml66NC+b*kPDFk5X*d)`(sQsE$) z%y6MJdox=DZ*kg08<7+@nk>KKDmp)HQor?yc!iMfW&QBg5Cxk31#lY+nM?uh|74Rc z6SEtYIa4e=wHv<}w#Q1Tjy9Wd8LUX`KySjI;X-D(P;Xnwv55|&k6^9v%{b4?;uZ)E zpVLw6aR&(bQ}z)$(FgM6xEBQV{d*Ka`K84ls2osuz}oD_fbJbnYdrZNJr%KX-Eh|7 zvTZde^z=A&T4Tdu+`E$5L$UaHef3o?z4x-ViP}lPVircFs}-KdwN`JmHxcg^KH7MbKJg2G2(!Remu7^ALk;B9O z4dXVS947y*{d9MV5u!Ji7B&#(G{Br))d_CKCqkBdaDxH!)m*wW+|~53b2oV)=n@u7 zs3`k}M>k5hIN5{iN+IFjWQ!s?jBaVM%C2-E|M3pj*_B-%PvaR z|FwecR7r;A3DJ^zK%|p&P{Ie0t)wXcL*EOqo3buC(yq$Yu{8actlgY=wbWV+`s%=j z1!4bZ&cYVQONuSCl^nOQNAs-cJ zS3d87L{@mVhfG9dI{dAVkoLc{G}}H?Fu!ni&r*RlN~ln?wqW8JZa2$pe^8+15m){*t*vT| zF;vbee6bdy?t57e2ftwhL=4^C?21M%uSc$XJrI*zxI!?C$92QuMuh}t#w*4$XA2fy z?&!FP{Y3I4l0De@Wj4Bjp`tdfA9B9>&xRk)XCjs-@PGjdzjX;fJQw}e-pQA@A zP@&lR8jA_zQC%<{0@D62U-%&Oqq6&CGwa;RYdy#51Xq%IdN>BQ-@Ly% zU=*Y-&q<^ZT0G>FEHbUv$snuQjUs}3GM7j5Hy`=z(&7ARw>U#&S)p%u4&oPocx8j% z@m{+6$Ke8YZa%Si%Z_pj7zONJtLg1UT_R3@v=)lG;Rcj6xW7E5O^|iGsJ3uWfE${# za#rh{altWAwWskY(?8Rv$nkRW1wKWUr6I?r>>;s$^i;|7`Bd&MQ$x!{PSMCTu#=Ku zTX62=a(P;U{#<#U4$1`m{kM%LnKNy|S(7W~L@7_Iht^jrG~JsezRK8lyfQQt-}I^c zldiX3#m#@xN<(!wfIPHf0xmw@Tey+8#rDk*6ew!K&6EzJYjcr|@TLx(`gz}(*&p+} z$tn`Ki3|Hjo`7)nlJ;8J?;8`DA0bV!dtCt1%*-f%yf!A_e_^mH`_GCcJV5t7=^u3| z35CYZfd&g|sz3Loj!QHsoRmE--6wQV3+^5peIb1icWU z<#2}IPalyra*xtE(%fG%T4>YA76Xd+6}r{XBQT%C{AcEaQG)gf=oTRf!{+rFXf9I7 zVblfEr1T6E4kJI87@ zVyGp)8fQpEh)F|lXSD`N9Ze2h?99h{#SY?GGwhQ`l?x@!Z%vTUcm4C{Jfl-0*nLE+ zI0wk4nD=txx;Ag8_)Myt5;=g0Qw9~d7=O+e8u3~l{^{wMeiwNuh5a|=ToU8%Dt+v- z8!lBGGF=l)<@!@WRcfifi$88h6Dd0czRSdC2EQ zQ*vV~S)P+eZek^v$(@haua8zPQv@G6orMF=7uHzvU$O#RPfG`3;4GN%Ir) zN6ch9vqDV|prT!0dP%6mB)hUC%G(tBeLbzHD4(f}u!0UOvO-8k|e}MCCY{cD`R`iZ1h-L{%c+MZv)gf&7YoeDClbVsNw6ZVWwACME zh0`}!YSh6C6%~`jBfvEdqV%fd95I~rYP3*#Hp4rKuy;JXXA&T7J+azfQv>myl0we_ z#dFW$$gSSzRnMi|{;)*y?DC4e_P2}m=t+Xp)Qw1+XfFBfU8#?ToQyzaXEh3(j9KHW z5|k0V^{uLjKDQDc3u41YHexV4eiZA?OcnfBn0YwcAUFpjl71GSaO0aKW$AX=_3g$g z=yTW9wCW?T!|PmatRKW*(#eUo9C7s?mnfK7U5lN0Rj?CX)#g^{73kG{`KJIvrk57u zQ2u~O&v>ar>R6s zkNqk46k3V*24B*Zy;hQRFeM`_R~FMKA=#Pd@DcB_A9MVx5GO?|1l-#s3ijqUbcb{k z2sQsr$|p2_kkjmM1v>_U)H;i9Vl7{s?AvxjH*SU!eR4_t=)6xS2L?#@ANsP0`=AG1 zLTu?^F;wLX;)>lv{%1i;uAaQn>1}1{3Cv2`+@ezrGkt2_*za2d3Tu~}vJ%8Bn_EZ* z0qhVK-DLO6Y>rC;^{M0XFL?lT)N^`TOR{s8NKo@1 zH{02PeMx+;W2EBP+yJ-QLBR#N*gi;RIq{hbDs48*c?8o&f@ zAuRt)+C$zm`00<4#ZHZ;-doS3)wjFy1-j1eT#?K|+=n-=ApoO-e$z)^&Kw$ide9be zmD-pOqn&ce0}E2}seIOXk=FuOa}f(v!^c|*;Un{&r~7p=j;tmS_MhP|UjxYFT@vIW<2jZmD_%AZPu#C*M-1Tgz@II`J&b={&{fWOQcPnk6#i{U4;Vgyw~Y`=Tnk z|2=|fnj5y;{R`Wr(aQX5LFLS_ySeN-wVDIr6ib=j)FE9r+`<0L!}zSK3lSdy;jK?G z7T|h;aa<6`6*Bi(p2_Hl2nx^fwYbFK{sL>yw5D%nmGAjRV^{z$9WTC=7d&Sk5AbpC z8RE5j{}QMWy->!)}c=wlb+=@TJq7BW&AO=<q_ZGIAUt~JOnqSntM=#aZK<;xE1*7LIQa?&mxkOt09NWkxw_OFNO=J->^u1$X- zE(}||hhD2-UdDAtZ1FQa(Qh`J-!MYg6Z4b@KoFp{Z9z8$Z;c^saayU-y~C6NHQpJa zEfa2^R-2_!fL9}izF?6Np3-8f)9=`Ujj6=aog)0^>MpW>%4@$@DB8-Kq*+!lsi`tB z>ZHCX<W{tnoMG<8|!M|^p#5Q_W>!IGDVK4q!x86ntp0Z4d!(Ogne!$%qn_4+ZaTF+h|1k&4nGAQx7w(aQ9iW>@ zm9l@Z&Id#fpw3(WP9}^gOjqu`g>&4xlFE?Ay^1aK$VrW(4)kT&)pLH%xo_n0$ zlaC1HSFF{Y*)?|3jgj@3#chUYWT>P^^)8w0nzI9r8r;!7Jtq4G^ZC3NcHn?1@6y&c zs6wZA+cbdk<=ztM9wb>Uk*p#3YwS^7YghTmQo4Bdu)u6jkC~cQ_-Yk5TRjk+km0Or zwj`KS>07?Ok$;$VExY6#iagNfl6JsZ*1++1z9S#l|bH4))8fU<9Ix#62$bGac_T}`?gEIeh$(ew!;?2OJ z3!j~Do~#Qe0btw9`~=GaXknAPT=T`2XiVS5q+uElq4bu`69ap$4cXrYvs)fNN?a;2 z;haUl`)aSDy2Dok_C@uG*tp8EW1OFk#o%^9WtEIQW1^ph&#YDh> z6w-0h+}mWU<=pI>M9c(kFr|!V76VR8jh(;dNMzUJMkmL^GS0vBZX$%V>C&7G{`lI} zjOPw|$Ls-SmlJO04(%F0)P3#v5oqiDv5g^L*FlOS9%YFXQ-I%6Mm6=wov? z`h9wEkIQc21(1b(qoI_0tjUT?*L|nZs|2Eeh(3_|eO<>J=d?}7Jl^|t39>CLja-a1 zz#V8THk91#<{>0?rau#xbCCG*9abuPndC{80ZncvusZy{K=jnL@TUQ1Wz4b*h0;ZRiUbxG@&G)zV*BIe{rYPsc*SFf^6(-B|5(5tDv;akyD`&%i z6E*^0+LJP@J~n}Zc~i@0{Ujrb8VF?x0Gpg9I-fP)hfk+b4&No(vHKg?T4fk_t^Qb; z3?A91^G`ZCtN3&gmkj6KT54t7uYbFZdbg>uuRfP^sb^91&R%x2<*DFe_kMfGh1FIX zC}WA?YW9U$?}ZFMzu1!Dab;HZU1m%T$4bQ9jdM$a1?$1wm3|`Nbbo0wIi&ZNdE|F zb_}ZlI+dX|=OakVuG!+79@>fq?gx|AZoDu?&nL(1j_kwEtVXT(Lt`{;h0X<+*mOx{ zr0yz~%Gz>aUfV3wsE6g5A9<6q7+wNw!J*E{UI&6E4A?HG)Ktr;Wr}lK$R0=A?$O}* zctP+7jho6Vuc=;g)<3T=J>!Qjv{qyP-WzIazq`#0g5B0$WsmDLaofREN+0vdGYJ zUj6|~vxO$x_?+*BWzs>{{Jm;G@CElM^j+KVf1bXYc@eZOG~(FPAiLqc$lB8ydaKp@Wpj z^By{Z^>dDQ>P<7fxnlM(Ew@?g-0ADy9H-v?fG=Yyd`ACJ+u=Kr1(;koqt*D@>^L9&4hOtxy2LG>*iu!V=$kTCvfC%Ny>vP~Ej1&sUZYi^G_W0?9vwxFog+Pnddkct zv=!&-fSCoxkRXXg^@s;ftG!&Bz+yhX7l!0>$U*3zCNUU>Q&?e4s3X$(Y?hrr&fp`@Zxpauu2BuS0#$*x^>M7Y-XU&@o8Ge7 z&7I5Is>I$hmZW*#lD8+#OH#GtXqNOgAnltYaQh+3X)$yQ#m)hDyjh~8n=%@H+Iup0lBf%P7xV)S5s5F(Q|Uup{{zq zIK=f3xk~93Pvf9M0c7{BN+p&$;Z#hLWB;MI2a*uo>nvrxeWQMR3<`+Vs4i_>3dUyE zmeupOG2Sx2In+ zu0X9Od_PijbVwvB;_<0ExG?Zf<&2?KMJJaVKK9yQP#U&2c__1MQ~jb3x*V{goUlEd z_&G!#Zu)xQF#c2<sfp^U)hX6fcjO1szAofvY_hrgsCH-$xsbYCJ0PFA?;dDa`!6 zFn!)S9!J5q=>Pte?{KA<4hgWAvuY$h7Y4p_e#%Kvc)N=jTxm;lYim z9n_S1Q%ul100#MPbfIvg;MwYeqFMx=H+p%E^-vlpd%xuMXdc-L7cC%s(Ax%QoAI*B^|jr_D^qGCweBE_O~GpC`&q8zGHrNvktB9me1U zFH#o*_iUx8;W9KV&GX&{UDL9v+o|Mk|Efaf#d`SEcl(W*+q|`HGn1PY5p93YH~Uiy zglNS=(y!y!olXxMQ-ahw4L6Ts88Byv#JlVCAK(q3Ob5BZE z1>WDpEBzA1BP8rrW`tzZI=Lp{Al^C%f5o0wxLy#hBkBJ1;QF=qh2 zU*PafH;UgB9CheaG)au-7%i5E+vJz}3bS2!q@KQ4EUVxiuI?$xd3R|8n`_yahrTd? zX{4)&dPD}qVO`Wd_kTx$(x-nnH=X=O7$-EyN~|j63(cqGd+{uQawEFbjAXFy%Mc4v zA_XOL&|5zRo~k_VFYOl9VA?*G`0o^eoz`{Sb4Pl;7#H`}A+0|b<&lbwF;6@Hi110) z;$p_4_^W%tyeeLXu5Cr^?z8{uCqwI(ek}b4p;m8*y5UD| z-ukk&=U=p>R(?3oZ~gor7XBTDpQ=5=BU5WPnAiMP6NaQ8AbOgzy~OyH`^J5t`|;$< zoyU-I!bZ)7JbO})sD!k}&z)|1<~e$Y$ECqb!1F-7jI+b zOnt;tI=IqD-uoH{iyi^k62@pvL$A4AZj})k@Npw@sI_I2RMke95pB>SZ)&@}lKe0z z><0SDm* zO&oupNJWE?gHIJ&{xE>n76(kZbNj9qUsp~Ic9sbi{A+aX#VW&wk14;k+17-We*sL^vP(KZ>qA9;)wuSClntLSk%XOO_U?F=HvRWUW+Wtocw;RF*MwBRe6x z$dGJbg-BAE?7N`|iLo0DW5yV>-=E*V_w~B3`#SfW_c_mbzn}N>U_TXi4Q~XQ`c(eP z$O>HgpE>T~iAR6L8=n<)-a9w{ta$JY-eQRF0>$?c5l+ke-;%_*<#^H`FmE(0a5x@r z82svgK|amqEXGRy)Z>I~#)-H)jKF87_* zrZb$P<$v6;Ti1wMn38|{Mf5fsJ|X2tJX?*{u|SME=+%MR!B|y zXd>Qd@5%Jim6VNYTgn$f(Pw8_Muj?Z@5f1*&$yixe6o6}d`BWu8}#)y{sp@|?E57r zL`uTn$NE1G+4xhe^!MzI!l#oR6N``c6O)NTvkaYNtRJG+`Rx?SbJb2Xj#xfs(se{;&1jZpkDPHj9p~&mIGRwbz`DV}om=r)4LOeZjvo&v%@iettf&TrhZ{n5 z|JgqS-aCE#?=8}7eO_k6r9Sbv5PZ__g6+`rsP?jAtKX-4d;q`j*_#)thHjqcS}p!D z&&aL)=aZ1JCHuFxTL&^2oo}P_H|z{5!t+UJonQalFXT}Ej6wc`fg9fuI&QziiG6Pk zYEsu=F7_yY>OrYE{C}xmX>Z@AX04vac7W3`kvAK|*$;;=SFGPv`uxxCD|U3e&gS=r zoF9wFMdb~kHy-SBQ0rn0mnpZr-tT@zB-}ZXeF0})mg>6_17X*f9~!HfxOO&g?c;9m z&KY1((vDnHbL}=gd2=UVb~e&ZZbj?p`PLV)Q>gG;=3`lltvps<=FMMn&(ZJ2*!R3O z`P1?@V0Lo6am^hw0C{l>3oy=KSng~11}O;%AG#6xcJs5DOnqH-v1zUOs#}Q>$u5in-#qmicU9bt(@68L}Lt46grpGX35#NOGyb zoTM+FW!JPi+vNLvAVW#$`zecec*cBP+p42Gr9wy6hwmvjt-6No)l^6z!C2Y^) zZZ9*c3MTYqw$~;!mYsuPGgel6HvH7tOYXZ*v*H}(Y@$wUBqtFsct4!TY^ZIM%yj<`-7JDjmLPEqyYobHD9;4dXtaVfBWEWU2dPv8+!L+ zhxqH;{S|?C7vnm?PLX>o-+0>hNl5UKF!;HdR^^|M7}bCEF5=U5_QkZGwuugjPoQI3 zWb+%f*`-~kg@tP$Q}K<3$E$3`w;p|8l6lvv>9&p{^HvJGu3qUW80@_DyLWyxeb8rd z(5q*9`sVP!AN_SWCGyWb(!ZmkOJlKr>7-s!>ql`u@8pfzMmS&3_k$>a8)TyRgN0 zU?;NK0v&mDpSm=JJ9De82wq|GVK|6N}4jqQPT2;CL{W{DJebotMjOJPh(j%||c z^NbcAuahTWe{JfTHa9=kSG~Y_O%GoJMa`YC&p+4+kXe5}J}r6j{>y@K23o=Dh)-$d zt%(J#T{n0`4f}VxYRgt(3Ih8|t@}(^)Bl=rDjh4;RVx|qo_9KSsqO-^e;z%XBnAhz zM-G2$AU}-x-yeiO;TE!eCakOeA!+f!#gl0xPi6|~D;w+^tV0TJP0O-l+s1zm3!Wz|EpuYu&?M~ z(@50e0gL;&AP&LE2OoP*oQxTO2q_y;Q*v^ z_6|}m0nGMw+&v*kuw|1_ch_4r=K}WdEGKRy=^Id}Bfoc3hUBc|8g1!+)Kx+gzV*Lw_L$d6^k41k6XnT=BcTyk=Pl*Kz z0T4q;1qu-yJHQ5PGyq0Zwud@g&UE{#1WF{o+327J@$OEaXAAxmWE-v&hXok?< zAE7hH37@AS%#-`I?iRLUmb*}b+ zo?!B_WURw~ne$utfFnl@`#DBc8QL!YUJ64rI5}Lc>I6fGC5sKjJhggXGZ!zl39*k@{3O}WV#8%tYtvf3K}z?%#A;V3npJf?l6Eoy6?aR8DQMouo~tf6`fmQA66X^bcAVeH3|vX zLzK!4uNUe`NI*fMj@+}VSe@v@e+n$uT@uMoVp+ZgSGmsFu06W2p(XULSTY!@;B`vf zx^e;a`h9Xf0qn2ju?G)0&@N%HK8?1?J^cExZW)q{e>15ws{4MaA+uMZPuJshGd6-5 z0$tGK7Dl`MQ}sAUl;k358ZIgFaa^KBA>3DKJzu+R6u%Hs3xQ;ik&!e(-#4_eVXxdT zGZOz;kL&?!Ifw4s_9>*KJ)>g`By1heorxw*lV#C3dZfZ)0viaEq0sRI_dq-NYh)BR#X;BFUZxfYjBho}F)d zkRzz&yzA@+

GnrN}~Fw%gd=?W}g^hdgnveT=b=&8fwyb$|Z8o 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

&BkfoiInLF z56(PdOJr;{#^5caN#n$3GxHjt7+YXrt@)SMnwaX z-lxg}v~qQZp~6Qr8j}WyjY>Hd`6T>KY0}*qR@r?_!_&L~$GfC{wVb1G6~HxohOtEH z>cImzP5vInU6CTtWZ+OZ|oD{`s$8N)hx1i#%f0T+2s4+vsHp0J=^g3 zVvUmVOxQs4^<9Xuhp!vI+~jnARTTD2>&@nqDX)b=t;We8b*YYLaDWgae9K^CY|SMV zA@ya#^0tJ=PvQJc-Gt_wNo>iEW7C?dp34rqoA=#S3BHd$U2>x1STO#f*%;s8g?Vpi zoK)mIr)`3!K5=48UXD~Nz^UYU=PbM`S zQ$9Ky59FC;XAoiy*V31dKAspNq&ImM4WBVxco$_sRMlRnO6YBV@fJ_pdR0Jg}_!=_Fy$LQ^+KD=%q;j_n@Y z_4H!|QaE*Ip3Kx38_)REO(%66ELD$M?wLUi@QzQH>C2B33G8!h5;WtUaQpVAdp1rf zoPOHJhz(MYinwuRrZFdDzA>uC#CT@I3sTBU*?`A=JNC zSYfhZtj0-9ba3)6(QALZH_3+UaBhL0+4Ya46#~thdvI&nBQof7EtPT| z+ou=27`1_F+THSTv)vEB9_}mJ$+N`TzK>?dPZ*5_n%qXo32cYN`X4z?&)w&9JByHc zEkE5q=45$>U*w(s59aGsA2F0tD(okQ?~r^eH|um&^xV8r!A;++CpZ&9TO}Bf$gmt* z@&a{DnElSj#+8Y=5;UNXpwbG;^p!0hf;PBHjZffKOG3d$d6P^oS=5y8I$Ly zOz8A~URQoNZ_kiJW4hIlGdbYMZJBictQNBANZsv$tnQt9U4un7gY}$YQB{u^%tn4BDHCE8^aIi*+b?D1_q zaYJZzbNS)Hk$$s1(+M;?tLfc28F>?HWzfb$JhYv~bXOMqW))(U(v8fr9;Iu-DPnW@ zt;92)`=>vMHRnxi9ZWYKx+d5BXkv4gNJ~&K&>T}#P-A~Xxc4S)9dVb};USrHKf^m? zj~J|^irh}t!9gP#G{41xXG(7wGiCqqRB+7VmCw-dnE%P!&dUc?Tc!@!WlY@2$p|{9 z_95iuDk6($w`1D5o|~L0D_Ar1E1H}l?BAfN+Y}Sv9ORH*`F(Ev)ZK}Zob>dGW`UhW zyKno3oz|9^!+un0U8uG2QInoHc=JHnwdc z$@6Iwdl=wML%)kxq%-^t@A%~T1{^d_plN1QFpT#$`ZXIT+nFS2@I)`=>~5aj&k?nx z#Bh&HC-~dZKF!tKAo%6op6?kcrm8I?2kiE>YrKEZ;%9X`jAxqNB3hk%c(P*$Y45au zzuEqQ<%6*;ZRB%r?_}X|*Mk$^mDq}$Zql@gFHU&wZr3TMP3-0wm$ls5+?+Y_bc3g} z$p|`o>>AyaZhWM%zg2K1XFc(gg(q%$bN}a!f?`7Y zz7qrG&8ZW#{+aF5My}Xo;oYALTB64M@7@*-6>Zj0l^5s_C0-UdOjn%owEI=NdHlAs z$)NW4BJZ2D^#TmF(w1ahEzYKFub*eK1lNS)N}jgU zyuozuA=_pXQ-~3}d_rc-pL@Ii^8-COdV$yw!?^jUc5|Mw^~s>DitpWc=w^oq=d$oWuf?&GDIUXm(yv@w3Y|m3t;~Zx8ereK+llI=if;b+g_6>066( zc5h6etG39*2(!<=O~1BpApKlnOOL`}=zhOPy5Ea-PGsL9n+`6v^~r$z*^?1qbujov z;?>q@c43jlM9&>Pc>!)Hr=FVv64=dTb3)=~4S@$$03VV`K12?HtRhiXsg}0cUl;Z& zdD=|x^b1}ih(6A{LtZ*^Ys-dq;*c26c=+V6JQL-Xo@Il}Te8o9!-~+@>%n)Qv69T! zwMz~iwlgK1G>s0}HXREtH6)*tzSDE|sqxk1%fcH)yYqT2@Po!VyQYo$AK5@hk^}4c zl@sQEW=X*|D%d-6a(3|(l+JrMDlDK#Z2K=d-IDKc4ia8pzHe&u+)6;$&NakC6ODHm zmq+x7FYGlp3YY@BT{kp}D<>T4g?a)b&y4ViYyFQ71$~TrxlWL>*)Hm={RH-o=MK^K z_SNvI@&P-$rk)KstD0g3&P|nQY?9gyV~w3JGIu(kYgaljRX3G1p~FKjZ{hW@cm49o zlAtR{N1wJqmABnY3hs;BtR=AbyLh-!tNp^kZhKFQTaW1WKd@6wZCga+Khm@@=g73x z7_DZ{JJ_?$_6K5uGhWHU`HIjLHmAnU72odBnC@%Ya~N&f;_u4Oojfqgv8g;yrs>HE z8pVcsE|1GUs2B}QxV_w!Z`*UWfc+*ybiRGVHDOhl-NT6-J5#xq6!MvkcN*pFZcI4e zFn!%Zk{zj>ILI@8&@!HdU5haveq}2)U>7!#0=_8Mo|bFbKo!7W?BZut&oI$x@kE`? zY8g`)oZk}IGg!3Kc@3Ae)p2yq?KBw^n<0m0WAB!NWMkEqO5$@H8!bqahS9*n7P5k1 z>rfg4J%8w)M0(cFN`_K$_r)ruByP`O+)mNwxt_bGcfQzjBS!c+e4}-GiA~2I;{z>~ zdj5CsRGzEcoSl3iUBdS8=()GIWsI9274^^cw3v31LC3ayE^od%k+_4eCwRTd({kf9 zKewgia&W=MJrMX<_vXlnNT#r*AmdE&t7%>T;ydsi(-vzr-+()pH+k-v9w1~8ha`50 zaMSbsgc?NWoBnHb^a2y^RBCZWmp;^I2~yTs^7V)p9v)C9<{TH@0l=y}D+taxGVSM= zxq)~eEwp9aFh19UE$g|L5&9RH*n?a^D^k&Eu_H8m)gWP*I%v{aLVm| z7$8(#w$X&&hc=-M<(p~3UUPehka4?cn~LZTDX?|uieW6*d3rD>J#M1CtGh}1-Aw^2dV4$jrQCoQ{`$*nc&d?TPV*=NhKvtNS%W_=0g!^oLE5(R8NiJV= z=>g}pY!S{u@4lN}Tvk)-431H;E<}3x!K7M8d{$$HSx7u5<fDP3IH;Z3Gzy71=)U>$hB4!X}XWodZNuu{%bW6Rs#V)_2402-=L ziZPYmw)_L_lsD;dFg(o65)%jjVz_Kh|_B*Dm79>isgxu%upfQr!_RNV+fTWPHqSR`cM?aW!SPFqJy! z)UglQPx8-Dk_G2h=(KV>hMBouD0ao^adKBrE6Hmavr0Ga$Hw6L2C^VC9?Lfb!(}wy z(VUBiu>@M!TtbF~15;{7v9Mx&aUo?AHB6lv3r3}hMtPTAa`;nKiwSon%9xvLQr#12 z9|IY-D?W=S_DEm}mt>>~pTx5`Q?hHAZr(zg5%V*RJmF%_-`{b#Fo*S-Mn|H=7!`5c z3`H!|dCCaCC3~*VI8dPjy$U)3)f&i z^5e5++AvnRXo`_JTEB>U%I}9R9(9V*z0xw?$%7xrjV~5@`h0SIKuCwL>cL0LSZ}ce z|HY@e#uUDbo$Q!SBpN*T=ozdI;vOjz()6fs1plQ@+{GPZRBL#P51d$;HeAUcb7Ma1 zR;&)mkn!?!=HVZ!t!OyJ%}|SDk_nV9!OX&jJfrZ8(F+eIv9YTHsJ53<&(Sqvx<>sv z1fqGu?vBqih55ad0ty^zOnezfWA3iPsJbi1P=y~ovGO}&1pX_em?f;~9wJunF7x6F zCGItI?PDZxNJh~lig3S)#GT_}z>i$`$?M!aTc(l3UKcuk7foWGC3jax3eBipK*ks) zxO0ZX*3%Tisp(y+mJx#ZE)natlc>@{qfk)j}%kwBJ76xK9qqlO( zMuZGuW7LDuj~$a_!M%7a!E>p1K$y%hi+)NJ2w_mlq(t6Ij&F>z|DY|UcE13T{!xT;>iN*dSdOl4~j)@ zB=%RSQGALgQG8RV59e*n$L7ne6EjfN>$uycr@iW8n>Y*L zJY%2Sq|z?CJ{CTYxlz_S+Y4hJXdsB63E@idyu+0`pd)4z)>ZvpHX zT2;uoW3KJm24zG{xi4atd5ajjpIjyRp5PWu@Ok-~=+*o({p3Q)4iOEz5U-R5`Om+* zKAA@Pm{oJgd&b^b{2+IAhv?y$#4Dy?8?N5mW1LLf6#aU1@be3}`Yz;De|L@8CEi;2 z5(3RA2BXhGTuyx-HJunA zVQ>uV!XpLTxi9Ma7>zgO2kJF=X~tx5F6nzPomn%1SUQyCpm;!852HbJV8$FCE79-E z%*~1}l<1fyJGqRLoklFL3U)6XXVD<6{2)pQ5golHE#4&tQyaCIzrjVFuL0HDMxZ<< zc15Z1=Ru6@!Q@IGc<*7w`P@P9CU4h|kJaV@cmVd8LI$fTZoer=1Cw*vKU1uYan(l^ zcHF>pzr~hmC)^^K84A5LLrnMy3lGtZSTmj_Q7^`735uk_+6$%&0|Uw^c2 zur{8gvMrQoz3?T+$Xh_qk8#<+#{wwU25Y-HdykVb^-%*HlH1Re!#gN70!QpGqkiJl ztC*Isl?39_TxQtFG-DK60)OL=)xm8IWFkZS7Uw!t3q$M($J$H4&;{t@W5DqvkRCqb z)KE+(VEe}aL0}>_PL1E8Xh?lMXL@vsHi;@T2$`Ek1zZz}M{a!1%|} zU>UHSwCJbm3jIr4IYW@OOOV2V1Z|v$qubG6c+iVQo@1Y~YSm6aW6PIt;>yT+9XqgZ zg1HCMf*81h7@$tMNqo*Faj=k&-6z^e+y_;MxL!y=X;X~gt_CvvswENUC)sC|Hpp7| zsKM0)vqnXul_h*EKd?gttx$mF zZ-uAWA`L<(d=ufRcrq=@axb1yJjFO&#K(4U-PSrJ{@5U_h7-(_I2Ta4{!m%QceNOE znbuOQ&oC*#B;)~y07naM26Ax)kgNmw4+gwX(`$x>g6=E8DJrwFx4)wf6Pb1S<3BOf z+}UHnqNNKsJC-_hl1m^-9w|7me8n|*f^25wj#xaCaI}A}dwK|n?)N7o)n@C2st9T$v+Y z2PKr>uc8!gC0b+FtZ%X5H8@>u{b74RZ8cu@gFOHEB&xA>@PIGMO*aTJjpww^z| zei>&_Q~yaOJY8)|=oIHv&G%G(BsGNSBz}VH3g<0=GZBiQ(iE(c+!N=@QxmO|+^`;O z##@L~Py6RGENVx!;3DcSt1@WcF;gf$2Bc^OeqJNfhrZ8a4^m~?d+|mMluMXK$SyKe za3*S7yx=AjRhQZ;mMD^XOJK8j33naDWP>uPc*I4AKOel@aS2E7o>#=`KoACTqQhck zW<{8;Ly-Vm_R=DeD9(J7so(%T4N$_lc#_N?m-WIc%-zndQ7kU31nQ?-^3ik9(!j$R z+Dza~h!VE}9YFPYFo{ZoOci$dD6bkWTi5GJ{#b)g6wBg-ny+ErT(}N;iGb0);F+iC zcQKf5z$(4aEom}tF~p2`tb;Gm(UsQ8f;jkhi8CBgI3-mO6ZVLIAwU!;3Z6GVz*^-c z$LYHyB3(TB*CjvJy$x~aq(LkWK%?n^vfnf35Tzt&uRC_kWi8=q*e&3n0?))nKQ$jc z4XMeq*up%&@D%fa+3~_0Yf5rPbauU!296%g1kWjlOme#Bo@GW|WL7oPfNbYCkqn0{ z6ElDjmtn%q3H9QJw6%~sO>>GV>^YH~3_mY-y#%*9^Y`@u+{a5BIGAA0u}ayh#r#8h z0aVnjDs8^1GAV$lSXy_hP=HF4s=|xVq3a2|RV2B`0BfwIjT@B-e5li zfq|VJA{rg2<)rE3Lehheo;}9_q10Ur$~J_UbpTTqAzrhDq}7Z4@v$yK4UV%-z`Uff z3plOoi%YSdJQ9xqJ&okeCvjK6k$mPgaN;3XQDA-L%4%J1=u!_>*>+}=n!+4&H^E)V z%^s^hN+Lnlz2^h~7DBWGzB% z+fQG_BSh?VN5#0VEqnd8l0Pkeh`NWq*OPP_>>q@Bb5%UWWONA0<;?)z*HJp$t$ulK ziIgeE(LR@Q2ZSz|Fsp*iF6r{E^$ZIOcs~wSh{O#a(%}ePDQQERb9z-sS6iYECvQZ= z+%Cq1-}KEM%anmHYH8|w=oLa$Xve_clDCan-+i);6ksV{)-~qC6WQKhf}P>~1g-2o zc+nZEU#J^t6JWc-hT8={V3fchaY#mG=yZ0)o$aS1ZOKbh-k4hkEx_W z=p4n8Z&*D+lyHHs%6zUEq(aWT^@V|vl^VBZaJ9**TUczsu*^k8%uM)}7%-KiZtzSuFrmkdN8*-Bo@EV-F(03f{c$jOOy7{I zY5Ck!6yum@9M;pJZN!L4o`8#-)neJXLExM^2qqFdx{pp(S1_)7lZ z`T&|<;#~5;$A~`9%46_ockl-`=!%O1Ym(e`0*V4vbCJ6ryk->qy{K&OTu8I?|9K;mk};YG%^nm z*VB9e)kUg1A~5I}IgZ?SH@XxXc5;2;K?J0=oQ7(pL3NqVJ5FI(Q6n=vA&u68GXr=1 zIG~mYe#_=eGrHw#g1DRUXPI5VLm4v9-N$0wviZjN2_|B{J#-<(S`Tiv>|*8#7bya6 z;r77vko%kqN8^~4#>~-R2D*VGgcO&Fc8TnwLxHOs3P(RLKp2?R#pYiNkm_RlGUprY z0G&rj&iLSVmFOmJV>noQk!uFWHk6QJ+MrsHlad>*;B&wtb2rEW9z=f(A;8-f$Fdmb zuXsAk)i2}ui>;?b?J#KSc5Nf^&XtL1`>;DXPk^4MbbwOQmN>w{mj6uA1hn36{sSx9 z4ELl#bFbT;KuV00id6>jfkZA#hVa6r2M=#J?;t80TpRa*b8Gz(P6V7a1u#k#c}@#p zwkfpQV6zxqz6JD!6cmOVqiFbW&>*COAiTtUmY?WWC9IFq@zT1a4!>DmG&&fe@7gm^ z9l#X>Uznl$`G0GtIk|aJqnw0VT%qkU&!nA{<+`qd-Y>BerzBX9H zVp!{wiK{|Z@O2@0m2v7#hx$`peG*pH3N<1lylNp$wt=rC;lLoQrIy!5nek8QB~yF~ zp&<43n=(;WNjl(N!R01W013+0ZlOE()pGTam4shMM4jU< z&|^o`^E@2XwlIH@ImN`7Z(*H6Dg}yyM?mC=?nFaElXxpFW%>o4k5>Xnb<-yW2qAxSMKN{q&8Bg*j*Q=B8^`1hA`#bUd95-2Q7ZSqxpUt_; z%n^G`N!jh9ZrT&8&DR8XbRjjYQ0Ho>YLzLd09zs;w`Gbav1yC49tUr{bcZ8RyPgVv zH5Qg{2WT1xjmr~o`vn5XE4SZ(HV+CNLLU*o=5!3XI4jhiG=w$~yho;$!jF1!Oojqf z3gdR1sugmMv*+S*n!;yFrqh;CX+C?7A;o85c4NiHxOgu_yx3FDtgGk((IFTjsJFzW z$__@<`^Jie%Bz`A&5wgG-sl&X(U&)*600D+nxNx`1%u47#3i}Bwt+ku71EgJbFSC3 zgx^QeoWImvZ$hIw^L_Q2GTrDk1UtoXR$12f)HdMeU0yJg{sd(e$Py+B;uNG<$&g+9 zRbrW@j~-KfV%-b~ai|O2!*jLq6Q7nDyo>Yqk0E(1Cj<#hfYm$gm6KfSChts+3$+ zEqvNQ#^{HvahUg(R#y8xl@7Tr0Zw)T$`wFds>HQb*FGjP2+iY|@rG;MXwV(jn{u;OO0I+dD8M=~kNFndf+{D92$F6IP3@eN#E_^vq!9knCEF(JW0M0=tyUswrZ z@)(HU!mZINeAzO{Jx}QLkE@q)odHaIq}*;1sVGbC)}z|)n0?nf*|Dxld@(3!YiQva zsP7^K#gI{fiU+xDi@al%OReIGl`^^TvFzr|LHbE<6?*09M}W7<_UXiP7jeUxdhlL# z_($W%rUh;|MlijVG|B^oo4*A6D`!C3u#g9=rQH0|?$Ha2vlzPLE}J`UR#jo7!FEL9 zFW|Xp5sD#}Ly}&eDUk1;Xy%aG;T4-X9jY8xL@MV;hjr!YEp|MOX#~}mTZ6Bhvx9X6 zrj|?Sb+ZXwzAXQOyb7K z1imrk7RfwBw}S?`F)1Fz36?gb+M}jNdC0&VpeV-GgJ)>YQ~Z^^3>FG~l5xx{O{C%z zY!KU~7-}jti%n%=Y>_VmN$7*C5#g66krtW^tjS}ZZ|AhEyR$odQlWZH$3k@&?xC54 zS=?rx+77yp;iWBv95}b6-X>1+9Qi_iyO2S|qU5OBkryWN zL;y@cNS4k8#SKo{8*zK9S9U`77C?_8p3(0oeQJ z5Uu-(Q@x%>@B;>D19W87mIOye0XRfKAay17d#w$b&$*_4o23z!>dJ$mwgWIB4VF6m zJTP@U>oL^;D_=&IdBe&HS<7+QzMkUz5r1omvE)K5*_ z@9TqFFK5*Tzk<;Q1^xi#bWAM;Y(5PqvjEJBRrLyBzz9sxr9@!1#;Eg^^+4o+(QlOP zVp>)JL?8^Ad?2F(RRF4lYZB~vk2M<@8LIKl1IzstNOqZEM1**>2%8Z%+o3sQZAYDp_fQU#cN{4habPXv=NGbwS14wr_49(D;!!UF+G&3;6z{~p`e5cQK{m;JZ zdrt2AXy5zZd#&|bYwf+F9d>V*xeF2^Ghete{id~*Lu$p1ZoA-jhSxBF2mC<{r9 z4=c}Mm~ffv9g|{GCeoY($fE5bFBtdbaQq%F0r$WnI~+~W3hB>^y;o-KeO-n~)$Bew zmL4f&-@Vvs-75lM=)Dp+z%F!}(kP__nlmvjej<@YZojTUk@^D zovx--_THIg>59ab-S63p`vu9LS#T|pSoAYP?$3N2+x~j3j7KaVwu{|W)|etf@6FwN zy$VzK6WDt%neptd?h&%+;TJ4E4)Syul?cR1ybF|q)kM?44T4TRX4@MNH!3*FeCU>U*;)!AD){TjMAr%+06*{RJ@PW&BT~euaDZz z!1yU`AHJJu2BmEuy_;diQ?9aIeSs z{Uxm+OSH;*j_TSc_vdP&oBfoQe$e@)NEqCM8jB-k~hb=LDF z*t~n6jLY8njQ=-^pl()V|BIB6oWjn7QU7hBwP$?(@BRab?11iAz3$G(@Al~b6OtTV zcL4tzlr-<$9sM^UyZ`6b|0rd^d)Mv1LFWH1^nX+i|4Y&TS=VnFT*Ukbn7>D(;AQN% z{9U1K?|;$T|4W+xku?8{D*v;=`TraEOtPK)FSe=->gKK|Ta7g+ZFk>4GX_Jir7`gArY$7^dBTaeDh^#2Z z5d%=5-_!AS+NWGG1&6Yl3yt-$#~$YjDKSTCo(-ofcXx1dq-m@8NY7L73R~w>OrzQ@ z>I#7s=~%8t?Vv`XD;)Pu{r>QM2kHpg$UMKcroCT|5FpF(@#6Va0H&5})_(!(Qp}2Y zKNBQ$w181TU{jV((JR(Fowp;F+n8oZhQpyOEg*P7b1gc!!FJq|TXN18c^Gx#5ns2V zu^A);9={C|!s6D6oL2?nXjgYA71x)WHQ7gqN^Ir4355if17YWfbYXVRm_1P-i%QPP z1P*e?3)vjn>#;-FWCNL@MEW#$D@APUwuh_?0_2ZL1cHk!AS^=i=m8MqvYztl{H08c zu_NQjB6IL^?M@%LjUhR^$2A$KgAy5ZN6u+TejGN}BOl;Bz7Y=<1}tGjb?&mVnumip zzaAH6iDEMS=oW(Oqc`*&?wB3qZ8xe$7ctW7jg~uETnO83Qx7DVz>mKLRiEi2bC<@N z(o#u!JQ0k6Ii?@|KQCrK{%RC9i zP}8tX#^8Sqc&Shi2w*#FZxf!SRZy|mwestQ0IBc98BV$14F>-VG}wF$s60je?dTaC znLRrGhILWCl9pe&J!D}w^B9+WuF{fq4ZGesUYNga->C+AFG>tM&dXKrRA)9W*)A?D z-(GE_PoIQW zhXLM#G;nAGfo2$%h;4Ur{+g9Q6pCg3=x^rM@y>-9#w6%j?9Mh_ zaMsP^Td-+suEmP={-kU?2lAGArO<1i_yNmC;l=`3#3c&@J2ou#nU4Fv7kQKLq+E;r z=Ie3E?a}f;t0)s7r)Y}&!?ODwaJ;*-JT~dfEvW#XiZ+hCpofG$jHMeM#D7HRrtLKO ziTNQlwO3P7q#dEMvNBV-b9Fsp6Z8f*Eacx)Q6A@FL&nkuFdV*C5Sgv;ak6B2hlCJU zA$As=Pd$2vTEeJ{yfv&}!CW5P`3;?u>}+NPB29sa`g!Nd_J%nN$+|DCVg9^V6n4x_ zO_%H=%s1sflEbvrUr$7I5_ULrnnr5C_>W6iE(@HAetznEVEUEU07_2QQ%(X*x9*Q04 zj6`MZKJ5~V$C2|0r4>h)FSR-24k7+d*&yi3!Kj6;GwmY)VW-xEovV#Rd)8Yy3vx77 z?BD6r(H`+om54-p0LjJE3PJ?jzMLT&&Ao%;XO!V-2GCHrHk`}CS3-wm(At}{PNUh5 zm<`pF$GIx&PkVgg?r=!1fKS2Wo%PIHfoTacWKG8-)x+QmIdsx&96O7cK~Q2@umYO> zL|=Z@Dr@Xr77WTvW6lAngyvs~`W-GEqfTv@+pc}Cjy%?diN)h{##lzoH2~EEKo6mc zP00KzwlAx=1^&ha;~cb1l9pZY(J$mG1wVeNLN}iyXDWzW zMtP^XSe->7uDXGGDNW%iLi6fr@A%$CU7QM0os2Qzs@}Jio<@Q?CS&rRc+aD)eJIO{0QXza7tKe+;Njq@PDkqKQ^1aVy;F)lQ!vn#PFh;rV@Rdznd6L+qj zDZtI|^C&qo%N_LUT|n=oypKMKgz5TPoT*INb@Y1HCillWMnpt}$dr5qJ!lBvbEqrT zo0VOfFkej?F>~O+fzT0LZ~ReXHq0NigzX98WZ{_*z->b`b&$;at%@^gou&VKU;4%F zT%J3V zy!OQm!?a~Mv0>!dW=!&GFaRhiD;0e8A~C0#FxG9%A?ntti0H69a$cvuT%Tdv}d zcw?qq^T30*@+sm=!3@j-Teiei!XI44;4E$=kRB(3V3r_63kBhJ&^MI%6iUe3;>C+YZUMkD=)K1AL z_d<~5vndnemsyp@qDtMtlnp&oBhK$T1s`QI18?YMg=C-bpEqwYFC zV;GJ2bZoieQFHRyWuEcRrS70VaqadU*RJD}OWo{S=0u`8(nK_RK1%+jV z{II&Om(Qjo?0lbmeT2STBDD7(eKuurs^Bb6{+#p}BY7crBepspx59Ii2MZs`CewfH zcP(*B^ToTEeG0;Bw_|5Gnw`|ERYux&z5dFp+(y;%gP_||1I3klD5LB9c6zjg7voS7 z;A2XowfwNo?Nfk@(N6lGFXCR3wx4UJ0c-;XwTJ9T>cnGjv!T-4(MY;V#>#(9x{Z5>?g>?`gVfJ2h=muVqRvj)}VK> zrMh-Y4r|jduT_k>m>J7s$+Jcgc{Xepriu@X;fA~l?Ec0h+_%?W? z-~$;Yo|`TB$oJ0Ccal~dvfjsZgp1s#Nkujqa@A;@F+1<$B}i5(Y?pC4C$Na&Cv}Wu z>hqZAx#Y(J_DB}0WMbs54o@$UjAr~fhl=r}8~m+*g0!Uc@r>e;)^Qr+LFpi%+fz18 z0d>~z@xIZ$T&G>f?jOk!LomJoyT{6mPYZ&4RdEFgk9a3IwH_|3znn-r#-Pu~?p|-3 zP#ma8ODFzhC3J@7tysKoDdeU5e2Mn@VHt!{>fCJ=E|MjE@QuFX_wu`ba(XSA3otSC zJn8sbZ%^mopv`50e|uz&e-38(2998Hivg++&3J0Z-UDyq9$ATP#5%R z$)^TapNf=1OqCw*YCSHE^dgE;@a6W5%M%Xwe82K=dz2F6kEF>l?l0i}bemaEzoN_5 zktCx$jHlsk9q{%zHNohm=-mdW!eUjCFaXWAYQ!*wzr+?GJ$anjsn;lLbKGg_)}M?h zX#b=M4QJ89d>GuIAwCy77OF$3IZ(HRKrk+is){<@%snNQMi z6V0k({9YWEKVNtm7+9KwG_1auh86GPde}bmEODQrU;T6WJ~Q~6LD9TnS@hPCIIyKC z<)X5(w9wYWJZ}yy3db?qTW*3AGCOTe4vmXel8T&s7jyhl%y5209JeI8x_qvV9@z@u z)W~;fWcoXtX@3*(Ko%?z?4ez7|qiyr|)DzQ)+jp9~(Eg3~>(R0y$pT>>hdyhff zOe*7I1}e5uY3F6RdzM>1lV4lo?pSAXmYX5W#x8s2t16XZ!erm%SUTpac0P9=L~DsG z3Z6}{mw_racg1+GiyXu|GCe3R=rsy*ERRNK_j^hH6f;lQV845_K#0e;t?Lcq#E5%_ z^I4+;;I`+)5V<9qt2oCNWvU)(AjFHqKErZ9Yg;W^NWuoB9+!1H->6Xi!}~01 z$STd!ymWWCGcoqfq*PZ!Ns-z=5a$K|ziCuuzh*h$B^X#Ido(_Hysyj*z83Yx?05Y% zg}{um%bVr0y&Aqj)(xXqNN()Py^I+r8diPCz&V00Dp4BKf5(4J>9sha3hJHb3Z7ca z+u=$?zx2;>)`=8eVEF`oEr0b(uw`gyX=Qq=n+X|E_d=M#GWcVk{%$&uP{8{oU9S!Zhf1tQyd4+WP4)Ywu(gKQht2X!e z)NW~|aVg0A!8j4ZmJ`%GsaM$^_jJZVoz;`85KQK2U}@4^TUa{HLK(d4Js5nGn#wd%rZZ`ai(z%0K8Czz8bzY21q0xrJK~`% zQgNRzs?I>T&ae9eL@J~z7>@`7PsGKuNxx@Augk??jA0npj$?scY*C|Z^mrv(R{Tpj zEveH|CN)*@&s- zC%HUiQbU}kkxYT-1UZVwFmG8C`%S7UTJFO@!E8c$sWUc*ZoER3cTqP|?#24V=z2yY zFK`|enQpQA6=$BMw!iMBzT>q;eX_ivBR*TS9ig`OuX$5UwL^z?Df8xA#>}jjS)hvu z{8_Yp8jeCvH^n=kOQpl)V=_C9+}*SV0tE=K?~bGKaymkRB{$A{T($d) zIHuEq$!BcdF%e{nenha=o>K1!<P!#u;|)3H=8v;rsqQTs!9ylz{-_3Qfb3jc)LetS|tdkp3GbQk538f7pbi`{;T2VDtkctCX(w$ycely67p* z{rIL}XDwD{J)3&1V7GyWuAN@`;RJP~Rnkpiw#AMlUFEu7G&hF!du&w8Z^JKKS>soU z`_o3OrI%Qqh3h(#_6AvX>ENfH8GEZ{1oL+apBmnh zm^tBlrbS`1hAdtm8A>|@C0goc6i=Vi1he1xF zH;$Qh-6w;D@#++;1604CiKl{@n%wZh&*3RI3(vp;ziO5X((Rug|c%>GVnEyA|e5>^@^+&Nsr=P42oG&u9h%67mVgB70Y6>d#UX=`WtlbL~ z^K`a}mRyf$hg>tcbB|#^-DmQZ=o*(M;Lay19j>mW?^Jcjs*KuU{)8lNdTMe|;VIqyGb#Vn$J;bf36=z-?u5*hMBueW%GW&(m!^zNA`uYZL+L0gkCV2A2pLDB~!HdCPvt)a0 ze>lv(nFJ7KLgu#Q-k9kLhPn{=#ywz?7A8+;wKwjRv}y) zZ-4K}znQZejFkK%@~q~G)T*LmYd+Xb2`)mwHy&>7$319AjJO zyz{NG%B-GkRLe#*HRgKr*8pmx}l^bBi1ctNT-*+)mO zw?MQw`GC2qt0Zsa!z=wDSnCFVMWiE2NAo)^MWj~4E9qd}R1Kqg6 z9r|sv!sf#M4Y_A(DtTwaLf*=kcSFrLetb2mlT}nx*Fz*oY^P_b4oIh-2#PsC*%^&| zvNW=aFSm*3?=RX#)p4DOLtMK4;X1c4=U%c{>U9r661t|d*x37_7ivu$9W~dS8@zR{ zv4ksBA!bceVt0o(P4N(!Ap1AOIkEUDJia`lCkt&K(348TW$@Pw-Qhkr(zVEy=Bm}> zpo1}V1JA*ddQ4{z@vPzdC!VhYPcJA6<1?xQjfXLa*oJ~tW-kTb9*e)iZlH3`KsmV+9?HPNw>Pt^@iob+_7cWggEx!v<|16 zjxiieFL&nq2p(SQ$SoZQ&|mODb7!zQrlg$5>7}+-?$KUeG?|Xr?T;1ZytN?KPL^*h zSz|%LqniCK<{6@wi()?s()UU0>WMAIkA8!e$1ObDo}U>H^6}|W#>IZjy%tDisR1FKf+7ax6C~U(UpJC|}RI{0iKfliU6C5J9p}Yb+}kn`PnPIe=BYOHu9vP+o3F~y8=Y(74uoGboo|;lLenarF;{Gzf8X*?MBz@_V@C zcS15T7_W6Oy(cQ6ALHFkXVl2gS}~j^&J|D=ptI83;pIRk5+z5Xa~c-7X0!fBl&ggGGnOVZC}kUaK=KA$cUTcaK9T0!Vzx_*o|0BN?e@!=Ee$ zh2*tnr`7gGN#nx`Lwt<)98Wk>hWylIvZkZ~f+DbAQi&eP=hr^L+D*vCmRA@VdE!Ql z92#=$vo3215b+;rGJ7RmfU_c|MQ#`Wm5HTK3xd`0HnvetX7SiJ-4e$==KuYw zhR~9s?6qt|`5}C|w$ZNWcQ7ru)afW%gF0(3!SRL%j;N8{U(TA9?jNFl-#UQ&!H~i= zT{`Fd#PCIR#A(8<*ZKEg=PCVD_k4X6+pfh_z?$f8yn1JN2{kG?g-EN!;E;B+cJ%p! zv|hRnS-Z_Wn=!Y-p6v>eu;TBRnoMFXf1POFQO*sWn^6S?+==W1DbCo$1Hi6%oayy; zf(a5vP4i;W2k^=WCR3*hh4yA&c!m=V>L@^=tb2v{IGu}Nyi}Z5LEJAyP~1>ZSFEtN zT~g8lZ$F?*#;mFPa@(R>KY_e8cTlCVP0_zu2BDbg@+;s~QX1Eo=d0**X1BG;^>wb> zD!&REF=j*Dl^Nrr2t&b#E(M`k>LH{7Y>=n`w~2xMm2DuGs8z zFUu*3z^mEr$5Pi_!-*R5{ZgHO-|!pMHW%8m&5JK7up*+wN`=1ImItg_yXigW(B)z) z{1n}X&fIb%vg9O_bp`$X{kh;;`;&gik(vMW z-Nl8m$K|YE>pTxD2L$!ttBn(`Q{tlTY`~4BW9;C4xwWLUp>o=b4Z3LTf`abka=aBWpB>XGx!f;Sa$CnV&4T z0}A^+cMH>FY?~;qFq;6PU{=J@BIZ~2ZKUK|?90CTGw0Ps=9ufeJBJ6?5$v;0QVNVX z(&11xIo!76Lz3?WkK;d^+X&Cij@kx5c7m1fM1-qS^&PfYb%g6LVyAU;M`?b8$?Cz$ zU-??f5A4^Pm+ghN<`Yk=8pO0$qM2X+c?3gRNn0QNq|X<^#76Of3(B(Ed|# zG0|ISs7(22iLY90XqIq_VI~LS``4+QAN#UZ#b*+xh54ec6Xf@p`l5|1P|Rmw0j^eb z@Z;dmNc5~xU9c@4l3kIfKdd?<)jLoDn8j*^TALa(hL`SmXxIXM-pL!eXe6L}QPVMm zNn+gdkLZ9TGPV|t;}+5}9Og#;upt?);MP;-e1P@brWFab9Tw{Iry~M5UQ}sjb4;Ey ze(5S5biy;^hxc`5p?rU!mr_GcaH4gd*V04Ub5*nQ)Ky(aUSJ~bN@?8mHF$_%lI*k+ zarJ%e(U)jz2!wvAzp1J#^~x@|F(xSsE2f?fe!QJng6US@b zd~&DavQV2}gafj4NK;~H#f~s1+kn@=BTEi(O!mr^Q|n%f&qL!yggYaM*C# zR^{*&u-22ILOg@4y;fww=8Mg>_i@c1K#C)IVO!;e&cdn9yB)uqIb7%2dsk&kR#vv| z$3P)vl{8WDaBg}3;>ECF<4?NaM*&ny@f%HV?L^~`&jmwJhN-fAd+qeFx0~Rfl&=vA z`$LH(C$$?%yLb8gii}h>&jQ^GzqF!{b~?Y3RY)B6ivHT3lFf!)TQd6}bn7Y2T}-?qsx=d^|(v+OyxJyJ@}w z(xJ#x9HC4DDef{rLf3Xn%$wcDU+&^$OElhu8_(z{-v?icVgoujU0NZat>P`Np^;N%y+=HixhLLyOk*>lKh~L**G~!+fe(Fg z)1u^n&?|RVBoF}!mN~IQ-3|eS+kL@0n!;mLLT%Bf?s2Z^V~OH<++0n`%Vc03GXKN? zLeIna$rWHkUaRI3QsUE4q#WpTwcet^Ch6{u&Ayc+??y_{upT_;krw z+=MNVTW(NP_dQ5qrBd26;&rJYnU8CMz|aP_^dGaB;L00?P|h(TI|}2h$Hnz?f0+d@ zB(CV@!#M=iCdZ6(=Bzzm!+&IZAoJfte6!kUUKxEbqH)jZ^S_qevXz1*U#Eh>TCLo^ z5r@2{P6f-susA26@t*Z{gC^+o7q-vTgP9LQ0?Q$rwXGNQV1<+SHTBDX+AzcfQVWh; zenwx=b)+*pl4NwsWN=;=k-rl4upNinx^x8${IrgHvy(^eDYEVRz&8QtR+l^`v>W2; zj#w%$-#0nt6l?*mbU7~e22LaoW+{Lmt<(XHIpqdVzGpb5UX>y20MgKPseoq%P@QT5 zA43O(WfIJGm5UW$UXq|?IxYKWV?rDB*Qb<$T5{~!f$12v94Fm%h@|*@a*B&l#3suWy}}mPFN}lba2_~M#nDPWaAFO-G`K+B zymP}HR0wh4Q(oaMD+d(m+f5M<0|*0-UV6T}(SLVm8mdd?mjqpzX1~@*sB2U|YLr&5 z>~H_c?=iffMXk0={Kbf#KFuKF%(~AJvnlt?`Gg#GQLceK8Rbi5PUE?x3?dAS@ckey zZ}_{DX@U5Q$1%l~@Z1nrAe?6oD0FS^S`0PomCUH8)tr>`N_hNFk5$PisI^?HllR+| zSY*r59o|k{i4LM(+e^fBJE(IYkTz4KUmqp;p7)wqc8W4au1f!tG>}gLELg5i-=?_(g=}IF3^6 zQN%)n$FjYu<@zq~52en(4h%4->;!G zeE8!s3WJ}T^^S;+v|3F1GlrTvsVp^f3KdkInI!@l6d3zp=aeP6{PBK$j+tJu&hjjm z;NKjS#U5|&fCX>+tk8dsg|T~a>P|+{uazpKix#Slo00h!FeYiS_T-iIbn-*M&DFNC z{Yo)_4kKjyCjB)ej@N3j&LkPU2l&98U+F|=JuYQ>`Xza}n!T0=X2CpM-^Ekmp-A|! z?Ffh0iiv6G>)X$u!@ zbe2Wx$0!>js)Zy7RXu+38JSn7?rF8xKfDAerA}wd#~*wW`6{w&ui*TK_nI+7J8*2CKGyRX`hR>;Z3j<5K*s?>QkJfmYONGH|YrMZdXlR$yvdGK< z|7qoQYQ5h=Q@~gt{>4Y=#dQO_a4@u%Rl)G;OCGA#F(j7mlPQ-X%nCD1FG zsM@WxTdq37)8(^7&tJt^H^iCOHo5P(L=A2CyNR2X7r;gN4iFWW(tiJNaGn8rB@1T~@nESYu`n~VfsXu+b#{)+5gPxm=?(8^LEgQQD_Kw1odOjV}0!P?pvzw+aaTg7-#7RVjc)q^r7{B zd0vhaHUe?@R#vdfl(ki7^3x;?xBQV}{?%p(lAWac?aCWO;w4XVC~_G5%6TA9I8H1U z?9lj|RENk;+O6F9NR^axKJHiW{Y3>D8tc$u7=8KI81n^4yp+!@$=@D+whRX#6Y@f> zTAIr=_Nn|%^VVXyS=dlg%1L+N36PF^rj)V~;xhAs12%J;ZbF{f-^MLq+dnR(fj+d& z52o?_8@7qfw^#W$_&&5D^GHoXvv5MtO?Y)+V%{AnGuA6wk+{7uz5>q&4F~#g_Z_4s zbHt(!VKQ26x$s~eB;r$rpo}s@IEa1gEJ}KY;pF8QF36Z*j>wf1=LnLfpSC2$ee2Aqsqfsa9FBQxQO2uR3|n^2FBj* z^-UdAb#mE;pZR5t-mie>9F~w2{Z(LQ+>EKp@yqYeg8ctt*ffxRf+*UWn~}OR$p(Ft z+bDws8K)sX`Z+b~Ds0QG^-;wec4qeFcEhKO!mpMRaobgxun6QE!0f90rT8QhC)9?D<&m5Ne@#-orL|!a^fjxG4GRp8N9B-K(7wha%#kmV z3$Nsx=#P7BClz^)qH9vE2!(P6G!7mehgVLlbO&L*nErL>bmVpXFu+;|i3f-y!gu=h#b85mPvR#yCJu_WU43J zwvx^IG%&Xf3Gt*)&$Qezw;T%#4TtLR94Ja}=5{DbD|d=+=+QCmMkncugUjbGcgIoS zudC63XoHSc$a-dF95^#)N73u_BF7iOP64??5J()0VwA5aJbUK#Y#La&` zd~|RKT@>^o0Ev1>ZWY_Q#!-s9m~;g$k@e|lAJyY@j_>QIU*4SZG_qLM_k$~3xdK&M zhA6v^w~wnf5}vE7#K{sEdGo+)F2CR_agzN8FK9^5+>-I<%!x!ceoU3sTg%(Pn-~*E z(ESyV4lbFGn@qKY>}reWJNpX`{Q_+V$!9a;Ll<#d7JuiY*IH#wk8Rd0&j#m>4N4s~ zz!H{^=z@&Z?83=(q0c?6d=FpKk2Z*oKOig7+AuDm0HycnVp>C_FDqu5K2;aQ|9Bcs z?!VC(8c(RH$eK5v->=MkE~BwE>-zA{>PwEyaEXYH|L~168djJ$3k#T?vN(7HoV6hZ zW6D878Gd+MAX}APc3#Zl%BGJ9(oJa1O_c$}fhbWIxR1^OLM#nkrX4Ndu7 zQNEFv)Dv1(MeVat(`%SZgfKze3FM5iKcJew!SWRRrw-_I%wl^MwJ%>HK^t+)fW}Rw zs}L?lfNPl-X|a^NnAH8@nO7GQga;`>-?E1Ydtjlq<}6fq3X^S2zL8-J?19Xgt( z3!2r-<}53VZ)*9}OdMtp#T?UIcC!9pc#@60mS$lET3mF-0)fdh>^Bo0TD~UbUDsh3 zJ%TApS{ z?r$dWHTjx(2$uRqMYBVSr3;t{#5X0fL!{$DWo}r4gtM(N>5^djV2?4zz=K`v#<2h` zA!Oyto65(weJe?K)wY0>^H8&q4hziW>xH=wdUaWaGidqwcd(o#Ymyj2&sIob`B`=h z^2_imz`M$8>6bz~nv=)qo>dUOzc-NvhYKu>uyh?nK*XuV5@bP{ z)x4v=;Q11lZy%$qNj;!Qa*w6JJ}cnsKk<4;tcP3Q8Y2rg~OX$_?*wq`{P2US#Q2E?A8S2fm&s7>-?>YNfrGWk3bXUT`+ z+KH_COk<~KL2UaUjuQSUA-!Ir)t5X^$f$uNE$-TYwKn=tIZm_2`DCF%uH5+20@BYi zk3>9KcU`jD3*+2AR1BOg|0zt>=Qx_*&z9$G3GnH^8(ui1TD7Zn8^xOFf#-IXqNN=_ z`jjUPItQGQ_2uTd*c|iI)w1)#NsvV&Y|!jE)4Q3R?W&()#^%`AYNa}Z(`7UG9x!E* z@VYtav~W%sqQP>B9Jz&bT!2dpO+b(gig*C)OGx_FLd29!8FQ3%#)WE! z`ti!JnfTV*^pE`fH{-5$Wp5k*H0_8gRJ-`(z-z>Z=cloCuV{{l(u}DA% z9U3UQ?@~RrvYEE$KD-il+j!7D3lglGsqS7fg&X_BN^Lp(zqJiZ$5vxzYc{h{EBlqs#&1DEh8fv97<2 zmXbb5d?yM2@t}6RKa{KK{rXq+_iLSnu}_$c)}PTzxI!i995=GR1w6QXN-h@_!5J0d z79}S$(Wqi9@c4)`Mfy9ExY2p8dQXs@tVOm?KsbAf1#$UeeRZw>Mqhz7uTD&-nSJ3_ zO^VSE)Lw)7VlN`|jHP|n12v@s?HSe{G7+(kz0Ebx7;2NK!dj>D#6*)L&I2hgqIhyB z=PZkczo-RRuZ{xZ8AAw#a>{fqJ>|;Xr$Wteyi*3E#6L)Q;6#XaxtGs@S|v=sRN0;| zHI2qgKhVmsBs6|S)ZgeW5MIG-Xmu5`WXx~2+4-(_;lZ{;=l7bBm1m|GeF-|9szND& z!e>jRI6Bmz*9;~qYIh9iFt^#j5c6LnOX;N^Wc7xeC+i8WYeuUt3Il%~61r^=hr&N{ z2J%2Cs@9F-vTB#zCa(VEl0YIX2SgZ#o`^4Q7^}jVHH@nIC!i*Tv9&r=pL6r*Yq{+= zPcthl!6_dMe_Jk?K7XXb;-`YC{vF|K8T|r#cqYB4xB3X$3 zoFfcSsvn?qTLiIUJsyym1ynPsnaRYRMSlkV`f%Y_v}~IBm5glB^dZ9SP-mAA?h$}3 zmRjU$7(B)r1SW3uH%vfeCSUYFA?WN88?;rtNWua9p`4L~d>^D8DQe3CIb< zaokexza3}-7}fK8Q<&DXDdnMfBwEDZWjt3v|Kkc6-hY*lwm(}qH{eBBEFS(jKnB}b z=*BOit0HaBwplEpvB&BYvGK9FQUyw37$-$4Ea%M<^s6TP}| zS$8N4+CnrRRt9nOSC}GqP{&1Jn=`jt#C@_@wKvSOV1zw}`yb$O&C93IuSv~`?+iXp zy<84CXvz~-Uizv*>QeO~&{sF(RYA^(XJD9@xkH#21>7XYXVIB1i#s!1Y>s62wRv4N zf|NSA#QiIvZiwik&ySK73fLoAFZNxRT#or|bUSJ_kPHqy&A2`cz`}Rd16klZ8-JNpj<#~q~}}c z+Ia`K!d;?f#d$*K+KC3;x=y-@o`oXc+n$J&O7w~Fy`dpdA0-cY+?bqU~c6k^7ZZfxD zG=!vzghVOC{KmZQ^Wy1W1jW)l#&C{ld<-sXD_5XSQc6mv+K|yRWLv=$N zG@#)h6oyhPKT$cx)gYl3)YDDB%~lJ@)=O8o_p$aKD+0p!cHl|d)y{K=3Rk9rgz?{9 z9!VZX&1Bzx+Vxm9nAqCB4(V7h(e*c`hUaGe^%3~v5wX$D@1=F(KM?sGE8zHiXl#Y} zmXh;WKw;5vfPiZiFL)+fIw!bqu$8a<-{@1*fg~!AUBT}zyD4WwKhT#mCVv@czhUw# zmP6!RoWv^W7zl$@qRa)?0K0C|6F&QCp}tYs71}J#!}}67qx()3;hG=dq!~CWF@9u^ zc5&83K0cHcdeEkueVF0FkS`#EEs#vdo6vGO-0R zA61#B5l61Mu{}^zra0mi>p&4c#)x{twS+577HRYgIJi1WV!DrLf(#MziP2|kLiP17 zmB6la*XPpNOQ(AMz_*OPxYo|K|A(aS0BWlF-nN2@z^f=-A|g#nq!S1rB3-IV?;Yuc z7FtAl7Xj%(iUJ}a(p!+0Py#`^^qSCHfIvvd$N%@u+`Y4V?m4r2=ib?wJ>_}EN}h&X zh1K#lBF+^jpXi-~U5|&W^$kmg$`vh#8V)4s7cfC7LC25ZSjYe8c*e)@$&vf5dK>gm zYUZavl!RtpzPM`UVL`IK6owl9vXqPbXp{MyH#?{5)4SVBSEPYx=Wvd9T}<~42vNhk@$qzNUi!EfEY$stR$Wdz z>Gyec++9FS|PUj zWx77~R&uIW_yg_6${+i`*J%RyPwZCgmzeeo3#z(lP~gX#SBthD=Wfsb>o47nLAO>)GXno1X#ddo_|YB>Ns@QIdEdpRH5i*^v|cnh#0n^2{p3AMjwq}m zx82ynnU+|#RjYOD`OlLNZZFN8G?Xx{tM9HG>UFBVn{W& z>&rdriH1IjrLBA}1(t88hPsL6*FEDIvMwWL9^dn>ZeJ+{jLl2(XOU8}#0^U+iJ_uiv`Ce#Tsmy|yT*Tj3;R1ayukVUoQN_HyS5 z2M@k|=Jgbo)h~MBcBP)h8Qr1vF7A66&{w<9CRTyg9{KH`M=-;EssU*wi9KPZtYhv) z$q?V(6_PaTSA_jgObd6gq3+Pb-;};nRAI2HX~mKn#`RMAk1dyP6oaUFhaPX-71ie> zKdf%g{EBd)r4=>n&=<(8Sxf!lYP5;CLSxI(u#iTpxx-||uabqLWrKgBrSF$s3pNtO zU3a!R3t*FSq6zNzj26l7Or!srlC~VuV7I6!7dpnd_*pwMUadp4qnKlL7?I(i zUFccZ_bSIm5KH9?I24;!vfU`>P7ZTZ$~U})QQI0zS#M!gWmBuFr~k zd0;Ly>XL!a@EmZu|HpKQt;$#$YB#XKOQ|T?gt@=x%bN=fhy6VM*WHGxxEL4M%VU!x zY*1q5@C%oiKBGa;E+k2aHs1pJhM**8pZwWs@?|QD!uB+5hOAWCyuL)sps5<{?TG2U z-7aW)G2GW@Pd#Djz45NW;{bTp#|t=dZTVTh`!T<=NpJfHsnzr2O4vEo=}^_hMs0iU zQOO&42yt9WRXC&qv1MuBAkh~5YYu7lU-a*p(4z?j$A-D1L#Ca2TZMD)mYx0#dbfs$ zuoKLTn`680uemQlw_1cq6O_uiiIBy3znr6S3T4Xue6>w!#%)G9f6l>eqh=0$>x{@dPb zc#~mt!EDP>-qX0s{e|9;D50ZmX7jEvmq~mTkD{gUz*$D#59V4pV%?l2e>je139tJ% zl{u_T82f(rP^$4VET+SMK{(gO%8E$)5R7d@OtKGg*lwhs{S{ zScHX!hy^!2jI(sl`_w!uBk$Gp@P_tRCFY`o3YUHW=x=TzRmX(QGBzg|cHcjP*00O9y!+B1khlSsM4VEU? znT+SfTk{5WSDZ8EAVp{kZv`}J3QBIke1xxrY>f&Lw`$k4qQGonWR^F!jJzg4H>)Ka zUW~<>qQ*0ajA>Q8Uib8Z3PIjKckhg6TJ#ES{-?w+TH!5ZLrYU6eei!4eJl(c>#zqL_c0L!0b zzp??G`dpPtu%}Yn7zN7~knA-{(o!L1O8%3_EUh-))bCQJb4}50RL0sj1qP2y`CfM_ z&plr-tpE>)Ng4AeU41>=_KJByY+*5_>=qHM!Jd~%#j%_w?xgxQLkuV_-Wy8{8ZM1JR)BD`<_^Cdf4mE3FeZvKDv1~{?5Lfz^xTG^I zS;Zh$B9$GahzT9h`~7b7JeRAR78flb{@OECIxOdEY}O*T?-PK}R`PJw=KaXBEKzEP zN#wvv!XrM0n)2sKD8VoF@j6BAH@qLLg?z2+vK6bCE$%;;{12}%oF(p{{WDzzs)(L@ zvHmgvZ&MVgUy+46$Tf430jYM?VXc>63bd17vJB)fe7orJY8kc>w%Pkw91$C&12L3f z{w(!XLCnMXc{!8#^Y*63<9e#m>#{4AM&Vf{nA&TZ*Fk?9_wW2E9;^lTQGqCldw=Ml zpAW2<1jyc3R$K%k*zjNQJz{NrU$wTv^B%sMaUM>m@zJtK$#7*%$GrsJ+%-?R-ys5t z&QYdAE^XFjYoKtod2TvcU<=^rs?F$%@!>cyT42hE%G%dj`^-=H%a&R(*Cy`jy12=3fxY@p62{)^h?k<*#u4 z;j3?t_o|X#)QJO)_anH?lYWZbqzLj`lp$h!2TmLCzQGB&alX&C^n&_9q#fUA5HG5 z$jPDLnr>Nd4~ejnrCOO$Z(^v`A-!ZQv^ju-&_bwSIPR^RlXuESA?BBPrMC-qje*|u z6~bh-ql`gn#K%%f_}*A)osG!bRi32ulW$3TrKu3K`5$La9B5mLew$XI@<=RFKRCH5 zt{s1$E8@$H12mQE1@AAVf-VUAx{?~ePzapT_}DzXXef9}!q&-==h<`kT4$e!2%Y<4 z%l%`KDg^J}r+n=k^Tk#ca%EbTRhf(BW^9G%5p~;NnzgeNa%sGPTFKZe>&d|>D=7;W zoY%_qQ&7jxkV$_5ci0rVkNt=2hu7=?Tim&Ih3sxjURnpD(`ReTCi^VYnh3v2`cX-P z#)EEw8w48JmBc481pEL~mdCrdIgI=3bXy-+UQ%8d+SjR5GMVrLqw+6jQPR6_znM|5 zOq)hG3~Drp$Gtdz63f=EdoX=OGm2Z%um+1y6N@5%R~z{O8)zVZYDR^;;kDakx&RnpkP1O9hB^qwU_2oW^RA ztFTkx?B28>0DA}L92&%cwJ%pD4r;BVMcfk9pTk1qbK5&)r=M1#wYt}2zR`$kp3yVj z23oPwAXEasRpdvXsb~8l2cA;)m-mur{xbFl|4Ns4+F$cbEjHJ=VOyH6kuNmx5LL#c zM;PmA(#IvaK`O40*vW?AM zh#*M+2E&+g2T-@_It;gnyS_l%GtLnk_Y}JP9OQU7EVawPclZI;$6OHE*XRt?)#_Jbw9GFIxrK)x4yOB<)A^toh8e(Nz(VmCVm3dG*%8==mQ{3 z_39jbp{epC(eJ`BdO9XI0xLw+QV}xv0J{s*>!^~JgXp1 z=$34nM-U_)T$O4H7g`VVe;EyG-7VE+|)0t-RrWSmcW= ze(HSh@Z?wH(Nc;k_A)Z*op+nOe{HCGF%jBLjYV*oc)XSgJFzcYokIiolFFI%5>h#^ z^GhZmYPlup(|ptC{?o7eNpEQ(pJKrYIY5!P)~Q|wZT@_et}t?gt4idA)+se8V)oz; z3;kl}`0TWxbM66mnwFUWnp$Vo7=@9Z{waVgZxcB;Va6-U+<+^(?CREtB#YC|rW#_s z|IO7B$U1D-m|NMS#@xQF)2kJRQL;1>_p_;9q7%n_r~ zNug_^BDZ~qI4VeLiC~61zrF7+U9s>keD`_Mt0F1Xu}WSat!_iNXz{_Xh-!t;n)jwl z)+DpERjYPe?@hT)=9#=dvgF#@>g(BTjV64(IB({fdlg+RmzXhxR%zR&G4|#8!Pl>Q zWHIb*tBG}nu5?t!_OQ}_C|BErbcB)+3P&X(Z#&zISr)fdaYE*R=1;Y7be z1)qDbm3+5a`U^Z}S+mIt%{^rcB}NDVWHyazQH`~_rSGQI z5k|2wACZ$zO2zaaf?hdOQ@_AxQ^{$eGCS!WCpLZjD)8sCs;ur|`TKC<=Fr%GQ&PuK zY^QXiyV??J+5R6CZYnB-86|xsFCrJ%#rhk;3D*yB-*SuOq6K_s%%gkkwbc~*M>x>o zA)JJbq*+;Fh{;0Ov`46=F`Q(bBd+Ed! z)X1!Ia#)ggPV7XG+Vl6$xD%Ju2BUUgL-t=3t1VqX5>UNqg73~>;3mh>zZ+NbwdZIg z4dgEvPbUUJREWy}h=>Z`#r7}q_BPPBhP_fruik4 zTBm+Yx`Cj%G0-DFd}Co0yK3~tVe||4Zj*M!y@j7*t-3?kH`#j>08emYP{xH6gI>zv z=d^gG0@z!P;Ng?C<^mlebTg8}@HF4|zUS@W|B@|OGN{P_Vi{K%jnw*-z<~wV`*wv_ zCn;{~p*bder31~Js8*4ws1G??X3#~z)!J_psLh*Rip>QcNv&bz@4%S)TI%*e+xM@R z9r1@esR2zN<-6>f8g1-ToBYfY)pJj;qS%I2W0LF2vtMs6dOXc8mTzdP|Cb7q!3!iM3h^i=E{W~NWGt44;S=0A>=&!S^^}Omh zYmR35{7Uj$(~h?i>mts%rw#B9{I+lXwnS-XsEaO`I$(lx;%fkvyY&R?+XF+R@Xg?* zMF~FC0-}*hLcqwe?ReABG(0=Kj_Tdd@vKL{U@zJq_$o^TRUO^J;P1(=4)zs_ZO4=F zwwm7)U*npn9&R*HJ(NFU9m_U6pZIzUg@=)sS>Mi-gX05k?l*QeXKqP_2GPE!skHr! z^l>Zlwe~DB34ivQm8k&zji}OPZiH zFs^P>5sy0WY>cYw@LmStzd+C1{8!qj_aOVZjulu!&|cTMSu3Us0TFvdZZpIrjwSpe0OmKOY1It!A5Oq^5uV$-Lh+49?a!$&%1(+XOn01w&B?^V~?$90dW z*k)#k%7O6%Px*t-CaAn3Kx>k0!-9-3q0PL#tVF=C$^=E5&geuDOh9=kh+p2e!bK=M z^2|ByR0$>5kwO)Bkan>$s{CIhcozPBA9}jN0zqeh)fc1XUw2At*;I13P1v}z!XFyk zH4z+pFJxIfaY&Q!5E<{d!&rm{xB5>to%_vh8E=J<>peKm+y#9Oxx?BMhIEeaGK<^p z0y9XyLz^@gCuT6_xOS?$M8k-sZ1bkcaXIh7Q7IT0Hp>q~EaDV$STje!lMsxO$M{{) z`|?Kd9Mw&7fF0-p9(0StI0;nhc+eYH^@oe#4$f3%Y-#W1!hKY|C|D^!=xs6v|Gox3 z98a`dO%Wz!;CpFL-75#VaMR$!rOAi+B<5gm;nQNY7`?ply)6Lj@Z)T>hCGy$xL3Fv zdjam1kN9$3b)Z6^kBxZHURvLgofj)`mC|y}zVd2&X6m$+d8EJLj2E{>4z{VJ`8rs4 znXhnJquzaYBOBGK^+7o)PbSCjm+V`Q<}QZ8L%vGliWxvej(DT|!Sl5HQQnn_+P_*m zZX3|SaaOrmIbz{0dh4qH*8NsIM0ww}Bs0R--gOO`2N~< z{_P2H(_R}=(Dzk>!tqh=NY2V+lsbjiTK`aW$HR_(-19e-n91=b?Vp#tr516_i;Y)L+k3FGS!V33ax|i? zs3Fb)@OS%9U8%eJt&ZCwH%%zgxm9;e{S~HijeoV}-kiyb{xFz&)3kurv|5JAajyhJ z@R4iP?`ZhZ_#%in4sV+iPUm{cVQS;|%JTU#r`hqb3izW-K+!#&MON6~^`!3!Guxtb z-2Ap-=I$_Q`|TC3u}(dlpRhTkm3?iKtpW0kUO?OR7p40hi6!%)r#Z zsR$hKga1ZAiN}Y$a1WpLmo8p&F~8Y#{T|Jiyo{?#(H>}H#r&sACr~_TpL6@`xyWX(A4VAMH+esj|k7vsq8vbSdUN=8ulZ>F|bu~*c`QG%y%s$hjtVfCPu z#FuMdTO(G+XBzUYNgo4*A#BdiC>jJJyfP_fL_rkiSXOg?tA_(oW6Rl86|-g(g@cg= zmqcI|-~0Om276hSIchO&W#~63(X>G<4|h%m&M7F1rcN?GIj+4+b>S-DkrPS`QD2`N zeYlq)RVJ-+*yrI_Dvi7hOew6K$wn{k6#`(6bm-`>N@(Nnr?^8dn%axsxbi>89XZrp zbv!hcR*YsgU({NK+EwXSKYXnU9TTK=;uhK)QXm1iex z?Ly*bjlvs=h?LGeW_vH7_ep~xX7_v|RWM^=7f~>OF~N-?G(W=&?bfunz1LW1*tN?d z`7y14RvRpuu%f*GdyprF*fTZsZX``CH~%6@s_xHL3y3XvoAYnY2&fW5wzTL{FGofp zTlbm@SiQ8crn7+WdGAS#_z*fg4H>ZLX0~kM^7_!Cype1XD`mN95nnBRq{HAJH2wL$ zzquRNs?@(zuHE15y#LoOPUQ|J2_XQw*!Jzc|23sh9rlxug=p2~=jTEHYbgs1$sr4b zrOyGg#)EQ_3hIuNO6(4kO5ood%hgqI@7MttU9|oEXbSs9lqQJ`X=LN^B4C!>O1n)Z zcLqmlQ(S&b|Gb4kk*7}~@wt9FK)R~-jr<~Iot74+j7iJ|ZvF4v?q<)%@tX@!wohG9 zkXI1-6qY31wgYODMugUO;#n7=`esW;qr#dxB^3wm@ny8?KHduqwxN~Tzje*zf>Ii> z-yQZR14`boxsgbCc*mTZ{=z4m(y^r>T+*53M2>?>SwP!D2L@|!u)R$-4}>BLMm#OU zk*&}NxO}gUGZfy>^_#r)CU?pPQ?A5Ct26Oo!{&+u%0_B|86n~~PTC9cm@mCF#J+_m z*BzclmH_ikD{?H(#E-i%l>KhF=A^mLq3U({W;@?w=Z5xB=z)>|;Aq#=%tUOzsrm2P z7HqkF|H}Zp!{j z{yzWMZ^aV!+_%3>z#|~^C|j$`dO$vF!>az2T>@s z@E%Asya-lY`lwYWkE0ocZ9#?qN&tTof%K%~CIz9!4XCe%(FD|t9Eupe$+Na#DJ<=- zLs*I(3In`;-o^YT12Nctn>HO6>;+2`yD4AkRzq4Fr#fpi&~x*V5r}BNKWX-;l{}v} zbfPSZl+yul7A6bbm#?&4VOi1h3+%tSP-CIAGuC2-0i5S-QE=b_-9T#|Y6c*VT?-y`lEo^%ZM%_U;%I*$VFB8G$yB=%c z)n=O`L}X_&A$k)djC=ks-#qK$Ia7S4O>p5rc7$0*21cXD0;ASJaOG8y&^L1=MB~$g zo)>8zB!S%M{sIO-Z%S zpOoBL5QU*Ri8Z6f!sj(&qku)3UXmA4_a50I+zNbmvareG-$vPg!5Ba9HxF$p=Kw(D z(+u)efRA22=oPFv7uA2^V^6$pCPv9t1v32=*ix214|g<+p4(Y!4rp^lF|i6)R-dPa zi&(e%J!t*xXwLCNAt;Z_9(LmJe^$KNrV5v5hw;7uIwVRo$~(g`^KyYJTC@y?Gh}O$ z(BweTo3}$@O2>}Lkce#z{59-@3)XmGQZ;Tp!Xzs>TBP9233Yh)VQ_vH(C{DG%l5VR z6kvb0N(^=c`%B`dFb(F%U3SVym!08ur;CKM%T6_g35)5riuWxAiSKG+Kbaz*-hF5r zUNKRCP1(?3&DBJHFZQ~*DPi&R0YTQMqs^F`O<-;Q2KM16yhasct|9t(|DAm*njFz2 zL3hmlKD0VyNW}K%+(n=L?~qP&Z~d%NtMlQM)nKKT7O@?ea#l~~-60U|rRIF!MUffh z;(YtUaGIQf|8Hlvf0ttT$v&ZO$!yiwHaa``H-&Gqof6tY;cTIJhZdR(v`{P+kq`SD zG516ry9=;o+Y0^1d$8eM3iGAHyr}%s|5G>-iDtqHuUOGb!0ah{l`sm;&P8(FIS?l{ zz4N~rV$p4VpuvD~r9qc}u(YiO%99q#%J#(oG|7+PK%fZpYM3KUXj7}*qqFz^{}Ouq zC=!=5>=w<*{UcNEh}O=+Daw|Ri!DFOoTVtG{dK*;6u$k`Wr*V4LO}qtD3&}Bft|A| z*x;F^HDQ6mji(IQdG*36YnMisZ%ZHAN1}+b$RXjBhnJG*?F**=N&d-Az$xjMlH}@h z%cK8U5v7AJq7hgUz=hC$s)?m!^aT#WDYpM7aU@t$KA%zS58HWHMJe4iKJBmQZ$+EP z<%dp)8DV#ZzZB>U2wJ|+>S?tCQ<6h&FSB^P>&30^1SiSFow0}5gm$R&?<$(};X(;Z z5_zF+aZ6MF2>a;=~tTAY#oP_|B`0%aHAR4 z^7D8ZGK-WX`Bdk~861eZxA5I^r8o?D_nU|9*)JE%0@%tpM7}vgKIv$f204N`=-wKP zU*BqcKi;KK+`e)?WD6|76gl94*wIU}xUGuh_lw9;hT45Ogp_s3+Z)j+)*50p1{vN+!3iX~D{JzsJr&4Le2Y= zUv0pAncy0K^B=2`yju++MVgnuUA!K9=D*x7s1;fkd10ZONG^}i{p{Ub%b&A86lA_x z7NOS~7BbX!SbJ49|DBnq{)Lw6#Jbg;hY*r-f{i@K zqg;}*_FG1ELN=Oq*m#pk8}FzM5>NkepI@9H`RV6QNBom>y(U0|s#>`O+{Z5-ruEkn{fH-SENWaO)!T#j@-h7n=N?~V@px~lea|b zAAxrbxq770>a9JXYMjJ?YK&zi2S4jB3nwdp^){mY^=>Mfriw9LeOK0) zhY)pkKwu8W7`g09S(%9wCW5j|GtcEyp`DN1QugXjg4Tlet>TX#_Ph<5_3t4D-aq5n zg7oY@kl&S!uevu-mpZD*qS-zraWr2nvYk?#0y)36)C4PLuJ?&x4qQ-Ma%SW74i(cU z+5Ee8fTvc8YktS|irjSvONvnaMZ2rimpsN283Fp}SjN7|KxNmQ0vHS<*MF=p?5KG> z``7YT^hsi^j-Vv8t*V4~PyL9T^>ir!L@w7IiTMhr?ozjvu`&%X$#QD`YL>wKhDSwP zkO{id|7v&D`=Fd*pgWwQzvm^1JZ!GMvSi7fh?P}|QEq4E=Ro*J1gxi|*xVxyC&;tY zObJ{)zt%Y^^KzDl@LGt0pAE<6n#-UNJmg4$JsRbr0FwEUBjMFY;`nX31YW|9NY$4p z&;~O1$Xc5y!V6&#i1-p?3hlGJT15lU^gAree&mW~?te|X}bewq|yj{0hjrQ(R*JI8a> zv;CfVWZNJ+aS_Unzyvk)po1vEsV@J`DD1I-V-MT3`U$Bj`TN|5WC4v6uPx%xxUGBb z+F|Rr;(IYw(-n0(Z(Qbx8Uq+3x*8j{qibc&|2h`m%_q5#UKmq{3*MhD`57 zP0TMII8R%_>!g1cl?V*sZxpJ-yXv!WF8RqWW;sJOcXu7`s@9m%Sn}H{RCW0exXJNA zCUjr9Jhu!q$$KOYnzibwD?1Dhi!pE$y#Q8Pa$@X)PR`(!G=;Pdp~cEbA#a7hPbzwR z)&a%~W13!bePV9ROj3=~|E#=qB=A%Rcl)q)&)kpQhFO>9DR(gTE+Xcl3*lhZVXL!m zY;T+BMv5;KCN9ys?3?On?AvTRX?eENyedymsQud5wgU$ZnfJz@ZP{k!PaVsJ6QH8* zg&jV9j<@$~?K<|>KqGHP{*)bG>nFu|C9m6RGj3=6a|3q=D6lyO!|Kcln1fuH&IB7; z?N`Sb+yFDPA1sR8SA{du;8R^1pMOnrpu}Q_~aW)1CjVgUs>QfmT8;+TDZnhZ)9HO9Mj>S z+nQbf`>ti~O=eNd0olJ-OuEl-H7mTYxcX7RzTEWtbcY^I1bqOM%BXUl$*@>q+#mT6 zmtfEdVNFHC(*ijbK~*QMaCfhsdPX>IYF&aO zn&*zFBb)OQzXRvd@HJ?CvJ7vJz}e270VXqRiT;bKPmc3WhrwzR+G`z>TaG6zV!`ca z?Yi1S-%QTtlDGGaP7VaFXD6^F^H1e1Vlw3H^KrftZfNSeTKqEi;1)|wTyxJ@A9r1+kCao$L!KWuVZwbFFceLit`3_4(^WfZ=j)# zk)Kjj_st%ISEfL##^!}D;FI?G@mq`F+Y+W(?U1@|c;}*M`SaTMycoyBn;HGH9>q12 z7TY>n+ z{a)$)<+_a#+{24UJ$*+ETm1OxZL+lHtAfGy;Ywug7jMBj`@@+RKj^6iy8_9Q3pfx& zP;(w5Iq0Ys9alMJaBsb*LA{4a?G_+rI27jE%-=nG_FwR%$7rG|Jhphq%@#dt+g^il zz5bI$CUDh%{T#B%2G>L5Wk6L!j=2tLL8Tcnc}@domLWK6G5Ljx6WdHyg6$#>35?07 z_iewy+|PAvVBLuDw^YF<^q`8aR*1z@MQB|&@%f?7I;=mimNk^_O{!PF@R!@~uEDdP zo<#2j%kk7ivsB+KlHh@E7eb`lKN4z!M7(J{f_)8QgPXG97u3pGDW&z7d_zP*q9jc73rd{o2L)v zKCFfnyBu~<2R^7cG%M-sy|X^|A>}ZwmdT`v1Q`#t9lF-nZR@*Gw${z3UH!8J64Nn?h#iN;|&r4ZTfQ(|6&3eL^ zcvgOh-zd|jo5%gXq++yE2ZSy&e+D9h&HWEaPJKTZ0+fiWi$`Moi$;Sr zDr3IGML+Ml)amH$gHGb z1^KShPuevMHC=Pn4N@vS{ic$?L*R$eopE7DQjBKS;C^kE@)05F06 zM544+n=faemB9;3$NtPIe4sUOr($BI!p#8J`5ua8IitgP9NPT6b>6tXV-OkI(j#IG z7b-!|_28SIMdGkjskWY!kC%ysBw%wz$LvFw=Y*d>?qOphZHAiAOyiFa!)8dC{ZO0( z!V7&J4SL{~A{N5_wH9&>^KSm#Jl%Da&v$ZXh^>)$?sxoyjEm#~sLb zjmmr8Bxzwzx3Y(~!m#1!d;>1Xg-EpE7dV4 zUhWU9b9JdF<#MOh-~-m42%PGG9mkX8nvT{@eeA7rCW07+tzY;MHCSF{j;^_$VE(E6 z4#DeQQrYY*s9IFS!=>o)&YFm$+`2iI6~(+7jxvL|r0$D%G} zL2PJ1PcUCotV!AyNZjF%a-WwHT_EHFo> z4%;U&E}LTMZp;C)EL<&SF2BmELKUQ1oygthG1D-G9`Anz!gs#UMq74@-8So{UWbGH z1Voa$4TG5;W)koy)v#Ls{1%Tj5X~M15J)$L1 zQ!@^0hqDS$@fxWi!@2VL5eNAU3 ze#1-a&{dwu43bJRTX2Opl>HO;Fz1s#K?`lg(KF1{i|2qF61G~_${bR+T$*AXu(woN z9=>#}Op0`4#6YZsUk^oAmTcNtyuf~^S?|i?HLRrPFdlUpnnB~cGbfSyc%OX(z4zdt!y{w`Wa*fVe!8K(w+u+8>d1!R!<~*T|5FZ`J#g*<2X%+IWUX1 z+Apv{{hAYTSpzvwmRoSLXoYV zbe>Y!`@624jl3$?CO%kcTPK3TUnHXi(5>pLIbcE<|d>7>2zF`31&(K^+k9oxtR@tV88nJC#15y{t%osxuykD4oR=}A44UnoW?7F zbMRuN7`x4zd&O%33HRecMoH$mx6>m&Yy&z)z}OlXVIkll*-NX zptA5!kWDlekn7k;0@@_C={!LDwlj7V{~$52AUyBZw2$H?(`Cwcx#2#wa``M+0#slx zWyZpGb`K5^@a$;H{f0RkxhbkM+(vQ2_AOZ{g08? zJ;nP;1@YWT#q3wWtN65c>3utH{2=m>dwv zK2$M5+aR7f4-n6+N&fN0u`igIO2r?p9bqw0V?jE6oki8B>9bgR;xIp$RY=uG6 zOU7xN^XSR(O_o_J>9*8u`dGi?Fl~N%??T$;)ST&`M}5O(x()0eNB5fRI!G&M)n4`% z`LVX!YYrX4HbXV$QsRBHmQAr#A~e7Dx|kh26?4LWd2in;S&%Q{?Wp0brRp`Z_H<9> z5UPRkCMm$cHWYu`W^@c(U`6`=_HO z?g@v%D{ZXIF3T}1{k}%iTHZ-TiQG=%CtvcLYCPPYj|Uj^;w$B;f)#61Y5tif5JO58 z`o;ENg`+W&^v9~HU`oA8f^$2nKcUW5z~!ei+h8akKSr{-;6G`F!v12!pM&$G%xCy+ ztMtBufz*{~P?)@Mblz2R@Lr`dLA8-m7CZy3X{6p~ zM`9%+$#Gq#horgr)B~fQ_*rHSl`)=$Zs<3=Rcl-<7-j)hEpjvFo#>7-a_D(kHY)0o zFU81|W-qE>@l(HF{VVZ)SQo3v;;pEX!;V9LInShk(s3wXF= z&_BfXrz-S{McobWkGR2|tL@no%XoA-Zv58BO~_IxNznD5XJoCHcLC~wQj#?E0(dOA zxmTJKOsxOaKR3ue?ivnGKQ7TJ`$(32{p%6d!k%r_e?CUojei6WUi4Zx<(znz@nVnm zi+a$+^%*`@c$i{Hk;Zewn{Km!bi*9YSuZ@*`k1*+r$KLGj=~3v!D>wO&@bx1V^p-F zWd7Wh9KOeyvMF0U2`I$jdhx1%Eln9tNYR|`Dw$y|Gv9AdNdsWxZZee7I$SLZ6v8lY zDtxKVA09NE2Y1_8$Q_Cz!$g@BW|oXZnM5|?9g8eme&}Iu&MmUr{0%LlO({=)+eEGC zm{7A8kUFufhZU`2%ul1Mt^~8x$EYC8FyNZQZ5eEjqdamkU1=5Mofn^d6F+~6cN*3% zFiD?sxKNV(2@+SG@V9HzPEhC=h#lje(?g~v1ys(BPYQo_OiTSPJwGO(W(o?7Oi+HR z^%w9Nh@q)q1Xo2n_Iuu~Ts_rjp|Xudc;5}Er#jG)?T;rGW&8+642Za!^4zVi{E)PG zJH7W{4Qd5GALhcu|9r?k8e+<%p#VVDiWIwg!(_3yk#C0V6IPOuV`^DCY#$DHqpBsa z)aJA!C5Pc&5WK9#*{=E~oY15y z!!wtfRCT8J$frr~bCP=35%;HgXWQohhzJ?Ty!V=Igy{y+nyr|vo-@Q}-2-BjCo43o zB)IkanIcU6vA7v_{996o7o8Qyx^+5gVu!_M(5)`O}K6(FLONaOl;DXLjUE&z)Q`>D7IT zb^3Yp3GS=xdOThd_!$x=L+v!97LgU+@XAcD%$tO30H&>|Q_{qK#(RBYDoc&(okfqp zKRklczz4HtA7gmdjC4akAzI+>_d&yY($TG8!b5S-#d9UWO@p({40alipDN;FC45df z(tbQ3_u;I7FOL!SLosp!Y#RVcCwfsG7vUYAUhCMJQyZ^sE}Ys0M*G6t@t zX_GCLE?=6O`OC{p*LrK{T#8?RpUoiM@bhHfHy8PBuzU$Qurrkp=k`@w&K3>nAqZtCT7 z<-wfVB&S4Fyjhm10SoWgw zd>P0^mQil%mfA=pUfl^=&LK+rh7x;X@ORx~-z?`I&qdW&uq@IJPvdRlqGZD?;d`7L zOtw0K?*flrZr~mZ^XIG^x+U<->1ooX|At?1EGPK9GQ*>pNAzz!HjC(N|{iwfnytd;C z!EQuq5gu}61CqZ-lFQoy4KuYq5k6?fr!~&zbK;jbYb^_cIuP)P_NoiNJaBjxPD+GO zk&GMp{#oS)VGR6;3yI}`7%&L=+LCX5vIY|r#149+ZrsH!fQ zaP1YRI~~w{`DxfX>HpF6ok2}~U)LxKDosSCgNjP;(gP9r2?7EF0#XCgdxy{hNRt}r zJppMdO{8P!(o2vQsR2Sq2oNBFgz)lzX5N{5?!L3m+%t3Md^mIW-fOKUbwwg_{SF=Z zG$HW7|KH^isc8(&C2zcyK)gI7H5DK^@&%6rh#lFNr%*nFPyPwV4`zHf?(79nVWQ{M zcE7x-9$zi#3i%>=oHX<7Thh!M#IJf#_4v#MXe4Zx6bh<-n=UP-k|G!+ve2dD(#S(W6{u%hLJm5(vRu$B*|ZLh8+yqKs_nCs>^RJ6f1hYN!c-k0nH7-l zrEvQJsL;tcJKEoz#^%&nRXbB=U%E|W^fn2*$Q0fzHHO-Qrg2qA302(PP)Q;M<1to99cgPkxVEZPxr2L6 zHWD-s-|h9!5KTGxkxcy}!#G>a`+|qQ^+shR>*SD!$%cFSS+671Qyu!5IID0#hUCfE zTbT?69mo*9sh3W|Jq;8URbym5bR^+pcfTh;6 zj0MHGmjfw-GOUCi^trwTWC&59{YUQ@(cPj(MFh-bw@ORV&2O&85*Tg*aYR;zmRsGS9M9qR(O?xnn^ zHnD0nks%!dBp+pd`cT{};4Q|*_#;EC+8WXF3O1elLqx8s_qdcL*w`ksI8mv!H;Bbf ze(^ykC9L1A8+JfjZj8t3Vb7krTJ#ds!lfm1`;# zSbJ~|%WVUW4Jza7^2b?8oO8o!{e&z!Jep+^t9_}jllCzFcJH~LUZa-34n0l0SV)QxniUHl!lZK~_D5H2r-Z$dr-XtJ>8eQ98$ z-;(k7eiVF9Rl~h3PcaLZOFjZ%v&3x{ibwn|HTVn|JsKV==BA$zoI8yVVVjb@Z`FIW zcV=k;MAeWYJ)nZqlrq;I)I*A;Mo&S>>0P8p2dBpi=gvH4cCc~X^m0y-*FeqpYauAx ze>IEF2SVJLyVY~fh`83J zXV${*A9thS`E^q<9qh^l?Lq*&b{eb;;zK$u0evfKRl`htvph1SYp5WHJc&pbzTMjX zVq#@gpLgO4Ud*2(L12}GkcFXKP;gri?E~Kvyr@24g#71+lcdt_b*%Bo@_ep|yxKPf()Kavv+Fw|6VP}?P_R@zF)5NyK zvUBOxZZ7(rYQdeg-$W!AIwY0*(o7`TD+MXb<|ZF_nMC0WF^|lxz3z*;0S0{Ki95~n z@McLO1ov|+-@y1$pSQ$FKeyQwL-||fpQ<;paT>cx?f(aw<*wGnz2Sw;W~99?3*7jC z+taa8Uo_&8gk2>aUCyV9v-^G3FN`!ONipa;hLQ++fCm{i{Mu^lN63>eW4SKBLxL2{ zdAA>J4wZ1s`y+Tn+R&732a7G8oUk_fpidah^Ax1`OwuVXBgC>ChE?Yb>tw<9|9yTK zqWwN&+kL^=wLhcYo8e$KRC@V|MM|b8x}a$qtZK&Ruz6c}gY|7^0Sy>IXVV*q=_0&9r|=wtBHM+9 z^uYd-5O%w~@|FkMk(pVmd4`3myh~ELeiEljixx?TIU5oSm7B1T;s-C@E4=#Sd*hgc zvg#eiV=S+QkOy$3UdB=&@E_d=MJ0SWscW|Efp1at#|?p{x?|8cpuyNL(tAx2$qH(& zQV@zDw;P~YamLW6w7098lxA>DBf6`Ff!}(*a*nn?n`C8kwcOA2+(jgP9{W-ePnAfY7(jjAb`J-<9bz}+`9ouv+v|E!_O!5J_ zA&kd=3nfoR7Mi^Ib}e$C~KAZb}^tTujHS`-2jc zEWd5Re0X@wB)v}*k4)R<2#*iyx$OeX|6Ju?5;8Wu#MKtr^$7;50wW4n3Btdd2xgInFrl$dwarp16KPy{!I6t28v^aRjY`IMpT zs(aTm$!lobYbB~A75+Jb*ihAn~dac!e~OK7pnOo7hj9BWTc9#Ik$XB zdau+MrS6pLHVVqOUg-7u@JmXDOx!*@2OA6>dqlqPUUmc7je(7JyLvE&4RPg zCe!+t6X`_!9-Ybd!jRN;0;A@{_sv(x0sCN_ugeZJt}{=4-*xsd@; zO;`TJ*UooBQD2V*7?PY1T_j+)@8bH%iL4XHapv_!WK`SBcJ;;lBkb#~?IbNHrcNUY#YxMe_D5)p z$K2zombKuKNWlY$-Cqc_1KN#W8$MFB?VAPzWek9hVPZ=L&~>!OM>4*P*~L`vib@j89ab-1Sk z+|w-=iI?-|KksfyPoXoGO$Utq72bFKMTMAkniojFM*HlG1br9R?nX1C36m^ruJ#%>GKvi?nZr4*IC>FAG^GPW=bSzCMdxMbQ`1BK#;<>q8fG+s|0q;#5` z=o|g};#m9-c1ql-!rZKcUK!L0-ihYhd>lIDA%@mKvYOF=AMM$>3g_S+|7S@ppZKzd z`l=!ZqJ{jI^VH8ER=zj`5$VsoXFKuY%GC|iL;=_lm|Kz_<)CiQD#{aQ2^iz=*5<;w z&6(HFig)DNhY0eC+4yOqftG}t(DDp;$-Q+t- z{P6RHc-j_D_Soi{ug;N0yUTBu^^ch{Z!N_{<9i-o{3jWnW$YGq7gPOJHx-1-bKl~+ z4d6-g5_!v9GN!wjdfrjrYYkN#Yk4^AGb=FR+$(Y->8lI*T9^a4 z7eXrdjc0`^w_hde+|n#f&^MMpo!)hO+@0EGSC*W3`8aYuakS#qV+WgY)ML@Fc>QXa z&HUFX-jaelu;G@ekncU&*IQfr-b$z$H(Haog%;Xl^_VKYj5oUGbw`@h;uA zAVW=7D-YVjm+fM3Hwk-LFVbbo8C+3OPmig%;{dh_e9NH#HjD0 zk{ZK7wl(Ztd*v8%R?){dqM3wzH2;Nz~z6k$@Og})l4akO)oQ< zVwyBjnnb}mqk5h?Y1^6yb~ADE*YBuHSZ*R7ws04k&0Cp!DJKjzeA!>4ve~(ruj}%s zhOW&;)s?qS`ai@_>%WsCH<5I@4p-NRr2-S;YLH$`ho^`=c*M+({c7_X96XSbiWc%d zfT-^0H~@IPCAr2#Cv@ID7PbM%IQyEdYtyxUO*mcOE(flXaq-ui3O&0wRW`8jSm@H2 zI3fv28|)Q7J8B1rue@dt49VSvB@g=9(_{8qmfUKJxNwg!h0RTB8wrCr$H}D26fbUp zOn#=xHJgGFhTkC`iIWpdGdjhi0zR2Tp&kngY_dgfTtV~pEvM;&7k*<4`Yl)~3R5mW z37Avq7YSqLD|J9hd0e1^9l}FuX?NZ3OL>SzYq_^YE~0;Xrk7GB>*U5;d-36Hjbev{ zVPC_iNj(8nZHx^Br*<>r2wTbFZG zD{PC8^i@5Y^yvhv@@Jdjt=*d1@7?97mdR&^M$X$3!>rCP^dAJgU+9Mmm=EFpStDl# z@xi^^GeT;v_ zAH`zeWp1^B+WzLOsRf+@)wPg3fBB|S?_8~)A3L1>GQ_z)8Qka1ggZ{cYn zx-FwfxJBB5o;mroO_m^aRn8zl2YM?4?kAnn8E_D}CN`Z39+D_(SZKUV7 zTJv4{bp|=KJ_iCYZd5x3jQVmD@5653pe-TI?Q_8!HDyQDghF&J?eLDjQUg;MDvP<& z9Ajb<{%34&J@0?+i2W<3Ti?Tth`(3nczv!P$!v5*EphwL4uqcB{(7;jx(sy zsQRI~c-m48#-c=GM`o=8--_^lUEN|qJ*c;NymV%$i)!|6CdLtN-?}_-N+5^}&aeU3Cf#8HG(5-cXu#i{v&D%VdAnw9bYd5GD0Uu;t@JvDzl6 zrQ4HpDLt6oaI+Ev*?$Eq!A%xlpOa>`H zI{L86BuE^H!O8s1HCoK*OWyuBi!S@UY8!HR!BZu?q)bFBp12x{clWDt8jX=G)q>=@ z^c!3=wr?4+oBv>;Y##ltU3oB<>v3SXC#uTzg@9~OSo;wU7tU~YaJzY!Ol6a*f##YvvQ?> z*=H0EX$E}?y5}@fj>=uKs@i7Y6m0g0lAGcvESDZ1VJs}LIiy+6ibac&%%v3#?|YGS z)ZscZf_L_a%iGUwQaKI}A36bX?Uh^0_}>|->`C(o?DM zT7o)$UzXrQl-OnMyGHd)WIf|Bo*fnLFlSdTfilfmm1HP?rz3ilGrb(kYB`|Tu@;*f z1M-btBhnwdJbD#Y|7!p6Ip`^LD#B&i;oyuROIr)JvK3cwTkHMD^=$|`Mc>1YZ@aOL(T9C zZrgpC@d^^e2FT%hgI@adNQ-%?0^;%Cl`9mQ{SP+jE75NY=JC%r{2}LH&T~J}^x3^z zZ9gX2{j+me^kFn)Y7Iey$@zmsFNZBk%%>u)qU5_fKAe=VMV|wFTOtEQ#vVkjymteX{tmtRpPvF3;s^4b#}1lz3*d8sglwic$=;agC&eBM->`8Go)@}hk|kM zsz3kCZM1<2W8+<0R87vCK;j}D(*irOk+hfly^)+*x`5XlCE=6^9F?Tac^TW})+-a6 zR*mHC)5=5eJlCZ00qsRf?dqCODCTO`c&fP1&j5QRc=vsC_`Koj13*zHDWFqdMRQm9 zHu+<28h|p>#1c61pTWXFIk%LaAP~ z1hlN=5!}A*2sC~}-gPhJ%fosX8_$(s9EoJzl+vi%FW`ZnVg8TO9hgS zOQ)?^9^$*@Ktb)0y+HXGdm{Np{f74w9(5ImgIEWq5gWU++cUK`Eex+i1OBlPmqaj* zaI1A^W>&cr`IIzSGqD)C8y3E@Ysa1;#u2quonWK4+zD;2bR9{uA+z2kmND+962}c$ z!cg+h45lDfdDQSjzbq4zp#GcmZU-E}OLx)BgXOMVUk*aK537WuEGm_`X|xc!Gxw82 zi)No=sArOrX?9*QDWjtG{V6w_y@g?5lr`<2w|3^YkLSJ$Os2klPP_`Q~TMNLs@A{xI`N zrdOk=#DL2!cFX{NP;#NXEZDPjUrjLr*LvN?3)XI!bh~erv~1*ZhphC_1|qa&DM?9f zE2w384`3D>{JoDN3#n{`aX#_@*_!ZPB%1!{$&Ht%PW-RqWNIw@VG2?Ed4HFU9yLZD z>>s~svBm3{csOJ^q`Rzm$MWUkr}N=xMMZs}Jh~kgx4WIFPAw{*Wqj~4?i9_~S7ZY) z-P>(D%?M(~Eu@5nu2}MN>?|?m(PFYN!E^+b&etxKRR6kTa7C&6C6p^N8-hy3MzY%bOfQ zmyod{qusnyg+)jcI}%j3@3xy~CP(cFhY~TK9%Mwhpz3hgIf5ax;?TX{>Y4fl62IN} zKe)f@Lr zeFD7ld_&aGzfA__;XF7m9I)!2;4c+e2Rh$&j*|4w)KI)jLX|AO$`hCQM(Q4M+m8Ew zVp8MJ@6!uk{echtBhAgMuT&tm&Ts3%etbg|=NOX>JpAHIzcKvwVCNnwxvS<@hw|wO zK3k9%lbiKcZj`@AEgX~_PRo9qjpKF@mj-Jlc$Tg9PJIDML4p>$U7ihl){`7fn`bvH zRwLYlD7yXeq6ATF`O+*faS{836&jZL59q2Lm#5|gZF)T)^iyYHMC!!9McTvG^LNNU z){y(N1it!1@!hNi$)0@YhA(edRS(C*o;H!=xqT*1CUzIjh2)}$UqiWjL;qV0wznIs zV)Zjw-?e3(5IRvlqJm+emwwa2-b^2yv?sYWLc0`;?q1)}j zn?Hoig5CqKMqlOz`p@J#tgr}j;_BG(Q+4mB@~_MB7NJ=G+Af4lE}+64L2%bas_YaC zH*zkzffV*Cx)inao;C5|u??5*byv*_p##Adi<5m=PKNW$pnfWRZfH#}aZS%_=KVv% zlSSt5!5hd!{pYHUj3`L8N$HqG4pTFgk94Eu8BPW`R~^TkOA ze|n!F-s$rt6Crqdt#jWbTrjY+?wD`48c|cetI(J@Fh0Ky&Wt#d;e)e!oU?nBzWKT7 zJqDQ42zB<;MIC)qA_j7-b$vf{oB7@_)S<*o^5Xip{Ne9lbapv;>S%Fo^wsB^l9<}%3*#WMJ$gz4D#5F{{7Znu-%DsK?zrz(ifsG-s?%nu@x9Mll&&%vwT6On`}O@P4CM z)Oz2?N4RyV>{%>)Dop7TCX3FnQRJZmA8iFseZ-pPzGslPWC;Ap1qfC2tY12MiZYXn zJ6WdNU1xm_AlCkXgCfWzuSVDHsA?vKN{^9dZ1C`v>@!o#DYZ$8eC_D!*KX%poQudNfGR}&+m!P#YPd)Fa_!|wk58y^rx?=4%T^`F_) zD7j#kCf}dyvcPq@K7`&LrfXKJMGo_i9M#Ew7#j2JTJrp$6wDm{+}%Yizm}@UPb+d| zpiMTE^7uiGzgN(Wxf31xV6qH%ITbze!jD#wcf8}Y|&f_Z14?P-z+mtV9ydaqgfESwpmoXdJcmb2)3TI;!g z`g%@QD!r{z#KRWq>{iaR$A^ko8AXe|Y}`HRkuwfCs^O~qzb30Hoh*qnrsVfke((i+ zaEceg;&S`zWuxj;%}GU={0>~)bQk-PlcPIF7y4D-?z+LW~BS%6pR{TNlA_2)!N9Nm z2whnU82sxvmpP0Pg7tbeUy%Re9&Ye)P!c zEN|+zdFU6sxNIl;22t*w#y{lLCk0{UsFekN8QZVw&RgQ zWs`2)SaaXl0-(N@1+z*C4GoY&jqDcT$HdXCpa$y2UAI-sBlM|lZmSv#Zj~+AGO-r{ z{vj;c|Ij2U-{y9TCDfa7s&>K!&N?IU)=bX1&}iz5qq`sOW!36P_y!IBY(6hZx`Orm zIv%MTKe-91SwLZ95w|_76Ecom>8hveK>JggPUyGQIx$P~pCsLmBVXd6rD*M zRoFWGX32CA@PeAPLvAm0JB}|_Hnv^>4)`%z zl&qgQ5?0QZx~~pcKG*G{@thd@`H7Sc4=v^svskj>jQsJPz{bqaEuK{L%xYp^=Z@8| zFXUt>|HMo>4FiGf0D4T-t8`=+BhX-OCTNSTj-&M%vgy&Hmyl)T8zg~gZLakKw&@`h z)M*JV^PoJBV`$WF+Y88xL9Y`m;?cAuk$Sf+K``B){njb(dbkS_vPTpT#SZ};(LM_X zJ4?u;93q`mn;l8QfdwNzyf!-vz@~{uCYC>`JbqwrRRTSDH?Izk-{T?KhjFhwY?RZR zqqw4W@5}tf^Z$)p8T?_#Yv#VXpTQ5f_tkuv6a^O=fo~Cqb_;7;$*ZU)PCH7ZA zappEVMRg;EyiT;uVov#hsxg)}+Ea(4D@SD^_^eM3F+G$aggn=-^r3^OZ4|ilOYhWB zp?_xg^hB5+P4o<5?zkOaUk!hTdq~H3lP&_XG4FTN4#JS~T-zeSA#ufyi**h>HBWv2 zX;QJyjfDUsR$dEp8{fZx95Vm;^1_s#TXyCOc3MOVKlB(mggRXZ6oq!z-DgUyNH893 za85ElZxy@DRnF_Ue6|z+>dioSL-JJ4u9$yb?0~p$?ucH=%_gkBGc&u?g4z9rAHQya zl>lomTGUo9X zj#9k)MKK4yljnLsu>+eE{ezduF-^kOh77WTcA0y@Z$*GVa0K1sEm zN>u)cNsZLFk8eX}&30$t*G#$mQ&^lAaSeCPdx74zz<@ZoT^OuTohWYSKESf^^l4HB z_~-EC8VWMNGW6xch{pcpEe$!^0*SAygE7|`64&`%jm2&27u}02(B?*S0~%QQ{CZ=;B>iy-UIN^By&i5fot?yJqr9=fEDexLkWNeLR|q<#g+j7|5)XubHyuk$i;a&4GCj z7!YAf_Ob401AnI-ON^<7%Hn1weo=u*TTP7$ffRJzWB4bCV!Zql>Y>0w(^oY0GK&j8 z8*T}wwJGOZ9xS?#RElo*2IZNndc_(mXYz7Kie&irvZ}_A`%+yN<7SfjKZJ0yz}MZO z156Dc&;CL3g0y69nDa(#V4vQ_|M%#Tkxp_#Mi%Ohu)Ik#&t?M1(d*EsnvR=($@3av ze??C(xQnC63Cyhonn|<3ayjXC*sYg3+llN&Z9KRgmi!(v>{%;OZd4iaI5`M;KUY?3 zhInhop{){r@)M%1K;sAJ57qi%O(kp9aaO;f6431Y8JNwnJR!1+()S8eQ{8Q1MIUkQ(gS&Xc6vY7E}qE!0t{JM$% z-K?egYBomyPo1?^tHW^Si_j&qxZQ`%4e4D-#1VhT2x=j5bKarRWZR%iU0pm3d+#vY zfF1D&F{N2lDQGU@&ZK3{RQ1;aLJ2;lJ9&<*9B6Tx-uK>8q5k5ItN+w5bk(sqBEw1< z(7z8Z83QCW4%%*13O;~+H~iItx1GAm4czf-KCEIsNtEYtI+*`q0h!{O0_KT}l|G3+ zgXG@IhVk~(1c1yp`Gu&Qz|m`>f&1p2uK*If2Z(WfmvlOw{y4GZR57ZjiItZ_Oh2uz*`KtI$D%3fFkAmx2e$?qAwwz_>*Menc#Qc>Zkk$r?2U^=LVO-k5x&UXYZuh)s!^|*Y5xc=CT+ zeCy3mxZ_aD^Yh>}OOcId$TJaWg^`$kte~EsK0Gg?wro+$TmtQSMofGq`d~eZkpR5w zqs1aO7M0@nn&nSp6r+tc%dGlso`bDkMe8*%Q*5b&CwDyCt|g0}TC$XtZD2QVf9_dP zoH4AZyLLuB?$)6r^u$-urSjTlQD3en#JaL>D@82RKFQCiqgLrYm!biEk~%c0UxSey z%7PsX_xhTozUsMU7}~6G7xy}ZzAb8#X-X^m8JEL!&Oh${Y(jc-YY{`%Z*5t9L9a99 zs(5~Mf81pmm?JN`-|H&U+O2qvbntTt{ycbp<`J*1NCREJf`x{PMl$pzDv zA;2sq*eq*=0dAm=o1IT_x@-lPglxfe&%6o9%ajU z`DnNpM7@j8?+!l=agBC`r!qZu)?01Px6@h;J}}Uon9rK=%;6UIVSnv&rH*)=qH7}G zHg8@NTxRpj84?`|YVpvY_+o)~{Y&P^Y8DRG$SP6qR$&EwJQQgeS>jIgpsX_lY5u(8 zxa)nV_fRXpB_DJmjubbvSL5}eU6#!nQp`-GegloMG)aB2akD6(tPoDc>3#X`!AZ8t zR(Z6`q++kjq|k0SM3cK?%4W}-ey29?5$2H8_zo%Ztv|V!WRhiAR{zeiNcP{K=2u~~ z_{)M%VGEZs?w1s9@~&*Fg4t|Oyw>RjLyg8(h6^#)uHOu7eL3O;6ZySlHho`({l3

l45i6Pv9-;72@=2|IV(l(A>rZxUz`LTH9*arA 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�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 z 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%0A5m=Bo4E| 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

  • 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 KytC1=hexUJ`(;v-EgOr~cjm?%ZtTCA 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

    AXy)B-M(xW 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

    Jf9~@SP##XaC?_ZxF7!6+-_tx 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

    n(wCHb9d{0`G$+=|IkQ3RC!d-95$(E+ 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⪴U9T7v?I6z^7i`mO53Y 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

    ^%@jAnW=9>!38_T$rjZlpWE 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 z zUC;GkQI<)Rh4Dmv_1|#bBF3YxQV;!Sc?ehPLG>^{1(FzFRXv3Yj!)j$>|kRPmTzok z@VVFXbqG4QtXPYj#;n6)ZEzZMI$wW9;cI&e?Vng#Y2&4`No-8+ivDBnc$@x`!Kd?t zevbisvuSyBDvL1@*_hGv=o9j5Vdwf9vGYbdQ4Vl9*fyo-D751Y`D7j)X^~s1p3AGA z$I~-lAbb=VFgNjNOEQZq1pOCxeK{T@6tO)SPHK@6@0ZEkDGy?YDd`LSm-NbcTj33l z-3L4nYwGDM>IaQIV)l=qM_lZjr%chH28jdbvo*ck}C4ext+xol9ndb!TcPF^`o zHwMq`=^5({&uzV2_A70?xW9fpxAfxnD+=8O9A7y{-xt6`r>J5EZV2SOEfbxYQ3Uz- z5e|xTw8dSWG?=J=O|aEj!z(A_*8U{@dX=Sv4@Z|Kc{{&t%{v^R~pCXCZq z3?Hq*&r*+GF^+Xb=6g+IqD;}o zmS`n5msjW>VZ6@f+G3C#pn;Hv*#>xW{0Z$jEuK;52~QVZb(Eel{_+1HKW{lZ~8=% zM&5PtSuhSuv{V+2Lt+TwpH4Ws zaUSz2^-F^|ho7*C08Yzz;ZX;j-tf~!>!9Z6E}IE=*r#DlBy$@T_t!37m(PUhj?VPo zt;g&&JJQIHKfCIa=ne8aQdG^23VqW6x zMxQ+2^0le)o9(Erf-xxj#{y;lTbYv5H_G?q4W~a@xou(`2J}ZekU zhnbg6`TXNM6q7Z{)&MD)@=BeY)P@_-@j7hga&V81(KgTxDMUAwMC{y*pe*pn|J&HB zb&2P*vF~En#Lsjb67BF~cxtp`8^H)~OSBF0nb);~81F~Y&nKEqx}h-~bRINLWqJ)5 zN9@VTZ}?bhtg{?{`2J$Lj^B3Qur!l)Z{ zVc(!0>{pGxh7D__8ST|Zx8rEPsq83&SF*SYXanLf!FE9t7;QU8V>+)v;BsqL$%?O4MR zlsD3s@O>+fiHLIf+D&n$(Arh%D@SAg6_q!sXilc#E{9)_?*vL6>3;5JpcGa~V*0(% zk%hhq@ba8@z$v4gl0@rKvbsYbb{u30be@6MlI#TEXifmra%s+A3m6I>4%!j~J+Mfw zEAB<(<82{0o+4sH3I44+jh$pTMf8_6OVZPt&|3qv`qQ$9`LtYw+A47y6E>cmi*x(9 zLNv$V_s$`>9aGTOSj8hRPU(U~3`U%vD+}dtUb}&pH>D&mFEcmS*Ui9m&=`I+Uc8s- zy@E^O{iu>I-C#+IbljowtVC^$tgM%eH~5(DzV6~Z!awJ>>hKd{weO>Iv$zRxKTcjl zW5)13YPV)i3_DM~#rgkS;CrD9Zp+3$Zp&e-X)>Amm7IA$yNy};*+72)3; z^sVeL{w;#)RlYM@IN+&t|DJHR9@?DDp6tBm`@~&8KMi=&Wx|2#>SG=A`^2%Bjc899 z%BR=}!u|yKs!r$(;$1lHlMU3jZT=jm*V25RyA~+zpvy#i1Zy998JH6{n%C{H*)*9Q zkn#2Ncdb{9@y3tx)~l~FPI&Ju!QW%7)jZj&g>&-88vozv_*i58;{Pl5-^Tt!mIuE1 zjaQRrb(p1MM>TaBX~r#Zf6Cj7$;Jk9K+y;$-T z?X8r5!HEd%Dd~Be?=2JIX^jaVtjqA1gHBU*&eI-C_c8QM;g14;1MW74-tW;$dA7b^ zc(|S$({o3vm&LYYxZ6U8yR~`a;#Y5a&-tVq1&+HGGCPU)?V39uqcb!vO=v!>SNOgh zN%zPr9A8s#HRz+6YSNivA=rRelV+Mb+MmYqNkZRXo^8|5x0#M-^4671&wq&6pLJ<& z{T3v#IJ~u}-@DtzG5$ViQm?{sj@4Swl^yL`vsL3m^NjvXqA~hj&>lk={%MAvfRd;e z{g_PiySrSHys1V38Hj2>ucq`^%PUoKkZLt<%XLM3J!AXRvNF!qYMho|S4aB~I$zV6 zf6*{551;HsW4Emaw*gXQpEGiJn>?w#)Vj~H&Ds|UfY+-0j~%B z-=XFE7|(yw^9)++#iM{DAEh<8G>yS+iJyhTlj0n^UvqpE_-C+KFG24)4G{E!vG^i~ z!#$!C*xprSxzB@m{}SjuU|C%e7cfh_pH1)8U!{+b^(}ID^%=aMB>E5kyWb1CwFIRfKb+f$_z3z6_=!FqF=Kn%^^zaq8Gf|4 z{A8`skMUvLrN7f9vyIO9IYf7Xw|iZNe9@cEWXOc-O3{w5XncT|ouT>wi%+q8iL4WJ zH`Dxoqvx=4z~w+GoUZgse4lA;RpdbE^gy>bh5MH=rGVy*OAltged6SgX&(dvS9`oN z74+rzDHey>mLvy9-Ijqzgv8&L;fp51HrG)%(gOWuticQ~!n462b7y$>Bw1sgAcGco zotwLi!Azpn9#1`Ohkwq;P4=XhAiO1{ygsi@K`FPH3|YhE6mh(5^n6>@9wjYwqtRaY zG-iH#fx`%Ycfga-58zI)xk}5)k25J{0k6{Yvg{ZOWzs(N%hi4R#E=q6eK?V-2P+TVBN{-jtHW zb)zDg4X6z~63j2;ML0us?QKrrvnJ!TR1IY6#spbw-p*q4AP&3-{O1}DICPDveAARv z)Txm=+ua(+*c(^1+MfzOF5);t#;tR+Iib6y)&B{bWSgWncg-dbt5{Fv7I*s^-*0iBHxlz8MOS3L5LA|81gf!i{k2y|;8XCizL zJdc0V?+0A?HCOPqQkpq1pLHUqIk3hc>++J?J9Sd;1>S@!7ipK6W{?^?3{m|Y#VB7b zFN?icjcB_@Z?Oj(rqZ69>(Ce<$mPRvX7;pe>PHi<)7Viq&}x7153!ot6wuY_D0Fp3 z(7Dv~EI&srX9A^o${R28GMhTle{+HU|DX09XCm}o!bRDVl*rCrjih(bbOn5nD7Z@!&NAUaOW!Ul5H(DIfC*wSSi|o=XGw0JQ1JB=&@%$|^Y-JQ(>yaY`&rfsy=?f5b+dG3@d_l%aYwN2E2cNBwCPw9 z!7b}72V${8mVJuOFyOi};~l{7Spti(=}u*UzLnySJHTVnGd&wyKz)6*3>;HqXi4j~ zslzA!0FCWI`|)a4`@x3LRXxb`If8}l8{}UKUe@m7BdV&1PIKF-CYUjBE`A@0xAODB^8LIp#l%|d<|87<#p7xT@@5RAv8MuxyF9@1V$!dX_$M`5Qu>-SiDQAd$d%r1k08V_Y|6 zeY)$ui0PP^^m7=$q6F=&@c*goX8NA==|oqh9OrvwPLN)@b|jGVD+tHL8KX7x^CsLf z#Q|UDekOqnMQ$ZHN-!p6jl)ylj_#)jc6PF#YK!md7@g461RiCyE!=Ez)Hfz^eM=La z1u5WR?}Z;KW@9W#n;h`bPTw6xsEm}e*ne~7$5RN0GH#Qf5xi+ZZF|#un>0Mw31 z|0Fq5=>4`~z627|yv=bAH!JwD-dXhpjU)-b0!;<{IWFa7h8q=_(~9ZTX_K2Wd@r0} zD)^L4hYj}GDclD?>~UB*OiyzW;q|+jZ1AU0t4+iDV0HMxZx`m?FfA4RKc)vyVx@d2 zK8fM%32vUY-9)E=<_BJ3lE9ObGPJh83GQ!WaUOrFxZVcesPN$}N$nUd{gUXtBCu^S zXfYNgC20Y(i{biGtEt|2;G&K~ge%vVa2~8IMl^QGj)o+rH-L43H93RUM$-@I2sbt6 zms7V<>6GUZ&H4B+Umu5OCv0h2oX^9hkMz>+LxFkz5te`k61vS?4$gfA>P>sniSEr9 zK7t>3C@hEFs6yY5fi^DUX%atHYjDQY?&FT)mnkJ<&qtEC(w-qr$clW&< z-N?%HdNoWw)`8C0_6v%o&b7b7xoX=$XJuDAY)E_3UIe|-RnPUF8Sfl(2>mDHUwL$f z26Tq$0MXE0?T6@0MZ2*lCB`RToR$oFiOxQpxd(M9!)C{RJa;fqnox|7@$-t31b&?{ zH#-CCUb41#q?ApPQv~mJcR~`wsV30cXM7|xKetATb#A7dR&M{rnot|l)qFT9m>04o6I{l<}7_$XBNm}M-o6h7TMQqb-*c+PUJE?4(#29H( z<~TkMnF?P00veNOmKV=bM60mc^C2P0Mcpcw6g2?Focf~RcaykmF6(loTUmcVyr(;YnU zO0=imAK-iH0o^OKoF5V%sF$|w=lo*KJLqk!(IP1puDfqYkZY8OJ!`bXfU!56V!i>j zgmXp^P6)p`Y|I5u1hPpfB|)~(Ua-Js!xHR}EVcl^bS7JYe4WM=%ls>3}W(4+BZ6`S5XWy zgeX4uMe!WTpLKY)@Mk|gmq|bt4t^xg=rC(6v-iMx`+FhIKl1|rG>Y@!1`FMvIuiFS z7%1i(>Al@~`j$v5q9-@KQI|_bhqRfu!kWEK%{|6T!2SFQ9zmf9? zXGG0sZ6~^k;T3g68}?9{kP}Kq*hyFeBx`rzzya{-7$D>f3XgrOXh&fhi{EGs2YYiF z2H~9kiCA|uH(PK{Q=gRfF+Q#r&-XK{i|d*zI@W0CRyW=UO_ViY8Rb7j=?(RvOWuL; zjU`{AvFI8}Wx$r8sS&YLIL{q&E$A_<(Zd$X>Ks7hF_X$jqq#Itx^}8`!&K?IQ@Y8N z?v|eR99>wG+_%dCI{#pU&_00iAZZ=aT67;l?SO6ulS@)Rd*^0n3Ymv5Wak=-)gAKq z2w}rO|0&<$V&8*@UtmrB+6omOCTbI6Hyf5yhUi@i14N@Fm>Gba=A8nD(H z(wr@)IXBUHI4One%nYVAu}Z87@G&nzc5LN#CN(j~7A{9n=Fgk*P7`?t6v%s+@*+ln zI{hustrAZUz4fN5JbxGE^Vfpf=%9Adx^f<-dowRSZ|+vBKjPU&gO@|)4uS0+mA6<` zKjbLTcli6we~x7lF_!LZ9%60G4`bAHo39v4`N^6Y1vASProB_N`P@*bsTTq3wwKuz<ZsfTko{N=hQ$%dNT769!TL}OtU zgk;L59N;+a?^7@@Hb0isK>tS>fG-Sd~k}}6a(!`!_=a{t)XYx zn6;-1>=Xj@^}I`u73&;4rPf9G8S-rrz9;egH9dpp#B?F)89E!7U+6Nl#U3-@?49L6 z{&3fM!}eYinpdRvk<5158z<1N4!~a}*cr{T!ACt{ZAFm}>mO^k7Ue^pew2ssPiIB& zB#m5FrO>8``_lpEt%YDO!K*5C{-2}u{R-<@jOzf^xE7{yMf}EsV{4^6*8NHUF%Q@i zQqi2eJ*3W)_uO_*dcU0P4D_{%XiHt}VFB;X)0xqN@ZA>G;pfu@%TW*H59$8Ae2ndA z53Zm41+D3V^+6Z0$yvU3FN6M||NdwAf2tz`GW~*M_e)@Rwv+6kX62^IF zVQz4aY@@xgP0z;w{5bVE>=SK7z2KF#E5qx3j&>lnHDhOWBwE`;u#>3+_K(@|1XJEFx24%3;(bh^!rnH&vpAM`(P^^ukZ>^z8|c52`+ zpV~?^j5lObbX1^tI^LsJ9((|JK>u!gO5 z8b`~XbjW$HKxQZ<%9rTgg6v}# z`%L^7?a+&M`29;ew&{^CQQoS@-cFR0KhO?uYR5*AZoNo{cKo7h2iltD!Q1-4gSR!q zBPWyN`!;8oF-A7h6;4+KUG?cQ8Divu_1yO0_1yH}^;{Qa1R8l-DW#nwN~DMW3G=B# zeG1jb%do$?En*bt1wMmsP4$TkZ@nVE`LJ)Dnf^%&`i6Nwt9R)(!G9FS2zN=`(MI&q z|23W2l8NzV^%BuBQVz2%mrU(gECxom68=|Q;QVj=IL;pgnSZ3&;c4djj=(L$46%-V z-dq>JQ^4*qS`K%cWoI+lyq}OkF^fYcCO5-MU;@Ef*jpl7wMCDO>X_5Sw51)7No_Jl0csvOz2XbF^ zIS-LWwA+vBV|pQ!4*i5YQc34j($U`XHZEm#VLVv>46goN2dzN;LRo-k3q)Hk=Cx%Z z%ln}|od9g6?@D{z1WZSJ{8VF@BFggnmws)}*Ds!KMifuig5D`~)MiSD@7JWm_Q5`` zJ+U4zJ_YAHmz6^%8)ls2Aet`U`!K^C$Ln|5dGA6~R>(?v$mYL#$@xE6R~mv*Mk`|E zu4FN(ppVS_hedH8BZTkG0!-*8XcQl2f6eJGg|Cd5Y>2UIw8qJ7?&%!%_*Ba9lNQRY zkuTZoc*1<5g*dS136{8PjP6ANAL(rJr?UKuv$fM1_A~(+lhzG%yI$xSeI#hZ3WA1Y z=b1wDQQem-N~~>uV*jq8eO5@>w{%`h#$~idmdZ+djTTosk5q(AKmT`>nIg)}0G(`f z^_B7G=)3O=-Y@7p+d;odNpi4})IJeuv|+pS`9PR^#FE;Qaz(Y z{!bZPUMSMktB zr|&NcStgw=^dIG#=v#m9T!q&64Wo6AvX#6@-$Q(7=QVw6qRnMzS0in1X2CPyvFH!h zF8ZUKKPJ&u@GiolVwg{8Pob;nPc*Wd1Uo^q6R~wrha$H!34TI((YB@q+7>R__OWPN zuxOjYQ!$GC0sqjq5Ro5zVtn%xeS%yVXT`UI{)(oy9#rAFUkJTRCe1WX2ezCdkb ze(kBvz)7l6|4+Z=@Hv#81C5Ir?&${jKzkO?&Da=qbP^pI@VUT?{_y2^(FI=@&+{yv z#y^KoAn{|DpD^cam?cbV~UOmTAMSJ`>I9q98hA1!Vi7C(E-aVdqDL38X> z|6hrVa;&;%}j**#A^+fDJMoJ-0uuqT{$7-U9}^xeI(P z+3LRsd}nIsE$}_qT39TX73ExEWdH^*mV;GgfW8YfCNTLw#ue+8>HmrTcne=RkdJ`B z3_jl=%njw|wq+fi9#*UWr(zD;i#Zsf8uwwooL2M_ya4byeFVHg|IfeV{a5hSlSJ!+ zo?9lySZRYd;a$)feIsC+ci_Q37w@uw-_Sb^t;;8Gc)8%+5)APVG)^l9TJ=vi zF=o_m>VL^Y(DzpG2US1A#2Tb`0p%Evd$Alycno+W*Jd2Fw_-SdN7x(smlFLiD*9ht zwbrWQna)-}M!zj)cxy#37aYPEF#i~Ye-#pYTfwn_14xm4?Zs-iU2R8dm!}FJm?HWE zJ_c}c%s=ogl!digNYwQ}@ZbNbu2k?7sC`PlY)_u=j=v<4M%VAdxgXJ`~l%}OvtGyY_ zUMxdq(Nw(a2HuvQ+Zm7a;O{0;J=dhu_9*fHH~Noq*1zZV{L0`H@bD!p<|1^q)I4Xj zOW`{=57H z6`LDnZhF7xYlzOm0H&`ZalY7OT3ZahtN3DyKbu>=IeB>sromoDe-$|tWL%&*!9PAL z-c2MJL$qDSVhxA8`tIJP+tIsB>{B6kD5P3HaWDA1PM|d4i|M1d4}(IY@5MzQX;1lr zHwIr27z4CJFTNecUKeY^SIigI1nTfo$szVII&TS!ZQ(2C9cesNX|_?Cph#MyG*^oK z|4UlB1YRL$H{#~Jw4QIP3CJG^|%Rm z=AnWOW>(J1ym2v$GFmBR5PgR{=o8=z+Jknf%V;RtTS%N^i63Zh?f+rl!~SF56Xg%3 zE641Y@O=q86UvEp2dmnRJkZ&lVdm{d`)Y~&MaB4m&cXYlBEO%YYk>d!F7QggLdefR zH-R1qjEa$0z2dYJ{{QqUo2~1ix!L(-{Lrg|&A;9Ny(g=^tHQ&leR;kR(>HLc(<}QZ zQ1BvZzRu2oPS4RYu;1e}Lm-u*@DWm|4Cs6K6T)eIs*_3)oiN}jr<<^s1C0cWSlojS z(EInNKKK*e;E#A`)u4w=G<=%C(QrnD(tZa`wdX0lGuW7~EH}M{=~@jEJQwh7AjdhU z5q*(ZtP*F@(!5Q}3vv8Sp1O2I?>JQ|uf9N<3cY^7UEu!`=$ZM~k zVX^$7Hs4{ee~~mn zULknU8aBRPGJpS}h86M-Pd;vL0$=wQI?*8#r|%Wo_7_?ct;+B<@sVmxAni0-ue%@V zTs*HxPR|;|epBQj;01s_X&~nGjY@A8<#|YaoG6_)@N3`_*k{n)E3M7R&`R9av^4eG z*gsGTlH%n6DJKK>I<&WNM}FoLlG6i&hZs7Z943`?%jM_vXY3udpT>moRS2Icjs6lGF=cZ)o}IDYyl=$? ze*T2;u2e&ke5;_|txBh?s-XY$f{(I;(#8pX(p;L4TPpsNIjqndbq_g-*X@hZdb ztl*_*coSUgn`*GB4hTU|lh8pO;aKE)TkB9x`CFM>IsnIJzd%H3#vsO*Usws-t7Q{No&^8s|_q zKF&r#SA$Ot9O^OC!Q{GmAqF15vN_Frh>$nuFnV_ZTXXN|Iog-c|7(FOIUWf_d=qYO{6uQI7kN_#J;+IyCsy9hk~lxXi?RG(JW-YufNhm`i3 zR?3?Tv=`&{?Q_mcTLZlmYSRcI`%A+9rtgNxIJvwxmxV{ux8qcg-V(=RGX@#B-yQfD zlW@iJG5F{e-(N&Wl|C>V zEpVegLPiN)59FOwpgfe-8EN8~40LDI;v^5i`yogtb#uDMTQ!)sM~wGkbz;1=v^Q7L zSTbIM7cW1I&Y6+kyiJJ95=?U#6vg}jIF)$Y%6wOLjHc(QGTQpu{Xf_ApU<*07eS9nj74wp%y1O? z-yQppU`tvKwVTc90@fGwxf9w&V-q6QHuOO79{M}ji;pYNx}r6}_=*O^j_hD>0evMJ zN8pgBYPseQZFwAd+J2A8tj+cY)HgTSWYG5+Qd~OHB-Y}7!*7c;wXj~Ot;^}0ovz{I zXk_O*t&IX@Ltc4SK38b;t>pARJV2eiZq56R*DqvzC z@C@Sd+|AjOo|QFi5Oaq6b@w03u zTlp&|JM&u2B>CDwzONS2ScI}QNjR~vH^)4Wt?7alT2q|dQ48G7Y;Td9dm}Uw{qF4i z276ib7voi5!}ohb8V{@^E43Yb0qhNGza#WDhs|~1yN1#aWb0R!UW9Pl^J^oRxT>f4vR?cY#c2Kr{Lk;H5$QHD|;c*4E{XRku# zD87>oRL3hVqx0+x7C^6$<_G$`)XxCIy)+%E+;0R=&5SyPFPaC`e$1bq&U^#46YveZ zW6+W~JC%MfroGY_egr7(sAv3~`;6uq=UOP8BbTV0W7S=K$GXP84i!3sw}furtlZoa z3cRXD<)Iyovvc#-U_U0SusVB+^;C}P_!fJI+6s7zdH(z*U(19W`o41Ga|nI&5Gt#X zl#^$3*y6FWwpYr8x7=th+Fh8+97A<8`MN>K-U;5+5N&j)vb2Qb<9(DUBPS>K2L8Xt z7!oXeZ&dZw%p&29&_v=<`{U(6YvY^9LU7@Gk^Wump}3*tIA6dW!Q6g ztZM_c?Q0tUc%sQi8eFhPe#!BhA=$YZSc_WPA6nBinczi6*JT{PeZ4eLN+iOgMW#;L zM?g)xBv3lEI8f5+CCQU6ce=LH`#uV}@80NViAmmC$0l0oiOHp$7sThzGQeANX_lg**(G!W`x1(fl_Mw*YHWYvl1j z&I%e4=Lpg(K2!D!SOOWIH)0f4=X{!yMO<{sZ9V7P4D#Z%(%42xOx6G!L&bmTV9Ixx z&a44M8?yMN^nM`Ce^>f%tp3k_z?ukMmSCY9h4DQ7TESH~er>%)=4Vh6^kPLRPhZ&4?@>0B8$woaDvHLM=42^sAP_fFoQaUf-&UQ8l|^5< zyouX;FQN2H^#3{T?ZvqNMtd0N6UJQeku*Y#JMe*ZD9d7X{zCVTq8(%CxeYxt{aX6} z18nSQ|NG4o{u1mu$&gz_3hkknw$ZbmuIDzLv`QmMKhw1>E>H^9>ZCAwm)uP+8R>3y z*GcE@>ZBlj0{BMGroa(dEM%**k$48~-+-Q(&IIiN>lugX-t1goC@VZJtd9}eJpBTO1H66BVJgaD zHc79u^YmPv3*JYN6w6|^K1DyM%}-u&{>5IRAD9k|xjoZ${6yk9wOCuw5BT*tZwKMU zzB|Ppgny?~Z~1$wkNbks$1vRI<}0QDF>)FBN`&4a@B`Cy8pmm}qJM&R9uPP;eAU$y zH~{oZ)`-3J5%4pS7v&&-7PTLIgEE3IF@e_KUDf)V?U`TC1@*`@USWcd+F0a+j{tmI z<_Q`pNv^lj5^oiviJo^+j~y?9RX7u%@HefSXwvL(m@*5&gZ0N+HxDRB-z zGi?6;)0ZOFeh}>~MyJ6>kosI-;Iq4_EgHRr@k6Eoml3>?6=E%b&(mf~lH7**rsObW z@)Is!X(Y-34yvbfW!^K68-s@gI^-tpC*a!NCLUi5xICi`1+EeI+9d~q%=?RK+^-X0 zL8BMoOG_EoB*Op1I4UsiihyxdsO>3qhOPe>|0`w1dNm7vEA-8utN1$L{ag`qS%S*T zV0sk14Tv>WhT1V$a@7w!OQ|1xC_l;rK2T$w>w6B}ozh&QG*4)JLuYYVk$g)h zc@j);rz?z}rI~?}?WRsDO#h1r-MUcdhn0nWY@8h8&uwg3T(?N>x6@$c{%=D>{5H0i z4V+G7`cO3P!F0WOmuH(*(nfu{R4_)JYPvruDBEdPtYWm|{O|_kc`(rJVJc z!x-?1po4T<#TUG++JDfQ2aH)tJ_I)f}}*bB!eTMM>+*SzY2Ps?7TI_xP!0G^fC-wA42hOP(r{SM(3*FqgwY{fwv6i z5qxW-q5UL$3I>@IWRAncGaLqc6?{vgr}E)4u*>;9hwt6GLf?()HRNO~GNc>t>AdT1 z;A>>jW4^aOQtwe(3wv|;9;Latfj#<^&cny1({|`IK<1{L*+riUT!Y2{x=?;1CY~qJ zlhD}#-v&HM=4VRv?KQ#GwKUHo^_*TuTfU(-eaX)&XC$pZ^S}5SEhNS-Sin;F%IPTa zb)xLqYs&uj;QRGsn)f!-_`dSKu9GHQ(@EyoKq>5+E^i+h#XbuAU+kk#J@WKeOQwH- zudUuk)l~ZkF~&3h zHjuIS37L~t=pE+CObBlecFW07%UBEoADbX#fqS(1Wh`H~NzxE1zbL^!;0a0{fu?v? z2l#nP*=OElGu_i!0`3NjvL}kN*A^&SPv!bkxkiEWFdGX>x9TAuU*Ounjg|3D)9~?C z;_aahI>#6G`H3)mdyQl8_nsaXSr*u-8Zt_b72UOD;Pe zCYv>JoqXn(iS|?x+80^|J7|qJ9L{jg)iqq}VBY)O3^(Q%%<7!;J~z+S2Yj>fL@&T* z3%s!~V~iW^KX(oT7NlZdc(CX8%yxtMZ(;VERt;;nVJeMhd*?Gj=i{9>&T6_Zrn%lo z3+7ihG3PY)n2V3Oo};}7)!mco9xUpH4R@HZcY>a}Q9MJI8qVyV+GC9~9W;&?#uiiN z0qv?O&L-%`9#EZ4TCEE=G>Uf3xulaGtJ(xTO2~&4dxdanTPoE0&NfP4jBwgwq8|ZNRx$V=M1P>qjlQ&0^<}@RFX!F! z{$j%6#94O5p0wLcw{erZwDNm627DV5IQusFplZ*?}zzf0p`|f#m z#`{Tul3%3)Wf&Hujk=$map^rKEioKWrjuwTvo_F&+8jG0AISh3tp_* z8t?_mqPm~g`RDVEfEMEggemqZ(qeglJK)n|dDMG;hD?2vof(~(&2)mVWh*q=bh(R0 z@`Zc{@neC@Sj98sU_S^y*<6=58;?AS=p#a?k+pLOD# zR(zAQx@j%ke#qPQQt+?a(HMt*MC(P!q#)PuqP1(S&SRoOuAWVK7~Tl`ZJH0rhf7rD zHWm2(LX`PJ;`YnnW5Iq4I$O~>JTLl|ElE4w#aI*)dB+R9mfEwEr(4W?#G(w32d=yU zY<=)=$e)ub_-gdtpigA_@bDS+DVuY%vn<{H2!9D7_~??6S>)Vw!Rym-USIgaBsshS z_rrraR}#%TtQz-Uj5%0J|Iv?^8g3i#T*J@i#qT+7JXwq*qoa-7h6nPBK!$7bafhxu z(lY%!!EfnH&w+xU9^}T$?EZweZ5(tyqPP#08e(1OrC9g_V0HbTA$TQ__m~zkzFw$E z7f9)RXnhX|Op*tLC&_`P*gV_p!n9_Mfk_Mx>`Lk1=j3L5(U8km>W6c9fwaoHZtKSV z32di6-v{5_2pL42BS^?77mM~nwlYTef5yJMfi=l|t61bY2dx{Yo*+QK$QOq0BPI3*ZAG()lBj&8Cn5)@h zu4dAGB$Yp=jG!w7EW9dUVR5Qs7xioDvuvSX7b`ENu>-!2`L9p)m7p>s+>_+jMLDeS z5p+MJ`6>Uels}lhUsHV_A-?BQ{j2G_6ySoz_%4h3U(xsF^gWgGLI$L6<5Q6)i_*-e zG{@-sv7)XyLb^Njen+_^c}ES`7<$w9YxI7-QXb7Mbud{jd93PRF zo#a9HZ-CR%^R}}(X&&C6(Mj9s?nUWtoz_XobWNuJ5p>tnb?%f-%A)^|=yJfx7G1Bv z2c!3Xbk(9~AG)5O)Jd^)zeM+J@XzUrTNeRY~&?_->gnS7MS75>ZHa+@;f>)X)}!vaQ!++n!M0ydU689ort z;<@1eU#fI};F~m`wJlkdXEV#gpEoibW~kNDWV z7j{ca)3P%+MeuTAgVatVozMyyu@c8`tk9eA6M7m68r}z`pCy48SXat}TO`SYLz9>c z&mb*h=lYM2fCDX*JBD)ql044>d5OS(fO8w9S#q?xOapXW7BKlQ-uW@TfdwocM~HE% ztQU5cB^izYbwG;0#Gi{JznGgq;83D%{zzk5$?)+$uQ{Iiy~xM-E^H1i$~k(C<2xD) z_$|O0@5}I@`LefRK3lU$>ku-;Q5w$IgpT(iA(tOd?}~`Aru|zIa)YnM|K8$%11evW zJlla-nKNmef%|}8G*!cGJej=pC0_^A#C$S8Uk0wzUs>qPd`I6D+0#0a?#TmAYcT$n z0lL^$`=1Z_-k(HiuGMy(r|0MydR)qR3SHkK1m05UeTpaNJF>V#R%%lebav6?JnxSg7QIMQd>Q#t&oqycPM1)*CEFux7R zb;x^^@*;krH{}IB;b&pKxHH(^GA)py%Oxcz(%$G{kC3DjSs8pU8`qMSkbHwk?y z(4uR^Gjz^Yh-cW=p`70dr|79_1Nx_o;V)DU8|!$^!~K=ou$*wd$XY_CQq0w7R!JDljRpEl770vSj+^TgrvUt*12e zL>aJ$NF_YGN>z>@%@O&Pe*A#75Kp)#jVa=jAr|*ort6%T2d73$e7)|%`qd}0IM=kM z4+0J}qA{KkLU>6yZ(o?IeKV3O3lZ-}TLILHz4uBUi+iEzzvqAc{`FqMZsoTmKsRdh#q4K~mAHU2JG zxl^P)Kxq$)a`lwXBGM?ZB{@gHB}%&;G4!gt%1y7%%Wb17ca11_6LhrbKYaZG_D-b# zM@2o`L_9d?j{x8IgN%sYuTkOJs2jckAytWS$xX&aC{!$9Ju_)Ob4t3)~dUkkUJ{!qSO?ky^Va2 zFDZAK;JDEOA+?A}1Q7N_UUaz%DFY;_*h*@>8V2+~1}&h>f`p%==Q{~IgK1d_w6)J z&v)viJ#;^`lhctJMfQjJP37|q|FO_5y!9rRje}OuhL4`Qe6)c!gKor2;S=#k!TVu; z6RE5y(H2L6JkT|Ip^_PnHSqMz|7SMur#gLoYNwX2-3NGFZOrX9H$JyP1?DzTnOh5= zTc%4ubGuLUV~WImn19RkEEhA}`)fguF?&-XQyU7Jg>d^aZxp^-jND40+lXG^>%jt+ zLI%FWencC&klEEjHsMXQbuXbmKj%F^fAKs+<~~6czUpv6N5gIncClvRGpLr}x4@s_ z*D9VX>~F`4^6+k?QXZ4H@G^~pt}=_hGFsm>y;vNL0n@WL&XC8JrG5J^`jZ_5j8S5# z`T^&ne(w-8l}@aOnkxI!Aoy!DM$@>&$iNf9&k3rYpC>q9=tJCRI&5Mu85grS%^FeW z$LV~%^u?SJ>;nAoeZtozd{rN#{dLTkpBFY?te=ZiZaU6JO0!9oW}+(12}*Mp{&*!G z(@G6*l=eYiDuvQ*gD#__h*z0i@;bo_@jTPV#zN`v!s$3PLAazLPzLwhi2 zhfaEgy|`V6&P!Ih->3g$=^jk~&2)!BaoBclcMQ4fIl_a^mE(NmAho=W#)rok=Q20o z$5{JVTYds(j!@=F3Sj;N_2PXE%mc=o=-7d5ohKsBl88}1OuDX_j%#GF^BIsmJGdaS^YX>v{MaYLLVnDA)Mcd*)r zWN_X1@@$VUlA&t@nU7lDv;Q5l&to*9L}wb0@#37tmxGSEVh4#lCXwe;0ncFv ztIoHBaIhUJ-*54%wLb4%ets&_!=56(z6I=+3!VLxKqV~@5(pf!VHL?1$fX>2<A^Ug2j;b1~gJ82U z+6K6t=TA3!Px`g^B>9@bB`1!!mz}4-0Q;{BOfQ4l!~F8_@= znA^oP1}~V-!y?&&@~Q0i_xawDOw(l0-`M{{q@=t(hITI&<9+XKcAl;n+S!8AhD$Cv zA^iSE(0^7R? zwD)Vf^j|s}raD&GvNQDb4mdHD>1<<8lHsQtF|sj#*pF6W2d(&L$kVH$_J!&>9Pv=e zSASFW4e27OEksyJ8CotkQs5K87jMK^?2dDX?p<*f3cXb5^8jAqykC1iJLB3Go%EdY zLsx+4#A1?hN-?JM#h#}YE6s4Sco}!CxZ^vehc9fVe};F8e+Z;id_>d}^+!D7HWE@~ zjI1?tAE&#i9lHfx!{XZr{|vz*2B@O{3;Ezcp`!pgVxz!u;S4nkC0>ZuV)g$-h0BZ*G62ZjwB|(& zCRG*m663o8j;8W7CP`wpY>l3B{Hchb3H0|luO4SaZ#)~#4#cVPF}ULUvAsxRarFgX zb2Ws#X2Ly=A0H{OZ`NgTxq%n8mvH8iuW4L9mh!{tjbhLFyybKjc*lr)bdJ)78eB5( z5vsH~L<0ezhpk>YA%h*w`N4LD-b32wZ#c|E8#{?($_qZU($BZD z%eDgYJiz1zs6VooDgR(m>URzl`kE+NfL>S%|b6z>XR&DEG{)50g2c>$yQ zW>LB?vbc=kcP0~DBzqI=)?1?;zSe0h9vAZyCvn}Obs2o_5$ikwYs@+;Uz()6@7`H6 zKk$9A_>TDQ(GvInzJ~7b!&F_Yxg7#WLHuyovpuhCnmH`!G$czi!*a5>t3wx?(3eS@}BbEn)DY6Z3tg@O`wxKOfg^wzSu|1w3K zhg(#5O7S-?JSAVbw0^N>h7c_^M5S8;zUyxyzc=zm(Yz&F?eXKhv%IXFU+g9LFJYOy z?0vxJ+-U#S$WM1D<-ndUewKHZo?Sj!-celNNs*~}Q+cp`c+7N7XfKou%7Q!wV~7~{ zpo4%rpSa7{YfsSlDqeYt;CcThaIG6up9j_9L3Mag9Uk%AW?+RXPa^7y;^U>{JB9h7 zJW4--GaSg`bvA^)nJRzJ>MmWBj;i-Uz-TmnPi0!d=IXB*wU=st(;1L)GA9J&Y6-H+}i=$^h#Csn2U zj&(YY_ekad@Sxh$b9=qDJbWNU5I#Spti^`*fj3yF6pM$Mq1P{vgHsmBdV=L{dP};u zWFh>`Y)7oS_5v3&1P4lCRJMt5E|Y=tRv1kIRE^31+uJ>u{!Fw(NAM-O>=fH;q05i_ zdVxnqYv!lne_)jW=_2F?VF8k2yV@p_@JYfGJtN`Qjm!E_evD>O8qUH)dAc~DX=13J zj?P!24&Yyb0)LHZ6m0{J#(Ypw`8(DUZ5$Smcdh|vfxez3bPekZ9#L>S?6kOEcDPC} zI~e$uai;7|mmghvx&rC?TXW)@jyR+}mK8PedFw)W(e&gTJ|0C_J_Kro9Ez zjr@!OZ1Z;VGYIP-+#KgX9q*ngV>mO_hr!!!Y@UPTSIXzgozAmQhu?#ff;SQfd@tVS zyyc(7&YEjwc^gjN2$*C56bzmeeaIG-%&MFfJ66}6JlGJ(lfXQ7p}k0V(e86rhG+pEx61)39Oef&0A z>%B1DP3p?V4e>{OY3;+8wvW_>&ZI5@bS9mE4PCN0Q<=|q#PopOm^s#=_{}c@{vyG5 zoQ;L_v9_vrczOqw6~8fv+EyY9K8?6v_?2?F(1`?{3B3(J7DKPS9lpV#XN>cl+4PHY z&e40&+e%sR-xLQP8O|`h zKhot3AA9yN;of%zU&d3x@r6DP@R*jb_#RRF>jVE0&u<7%03Z1^VEI3kf1bwh=)aU- z{a?zrtI7v`3|jI}!TVQy{DE&}FbV!9<+(#+*QH2|12FLB9ar2NNh+e>9U343)>I_g zSWTB23^9(RhV$(jeG^B5ZYskU-?I23P1szs_+*wn#}xfTEyL-!oIv5}*JQM4qFjQ` z6@?eg?EWcT$_LVh8hEUA(Bb!K48kB2dCFxYp7*m^oTO>50A~{AIO=n-fz!&^pP-eS z)1EZaSyekDJ7cHLE9)xkc(b{@OVOFc8HYX=7I>NMf#8R5zMEV`$o>LF``pzs5A%QW z0Py_4?wS*=fPrat@RGS7VCXa`=a<6U8mq{A4LqI@);wf#MOEWS6XT0||0iRIao#8R zUx;(3HO3U{tdb#NuOE59c;GA!#nyDr+ph2^zpM-U@~zNaZiYD)JU3qN0lsZDc0P==F{X%Bf05}~ebUAC$b2Y*5s;2Pss4u%kf3#vRsAWvxPhBf} z*v?}ISe>uS9JenPLqQ>-(C7z^gl9A}(uWoiKB1FimdimN|=sA!YNFS7G& z@W6iopJuN}yQDx`xj@>~H#%ttU66&K{$#pPp8`j*cE1yJVmQ{7X%XYIR~2%U9YS6O z8+Tv$q!jeyN2kvTrY=h({5IM_Doh5vlWWZ+Am@xm=k?}CkY$cyZ}xP>JgoF-J@ zvfd223+D-aB>25r!P8OrIsJvr3E~-OsGLC4G&(n@$)J(0+~;$Bn%ctJVFr%af%y@= zDf~CKptW)hxV-T1c0t6MfKO32hcx#L#z7BEiceQ~%wJHw%J{11+fOy$K5U=!x#n{} zU*7f)bKipS?G`Hj`R}Uv98q9CA!BKL)@7@yPCJOwg2xO05~;6r((YGW)~U#CS_-*M z9vtF2gG}cBot=+C8=B`dj2+_5MOihqM4tW+v-4ssgO`nSb;Vsi{v9Rh2H>f;h5MAB zAb56Nz!Qk%{>vcagp3qrfk*NDF|V(SC?D@yi8jE;XCc?L01)kLzJ_%_(09KE;bKLas3snpfT7NzFb04}r{aw6vWt{z9 zo|Q1+CD_&`&Q)3eGXim^8~;H^dmgdPuPsS7P?teMcU_-7U%3^I*T6* z+507t<~xxllhQzU?UL~Afi%e?jiQeSe?g$lcj&oXOD#jHCTxnXQ<~kN`6S((HyLhZRKM{v4P?X8{56*Y)gSo2Y(kZJ9+>qvUxRdV<@V56j z`T7fC{xi6)$0pVJ=UL$VD=BcJpnpl@FL>S3L%uHya~`Af+jPEm%ihn)BzoL|G_@bP z;$mPA1;J*8<}NUj&SUWU#k$3sRru$0o@MAiFowbZGKRogUO$J<43Cd_%#nW_^!MakX7)l>htV;I(YDDygQwbH+&QVM+ke&aoai% zIPX~Th0+{w!OH2LrjI#4Rwwb;B!CZoA~qUeO|}N9Q*ybOXiJs`s%rlmF)tbgN?r8e{rjL=ONY%(uL}2-8@%x4y2s}^jx>>e!s1D6QaTi86(e+~Euk8WgVD0>9&>fFo!zvctz2=M$NlwYHdwugvv z`Tv7?{}sH_PwX?`XF<{e@y+H`(x6=B?A6l9av5e>~@5gLyj znXWTj?U$jn>WpN5Bcix`*^TXkc8u1C+}uO(&f%xB+5;E z-GhxW9*X|JT*Yojk1}KAd~rybPx(YSmCb9s3XgEnP;109_GSPZ6V5|Z#zcu_*I4W& zO|WY(^>~&aujo&O%z32fWNiu^SXNM10=OICnj4dcMlpU|a0-t-IY6|pkxJjSCbbVZ{5UG~L6*Q@KQIRC{}0C? zR|QW>78rwRRLG1h*BHPD4fo~J(W#6BqkRk# zi-FEg+zSi*8Z=rM!3Wqpe{+q?z~J|=syN%`3%^~xX?(nlRt<|$XXZLOeHEKUN*^eA zSz04x%S7X=ctZr2IQ@l7YA)3%LF}jeUrDg&a}V`x)aLpsx^D znSG-)&>8U%e7VJ-g(+=2W{|MfnWZ|Q4wrZR(KC5V~UeZ{dc_Q@Cb9t^g)5$O&Ixi@I*-wD5*5y5%Kx9$^c ztrI9k(Y*y-uw!BVP4!HMV_ylHgUVT&<}7UTbcTD_;OlaCx=z}XUhwWEt)Ode zI*Tc6jHWdn%l$9IKh%A0rwpB1z!Ui3*=XSU32SWG8SzxYdqax-lHoMr39!Gn$bl&q z`E}V8JCkWCc#61((*1oo{w#R5}cUpKWF z^W9{KFD4nHi=lskOy4M`81R=M_B@xKd(xi7^PFNv#usbnGzc9frLK?y|3g#ge+sWN zjOtVBGK#vCbm1yprwEm<6X%t2Tpj#IZ(&o5SXV)PljO2AW{|nz93M>cR4t3ct~!#? z-DGeufa$Qq4hFuzyoGIWpgu+pCRnM!5Z?lPzi(v)*Nu^ZQw)+_JXFXrAn#ftaC`VU zoTGR78mvq8)Uf5J4NxX@zeNGy!p?f;%5?!0m?8^+hpuXCo{dy2caz=c2uve-u^j^883{HDh)*(Z}B+VaN=QhcTYVkM^ZNle17)2A!;AZLAuhB*p>>q;O>y})3^J`?`^sIwwLIU6 zj20p|?w5bfwiI~rumUz?hE$wWu5yF_t=tdzWNa*OvaSWn^(AEH5K%MbsUQ6xXJ>S{ znVr#>?(OIXsoc4`{A2X1 zD7!*|vfn!t832cC6$Gpb6XgTfITo2D!xlz?Z9$NU2)`KRMS8@01Fjba{x0p=8bWqe zr~n*`p?=2{==YC`{@)|cpGEoss1N7xT`p??t10(B*Vn~$3jq_#2ha=h%BIKEn8KE!0k%FLaB|6J2+6kE5mA*w96KGE+$dN{i z%%3fgFR@t6IA;el+_3|luNIG<=0v%tkR>r0F~R7|oQ(?6Ht;gh)3~ zq`NG5Je9?ISNJ=?lY;5Lf$~BQ5p0~s?6**cfyxRr;QurkJ`d**4S3za;|Q>rQnbcH zXnoue^h(7WE_%oyO_LG#3O?)LBkGk6@!1k-Y^TYvOVA4b1NhvGo)UAGF7T$Fl%M)t z?6A;#%Ht_jPm*V*xaRX)6+h%kmWv&d3`1&n6`;WLnKBJ#>vwN0-al%>O6a z)sdbxMvDVJIf-sOfs-C*I(X#8L|eGcA?LYbzhN)D5xmhpENvn?D_;m-ncXEGQ$#5z zQj{}Jq^)zAjS;IqT-3k1VYKZabl!~7)Rs>6?@H#@mI=0BNu3>E*>liY5tgOM2WZSF%|ttIYnvPVtSK(!wPk4>aUPYp$7Mb}BV%Ofm7<*W z*RwM*9*-~4H@%6;iiQe#z-b{5|5}t=`-UrK@UrWCz4a4#i(OXsTMYJV$ybms(w^NR zeDSWKb+Bcju4%H?*hTFDuLtdgT?FdBA_ES@$Pb{)af|zY+jdhYeMR>fbcYX& zsOviETaA>5C;D(cnSHIWr|=MGK*!ANd^Y@S?+cn!(H*Io#qqh9DmqRw;4J3+==!$# zOisc0KXh(kU14t)F><(;rxRdw#e)N!o~(iaLmuYmTN`NX4JsbDI&I&F9On$arjs(k zOTWtT2SsPfWLv@bOK4NDd8N#BeIgTxChEy}-D9b2*qjU$WlvJu|2}l#0O-Fy1#qn= z$Jrwjm>ma|<4?G4$s$CLyl!-wN;T*ipr>M-^sy0Wd z%6;^pK%0d<>__?9+>y0eik#2JAp`cU?75>IwvhMOw*~AQ@JemU0@iOo%6C`vHOS0; zXe)h{O!Ha$t}-7*3)u4|dd8Y6I1cLe9;EidmLQb+Kb*#4H@&(4g zN@ZGA>+J_snOh#X*1b_R1`KyK!)6uz>=eMpz&xM+i!n%jk|>eD2;ueiipbaKDX8-!7=i z9rGanp4JN<45#H2#5{H2Yq9{WDA-rz!F@Jjp3!SCn1=_3l zq*2;CT2*fA|AY1_Z7nMH{5{p4SC?7)0hb-Eq(Ty@jZH5KUk#AM=;N7AC-zVq!Wj(~ z?h6{{DrB;t(c!ak)lo{dkC>hr&e|w0=V5JUJ`3AJw&VW*=c16E zmlv{%0L1OJ#K}^M&?}-ds|~GXC0!B0cX`~1udOyIT-IkQz;i)c-ez)o-AHL*ho|Vu ze5*?HB$Mx>w$!*=7j#kuI;UFEvzeYd&^?Uq<1b}rjJw2nGs>I}`5&&qF>3rj^Z&aw zs4h1pldnO<2~0V!leW-(@OhVRCeBIBYYVZTfhRMcLWqM#G#}c1EQR|jL79hC^NRA< zXBL2sLQj^rY=zm)eQrk~-Oc8!b zsGg=OT`bf;L)3pK*X4VupuCoyZ<(C_1wXkWHBL)oP?6@d3jA%I(@ACF>x%Bb-N??^ zLYEp|M*S1Kq`GvcU1!D!)wuU#?Xvy<4li*AJW}mxbsHLGa(MatEY0^>ZZiryXY6S= zla@p#R_Fztn}t5*GAMu<&aMobXE_SFaD93Fvo55j`GN+|iWj zP;?Y=fr$o4<73`DEj!P~rfC|dPw?C*jq3&B89qf7+ndkQpc~JhBap^ucEB$CYc~FI zj0T5|G4RjeCTlsotLJR&_hHWGwyI*t}a(O4zBby z=B|*nn{DoUK;P0B9TT=?eqs#v(tFhJo#q-tk2F4hztJ51C2(SOxqiY|`T{{`-GZJh zy@QR6qL*@2*o(lHXQp^Zu!zx-H&Xdnq0FnPoSxzS1vy{BEOexfi1cf(^YKUe)p!>C z3%1WMX6M;ML2vl|1>{9cuC4)?C#-YD2jkbG9m#@bfeux_Oc%}YtDsZD#dx}3#W$OC z7{)*aA4mQV;p14ugvf~Uk~ z%G#91MQQH@FuAd5H?4o@jrC@IuuxqVRUc-k>guKH19&LE&l`O$$ zkp!JO!LowE3h*Tmx6R@fmkzo%Lf9i?twvtS&O9g9Cu@82J?R6kWM{l4m=H+kk^fmv z$1~kceVhY0MiD6?9lnTQpLNGgVl;Io?1)u*L4o2tYAVJl^?r6H_Pl|{yd0e`n@;GY zZ0d-+mis>k?-*l0!IUIVuyLEF(3F*O5nEy!=i&DZkZeE6!8U?lHmfs;#+@z)d`SeR zpj~lt7^Me1^rmNx5%FfM?gI!`eaMeQZ~;FO2EjAlNN|2fajqLMpg8vpt&t7 znS!EI1A2O&QuY2f*6&J3E@c#Pc{5~ka55f5Yc&zA{Js~&`eZ!tc{*A>u0$db2qaN2>`kbIaC!XQ!3Gsri z2_LhvfP<(0VQiIg2{Kw7N!1qE5pQdLnP{drfcKD3D?C!@0SEmTn>N5FN_R|e<~(sf zHfAo{p)}^-=1=zj9nK(17f$QBpWq*3Y(ArY1=Aj%PU8@)lKX>31#GB!joYT7ZxKgz zQt(lgZ;Ip&Ja!rM`Je+wbJZdq_K=b<>_DrCIjPTlx+ltCUgLVhdioFkQp;<63`=OZ zU234r=c$aHrwbRciay4FtJz^>F=u)(f9i$AnRP_)sC%(kK1;y6cfPxY^OHZnz6D%^ zKfk!e{Uzf0=`C&-hUZ5t4S#;X(lDR!&igD4f4+B%`C20wuuOK|;=UG`ZB8;@N1ihL zD|lz&*&y6K^ zu6JIP(!(zXY*iPCcZkb_bl~g82%ckg@eb|Xaf|zZIF34)t*Y7n4l%MzQrYyLwTagI zc_AM$vO0u4tU>tHqWADyA1}&WO=ZT5{7AD|qzTq=zk5hGneYyMJ?=XJ<@LVId8A)b z|1f5J{+7voML%9Lorh&I-uD#mo@h(l`c7u5LrFiFHdHK5* z;@xeDuMxa!BHmqNdHK5r;@w4-m%pnk-krg@VO$~$yT+0Ve#+#{f=3>r;e73LQP}@o zoP}7d0Pa^rp<$)?IJu611v(A)E24?&;DCN2wWSsO!N)K2&H_C2fla}z(+>-lq4-0o z$#|(_oW}%TAM;U`#vQ(#y$#D`*dW5Mi6*MKLyG^@CPnhK`tA~6zkZa)Po#M%-r-qK z??LB1p?86VV+Yb4xJw2bwTI4`^0q)@EVEa<6c0Z}%c!s2ocJCn&60t`qRqh9A(QnL zZEkX!j}i1i;M2uJ)t})P`L~mlcS+48`DX+7zK4{@J5tF0)Hd?Mr}(deKcAYDTXKt$ zxAAoimk+S!!VmCGDhF{2l>X(F)pk#M!%NvL#?X3$HDBx=r46Sz&BXi@jY;P|TAru! zx#@h9pMC1K9Tx2vD}07yj@?z|?V$32(_n4HU*YY~rS^N!yNe>PsfNqHhmvn&8mBE& zL>hyLOQcS-Dvj%J{UOpw9~cYRZqU4Dq^tanAdZlFUguGo1C%C1lygY;@!6D z|JlYVY21g>Bcsco3ckblQmS|tBi_MRB-Ri3O4wU0rkZMx)V{>WR=p>xUxHt+W$c;R z^U(V+>P~jn5G1&#C;WF#VZG z?i)s#qiU26GAQWu%@FAZ6ez1ArRhd#`Vf*i=YUQcLRUCl>HBq3GF=1c3ZYA)>sAWa zmET79C3IQnilnPe3Xdz)bDxg+m5ZSJFxZ*x<2Z#I!zl>{KKP8B}7TTNe zb853Y{DGJWzY=t~7tL2-Rqih~1bWOCL7&t6$j-`5(-ODcb(gx+dhY5k8K;9@aECp8D-oZs1>M8+Jf5_qPhh&$pHQFm zJH4~C@hu#_QCvnIXeGExc$_Yf1FneMVZbM^~q)Ii4uow^Z^p+X$GYA`P zr&hl~hJJt~Y`o^*P~t02OYM*L=@&Df8ycyT9WiAHSFDaWCEBRz4y`H1!QvA!yrhSn zXv2C~`#-7b4HETw{zJV;554u)x_00nSNei*>1C2MD}e50G}1EA29P&uh~5B9_EhCJ zP@u_zraVk)QePvdpldtt@Q{Zv(-}N_&~ibh(^(nXj$B(5uxiCs;u1 zmdE?>WpiL?@1S+W;-E|H-rjkH))&^Ow_&BQ$!hQ34(F&Lk>!oX`ZI9<*lBi=K3Dx`}ghN<*$^it%BwF=$+1clH2&)8aX=Zt*?n5b?1XOm#rM zKcJFpd>+t$_*i!5I%aH*jKW#FGbk%uz?`{~h|x&TVRSFA;rtS`%S`po2j4tuC5r{? zZGtZQViw0->mQwFHpDshUeC$Y_O&wK2W=?*T%uFWurrX_L(gJaecIcoYk`K&A$mtR zS|yF4u|p$GwRP?0o~eRm_7fVZ9{2wrZ8I1-%)+_?-yV8kdTWB*oc2r$z&fHa!GqBF z(;kHXM0y8agJ;wfTTBV?HJUjv{#q(aU(!1(6nH&Hl~L| zu&oUI_*i0OwBMWBx&-YpG`3McnSLMhLqISL^g_s|N%G(4vNJjVjKynBtq6M1u#Nd{ zodw<_+aH~vw_IH^ENA?R%7{~nSeVHhW#~k({>RU>xhK!I712nQu+KcHueWk@XPGh1 z688hYqid3kHGw^W^?4s5ej9O))6eH5PDf}>O&MQ6YnW!^V>vAi=V+?LW8FjktTi=ugg_QU z`L))@4qw3hD8wHBll2E{C!j_$mA^)2dOGolr3*Y7e4&Nx48+<1|E{QecScX(Y{0o% z{kEdNGBfoV=ZV-AS->G0zsTZAa){WsL7za!JWl@j66iu&_w4^lx%j30FbB9c-oLI0 zJt3=IN8caQIAUHS^eY+7tQWD^jP!2i6%Kzf7af@H3qQ9p9yq%JH`V7i&ThcZB01cb zlZMVEFIpcvu`kUlWq~``S5aAOYP(?TirV>MEBrd$PUZ1_fXiBrB*_*^GeY33$~ppV za*@&vZkELCnZO^M$aq=wT#?6~WAZjroP3GsqURO(xL};m(f?q=8~0FK!#+xqx7FgX z(cPGkhWLDdc^W;3d7lxD+LY~e8kdxngfsE6nnCsF!9k)&X1}8~3SCZuE4OO7`gf^T z{{F3{HV?-)k1y~&R}I@qBz1(;vEt9vHa zRzNMH#lcr&_<)7hYG?SmpuKA?>u#KC<9Jk@{A)JN1=ZtW;cYZ^fX^`(dCZ;>nA^iivY+T*!TCTuk!8>)3=#gD zsU7&P*j0W_-;Gp8Ji+xaf@z3H)`sd(+Vm~e6A0P5h=0WL5sjlFy%8-KCDyBW9M5!S2EtJsR(^hamMsxI3<1j`)aK1V^zCQ$>F0NCrkCel?9F}empI@0@0?s;jkS}bn4Q1<}gzjptr{ewxxI?5{!sU)*zyxA_KiSqT3bj`>ox9q^z$trvrcPo6652{hL3 zj$ldPGl+Rg@7Y)zTyf8@pW$WyCVcoSF!0VjT3Wcwjcpgt2F@IMVfDvdH z=5rv;cWcB6LyUG4uNUvraaM?SP?_$HX>QR6rM_opT=jiM`G@#%xy-daI_VA3zi;V6 zJl@J~lGJ7w(baSnCWILAxgPA)Nda^(M%NsA{uugRbT38sHr|2K^Bp?rd&*~^`;6^6 zW{a}>FP-!=Y}WtONoT3tQ*=+JtE+pUR1P*O^gP5ZP|Bol^|$FHX5an?j~Oj_;!$(&|~j!77F4hU8UJ$uQY z2F>edj4Re%=q+pRa@o(_^Bhk0r1w?8H;FPklsGKf6P(T*AZSdz(ORC`R5OVA8nu>p zr)Lj?wY=6~tr={9e^86VdrgvTEo-&=TIbn{{z3JaW;0#|aI?n*KNNna?-|}k?6qr> z;`6x&jYYJLo{Ise(#AI_s>&Dh=LX@vgky1hYJ&`)4v9RTBU`6) zYPx(I>=x^EkHf_~Y<-2>FPA*a`C5bDW8<$ z)BZTmWilO|oEO>N$z}gxLf4_@RW7SROj0XoHp25+tkNjxUlYD><*`}Ori!9XFV65b zHK8`4?9WBnpA{(EU}W(C89c=J6Q1Ej9F`RXZ&#@NMdM8SUNsKmX&jjUWE1BNU_58t z;On5N7++?OZ#6q$_Yc}8+?EF`^7uu~1?>hJ9_Ocfb?#G^!DxcXI7fjm1OE?6=Jt!= z(W#vsO=KgTLsOJky5pDn#ch_<`=gn^{>)Q<*O;V+rPy zYs68)F&S(%JAq>d5&W4-eXK2b+Bl!ECJu?QK&;t1G#&}y@0F!B6UBX>W9*O?_-S%3 z_aBXQWfuI>!A$pv(@Hf655Sm!7Q_ETRBuQ!(G!uJenh_ui9UZWc=^4=I0hz9qwmug z?TEdNea`Sdnln9n<}d(rJ0LPvj)ZI>lE*P^BzWXZ_l4T6^vRw21KkM4PRI8==S?m# z(Ebzn8+d8zz44OHM#SIj3EIub$JST0xviL|U!m)hjPsGtqw>AE_`Vvx@REBt;Kz0+ zY}&Tyq}Q8u(g@l};{liHIgqX+^xTE6s7*SlIo+Yp8vLtH`ho7ST^6>=zKR|104_6U zzMT|%Rz;`TtkT71aeSC=8P`ukoQvQTt8>Q(bTN0@3+U~MxHS3ousd;`X7dWd_lmJN zj#{d3NJ%`MSrwMxW#i{;?59nVmvvV78ruS^hulKAx@A_TJ zux#Ky$>85sqUVFQ?2H38p*J9ObUIR-3}OsI2=^&dKp!3BU`}!A>5W(G|JV!Y@Em(*G4cB5|}<&DE&8i&V?PurDoP;_((ouP3MX)z8>ss1)8E*(8}{asb# zz{*nT`6$n0wn`Zj)^i`f@7LwmY47@9=6{%~-{VyE*85+~|KNX^|5a!5&p-P8t*YPZ z`b+)~=0BVI9kY)2Tf2_yFtHd|Q9M?X;uDg^8M1IcX59tdlh41-^gZcgBJnH^w;Y89ZdNRLH*&FHuw3%J}oBr>GQ{j=EZXpe|;^e~FicwS;)NXe-)?IYB;@ z0sW_X|B&AaJ&_ddZw~PQSlyKOk+6yDbdr~!CiEdc5_1YWjIk28u~Fo`aa8XSu}(+W z^814-Xd}K@m39!Ng>IOg#zT(F^6?7<=Fjgmbh#%Wb#& z7`V=9>$6P1(-~;wJ{7RnZL5iXklHyP3;z|U%TMepnu8mf6OA02lr|0@sVC~6r779e zp@E;tNazQ&b84Q4V(^ZGAZ|E8#(g=O<6Brxycxnud z9DdOEr}Ci7tYbK?lmYq;HZETD{Dk3WQU>NSi!Rve0}q1C8u|~^{(NPE2k1lV$s`FM zs8O*pbPAbo8WVUZ(;<(^aFf1w7B*e>fRB2O5&ZBDPNG9+Ax&Y!Vfhl9OPX8C55Guy zRsPdVN3p&0L~ia4ybCtO%E#C{u3I&h>zvs;(OlvCeo-InqBX{;4#Ys-g}UgSAJrAj z@c(!@C<-xl7c<=Q59SYdfxQ1BU#o|h9~NHzL^PdMSd`!QxBZHWh=2vCl!^k9(hUj% z(vm}uwB*n^11bV4rGOwgba&^_-5moA-7wS?Q-6N%b-gFg>3)vZ-h1tPeQqU8Y0lpT zweptH1LMB^=gHeb`I#?5VSsIalKBe)m7rus_5B-Up=rjG+HVFB(;G}yi#tYu`5mBP zwUlrAnuk&jRnF<{Uy*zakLV8sr4#$GD;h4m#@{#Ov29;Tbe29Tc6jae<);CFpOd@~ z$)y)DejFj#6CpR=XB^Ni(Bwoho z`4On%!_Sq>?GVjNJ`XBiO}$LiQcdM%0o!!fv@?gjM)2q{5XAw?$9=;$In@&&-?SIDVpUEs%>usINVU# zsjyHQ%8sS;SV43}m5ODYHN9U4^V5ifB0=hw6gD>NfX(l~5Kwuyc?j%fM6!tb6{ z!UFE($$qdR~=bsMDfGrO>rVf6D7tfAjh_+jD@0W=O zoD!vGI`i6GQa02c{!0s4`Xn*z%?VHQa3*D~AwX+8grWSyn!CnC@a)c`6}h%YfZ^k3 znRuC>9W_19qb*74omTwkUq?eMD~D}(BLVicfUx80ahIE;KFTGw+38bycVOp3X@?r) z=<&uR?Q_x9ch@$o9rjqUaav8ax|}rqcYn*})&4+Hk`;BXvwef+X7tnFh3#eXIIhdZ zXgt6e(mg$oC11Y4d8mMimI8knzeX%9>s+VQaL)m zY7o?X>+McrzT?j_v9;{l;GFyeCL2i{pJ})DCFwYbYH`vqa?t0QBA>A9^Y4L8Dv`;{t76 zT&s;w>K`-mb#IQJQkImOD5*jVX)u;jC@><_UyQ|_6Fc7#p8IZh)-g&UJk?Dt=QTt@ zGRDfLq`0$p$is5ud6;6muU^Fmwwyll<0mA3OQ-Ad8ZOU$b5Z1^-vc-9KtFw;mSD?? z83;MW_CjhJdjU@vdsehh1%wPDsZV-0bEQ<<;$vtI1u8H*U&3lj2HW{JmwSH5z4Q;ZTN=VYjm}n2PyjLFcsCuzw0&?ImLASsEfpY;R_1@ z@mv&I%v+0OOI6q_B5!_3N!ZPC^0-#N@N?a#UPI+=s?K=dzEXn%(LmM1HkHLI>$$v{i#!f=S1#2_dNA*L>8a5H<=<)I$!6bhTt9E_?ck~;g&^L3y_jEb* z!@~f`4Vv5j0L^_Kt2tc3XP3=#ND@-7OFq647@zdqc_4W_fVq$x{@#`ny6yq;Uhpg9 zA02AfGfTwrR}ZWmyp@bee%%DC$1VH-d%u^{p0Ha*DL*|}j_}sqw1R0@imjx?Zeqj| z&0-d|bKW%mqo|V%>J+0yi?e1yS^pT)U zMi*UH)98JR`9HI1d%)SW#1p0L)Wm*-d5v)BSxB#m=7pXcx4fbu@1$zs#vOI#CKV}) z$Ikz;+-Ccd!D#=Z=`r!gdIu)KJ|jdpMzAV`^QaJO%lxJe8iFolTF3Y^S7*-*V@kc5yCp+yG=n zx#$Pj-bG}%A9$pdhf{v1R=6c`WawjO$zJ`}{RJ(z*IUIOu+n>4N8;%TJnzTFo2=;! z3da1${;Bw$J2>t*_;>Qn;V9wMrK^3Bu)lrcRz6G*zq@M3`yt2NM#?hq_ODO1Pi2)4 zwXxQxV%|0X?O>vlT|**S_U`=jvMoFJO8g%6Fyi2y{e{o?(zZl`;e;nk(88-Z`=-HL zw2XuIm<{|~Bck1&-j=f#5y|K4PL<1N=!C|zSv|W0zV+sEJot3SNLR_%xR9^ByOD&p z3j&5dr*3F@;G^~%B@|@!XznI7Ai5mxs^3>#ECZZy0wc5;-*|WNjb0s$NI=O^m*5j- zEhp2B;++HKNXl&jyZbgo9hqd--@nG*^_un+H2y~k{4u8xH`E8QL>7(|7Iq_GGW_lNwR{e+5x>VHKf(GgpQ*9B zSFBrg%CXbSE3^{EXBw>RT#mQm@XuCWMI0~>*oHjrGM~dXBuWh-=?d;f{9^lQGs>fKTIq~zcr`75D4 zb{_Tk3{|l7uXO3`#>k0djr;_(x2FlnNLY#gilGKHP%HjJ=ouv^{Jw zrueTApS|J7fNlmCf2GieuVb;t#OIeFbV_m5Af_7T9jdXBqk z8_BfbFZQvcNVuqxOWMMADti;|6>^^Q#ylC z*WTyyqM7!7QW%}g{a99KruRFOjH_OUOzxP6ku+74Tn!^r!I`-3R^Lk9$0w_O&}^^! zMP8~!Ue>NBeut$>ZF2zMh1bt%{EE6<-I!j2T+F0KbyoR?eW<100X|Xop<;@DdCsbU zDb0cczKk(}XaAGudwBwigk$vxrVZojN$jKF2Quzij-2BiMGf|L*h=jQQfL z-NTgE{3{6z1QY7b&8ho}-Hq{!%jy?Lsi$2BH-mQU1hLPYDI&>|w>~iXg!BPepHGfp z!@ah+mOr0}MDYhc$IwkPd4<5AY3e(V$ol$&I>)pD>i=(~lgc z3Xew2XI)om7Z=OV`)ur=kFB}3QmnaV%QigV-BYjQoJm#pBIv)-uIJBHBN-(nQa7T_ zH>fCcz4tf|6Tml)nM-zK|7~nis7RJr%PF|N^)0L>Hg&uBUBi>U(NaO0xlSrkM%oHc z2TECaOQ}0kTPh7qjMQy1EY}-kT$=8-$Pl_Kt)@D{anr#ZIDCc=>i>ugL}yQFooO7Ra$K+{h7&B z7i&sN^Tg1{AG6-_;pM+0fcM_eoJgjB+B2wx4zX)V5anN7`aJTWz9dtAR;EtOd|7z; zt2L)>6hcrWe^&qMsi3-}eG`z)7x|t2 z91Se{aQ!!Nz6RwTp8HTGVOq9M6RE&KSKUSsY~lk%i(9#14cs3~20N_8jCooU#`OFL&s0WjvU#L2uHEbG;C+XqW^2@u#N zk8^3s^ANo3;_QqE)^T6t{%c*8$Uk|sVbeWg+ibJ)&Uztx!r76QIr2OSogSrEi-ui>y10iAJu6`uM%Kd|P{;zZ-AV+|AMO-|NzEQs=raVkPd{2dRH1 zEHbaXsW1D2NL;`n{`B@Tf05PEYeBvOuR@f`$kcU$%xbzzs5z-ZT}D|py{);MsI=;T z^6FTfdW=HA{U1zYwfR=$lL=?TpLkQ&fcUw!QS9c=Dkzps4KX*T5gS-QAeCo$qYX={ zwaeW9-7sgIu0DDR(z|$08>dCRsCgPpuWGAwhi&Mg{?;2D+j({;>PGy<{dm~FlfFCH zFW9Nf3;v6Yh~t2MZ0FCrdE0l=R?lywd=fWQx!4ei(|I`vh(00r^#pJFbDuFQuy*s` z)B*?|HQ;&*_h!djbDPZ2)gWUNtFN%*7q6D&$=cN@W#HaOSbe z-pdc(8YtW#K69Os7F_8m+uj*qNzrJ4wU;mJVi$8rm42~P{M#|l2iBGTZ7_=wJH2Y2 z*Suvq!)a%WflV@KB(rG7$~Wh5j@*maV4vForocjS)> zawoJzCxhkAi07Ibsd8TSXP=T@FS`QvDlh$4%m-}q5Yxt^#FEEbFvhC&3R<#6dp7P@aYf>Hpw`2)REpH;Fxt3Y{hQ(;<<2ydhiQS@;()-KV-hlVy> zc|NqOAK^D>d#n~F8WyzN{#m>qf9ircJ7<#k)^oM)SN{3Bl#NFtUv}=Lekq}D;i^%k zW}{qvG8x!Enh|^h>!Gv;nO2Ks+&A}0QyzABG*@W~PJ$j#3!HmGI&57P3~0L-KGAGj>>jr#QN)UNZc6D(F|Rx7h$<+sP)v3(s9k?O$TNZGi> zDM8YMrprrrxF^4**(t>q%+h+rF5QF}GFe-GL%tZ@k$JYy_4BO{}0#fpCynu>LH1Ge&70W5X~Hm2Tm6?&*Pk* z1Zt4*&;Q_?aPoZh!HM|bMZ?{t;M*{Ih5)m6+qxeYjL|2)6|Hcd*(}6!tiq4D8Hhkr zO3Z}R>a&WqysCg(n{0@Ooj&WF`@&#>|LVVv&Xgb*#Xu-qg-x1G7^!|kHdLleOSZ`K=*-lyfk0V4XjMWWuPYrS_(|p!I zk-d3>>AdNuET`@nJCapgAnS~ixvzvB?Pj-%GNrGSCBshnx}?e}{zcQuS2qyck&~2= zXGGUOu+VNI%QFbuPk85jLWAYk@?o0;WP;7X{u{kic7{vkX||Abq&M}^p29a)g(MC} z=vl987SRWPE?ZWzRh9*Na|sJODH7F_kh5}mPrUCoJjUE-HSkW17AQ3S+z$5mJqv4C zghPc&SmXo+iQ^yWPI2BJu9V$6CTuk5i@%iY4kX@XFP#bk)!1g6-jDq1^2M@_0zwt1 zHDr(@p~AGU{dgjl^@7zK_mi|VFe2OGJfLaFfPDDI13-&ez?;${oaU{6(-V{8QZmB; zJ>O~cZ(|dF&+M8z-Idt)FS2gkUw%~STUoC&cZ-{@^3iG3+CTs!_^YPy;+DjKwaJZS zNt#nBPRxz^-qJM8pveUHdQOC1~h91f{iA?DEDZp8qQ={31JQQJiC7P zdnAHc6%rF;dGesbw34UCvD+k%h+|j<=kPh8NSVZzGG!%?NE3xM9ZAClxh=-6>?SMUN|0ekzavo4etQ9yzs$>g> z*+z)**8EE~>_kMBQdZ*|;Xq#DD`A;CmiA71sqwF`%#(8^K(2%LY@gU?b#f2p2=N}h zZmn{!qU&BB8!4jW7G9*^8(}<>Wu;S^e{qKm!zxq0thwvQb6<`FB~ z9oy>pqxXm!PHch-{38n|_4IxA{as*2bOzn@skU{^$#8fdm*EX-+~z8NH`@KdJz@fh^~;5UnrMGyk6XikjFE8NNkz@|W-Zc$wAQ z*e1~16}bru&J4bprgR7lnQyb*W5yjn19tuQ4bx-ADI{Cc<6v%iUhIogyph@gD``*~ z+!P!tMcb-iVI(^?#N4mw)y+p1u60IuIepiv2?KsYYafgEQ3sR4>>bwi5~2(!pE4CIex+4ncu@|ZHHJNt-dH?`>A z^2atwzg^ToaZ)D&tZuzsiswShJDt<)n==d!5K|q8qzN`DWQ)c*@cv zk$3hQgXt~OBvrKIZS)Ji>UVt&PbD?TJ^-I|R|~+DmE?5AzaFF0`}TAZGtZr`UBeL2 zTbLx8264%rTzfZ-wLKFF&}u&QlGwGDsv;Gf@$E*c3Fy--jM@vd#%)v-?#7?R=XQC` z@h>jKb+;S3v~E^Wf?SJrV{xRI2U3+^;ux@xtFDZ*kn;ylc~d&xZ3!RS65Lu9JEZk7 z5vQ7%rdc@~iru*f6Xg!m3VgTg(kZC68rV_E~)#Z$x5sCOvIHCy^ z98AVw{{QCyNEDElL}eMk{rCE6LLerniW25Ti{-$J)d#98@WJWNLgyyf9!5BM*m3FE zyA4T81gb|#AVH=q>fj#B<4Zr+pL$ePuTBO@4X_Y7Q+uv7sqOt|F}||myiD~opJ~%v z2P9p{?7k>Q6ek}>YXPe7ecqiU->bs9pbvgqFEHlA>T9*%Yq{uXYX@pbOB(moIL!sJ zVAoTM?Ac?c5i%8arE7&d`|T1nPN zwCFiRRN{O9rQ|S~w;6LF7U&`Kc^$17599a@aZz$L#CGf z>4LISJ4^cmWUu&RAE)&y%i37>>)WP=%kUkT2Yyq_K6ysK_u{BIBQ4z!T1lf9bm)nM zYX?gQi`Xr9wULvhY=gsjUxSJ9&C3D-nnfl{HAQFVYPJaw+5K>oIVD3I{-xc;S_1?1 zVP+d4gAJ``jsYzqAX8W8G<(?uRND%=XNiq$0pjca-S^j;%r@=2@~a4X)H(#As6{u!J0?A8j4h2!Pyd#59Fmg{O{F>>u( zXLjKMlZzjA9#140(k0f?ND$m+I~frzgHKU&iPAeXyiiw?!_v%n0N>Q2hJ?_kvDEdi zp(HO?eC>D@4Yb#J2{)2igxB(V4X*TAfa$g?>>SimC^r!sLCJGz<)-oVp}a-nE(dV$ z>^cKyu*U?Te7&9!8*m)4?;r&qfc*R^3a6chsb5v0Wq~oFXmft~{@G6ZoqFg$Hs{8V zdn+l^(zRh}$x@e)qnJZg6>6kB$pzP1yl)RR28d?*;EuzZ(`Y%TcNfZS7Y`Mb4E0C2 z<2)4t5PSP~5IRh8DLuYLONVo>c!K$Kr&|x=3`3fuWGw7=PXZZ({i-g}EAtQ8V+b>@ zeitVO4608Ln)!XxvK^syGro|P8s`z$t|j)1xkMQh{E(P7eKxj2`lG|=8N5_8g~f@h zwzW1V?@5^Zog#krh4}}GwF_CT#BY|>We3XP(39u`MYMQS#oWq~X?0ef0>KqQQS93~ z#;Y%6rP^~-pa{Ikby=O3XE$bH=Zg(A%dsQ=S=@@U3K|~75Cb|~3=I$#h3IL?5U<%dkhpO_yvID=}GOF-LnoQ~> zW7WXi`N1$2MUKcz?_t#QO8K_Jq@<*?fxW-x^(C}(pr2{;c}6K%yE<07Sr-=OVt;I& zLCJafU6t_orxWsikqZ|)Sy3^7@1>1y4v2n7@03JSNA#hm$KKk8L3Y_)5_+62N^5kK zzpc9sZr4@P+2$g%(_bPO==X<1DGfRt*l6dK>ANL{xQ%wX2NgO^8arMWWs~)PYF()R zd4DEUMZm8>^CP{%$Po_3#%kZb+?}C0N zLdU4_nHv4a>&A(3bI0SfM6?;uejp*@A>G(=YoW?>L>IfPy&cQ^%Fc!X;_ooTk6q3( zcnu_5p6bP<177Q9hiYCP&(j@;wxwVccU%o1gTdY#L;!M5gx}DBe6-GhsxLid`iTCt z{aX`S;}ZMt4cZXBJ$R5TqUGw_OfH;)N3_!XkX|fY>0jS+66XSem_dl3i(WK;5+wq4 z&{Q6f>#G6^A_WCRA(;B1oI!G>%81kljE?%y<<0GKr_B@VUOvwyDc;iBQqT8k)r|wJ zwh^>47k$5WLzM)YVybzMPrXO9SH%4<9j!NkNNG{gGS^zEs!N^`_+~>Pw9FGBSC}bN zu#Wbz-*BaiDZ>Zh1fb-Z&vtzwrflJNB^I8;#Vz|!##4!y2ms4`;r5ouCTlE5|bl%yS0;s$b`~Jh@4z&orRwj^eF;u zI^DFav2luRwhrRbZQDstH=gUWi3hcuCL&jcB_D}meKNlm$K*w0(w>-FS`AM&VSEnL z%*+`#>NoK~xQM33LzXtD4mt}9X*_0^$u?%8>U41bm}ShKnTCn%ePA3%8QS|d@Qru7 zIFZe_APZReqUGq1QeKMvua+Wlo?^==xxYk{qhm7vu5Ay0AI0@LxRxmPifD|E#kr z`|+DStz1_&CvC8>uYK((+Pkez(|n<7TN{X~@Rgl*bHIRJHfkRZ1SPCM$4*Ko{KzSw z(jpgC>Xu(vm zHL+?`ClpSX&mW{Tzu-0JPjHwT{IvAO)fsVc3Wr-bpdlvNrOhizo`=#*gpV$RZVYroo#ag#`K!#AjQ7q{@{gryqskMFajUfo1LjP|Ddc^WRi}1n zFGK_cr-FtH7O$)%eeGKZ@~L%9)JL8Izs_4{)Lt^XnllaYs`#0Yr&h=55z9h z%QA?e*yCh408f z?@I*jFb3j%U>|ojzDnPTk~Eg*95BY5G^@QEVo$VSU6I>K_EA0&swNnZgmKbhj3)98 z+uLhx`x5$xOX)?o>K|<3Ee3|o@p|Q9arTCM^?BpJ>X}VZz(5FD49`qKluej+`xTib0UTnn;hMsh~?@x%!U#OnQ8W4L%0NENV@A3u}<((K7_1_Kwpy^5j?VC}Mx_S0usf<8*;q zdZJg*!jt^{bkjNTG$Bt;eva&T-m9pxGM#J!Q;6&S_0>0>(Ap(7IQznJ_Rl3k02ky_ zS=JMby*Q{!qch-=^Fz)ovX(ZJ2&dN;f^{%sv%O>kTitx~8OEHnBB`_TEubS^aAnKK z9u>$?u-t3)sjZ`bStFG9>S}ylXF5pKY>6!=xD28=Be}+DhxE>g#GE-0P+9Dyn~ll) zLl0!3GQGcaG`x_pt`}hLVc|53%*UgO;Ewz2*D4ZyBJo}OCY1lzSd&MCSZfN zvC9m6{*VAHG1^1UchX+nstx>q0)19(~6ye87W*h_-`vH zMP-gWs^eVN>RX5fmN6eq7ZZ#1^b!%=w#J@b$nSOHc`qZ=baF1!_FPd>e;{RIj8w?Au%3fmaDLqqWK#uLog7-_q=i~f&jkmdr5@F%0F(tJe= zS99EY&+TwK%s)_|{vkn)DNfo_^YXG#QR6xFK3$4Y!&2k4N;s@Yq5xTdPe=IK39z>8 z9#BSrgFc{KD%@2<1$8?ciu&>*pEeT{wWVZbn-AxJa2%~OOglKUdmia18?0aB!PYiT zwMQXkQ2aRMjjL_Mgc3z@O|tja5msD95$4yJk1JO(A?y2UFdkhBr0^c$@H><+DKqeR zW}E9+eRt6I3^`j47-K=&S$Y_8bvsVQdk`9D*u%%3f@?14W7^18BbjsMJZ0p`=0)sS z>0c5vcHryce+KJW!q7l~vq*EVFtnh+qFU>yfjo$Jg&IoRxsd&;H$E-9)(!03iUKYY zwE|U=uAJI2%$T9bkmow394i){NN=C9de6n(MFB8_SQD<{BBgGN&;H`y3b6(7!~Vja zelXvKa60j!K1*^WFdDaJFf(f|AHfX$Sb9>I8JpYP`=q%(x(!D3iP|Pw>?ZR$3%@@{ zHBS$wF!bu5{Dtc~h&pTd

    *nk7GFRitA>(g$aq|Fc)E|wSWoNp&Y6y z*@!mo01@>-N!OD#i3pPZD&rm?Wmi-lCHq^5YsVRy6ITpmLpg4O1FZkqUHnPl6IB@t za(e8JJw?uL(6QZpTDht;$Qi|E!RlI}?c`PlusY>{Cp=0wfPAQ@2_+fn6&Pot&Nho8iL8 z{DtbEw(_E-tOIX(3}7tT-ohKm8hE-tql#2B`~h2Ym|-CClcv;=+5%0$mZPHzY_oRi z3QU^NWbo@ak;}X$z9gvDZ+Zes(?dBrzYbi(j!qn0DyJju_psovLHEL)N`dh*&tQi5a3vpv*}=jy zzHIe_;_wiaU<>T0q(+yBl@dVRq&VdRP>rfYEet(eY&{o4s3a4Ho1i_ze`55(gxzTp z(sWEVEx^3$5O%N&e<%G)a`oi5AUgn$^1I;L_#86~F=i=`#@iV!Q_!`hXrV}-(Q$tu zLhJ(K2cW`0`Hp(*p}JiCEa~#-cn$+mJB-n4YWDwt>TaWpxtiE;@v@t^)I~*VMjQlr z`47S~FIj2sqMyi`%1>EHVKEevlB=QGV;9=Yz6qeBOMTkng{GsB>=h8L838-=8gZjc zssz4hi!OW!WdZ7N44n^U6SYR2B)66YLh`4h3vkQY<^rK$Vz*yUGYB(a&=%lf+g^Z- zL#D4}xzwmI)fM>oMNtNh9;KwLEEyl8V_38xrfaH5lAJm%9m@M@c})y=GfJoOU1^d2 zv47QZAJt{8>(*#mk>&9d6wx0eU!-4(a~qN<57W6{y8>-Z5{9j#eoP*<7D42i|Redkkot=yD2(qxXm7MnR)6a_#@ zKQY+%{<9G%|5^w#DDEPJh8YKKlyB|0nyRZP5C13~f6n8q5nebTtfo;eVh2u}bx9Z4 zKkq6szj7QeqTI0PH6H2Z#)ds)W34yTUPInI%dQY*jMN@t=|z-IRSnV!I3irZHp**^ z+P;QqXKuZm?jBvcE54Uw_$H6TYPBIAv4TVFg&VSJF_kqKXev%RUVs6DOf3kTpmSw{ z`=_lct=y+qj|^)@Q}?gRXdaFi^>IcIx_lS$*JEEmV5dfmxHL(3*+)xNRkE^S$7~je z0;-@jT&w~b_7B&D3!%e1i}s85Z$orSrpi21v86sp0DhUjG54;%(x#N^>`k9pJfWz zMNiHHA}&FAoa=G?dhsA}C8sZ6e20@+{sG3J@Jb*;qf3(rtq*A4vNbB(!X^#;ms=i2 zAgB;%6@Sa*^hdDMmoI4};}D60bokV!oS$ z(`jj>1|w@D-ylS{W^oMA zd{L}rCGtWyl%f9dVl^lwjFRK-p1|^P-IkX-q6*6U+A!$zaL#Y_IQg>r9AZa*G621E z*8*{NuF6t%M?SUDd7;%{B<2{5R*X>*g^#>`SdE_e*Ub%Z9gaxzJQb+#koU$N1Pn+9 z%OOoMQ}XqU{4G9%7tyadVQ5K9KSTT2z>s~<#gQv*j|aio9_}ek=;So>2d>rD7y?V& z>>82F!FQuyqZ&!|9E#;~)8RJ_TWYwS`Zv*q6Zx0%&XD8zX6;|$t&zU)gr#7G)evY4 zI~(|ZppDl?T?fQOBc1d)x(-Za^7fTKY5dbjz`Mimc9?_N|Xh1c~#USJ}m#FVph@#DyM26z4Zrxr70F1$kA{p&ea76?2vI<+6_+j}^w@3+jG3;()YFZ|TT5b_7I zY;9OO<8)ar8VD`C&@`FjFRl}F$$=m+7?xLZRLnZ1Q(rzF6mAD4vf2gm$(~J>%Dq}r zR};4gVjp>NAJSDptMwA^Xtk}>#4R|H>QoPWu`;U$*U6&fz`8ixNUZ0)2!b8~n8J}4A zz-5V1u0viJA3OQf7KEg0;DUvH2*zwTL6?|l?43^(*@MSTg**9_of3|#Ekdk`0{TVr z^((0h%O#~NQPHh)gw=CJj8@bb%UTfW3rDT*(7sc|Bj5^lwtz=cE;KB`CBa%sc5VCT zhBW1SAlTrur)%ALz|cUeiSyFSn36{l;I|8}|N!5dQR&KXJ7$9=1;LFnQw@hW)jKLiHp(qET8e_1iQcGVQ-60TBU z>03c?)|WqCoYf?ulJ!@dc`ARw zYZy?zjjb)BhX(3b=Dye1!?#4&i@uu1l&j}=Mw>NFWNj@>P;J3qo-qV`XA*j#u;KwBz(3^&Z?CCeZK?()o1r&@UU$t{qlJjy+?q?Qa>sD@Z* z9*OM7ZBT_{mYE2NTELeb4a*zv>kZ@LfFyLz+-X;OI25h|so~|#T_e4sf+LBfixGGx zl($e4AMS4ci?$vc&H%;ctSiTGcIEM@1}4>Xdai^N8^mTK6Y9SMTQ2uK*3j?rWAf;w zNv8>A_H>A{kFPO__KGwM;#GZ5DTW9WOUAhy7pA?fQMx8I&_$2K?Ze+A05oMn-7y*L zC_Zu)G6pn|5k@eNFQE^QP_S3LO4UKwEEO!iRM3=Z<*9J}ARCe7Q+@`>UC&2&%Z%fZ z)c_ghl&9%}2Zo~905{<_AYi#jzfz+yW{!pI+`n!i%bDOj>$pdl->VIaG*2?a4B8*D z*B{9kpkT6@%Otc)U=Q!1(Hh!8uhgKAV@vBOUSxHmi74+Qx#$I{*y&Ycr3hd81vRQw zB#!!QEXQTbmCLf@@!TpqhUZ8N@2d~bq}MMg^=X#WR5{4{&HC; z*Fr|r@H}JpwM$HDiZ^HcvCJBtKNw-Yuo+uD3Iy!{7(9ANd16fo}ZA8^S#qYn`<&ncU54q9?EEaCzs&!!J3mc&Z`Mxd%BT z1nOIP7QJdO49$bx#VO2a*Qf{d{KX z*#4N|SoG3RR8B7mA64e9-4jJ-pHaB@Y8K%h@S?&=DJ-uSo)_{T*NQ}uE7s&}Sfm&K@8P7jFmc{tNN?w|6%I&_rsn$?)172j%&pZ@tryd z&iTrl-4FIz7hTU#zY09OOtzFvfw&4ovrvq66ieG4IB`$B+Eg7txG?(8VL;h;hh$l)<;>F!x}DeEp2qcTNK@;zLMTk6Am@r#T-&Ea$@+WH1g0KNy*`Ki7x z_7xO}-YlPlyCrX0vJBY&E7SQnT|9p5eVX8n;xE$`q`vbDuA!;%g)sN|roKvUccpc> zBGVeRHu>&#%(dw#*UaaCtPTxkJlu=}a%lYm!r9zKr7>)Fk-u>+Y+fqwP|yF-C|EwH zwleOsFcQZfH)kjnd@p8n{fTZ)C)?t3L-;57Y&Unb*HsTn z)j)bj;ltoJxyY9FAByj3#8V~+r%KASYlnX%6Cf=!mG=$wRka@<9U8%_AvRG2Teg$c z_IK@14j35R#eR%=%cgE;eAnl&`J22(tuvNv@JTa`Mcln0{?4<5vpcsH?S{B4Ks(p`$Fg;NeXwa{H93 zH=E|TNZIe*XR&W+RB z7xOV^QE>ym|D->V7y0jZ#6v1sfdauxNi_(KNcQQ~jdRlM)PootNFRIRKAYjCXK9}k z;UQ&rXLNwsRI})g)So-eNY|)q?U_FdTTS?|V74j~PvMhc_MA#K3dwq8XEa zf2(czaG&{kIktUcd;+fbedKyO_TKKXzAnjZuGa=nrp88P|ftEZvK_bFwp*9M#H0z;St8hoQT*@pJREe*>NK20QO79z-OXxn|7 zPhXO=9uZ<;BgXF=&(EK2HRc-NB3X_}_#ybnNcqiOF)0nb)%=$}-|6r{1{rkLgjqJD z&$Yw8+6;g4iB{D=9)gFgT{a~&T%|D^QM%tPpFGfk9SO<$E@yTb+xllZwb(ju1btF% z9Rpb={)2f*Oi*S!5Me_V+GFzx@nE!Qev5_&qpYA1t;{#HcZK!YY z&-YB(5k$%h-=Z~lVXwHQ-+GcNk^7I=9FHOmZwq9yy*O)sFj9}GlbL-)o|R{=XMeV8+O2w^AOu9=Fw${Abz7WgD&cKzkKPfWLLldSas*(AU$i=Q{Iw z&2lTeGs#$kvwwX2-YArcj4HtTUwNJue~CqZ2x|T31GsYE<;uPxWp8nYk8zMJH;*Rp zgg@qWZoIt4k7ihs8+nm01Mfv8Sr>gfVcyNSmYa`l7fpZgxLhG^i^s!Ra6ZO)g@h??by5X1ADRu1pX^A_6STUEt%+u>D;8I`nP_U$B z-<-S~^BS${#YizxQ=qS2k8pJGj{D2oa}_LohDL-4+OgpcmFD)R`j<``9EcF{zJw;x z#n)Pcx|nxU|2V7C4~HKt`4aE9wl-Vb6@NXc4KYhB^@vluWTd3zIL?2~4@ZBphzW(;xvB;{E|W!p4^{ogtNa*6YgsJ^fwh$_)THUURP}Yv(3v; zvhJL-Qi z2-MuG62Qu2?)qUI4v;45>0B5bwc&sU zK8|uT`Kn3mTY3cYn@EaS6ia?hCJW&3X`?oEK6C6-AT^HqK=93Q;pAkA6jj$C=)J;) zs7+CpCsa_!p`0~RAB%6IVm6clu>r26qIq{f5B&8UlFu&1NfUSf}>8_{VFl> zbZwS;ocRISRgO~Rt|AnBz*@vHBbqLqhKAK<$cx)aU&>ceP<21+vE_t9j}OX*&#)H; z21blZgx)BGB!4qV=!u6P0tZhvsoaV}w#S@D)YL7&7qI3dx2_GJI11l?bH9@r1tY(} zQ!ULta{+j#+-!v%CB;-|mBAoo*#U)p$+}}hdLz4)I(98}9;{|QATW(LWIV@jHn=ok z(zH|uQf&GJ{wr|<1DE_t`AtL``PWP3;rY2ipj)D8NV@CHPE||2K?$gkT*l)d43GSN z4o^KtWk4Qzn&6L*>C37FxY>~~DohtdzynM-)7v2Bj&O%pPm0g(WW4VWp3jwD5l?zG zQ;dab|8g5OQHl7JWtZ&!y#Oga=$0d-!h*tH#=b1D^sp%_((YPASmJcYV2+iE-YDMBZ`8= zGke@KnL#N}p>8@z6C59-AJPJO;pDlQq)_TpmQI3v?qvrihxwgF7dK&kU%Ap%JVp2H zGYF$WB#*LnLAIhH{*-mylFC7Bs*_v0eiK@Zih4aPr?HtvukX9LUQuf+Hw zAJ{&W-xJAC7b1+4Yd>Hbz51W#Lq*{Vl_3UFy;xR+RyOlhSVm%Ov!E$j{IrK(iKVM; zT13UjP=nz?)R$}su|qQrI*T!(u6J>_XJyx?$!toSfYraQ51Y;!qb9}~p^XyR39hW4 zORKN;#hxbSBg;Nu9zCJ#S<3N;4zEXFs~4HQha4RM*0Nbv-AN#|k@yVpfW|kH*DL}J zM;jY!KVZ-yn@LAJs)Ixul8)?SK-QW_d|sF2@Psxn5es~IJOAHz_Q~5x-74qGnp^MG zgyKYt?@kR9*1NhX0GEzamn6sHoUAcT=*c}xUK`!u-qqq#sAuhHZ6~?z4($S zjts-EPBZXv+pSH(?@}Cu+zs=qxeMSJr3T!1L31^gH-x&uP735r1^%6MXi7Ek6{>sw z2uHGnWgQKl=imhS9IR%ub(W7MwkcCmPMcRsL+Jr}*z4clU;2WB-H693QLvk9YETMdVH zGbosmNUWhEpV~G9iosrNE<%nPeRXNF&#)ALsFA|FB#$MUo}*1hRZ|5F_}k#_0wYW+ zrN3sRN2!ib()jfa56K7E8kB_3vsP1IgB>uols5Dha*S>9{IUV)P zH>!|R;hW2vmwgyYJRb%0<8OKyguG~^Zd$c^QQGx&9{#X%LphkY9v3z>6=aaMgF}za zwv5E&?hYKiI_7RfbBHjY@R#*o66)F4wZ$FB$W^;6WJ6FDaRSr%wC67mn4X6~Dq#jh z!bh(}Pu-)ivW<*`meVRY--lfsWXMlELC5%HSf-oH&3D^4=P2AlVe7 zpuGy2XA0rzy3iZC$c=Uo=rEy^^p$E7)s-=S+cW&XdcNfPP#Yw5oA`k=$otwnZ#$Z@ zZO8M|E>QCkI`O zjBa|7;`Y^W*Z}1Z-gwxw(->MLLy-)=iyMeYev}2!?9y>2$8hV4g8@?8<#sU{6cdnJ zT9k?6PAwPqyl3uF)BuC!7^PHf(t01;Z|#2vyPrD3j0E|bRpW5;HvL2e+4JFtEGg_T zi?`k3taX|*_fStPJTPf#gv;s5-;DxYFmaiC)v{IHoG-2s zK>=&`mE+pu%%m64Q29(TD^B-GyLFE!8J~%Jcz{7rT3jB2lb=svmMIJ+v*!PXka!6M z`6@l3)o7><*+Bu^K>`0Dz+($d)qr?{1yN!N`4PR|zc^Py{xaJdQxH) zi;uN}#v1z~yrKm{{unC>nbs^JrG59q` z$ka!jhwDI<86D3gf!@eRI!JuC>n8HmzrU_RYb;gPA@V&em)9P-529Qs?VXV78 zjFjqWSSBtPc&!wqc@(|Dc=@{#evQ9A$i->bT1-oZB~0l$!rA!0UPe#Kx+Pe1f&z*` zo-L^ev|uU`EH6D?XxOPP9Qxea`}^~Qc^a9W$eEv;c+`q> z)rp_l-_XiyV_F{IF9D>`V!&Tc+0#elXmgB*P?7A83#af*G8w7>z2qRPTmah`!2iL( zkD`o6k%r!>3AKjI9e+!G7WN^~_^K8lsjNQlR_+cR)V3M?t39S}<%Nm_Pew2y9Q4g=!SJ(+~UC z2YbG1*}C4Ctk~s1%~^klJWl%lzDOapK-I_|idNa|C9PgD^YT3lR<$4q(*l8$DLuUk zoF;VJYktv68XQMSG`a|SYO~fm&?r!$*cXLit0W@Y$g~>=E@yXy+;9C+rRX$cy{@ju zgdJeWkKQ8vg|rg11Y75|Sol_EqqiPZ(AyHKoQYfd^ylLAoCv7CYvayph_!UNNveCz zycdCC;wW!R=%iJ}9%&(u{ix8nZyWxMUip35!oCl0y{D8APT*R*qd=Nktq_-zJH4lM zcgCjPLI6AWwo?#?x-=h(_k}TccDhvuYk*x8BT(-Lk;QR3RYaY?=BwU( z-Qdg2!FaTc(KT~`oJ9ZlimIBF&&g5zr9Fn$XK@?bgR*C^7tRwKdW6WYX@2ml#f&!+ zLo;8h-p@>Pe`|mkCvsxwJsgyg`ZMnnlz+BU9v!4X5ek1|Zl9m*>4)H5 zySxiGqVs5qB~A+@Sof1b?FQtnxl!@w;Yyf_LwM}8C zos`TJk98mKE50|~89^?PHYcU2Y;}w+$qQz3BDts4Lr(Vjk~0U-d^79|Lbdh5t~AE^?1t4Pk_hijOKse3$-; zmu%G>m-<=0EDueEe;DKH=-yiMXW?;vfunQ{^gUYa!Ow394_ae#3r1ZyfapDHJSnffb2O#Hzl@5*Fp00JEhsg8YlI^r?u>#2y(Us7b9Mza z4D9z*vKMISXlJ_(m>Dvs+xhQ8=fdJSoUXguktl+QBVkZ&VL5a7YVPx?EO-rC?Xd`t zB;~iH-({L|Iys#5L)?m^J+&Tt<%1Cit73kvt5TE;uisP{@xY%PGXmLS!_2QT2{m`E za@MsiulYIqmAm5pR4fgO9s6gO6LWIYP~<&*&l$PK8H+C#)a$~-45yTr106Q-~Z zmuj+u#SNe$7!@4hF_BaZT$XaZ-C zQ=RrBSB2e;*ULxo*caGFawtozl+#^PPX-yr4hx@H#y{##gI?3jL{+25BpK=(7{M4{ zA!lcGe3PT4hYe8hdHIFx&*LbVY+#h#Ut8vJgm;fmp zt2|$Vmcq!J)M!6F=2}b~dr_l#=njf_+?Zp4bTXZMI(by5Or%cyrHZ5(&V;*)xsysQ zf?XZ>FNL#^GGT2KGpxK_I#fjjP%tpZSibGup{{M^-tA+b)GLi2Y2MmZKRf5hbHHdA zYP0*!uSN>YfvA>kwxo3PjFl~+{t39-Jk8u&4lVzQiv4=&X$##>o0Meawq0rOoc88K zbJd1NV|PAus`G0fH^1+q|At|B#I?OzLrH)Z@=WX=2M&dsq_Q#o#8?74Q;?w%L_l4i?IouE0 zhxKOI8(a(%=%UTvM>E&-TzSXzXyh%S-dqM8h4Cu+b40w;S5FVk%s}g%HICmMN@37u zwHD4rz`p_GvQfHI7%Kl6>OrEc3sBR+YMih%Z=lP^#(7;AI!vP$+^i7rB(0iTseVPx z>8*zZyeb7*(c6Y1K;N#6b|EvJe1 zXMK>JK=RuMkGOCBcy7?1)qSviW!!F1?0Q@*K5t~B5H$C3$bz<(xUnj<1C@1*8qP~lfYddzMxAlMi@uc&^Q)KU#hL(%Ot_F+f zJly~Kt;@?(74poVNcmT4&9d%Bz`uf6VA?Oe-p4`9@ij1ryLM~WhjxEEDF~lBr9_rmvv{e#_g&fsH<(?%ZtPf7(WTdV7O`2FLN|6B z)-I>qMW&pbnIsM(u9Ri#PGrNdtQkS^Vr8;5^)K7f@OT+D1aF-5uYqV!i-r0zBL?d? zhC@-a(kj>0sWZC$wGM5Xk@@E*xBuqnG{qXDMLTgchkoa)bjAPcBac5_9Fa%sK3`g< zTHdK&^pBOsB|O!y{Y^7y;iFL-2d%m6@la8}>*!c2P!65Kii3=>xZWLm5C8o9G38=F z$S5!|$%3oCQ04v~Nf-I*BgbuxW5aMjG7Ct0*#lA=^pMtV(){cse<3c^wO~ZqgRnkz zifRd~s82{dD~T{E+Zv8&r&lr6`0cDQ+f^pgVWL8a&B8ojX>iqB3Yg8zC##djCUB3^`5*wi+8r$ zT+7YdHtO{qo2L`++R*6OvP{MqJ$Ee)t>iK!>C}I69mZ~heYj#|otoDZlalc_n9oFJ ztI=U>2&X|#H^FE7d?Js%ld=gDph#e^+H)(TD)UW_B3pVg=F0yLp$Zisc(Du|MOkPnH}Xht*)sS1+`i-8VHdy%oxClLBpka_zBgGg zhoBVo2+z^Xgr^T_;CGA-+=AtZnpg$nPu*1>ZjjYHrlt@i8F^4(aI_4x~BSqK& z8}>M8o?=q~mvIEOJ5<}X%P^4?&Xm>QmT;M+?P)T{w-o=t-Wgpn+o!;Kl|5T9&np`VVC%E#I16eLku%8EV9-ijk_C#f+Q4$m5Bd8 zjEI(?_}4agH*Jy$1a>F6)#?uipW#Uk(VRxjD|%P9RypNaYa2Pg6+2e-!U&f2V@xqK zWs7u9-l!npd?)Uabm%0r&!g1wxfnwQT@yBf(Z3dnNQ3aqT)TCCalmuY^#C}35SZ4v z6zbDI5947PULnvCSZGJ`<>_Ub!StY*j-P+Ne$cjvPR_xL)YFr<*z*hXbIAxV&)B`D z9enykf}aD*Ry)5<9=ry^H~ZexspGC;?wj!9n}+akE#Oi#+`51LN@ zR=m%038q0f@O|e+3UMrw-60pUJ25~y1kwlw9py5Czbg#v7_ zaj`@H4o3uIOOZ7%jji<2 z6h_Pj?QuB=nJCEl#KP>}xRS2L__9gKGFa@NuvxE3@8}IoR*m;punE4xYGTBP^B@Pw z<2y0Tjn7>e04kr}fEs?4lhM8A)M+=CyKu!khmZf2m;RMjd%6QXDG%GwWl#91^Fj%= zfi%XcV7c!;oy9^Pd0i{=Mh)wTgpaLYX;(=zwB7y?Wo1#XXrRu$;cf%rU%Edw%IzSP z`u{v-sIhCsk)YK!I2<_X9UEs%7t<+{q9=Ae;wCpLEcWhXK#CkI!P6+v&n?15^fy{# z`xs{d3h3fN_Z3H{>Bt4V$B*#C#hOkHd+;EPAxW7H_cVkx`w=tNMie~pvp1}DW9J$f zq)GBv9adFjMkW7!kTfA(0HLU=;FIUt5i}j63}>$OU~&}S2=;P5NI^XSSo;hWfGyc_ zN2VZZbe;yetY|vt@fo-aW(Q56wX5oIq2EDvg&13Jcv@Wu;2YQma~>AU+XDEOWM##{ zQqH^@}qK&C=r zeKgc{3ZL*ZRP)5#PA`GNAgWKGuBNz9_(k#Z(bCVz@bpwS-1`&XIKn^RS+$xECx_J2 z-2K`e`rQ)Xe>4H!DD@8iS;xI<3{-w$B#uxe*i`*6>`bV(@HJ=m%E~=%r?mTg+|B$ppE*5}{{swEx?1)fk7NbVqk7% zQcE0&avKhgeWJBaC^GV~`teJ}g_SG_c!g-?AR~*!^|IT+-Q{Vdm#J(nIwc$=RF^)D zVPT_S_0mp|B?bvGdzQ}3x4dJY1bytGN@dd~;lKHa>Res^>0o!g@}LosB+8);1$*Ls zA}-R&OL)!SzolhE=nTHT^!Zk>V|aH6;fIJJ$oEK6zuZIJcz%E)g!}{JSYT_ zY&JDZb@<%#|GJE%$A!fjFiU*JFS#lds%7Jn+~Ms%w*L-v;Iqj~(U$y4a=x(0O4abQ zSSdA^)Lh34Cmj^qw_JX0!B(k&a(#8eF9#^J|L>=MEgSS*z%bFFvtn`8kI&Z=R**X z^em4HU~TKa8Nr=g!2hv^ya97HAK3Jv)PF4-`KEXpE-d2Ai(k9BfH5+qU2s{h>wt zP@ZOWj3vDouvm6~EjK>_X`07uREF+mUrCG`y zW^%1te6vb?c1-rc>}X#s7rv6=q3%E=C2PeCZw9~xj3Xp=>gl{JeB@7h7QJEn$X;ZBK?K=g|;M>|Wj=GaRdtOKjwE@PHIC!sPCH6&{33v*#k z*xZqr0g4S_>^Z(Wxp;T#s-hAgl9(1JJJI@C?}0dT6LKMA`KH;|)CBu@-ojeDQ-4UTY_Wq+KmEX326#Pomlhs;J^vlJB{_a z_QddIiF@6XYi=Q-DrVr1Vz5bIqewzx%qAaMI(w&r5MRdlqQD1xCMFT$Teya|X3q-D zZ|DLdOcQn7Xz+XtMw+5b^-w?gyaBPHD3Awh7io!m?-fE$C>SZ7=F589Ob3^#x<2vO6SC&+~ucX!5ot=w?0eo@;{y9dg1k7S_ z^`&Rm{J^%Abl)8Q99t!>LUjOfJnZ~Ddk^eH$LyPi^*u)w@FluK$Y>Q$>os;Q1h<>G zrwy~T@{@(JxPg4uU%kT2^bVLT22?LzNtBa#pb9fCKeDHR?di6)hD)WuQ81n2pTbs> zYnIB(Wn2#Qkt>PxCsiibdjJ_9XHG*Tfd#k5K^i$dKt75Clw%vQhqmET(CE7kH*)3P zhQQxj!o~_`r%VI3*nb_fC>&dPAwb7W- zCDA>0h=#ENfXCioAh-kPKSVjPxEWJg#-JFE4z~S%=YERoOf^amQh{g^SHu&f3w$)cvokcju%XT2TdU#!f`w~dsh&du4LCF5yrg%EUR0>h z$#%W%to$Z5`iKlQ2muZxB7>Drd<+cKVeUX z-B@JT-qyKjhu*rD^&ISuf&9vM;)M~D$MO$%yL{~+dgH}*{{+Y8dZGSUQXA|iyVNIq z$4dJLVwpDoZgDTYCI5|=6jmdD6NwPmDIbwmQiZ3}1j*tFq7SMlv3dCj^b}XbRM%;+ zH}HUv&UJz3bq{}WSUY%An@V2HBLtod*Vt*0-!_?$)A}{{6J)T!a17Lkz{=Rlio=@v zb2J#~I)y^T;i|QVtV5joeg!q{ehoN+7k?t6J<|=*{|abQ(id5(SH08f4Ct^T6Int= zG+3z?#h-|ebppR*+9g=5f_)#DI0o9-E zDK`8$v`io7w9J#OaUwZ_I`G{u}g>;@&3+;MH&Ck*WIMrC9uWBeE{Wwm81{GlYi7Imbb;qQx)H;fNoiGU{C~_I(t{uOp?{(1Q+zZhcoW|c}ntYuLnmv!O^-)G^ z5)J05I<@Iig{37Q$&3HvHzk+Ykr(aHDs4~ibY7C8Erg~C#QZjuR3)D4+3H#W3{=~K z0qqO-pAC;L?tE^u=ja6Qjb8pemO5qAvJrJ0Zz|I!A73>B3!gY!r8<=vi+#kBbfD9QNv{0?bG3szfLK-t7Mn@yob*sDzfF;A0L&?cE2$1B2s zw7qWrq4$pybS$W2u{~`vN`PvA&umetAv8`C8ILpk21W(&&+*P6;6U;U(DuyC!p8?L zo(djQS-ax`$RM9Q=Ir;1Z9EUN6lPpezJkIF5M!y-e-eljfre)M<{^G5ZzMIQ>)N zPR`8!7sZH@D44g;dsBOs6tC-BEfzt{du8EsfliOZt>Hfmr@lDt0%!I59kA6*;n-ry zwfKa@QCAcZ_gZ^ik|{QNa9faTd5R9diFOZcn%wLIVSxR)-FnGCI1s5oVuF(XbJ}M*Pp^@9|;ltucHJW(}zy zm?HZ6EkZ^G}vYl zipi5(ELB3Xn_$Mes}d4j3e;427i}?pPx6!&WnwsXI(=0I*%w7(v>h=tK+}%-DNR3< znl7YuX7*x_`Hgu?QGWaDnueAEdA{UAv&bl(?WneG(=qKZo@!eHOTDx5@SRWzKym&{ zk;&Evho0N1%?p(nT2&CL;c)3L!ZB+|gfHcL-An`Rwzgy>IC@$YHJxS%O{P4S8qES> zK;Epw6^zQ}V(DRM_F&U~UqsId0bs(h{4YUF!3J{7-X-9SG`Qfh>jhn&e)k?E%^MUy z8xz1}I75}Xdtsp5;PrqLp~Xm&#nsS<40EpHv*>V2exMV>#+-Li%81Y49{+6O!!yo* zK7G|UBMG;?x9plJ)=%nrBuoi(oR>QYAA-!T#6Qo*%dd=y6?yB3xdW@l_-<6*P;SqP zOFv}L%W1;nKuVP21p1pw_A8SN4)|!iZfxe+wgf=*V7$Q|XCKa7O}cof^_Bp+VS;6{ z9YVN>W<%Nj;D+yAP0Z_8nky&r&uGI1l1)vwMt=RbN=ZDcM(X=~CEG`b`sjLKPxqCJ5T9Q_t(sht+&eW*mLV16lY9J{y!w zIacfaw$S-@i(PI+70)MsX_^3*zJOl4S_SGA-9%}gWX-zrZ)Xt2p>>5{++jmB!%_y+ z>7U=!rk1R+A>ANGAcWTQFoO@%z&4I|3R)K*juuZ?_jia;rt`o@0 zOv-Vb;W{+jA#@rD5AEL6yqq+m2Y{G19gI2AVv*SKrD!2q>R<^ z!N1;Dk+P;{De!B!UU*;8c9`;3!e2-qrkJpYQxhE;r{AUOHxg}RQ``hCx$Ag|dvMZi zMU6#JSI4ri-Et3~8!2IEwMx|K*AVB;=pxfp3=1-MeLn4Oc}shlAD+JF5`2_493YC& ztJ;3?BZtIiG`1vmbs_RAdCB_jv(v&KM0xPOgY*=O#{B z?m7d)O$;gUmYu+(vx51=_=~P+M>*UO*9o+Shx&dD-pj8u^&AS$dcglARMVOdf_CK&g0%j^tmL|* zOo|PsJ}Yr^U(z+xuE0KTXP8l@-?H@5!9(H-e4w`f{J@CiG$46U!vm-KOnePG}%EeS6s z6{o*23z-Plh5QT|G2UKPkrPEaZO~x?fuKTt*nL3}(j~VQp%y-HcN>7W@g$CLjdN|f z@kTOW*AYN zLAkWqzI|X_CElN&TYT>8hg53A&+J#umo7(yOcnI?>C{CdAn*^`m%_QS;za7bm2=FL z0}Gey1Hn2D_9ZIT1>^uJe*q#{ptWR@o$4glig@*#z z@#gzxSI-87P{n7I#+I@gHwHZ+@*cG3%QlOnJ>HRL!i* z%6txHQxH&anxuJlhjB_()T4v51nsHTgnb%hUH4B_mtH2E140TG)8EJkxU=c3shNW^ zTZ7(zD{#sc2{R5T$%)3MN6}UUKwZ)o9Wo(pvd4)On8sMd%GwVy-QnSyB?&PW@kl@% z!uim;><5(U7te0I{cyCqK(52C_g3{2Rr^bUU90jx*Qe&s7w;c&89kBx)x zf68TTc@tl5`CXc5RB>FL1AKFO!WWxbeMKY;K@;T5+k3|QlSs;oe4`UzmiwMU6M(6K^Uw_$O z!ZzZzqX5hQ;36oDv9TACN=nFgTB)*bxA0GJG=GOG49gZIn?bTZ3c)k;yGR!==KS<0 zz}r|Po0&r;`_s=HT3@~>I)qUf!&7U>b3|@EegQYaEBB}$j@SZPu2z97MjL&~Cm%?e zBve@l{kPu=ckfP#A#rg(jMq|$dJSj^$cUT_V9Ixv>jmrUU|VQWEX$9|I~X}AkJ=ZT zQ*kD#8T%ee&=iv#?%^Tovld6MQ03**A&h56U6&c!ZrN~spVlSN`vGDKo=H734K?Mi z{p+=x*ug69P4fH}QL9*bo?OZblDoU8Tl@JlXAToggE}RIBjG{&@j}6kSPWiu2{c%i zGE}zMsz|}@z>-?*>#=oN_BHkWeU_VpI57~U*ZU~&$>cL#W`UeBn(3NeaQ9*Pldmhi zc%Iy0;HlA|jyhklap#`>f$s3uW9&-GFt4^Psx6^fIJL$tq80+IU%8`akZ&zkV zFV5sdCglB$-gMj-4t;9Ri4K1WW%%n@06oPU6vfKQD7?30Z#$%7BM#LBb`0q1WDq)b z1J>$kQWU3NKzTzs4Az#@b!Wq7?9p<;dpNtWKxi3SZdpv)rE^`=X<%0o0-Jvo&N zmA>5OVRF7N864;$UKq()N(iHcTn07(E`&Zl2V45uySrB)ZySx}#oLV|8vJLe$m-!# zyNZenrUEtA)lL5ic`=&8t*2(SJiWKe7cfg!_0mz0yGH=P^NZIIGi~VP&(^;+b&Wt( z*Ilqc_Lch@mn68skzZzP$J-XVxCi+7k+O^?xuRLH-brK3d`w8S7tz>kv9!JYtsD?A!FbePAxTtv2rDH!$+rsfH-o@KbMDrQk`> zvYbetgfY`B;gn%MUpFPkw=Kh|ugnhS$C{@G<3YHlsG8???}J)^q~i<(q6?8TG#8`- zEfR!hg=@e%4Hoid1aax?rn^`|l5q76Qq3MM)F=;VHQA&<2{7rquXkflx>lFylFYi> zP|WDd48R9KHM9+@-F1oY2GiZ*rrei-!%GYW!202w{e2Dv)XkAdcQLH8gd= zsH+>qe8}05&oU=$X!#fv1y?!7o?bWy+kv#CR0h_0cn-+9R@w+IcP7mDr7hTzru-!i zk=;C~6nd2fTNQmBn>>vD+@;Oz>Ut}Fc_CEle^Ru&PY0DbO0O=o9-hVu2UyglM02Qw zCTF-0$7`o3_63nXBF)$J{H`KGmOEFG&P34t!%T~dXZ{0*k~H`EQ?uVT&lVhLHWwd3 zRvp@qv#SG_h$Cf*a^r1|H(l!a10rQTp4-m>VfSq- zneV!QP}T~j;b$&Tf2Oi5RTPAhB0$$#1H?ce) zC1rh{U7-t@CsVn*!aj{HUR=NJX6Vsr?4T<1ba(+5bSfcFxYFeQTLu)Rv9bT|dG{E( zF{EQh9N#zuEf~ra_%G}YSgpJCMG+4A7ffE%rrzWeL@c@e%|Yk++Po-_3_(q&4fU4S zj}Vsob#;cxZX=R$X?v7m96!%yG-7C7?t3&{DmlAqH0o~S>D%NqZYc{PIOkh=H&pQL zss}3}DQEtDrI`+01?ORI(MlCcR1eJAH`A6O=pUb{##C2h2_A9sNS;u; zv++(X9eHUAqN&{*Rbp!m%$%`a$V^XSJjA24!vZXw?MQ8$5cL4B3Y(PnTO( zWv~Wro(4A#(xFoIsw?ch54O5UV^>k*08{mMvZS#@0Y1;^Dth%9UPCtEI6=JHwa{tb z(gf7_I5!-SF!DVE>rJ`KVv1NslAFwVvvh#rd zok2~_B?$tBv#3cD5a(Q!6H8K4HJrS9tppiVio8N|@d_e!zdI7Bq(@wpNMLbXnmJtC zMCf#qJ}WyLw;`C}hcd59 zFdtu4`O`hPWV<|(~xqk&OHeV=_^$6iqv`T||D_jyZ6 z(i?C6p8s`p@FsrOWw7%K4AP<|m;)xYXT-qmfYc!Neiu~h-ou7b+V|zh{`_%+fWF>K z+Jkmu1OwgR;7+zO)zHq@s?A?yrr4gvmoTmIf{%O{>@XcIw%aph?cyZZx(Xk8DjeZX z1g3O!H$s%enGX0r^Z)CAm7GUbXle~^W^p@F)e?SLIYYHm(;cGep~#K$j-i}3-P+S( z*y(m&%L-D+S|tD>>S~tW0(Jspb0}j*H{o?JLXYk&gn~muEvXYwMpW%~y$~Fmp+c8v z%7iW*Z-!^Dw;;iMt8gHpd2!^oMT!K|a1f{{!&d)8HWP@pE&YGI$sG}ze9-(6tW?#* z1JUE2{{!3E*msr)^c&gwaP6y8c*-cJDM>{)5RYMf5t%ONT}3+Q(rmT^#BHilV2s9` z7rOR4)JrB~l8m`(@+Og;jhQ(neYH<$Kahs|EKyZ^P=qJS>r_u(Cuqn+? zELz?*$10Aet^)dwP4jnW;LC(*#s%br<8A_MJCiUzi7dyZeyUhmCHDVTno4lLh#-wo zUI-H=EM15bJ~Ddt7hK!K%~}>^k*gt{TpMmybBjm>oup0o_^cvlpp>*r;Wn6*)7p7<_)ud$86_C)87yQ!K ztxMQJ15y29tTfdBs@@> zoS2ByzK~XM<;+Pc6PTI3$)8h&2fkFNOxBitk0g0#vSLhX!ECqO=^^7M$5a33(duBR zRtQ`v3Iq(=dT@+ARX@+3>U4{|@bNl^BIQ)4aKg>FpH81hsK9~^bBjFuAoRX@-i|yb zh2C`0JZm4RC~(AT%$&@b#OtTB%TK+S_UB@2RQ= zz-Xjl8%y{LR*}eWIThl}zbiJD421hVO9|t`O9fu~Fm=ULrM*QS6mr)6;E#Jon-K>m z1K};q3IR!B#9?>_U#bCHXxAyUp3|?#Merl9D9x55VY0L#mR*rPakWA5DfC-KcNPGh zIVv1DZ8n#fOoaUP@mYF|A^W_W42@v6#N$gdPXtUS(xBmS3vED+y0a0R2bY2YN2)5+ z%mUMfhPBsSywDGqT;;6BQ?oe-H;=e4iq8 zdqb60Rl0>Ev%~Ic%t4fUeKZ|z18LAT- zji0jXvjT_i{&U;J87Hq5jr3TTL%BsVUnDIE{_OJcG?{v4L_O5^g3rotQ=bZSd7DLB z2a+|$^wE$jV(gQ91(0>Ow%Kej-J2>hvAU}oY%AFNIgWssY|yjwcH-tm{bo=B!T^$l58V6^ zrEW#_|5$iMUQoW{$0Z)$e`s%|xcF&_T2*EAmgRb;iD`}9Pb69?k{-%*Wq-Ibb+vmH z>g&w$BWcH296G8e3K4nF$+o?nR!25#e}c8ApLuc?x=`;oXy4%23>M5#Y$apD)_d>S zq3>dWmr+S+;}dKe&}17vNF`e|>Fz-^`;MJFyvWbz_;Mi+mEKp^FMyZ5k1XC9qqJ=iezdY1g?X8PB{9Qy!I2ZBl2YoQLquys=@HaD zQ@U%GwU#NPDhl$o3|(S3;MStQ&G}e=qWx5Ap^&9vo~FXNfO@#EU}&c&cR>SA!InlN z8~>pAfy34A!qyNLa#=H}qjTmVw-Y0{_YP_n)6A(Xrzz zJ%#TTf;*ZQ?tjyzd_gD4OzCQ~EZqJ^6B<_&LMf)oLO9ROTqe@zTx$Lr^dkO+3&X_I}W$hUfLd2F1jv;?u{^y{n(&Ld4Bw z`qRzdRm9G2==_!s;*!eVH+;!}N>u6nJWGMN*&6YYU7hbf(Rkt>4hdr2#<|`py0BtN zQ?`wtzfE_f(F^b~F6D797^J7mrZ6NCjTRwsY8l=O}5}FL&zd z9gxxNa9GwV-&R#U+Oz^F{z{U)qNiTrv>z?L)L zzDb3rh?52K6%vI6AbpJD3hMstznI&dAFmI;_YzNOPKOEbt}0DTzp4lBAQ#2zd6mm_tDKYbl>`20II5jZJYRMQJku( z$Sk60S3F-3qI>SimyKLO2n{gRD_@P>+$^Y4Zt7uo%5}q5ZWmDw0Ja#Q<()}RAb1{M zKJ3FvLG%8JWUNe0sB=#-#NuLUD?s1x?eJnw!)46N@4+Zxajd0@QhPF4!WJ517_ivn zD3bq0!6a6OW+Qt4_cJiu(S5II5KG=ZN^rh08aF8J;JRgted-6!@=-m@vNPE8q@c}H zcKJqIg87{eDO=BW)LcjLm;zcB81i9lK4gEs6Bxop(uWuS@c|26Aq{hy>w?6Zv*a;E zSfc%Q_P%d*%)8^Fsdx`d9Y-xPEJa?;j}zjWoubZjR3N}a946ika~xM&njF<-l^Lpvzefq^FF#Akd;Lc|(b z1TDBG{7%RG5nrx2t!lY>*20c$UWJ@M?Os<-+eIOjow`?&WDKbDn0Aozx^) zOLA!TVzdn7y&nrgZ24|#WMpZjZRa?jvSl@E*&u}EUyJ=%t6$+v_t*93uIRyN{tU6= zP|k(IkcB7g^{Wvn5mc zw3e|mm$G2Tvq+-+e}+Ds(X5Q{KDP_f zQexOQn(uks%ZDC4oskl{c#08MmPC##(&J4h?7Ht91dUc!5p=-JA3b`lc}ZELap3id ziI2}7V|S5}@eCZycL3KYpJ=?coijtSN^(9|CeQ$r$xO*pQrY4$#eep;!Vpnsu46TH z%+v+52qQH&j`6!4R{vt66MF%5ZL^Y5+-?YVB~rhd>WL4Q%z4yu&t-W|K%=f_41$FP z{X>KoBzdW)eIgxf|UH*%j2c2)PvHdiDT{1GRrM5M4-K}M9vJDj8BiP_tGWNCz- z$O&)v@K_L5pC<5h40g+%MLp+~RHK9lwVeOkx?*034G~?I2G;EKKAmR0#ANLQ;H^JS zwC@((?$!V&Vsr}NXmU(#VikvK@Fkx!9g;hvr162@sO_(%jTsM`U z9~zCYd+!iKX(V*=a&e)Lg>^oR_6~^R9bCBa#EKzyAW%QEi=U)Lo(EteET*x-(#yu! z>6B*s5Ob0xelbGyN_DJwL(9O)u&=Ztk5M)cTDZGJ0}0Ui1juuap>`+Ua`ZZRV}&va ztrFzdkB%e*Pu@AKf#_rj7uT8QVz*NupSe1@v4Q$-Gv`Xm*Qfe=zY7YPJjcE7$B^1$ zFvgUWYwo?b4TiS&rx_89a>^9vqO#bohC0zCzc``Ovu@DOELiw zds&6rP?8r#)F027_AF_-Ln7QZG5a3qR3r~1OwGRA*duA;m-S|{W^D41&=nM#M`f3= z@;)^Q(mes3v;FfjMotl{h@J#(@Zp_E0VKnR-&nS7ZBMxy6Yc|BbvY<8kZ?&5affy= ztcbAoY_D7N^XDaXjHC3Y*pzt4cfm+(V)iVxL0R_Gt{YYD-_coPb^~S8(9O16j~aN0 zcn1_e!_*vU*cBzpb`>Oj?P)5t1kyXbda|wrQH3iTus?0QpvM_Nt94OJs9M*x$Ef^D z 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

    KD8%O*h;j1`?uD%jOVUMz@ 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 f* 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%0Ams+sqfn-W=So$V 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�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

    ;>k_5r1xHi_91D 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≷e3Q*W{moaVHKw^!zkjdJ=uP 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

    T@zC^RL0Vv`&?OWj>JJl#QnD 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

    ntiW)`qD66~|Uq-IX`d99!gx ztk?Ay9UpBu7*FSX?GC)SzWxgRDO17&*($9P#t(f(=|j1tbp(9vWoxoko+qqX(eJeO zI~u#3hinMFq8h8jRj$v639I~_7L1Q5Ebv+w|CPpkjQ4?xVFpTWcK;ToZ`DF`y-WGg z<2|9?`gxW5KK;|V3VZ8HYK|7h;nI2ZW?${uupf@~C?0gUVJ=H3j7wj!F8)cl@LIrI zXl=qd4LYcU4)kT+9Qd?9iMh8cj`YN2jGN5A+%FCKRxa>E~-s zrI%EnaywX$csk`B@lU6<2OM^m^Ge|!rL#3gako13?HhPs$9e#7mAwTW&q)-TP&J^FGve?cZ`ZiEt`-;M^6)*Y&!H2+G4BQi5Iq==t zKmIBGRlM~hX0sDKb>%d=@;Tq(e;V2%_`Gfxo%w!aX;(<=vM68Vz-l6%& z^6C7Q-lpUqvCjI?>h#A}xd}f;=O5^$fg==Wt7+1QYBD)raedM`8=QRIH+q5R(z)1O zam;pqNcFj`fbi=4L}#L&(O&T9R*w4Q+GF58oI!CS-I%q;kEPf8uhSUarP?mXb>*%o zJVW_=f$DUxT2r}B&R`GW@E9od=kDSd86|xgVZ1C#mV=kicG6#G0o8_!S zbKo?p@0jd!G1n(Lj6E>scG>INZaJqW&RE!Y>-%waM(KIyxY`f7!|mVsO@Ut_AKIFu z@J+JjBxKEblh&M-H0OWbnp6MZTyua0tBK}YeIG?`vt+HAslCgfuQlIo=KT-G9M+oE z##-Z9EH}S0{(oaVEnLQW+B?j8+Lz$ zg5&_Q`k#>bqyGnG{O&Miyw&D1&Li^=Z01i^`|vE|s-m^dQXI)o(mAXgud41M(tDNL zS%tgfdjRa`)tO)RR{FkPpHpnvN0sV=xrfd91pDsjj8w*Jp5^(lY%a_4TvlPsWyO4F zO6lq{IyH}FN}R_ztwmFuH91b41VlT#<;S%Y$u4~MAv7Dw$4EO%3a`)2T#rQM0 zpG%eeBYgF!^6;g*K#9Mu+e!#H*vtd7BasSE){>#*zt~T&n6AXK^VgH(8zn<(zsu}l#_)UBo_*9qD z0cM-v$CerHCZFmW!~SxN`}b~%|4p**9mP80u;Yzx^zY+hKV*desbR<25&hk+VE?BK z_X}iS-XJ(`avx3YVvj4O*Bs~Z5T$obocsSVvE#jH^!LbqZ=C!0ZDLg`dpiYiJ~*B%ODjs$V!Vo7eD@tUYv^? zOyG)osmxltsMI4Av(%Nky4p|%Gbam-G9hvGZMwxcFK{*hhl4H(dxP+Qwg^rx;tg#V zzR;x!_UGz+o^<%i3NB6I%E>^NMD{e_h3E z>clToIOxfPCsgcf#T=+&T-Tv0#wQ&WycqQAk^X7XyI1%yioa2CUbD!&0{p1L^SM-T zW2<3)U3frg(a7t#Fp3wz7Q~A;?!nWk@L^w}dZ^*}+iSSqwhAApC;oSfxlc@m6T3oi zVw8VKK#|V0B=pAiu3P@c?2eHT+fJ!Jq>sVfd_-x zIEww95nfZLj`O)xcu1cWJQ(&mXkYM^qTPGNzP66z*HpE)1$fGfu#fQ>U7~Joss5n88PRI*)Ha? zG~?V9Zz;vQInD9BndW-im*#X1iTP<-#$P>;lWFyAUnKVBV&5lr3p}IWpz;b2>GZK2 zZ)U9Y!?B#-Wn!P(z~Q$waQ@$D;Cw26(i^DVgs)Wblu|fH_(^Ataa_Mgg|GBuG0#c(#_=3}#&{0jIiAPM>hTSVjdgM;X&en@WB2e zyy7LjhU#Skhwq=j;juSGI8V&O6FB@a36BjS!Yf|VR;rIi4!^9C!(;ENcxuIbqmlc+ z;wz!8C{p)64F0V%;>o;A*>oFbYb5HPpPU(K* zQE>G+bHYk%{ve%a`igf4&Z`B!8}kyD>yL#D7kZ#elne!K^VK^yk^kkhcNAvfo#V`l zzQQ@Uxe)xCkUz9U_no$Ic`n2Ci%sphClA!x_?>ot^;P-eRMyqqQ`lb z2QUEb<@-Efp54aRSF9@;YMK9Q_=Kxwe;4amnv2u;ej;7Y1bZpqkiRjN^&zH5Tz~Nk ztgdQ=?MuDUw$2OnvnUrKj$;<33xBhpuiXFhQd_>*V{9zmqn>>ib^KEM-WTQWoZ`D9 zBPZ9j_6H8VQ>W+&;iI~g`tH$)jEFvgv+i~M!K`SXN*wxeHr2g=Z6nc_uQ1MK8S8@j zW1iTooOAnws|&iGXdPvFWHw*IJ-A7cM?UO?b!flzu$%!Z=j&7s%aq0_>wS9HURS@{ z1M+UT;|BT0YFCiv_n3@}BEr{Q_QfOeilC5kngd7 zvaRiBR7XYD2K{5rY%RBgopLCrw9*I4w!bZuDWh{OsqgZw|CyPad+3BcUFlLF1iXNB3!5z z21HNva`%kfU4ox?ZrivAzae;_vUI)Nq|AMM$ln#pQYhG*xi8G8qa-77YrDHPSDr-$ zK9Zyy7s`cHGsdJr^Fr{^>rnbU!dQHbS|$Y_ANY8}w+DBA&JKZ z{~hCQL*x@5ix-A3nx{US7w)ZIF2>}A$4VFHg^@1GHjl*%e{YQP;=C}@jfWS;onA^X z?k1_RdEqyxBh7L6{QP5bOkVizojbOV-{{C?cIonhr}a<3FVGsKoXTGxLI zen#9@@&nE1mgt@E$<RhwQY<=>Q!^xx_wgI>wmxxac zoj2&rafH*FuGfhC+-}(AJBNaVYkCPah1*>>irpvL@I%IF|!2S16uhlg-KZgf2VCSoU+q%t0beY~N0 z&ggs}p}%N|=bu+mIhvkwYhv_Ra}<6m)KfZ~I?mtxuKdx({7|Uk+%0;t=&^k?erSG- z{zi;m7Nc*B(YMCvEipQdd+`@B`mbZO=yhnXx||g|Sg<-b-`)AC`j}AVQO&&wGC?(5 z!KfGlW#og4F&BV&!2AN_uLvh;-U}hbPT(Wb5_TxfPhbN8KarIXSRUXX@)EL-e^bS` zpMCc!ALcNg-zI#AXJ|fSI{1qgt@}j&wD{n62#xt?n%YMn;qSTs|G_r?(f0k_kqvLG zeIIw7L2vt6`~DB2o2MFk-ybVoxbG(->6}Qquzd?v^%woh80EFA>U|&S#Kw&moVh5ld` z{e}^v=O(a1R2RJC;xQP*~|MDoz;Il z-9g;i5^Th0x%sD4Za1IdA#IgH)fOu$Fz>(xoKteOiuiCEJkq z58?Mb{HA>;sSb0Qj?gKgn}xQ7c9pCJGH#Xv2HImD;?M$TA#5Bs`>=~Vb*;9KJat9B zXw$yNcF1<#FzoK8IdfsNw#iSZIo_GciZ*1&gFM3dQoeZRLy2=8?+lfJxTMqIJFUc1 zIeR^2>+%Ps{4+n-zK4U8wT)%Ufq&a(_z+N?-QLravz~SC*gGg+74pSquRmz;9`h!v zpAJt}Cx*38Y;S-xcqg~*viJVW$(L59`vb^5YSP5B*zjwP+)?l5r`YbkmW1F3LvG<_ zUmX5O#>=MiLq5WGb1g}?hsw&f*jep=pgIl)wtFy8>0F>XvVjG< zE%%;9zmxdw#K-LmEc%DGL6!wy97^}98JSPCYU_~VmCO_F zYuI#oxd5^(Q{9d+Xg1v2eN{Nb{;NbUZD{QE8fJH6**^>1M0#dfUTa_#=Ci+3O5I+( z!2F(i60#(W5$e++zNPdO?JJCJKMmjS`f_{JY9F#y{7#|I)Iof2MN@c2N(bKR^1 zve`#JZ(82r0CvBs%`;(RjC2WV2lW9|g(nT?cAvuQRpBtrn}Y$rLu-$`!%qGN^*byG z3}-RF1Lb|_qmdDoDcf1?Z=%E1@P|K%z8$2wKbTlcye@n^a{+wD6UN~ow-K^g!`JN$ z`+NHwS)?hP&1WMjzyekRO_bm6C36 zIi1Dn4*S0ae=pBIX3S1KuHiVtzm@7Y@I4w{8_k>7Xg<-rQHO#P^vxTEl{5|xuDSG- zce$5pqd0GGkn#=%R+|3dnb+Vow9Tb62dyuvJ4m(BcR=}VN}EC&$!}q;KswITjqfS2 zsT}RM<9IgZrSJPqWn&Hzuii@UBUlWUx5Q`rd3PXa02Tt{=Fz$(w)6_}U-r4r2Dq~w z>%`=VY?JuR%oObveD(7^r2^#6)HmpJBLy6Td(i(zKg|*9oVNy9MxTb9{y5T@6M21q z1{kjN5kF&4n5I4F+{nl?;TWQfEdBlus+VQR^m~{w9Pn+^+Cu;Iw+s7leBau4gyuEs zp`ZHu!C40Xpkw$*%0ha3@{VFYkuS1LndU6czgA|7Z*|aGkcF-3avyLr;d*C?9}3Q% zjA={r8*%%0X1(xvlFF&=c$w$jzwTh)(o$`QS3HM%W^jIjOx~UA0msmAmiVS~s%RMp zhfY14FP`duSf7z7Zi3?#ajtY|9W>OsV!f-pI=9C&Gu->dJNEJG4XlSe$2V zz2>n$!K@v{<4v^2%QIdlY>ihMzE?{1wNPI)QeQNd*YPypfm3Bq3!PC)9Vw?pA7^B3 z)o0V(%e02ROl>$tegE&gb^-T7rORs>zC)JB(_UtJ@KNaYfs?8{@r8l^5%w}#n;_>m z+|woA187Y%7k@q}g`2YCIZvKUiPF-c~F78rq&|dvO?i)N3^VWuH6i+WI-_eO>FP<bd zF62jg#Gi@eStNW=v+U>Py;LwY@C_yG%3TGM1Y13N{}R$O4y~kvU!^5_GMnmqTiO!l zJ56cmJ3c!#R%f{$JMDwdvl%aC3YR}UR_2hDiD%DJp8w?gwCD^$eXKER*0K$}mS54n zBsS-)L$X$v(AQtVniF}qE8y=Qd$$@RUj*jt+f@JZEApk#h8?;irEV&Hh2>d&!`R1w znE-uNS>7S~tVE>G_KAK-b?gh0Y5FH^%2N5+h>bkTf0y1VLwswdpn2Dw^v-+YddW6j zZdgt`rEiz?U6lSeJjVUOq8P?+SWd0XwdW}v&a}{pe8JGNfCfXK@0tw0sE*HqVgH7B z#XM)R&tu*?G+yp{4?HE-7=!N->}I};`#sXv)UIj#jCHZ}ebP6YlU)p<2Rrgc4c1KYM~}%G>&qBA#0#dV{T8tPo&BtPWIZE~;gO(L z^pHr8?;iF2R_ce3K$Dhrmfx?uSDq+w)^psFdZKC1i!WvvSN1UwJATZm zS(wwbAH#=XFtLW^w1aE%#aS7{XTFQ}t(w-yhcE{A)jh?U)&oQEPcX9k{W8Xd;1^in zn>+BH>%p@TU)bT-;Ot=>@DdUH4=cMbVB93}F%I|UaLun{JfvJ;5+7{Y^Lg$YQC`Z; zayr_tYmiU-s;u!tylj=vPq|F{KZB=u3;J#NXF#W{oHzK4IaA_ivVFvIAN$^y_d-7X zQ2D*gbd3*LBkozO?bv5q*#}!v?t?AUk;i%C9jvEDwmkt18s$7uW2BcgC0o1eXq`9A zH0&2{g1-jKo$GSdvS*{?CLccXc+PlF?t?)X$9D&e)IU>LF1&>?s*>3i9`L73`*g`H&^gQg&QNcWcn;!4TVG3g z$p1j$!*`Xv#4~75HVR8?jKWLJM%&UvLTxA>zqSW_$F!rgUgcsw-_s*L-(M8jy(8Eb zHJ-*m56`Pf^@xu%tHpk?y&5-1?u7{7_q1^{@lD=8l!(s>-}7bkZRA@5J~@1(Ul5=1 zb`f&q`2RJSgD3eONS?dl84v4~S9((XE|CUn(c_UcpEf^hfuG~%2OXNHJV%{fJU_oQ z?s>ViJTG}JJG+D5WnOY0<+N`z=3Y^JCc|f#m*IT|MBQ;a3JAtFtI_`q;c1MBEDiFz z&yTKM`GZVPvGiY!=sXZLFHs%u zPN2}yr0mj07id0S)H;+gvD_!naFg;@M26RYE$i0{}z zQ`k&Ho2OUjy|JIgqCH35@RWs$cC3JOqOMR{UOSkrF`5n^ND=qg9 zX~;rT5xTlPb+IRPW#22^p1N52&(cZ7byyQ(bbVRpgYkq;>S6i4Tj(d0p1-!xup`5K z4Sl7aQyRB8w0U_^>G{|p^h=TY*q13rdrHsG>5~2f3fmK!KLri!@_~frO+k;u@&l_J z_a@Ghcwm*=roPJ&G_cBdo7(pS^reQrH!-yNSx9uo(zZjOml6H8rTJv`eIj~YO~rSr zi)gpD;_Gxj(F3)W{@G7-p|;ZAKBCV@;`@oV>a?B-^be7~uF`&Bl>_H@uFyDO+exh# z0sT67_GG2MfG2WWvQj=BjM%>;9FLEU>i)ugeZT__eYxNt{&uo5USCgCr@tnl*9S-9 zSH$AiCiMCc)(^cGeC1L?Z;Pe>s~CMWq1TIWzI{KT*N3#wf1l9nL5HE~e~RTV#OPln z^!gCy$Kf!O(%%p0-w?eoLXQxAG*z+T!rXy>NBBNpBzhoK8SgnnyOS#Iy_D$hPSU&x z=&Q`?_AG+`c0*;p-C%0}kMlyWkEQ=+jD8?S!_OV-*;L8@ZK8+mN`HTk=<|_y*rTNy zEAftI zZ0WjdOYG6s9rwnb@J+Q0&CVsx4;|ey8+UHpwkyQauTWK2%~pGyMPTy*0x!00_iHP- zi=5zgb#DIF&dv}no+06(r{3DRai_X5y=>LmwJVw0u0iMTiZ~*M*VRf8(OJX3ZCfT{ z>_!B&Jv4E<7==D;NZ0QB%C3M=kl)d{=hJT(i24qqzoLeKm5Tskn)32J0nyasyyLg} zCV~~qRxMc^?dFQHp^jpc!kEM0uIQ}@l`9e?6?c7AJ{h7&L^wiaDzseGhJ*7%L&WL= z?k~5k%AJ(ki6=X@RzIi^XynG7cU^RofHr2h^IMsyUuMOxH0IR0scZZEP1`@0gXtzJ zp=*0)V}y`jTRSTe{2Nu3`$_yow{6^Yx1KI~&4P{E9tj_iFe~A(gzrmuPQvpNf^Rjy zs`*nB_x2jv?~CsvxLWg_=0vf{J0m(!YvaDnz~MEIsWzkqO= z(zy1^#6NMB_#>{S@+atez`vrNG=($PYJL^;Lx^9m{SIf`M0|+E1BZUb&6-a|y7LlV zkZ@|f&Noj&_D7_4Z>ZP#!M`G1R^m^{^B3g#d3g?edW3T)>V9cQ_^0B3NQ~c!iMsvg zB?MoK{8+pZCMC2boGu~b(%(w?DV#y!ODSE?I|w&wdl%HxHc3BUDgK6xN56^2dy@7G zJBsiY8n;P0-&FV!cG4c&py_7#|9z9jXR^+}VX{u&hj0bW7hA`(pC8c!w(h3_!Y!Kb z{-Z9MA5gIU8TG%=8ya=_KWNnX-)_|9Uue|jFKW`yACeG%2^gw;PFvZ~bOvcT0zY={ zxSW=Rja&b4rnIBl)I8Nxi66_R#+|9{TPCVcx2btOQkGI<+teVP9Z46~8@HuJx=+K; z4_T_=CRK$k5tesDtbOp$Y#t@uirD+U>FD<2ed{9cd%LPWnKe%t{e8EqYFi$p8<#&D z?qsRcJd&%%w&htj4Z@y_%ZuCcc=R5BIZD0yo}lsD@?5!K;ka#i1n;EEh8MDF#sNxe z>~Ub`vy2&XaMo#Qx$cIJ{-Cx4{#C4Rs!xebyqmuz8v3u-}V~j@@)hUM9f??Sx_kTiM>`cCRezv}A`&?^(@D$$5 zdCaqNeWKSmGz1wPu)iQb?Tg*9-N~4Bx0IUQM-G|U{K1s_$dQ|2!iaax0}~tRj|FM~ zSoF8@eU8a|-T($%asc5iCEOpq9`ZlP=IwSLHocHhThLwNUVy?X&r*~>y~;C`c;dzsI8ySRT+E%hc`_mIZJPX9Q7PPGP@ZcewaT+_pEV21*)O?kbn zzfZ}1(2(gW#$wBxz-WVA7JRUJBmQo;ioYAic#qQ`Y>ml%`yz5*S{9}~CH`<}Tl+hr zj0qvmRF*Lxn`jJLzQZ`C`aTAJM_|PX7F+1ghVUJk?2tX+18)!KQo5qMH3Wkj^Dl~z zlnM3NzsJ0Te%Q($4luX)&O+!DGZ^=PZ+;jN)0*8n_Ei_JdZY% z{zUu`EpL_IAUjDpsU;rpeox%E&&%Jj-y34x3ypPVli$F4%fpQA3%ifx zVAf;ZnfqK|wLKqbEWyK`Vdv*ingyoY=PCQRUxRwx13YgM&UGH=cLylXPub3Vv^?Ba z_fgIx^NTS^t*oA7AzpYmHt}%@*CDNb)P8@XjuYhc*?iFfK44n#0W%fMvnYo5C9F4D z56sH)o$b;4X(!A5-V?-DzJlSUu>h;^z8<9ZAiq<|{~_Kr{lCmz4RBmnb-ugrt+bLh z))SUtt;%GRm=ID2R>AQYH-$~C1Sf>BP82Z2t=GW@yD^Dk9AG+3Sx1s3|23P$CJG?D zZZcDs(vD1l@i3uPV^e6xGj)n-A*Aiou3r*NFlHuY+@Gm?zI)Eyy?dp#MHzR{jOOln z_y6bJckemhIj0O6o|X;5o|v|)mtk3!?IQ*6m|1)5cN4OI@d+#Ys2Q7hzHTGzV(Zq< zW2bMBKQe~pD;FA9V06yrEWfS=wl5EPwkhLsk%!QkfBsu0&%eq3Ve~Ea9#yziV>DkAxkrQU z=G1`*`kN1ASU2|5t~}jJdv%QYXvR()##8G*XV^}>t>fKL*#{01m&#Y@m_;LgpA%pIo85NXaYw!oI`V~_V;gRj zMcaRuQs)$X0zYek6^}EYtAhYpK&HRy>R7nGMRQV?;pgJ)3q8-Y=x`LA^!GdTXB>Li zp&xhXryUyn_0ZyP|D6;61I0S7rhTDeKYyEqU;6JvAMyp~0SN}~`9zNk-R1Cy-|WzD zbLgx?|Bgf79AjG<{Zjt>i9VL7w{NYJ{^LZSOVs(1pLODg!0&EhnS+)`IPnJ@`p+Hu z8xDOm#_Q3@J6>?&f8@}PEwctcoU?Or>TWs3jixqg#^%rD=emz={>;V=MiJJsiAFhu zVQ4eZ8X0XfnMut&XQHNu9^OoM+nJmxZCi`lCyheI^+wg>C4Zeacp+6ED_WF##N3x< z`3pZS$D`IyE2dtnFk0(zR@5xE)Wl=y43=*_e~oo0Ts-Wm#`ot!`dICuuxCE=^Lx7M zK0qNqS`;u34EA|mLYNkPpM|tX#98lG>l>$8&jdDzuvem7@WOg%^Enw8+d=aDC81vx zy&)yG$e@=F{UXQ@P_OpUN%el+=jW#=Tr{6;0Kx`T3L$l73BtFCkIX=PWL~Y*7KCr5 z_g=yy3GVkvgs@SSkp8tWokj@TVH7T+`qqM`uhBMybm=ZisC{8(S>}cG#s}u>QjYd{ z*+=_9EBCV?zAZKH?>cIa@Bj}X#3u0@tRH>c`WQCj>44V;f36qPlhO~Zzh*q0X6am> z4${rVC*m8Ml;`S4Bi&qhE31-hHxFYFsOG$t@ebCRbIWsiE6L8L`q7X|%vnGBhId{+ zcUwWsZ<=8%sGH8jy&8MJS7Ude4}f#3#{K7qqHek0z+c12AfE++&52w1-qmz=T%6fQ z_o1hvT=3MZz-%$@erM!oZ+$LZrgP=gmgh#U7@E?UxZtm%^w~FjmfHTcXr9(N@Ir(!>Nl`rD)&hA zOR?o_##vdv{T#Q=zVE;604G}d;G zItfya#z52hhK|rr(`s1z1zRFvKdq-6{jbV1{uyKPD&0Htjy~QUmOfrW?!&+$u7b{* zHAX*ibn^B)x(GNU+jx3k+Tpfqm4&Mh99;cP?-0EK#r|h-=TBdCAWFZJ`F%?aPIXZ4 za~l8c`VG=|Sz4RG7N5HEK=jh}&HV!XrSR;G{Va1ou3_kl`3jx6+S8MxT2G_q&yD7O z2iu&e+L-P!?7=14_sV-Kz%?!|u|LM%?B0sr3j?fG&I6xc#>Z5f%1=LR*`m<+UP^m; zwp4j{ws`a9*`rlIyYrNJ-*N%_8#DEORP*-sN34&8v0$Am;CMA2KD&2;FFwHFQz?_( z`vYHNoH<>FeWK`ewqKex+UFeG7}NFAyf^p{p)~!rA2*`!)_(QIYq<89l^^lTwO$m( z!zU2MrGl}DeWwhe4b;yuZIfT~mL;z$)OnrPXdcKT76DBeRBnkN2^v8P^e^&05+D8x>jhC)nnU?Vr$o_nPRg*w4R7&tDeabRY*5 zoYlcIGyFI3+?XF(toXt9n$JM+7}LQ>#k`lIFVMb(o-MJSVJ|qb_&d|uXENp>7>>>^ zKE`?p#)rXsML$cij+Wuk@w>zMxV$x5XDz8Lta>RATz<`Wo2mCQ(M!;@$bZ^km!ve3 z0Pn9hk5>7nToZR{7Jmc!fOd)e4d4$l0$@CQ8{!lCtZvhY-)Gr5LwVyCjrh=sKjp-y zl8y0ci?;tR1rKYnMdK{}4u{S;^!pW$&5gn%`KS{==+Lg6k3A0kq(i$rDcC{Fcy&zT z^G^Ja6yFI9hV!fw4?O7+uZ}6~h_M|lgIUbT#mVrD`W&7N?Da48>ln}5ocIqp^u`#^ z{~eP4Q%?M_L*xF2^{`az)|D^9V>;r{uKoTK4*g??jvV^u4((z%V-Dsq>Cg)tda*-i z9r`AR{*Xg&j`RA*I?|ZeIAe1R2KX^2{hvAXUputexBJ-E2R~s!%^03#VGk|7m^R?Q zVS{B~uEr>nSd`Pq!J3szC8=j5+*q$i;}TKDLtAIE#2j?oOsjUaPBrE=%qpkP$RQ}a z#`N$1z?}oSVH($H;TvlaGZA<*xL&oivmodkfL<-BfZ!~=;m2+4o(n!SkUGmlK^=oA z95)N3CyZ0U%elZnvE#pSD+4dh9RFJIxmw4}4A?zgN5gji#tmjL23Fm5%T4|3wr*U% zLB}}HAAVq#ho9cMdD})#t@mZ{)J`b26_koG4Sgl>5Wgz)4~4!_YzOrV?@!w-d=u@* z!b3bHHiS+HkMXSV7?%of5PW{vL*L|aIfEXTQxqN|Y*V5fV18kX@VKOZUGfLuC0;4E z3MtG;nD?2NxEA3RYn+S{)V#;*m0B)5 zM)3QQe^VJDsDb+3X(j@n1m4s_0 zEF-*;`iJm$#lON;N}WXbHu}aOe79mh3s+P5ZJe%G=-*^s=weHiq!}-CQ^5T3i}6C& zG^(XE<%Kp&=kh|4ZZ3AyA8b;d%L_%ix$r{a|KhChLc3IRUTBF9;Qwr8Y{m;c@`mQT z(80}f$C>plTQ+A~k;iy4XW-0i8xcl#6L$&ds;WZy`*DpA2&%C{*&`YjPpiOUcv^i#TJ}HR&mLP43x+j@AWm`2H#9a8Pc9g`G3_=1;W_4M}NrNj%smn6=zwhRUD?v)O` zMADkOmTR**=g@aL^t}#!pF=zH=KCJFo@T;5dDYbbC{j@Wg8!d!2yD;>5SSH`C zFy_z;0EWC9=aNoZeKD5NZ4nyxBcvE~inKNW0tx_5k*nC|y9$eyn*e3WQ$=;kIl9!Y#RpI6!+7K zk4t4f`{oKAvH>C+Ok5nKN({?D-0AxW)ZM#VN@hHXp zgQ%wfhZ$_p~fjSiZ7-KQY=L$(*J^S7|?%Cgc-Osc zD`Y34`Hwz0edv+$H2kvG} z(|8my9&5you19xd}zr)y*S0iIv@zzJ3?JcB%#3-IiXuk>Du zD^XlLvs3$x$GED@{S`fD)F#wtw|TZce^&bZhqSK**DgHMX36@|zZQ|#qJL)oQ7+n3 zlmBHHL)$Kx2|UYWCwQNv^|;pZ^RDsJMkZ0XHG~ZsJoAEG*eh!I?iN)FKX(3nJKF5Q zeoNmMUh%Kk6?)Lci0yi1-TCt?fH%p)i+C=%@O8av_@4Vl)JxomYhgcUS+9ygVtH^RQ zFR1SYx(}Jr&@bN>cI)=HpMfk?`voenF9d^!zZQn@7)aftM z`?rh!chWx{kx$e4=`{Rv(f9Q9!}Pxmzq7+(f$B=o{)l&YG4;R2>VJvG5OWW?f6z|r zPUfR6+aK~6_nc{yHk$VbbBTR#xR3pWb?f(Zpz4)(gu{JfA=WI$b9k~mP3>zR=6gEb z5qE`tf#$BjdkWr9bu2k>CA>PY1jM2nBFJM8c5K70NHDY-Xq3oj~J6Z zV(b;#;I6IB4{%1a>=(bh;G7ZN)1%*G%#|wF-(}3#<9IJW=tC%qB5|yJ7oVgW0uGl!^4PM!FzR14e2wC*_Vub9Pe08ja5*8u%hRTDsdZw z%*IMU=a${D{YYcqs%{HgX&%rPq}6Lo@%K%y1@T`$N^4-0)&TZ4Gyi!0Ikbh+4O_DR zPIXVUQ+@dR@J)yHNBurX{XC3sb*uXDbgR0j3+LZI{oH8yU3K^LcWJGjqceux9@7rf zF4TcGp*_^WpRU(qP>(}@O&>qv^zA}rA^n|V!xrN{U-mTYVg4kYwabsQE~2`3{w91= z3nJr}^NW%5w@^P@)Ie8ziN>f{?pmS!4c-fVERV)zev-b$2x9>xgR85bqVIAiJ?m5- zIr=W(HiBX$PS2MqwJW9ufgkCq`3ekjuo13#QSZq#@sAkfE4=~EW4d1W zu8Zi~agf%|Vfcc(8SgxOgW_G3C#YjOx-|}BBC>H6I5+mvGu~6jBQ-^Lkl^;vqcso@ z_WlIlEQ4D^o}2W%*0DlUf=@HgfKON~*diC(@w&FvJRbR_F*=*_yQJ|BQntajwWu}P z7BY>!hcopnl&`_BjbQPnF_#tw#je|6O}--QV<>1Gix+S%amE7vVR~!h@PrL2bNA_>c#obL-7(ym5pV82zWT^C_?ze8>v83EKj{UI zjoSb8dxpa0#fj)B-pMK0Mx^(3YOlsCdNCTOwmPvt!GF)C z@Sl_RSl$QR4ZZ)`R@F(N>D^T4IvLLqzXZJ6_42!0bggXGANW&6iSvaQ`^QfF7ajVm zR)37GGCTes9QvOe`jkWeLe<9u<+V~f_ND6nebNqY<1G%o%%N{`=sPZgYgy;e_s1AN zpHW=@RwsTt(bsgcd>Zjj#Om{BY(EJIVk_+Dd~)-rHnOpd^;g!B5<`emSTag*0}kDMq9Q zCTTQ2)rc;!j0IVGPzHQpLnfdtH4{H##;(^YKr5%-{EY4u^f_>%R{9J#2_T?oMCM&ZGY_#vf?=JPVcDBjMC#jL$eO@!@5R`#2}@m{XM7 zkMOsYS~HJv9fR|@+~IkgZd}6S^SJ&{Xw}K>4>}o_fjEs{=;ZaZr<3bAB;ofYd{x41 zz5CnS7Uw6K6ov;&I6l2Id|Qx zzU;gDmd#PF^QCKaP?PIirc&zcTMk6HM|tJ)^s6_p?!h*rw~%9=k6(N&Ql-Ztqrb4l z(qA~TQS9Li<}2{W^VM%0(0ufc$w$NPV0R6lO=I<~eN@-*4UPH@TP*#CH8It*jnaUJ zgfvDcX&cUX!r#H&z?SXP(W{-#2G9F+ue+Ikr)Kyhpy|r*i~P)O72NBybT0OTuSI?^ zbPD=f{{`DFbj}7Zc4o{?vM>9q9qD_8xNOHX+P>$QT}(vGhe2@K22xxk`7auyC^(bQJE2H4q8 zwe(?~Z=Qt7(nypl?T^xBs=M@5SkLzb$u}-xNkZ;JO!CJiOi>t2N*gA>SSttqGCL-} z9QKxAL+9xVeslPggxtUXrR`auqbjnj_kT&6@FIzX5XTmx#5FDr2@n;QFyH_yu8o2L z!6mFPj=!;IL}AW&hK7VcSsNmPbAUWRj}D?{gmL{aYy2&WdTB*CR^8WK{n7#5In(DP^=|#%t*U!(RlT}(Z%G{9(*^B{z)$WQ+*xi)TAl)xp z>?SC$Xj^gO-n=mhL!Z~cnC6e?b{ao{cTrrFW${_OOidhcj6)fS_siIEYD-+;YE$2p zLSJE-I&V{^7Wzl?wr9(fb_@JN-9+QvqO96I*V}SVC-0BC?iIir_Z;CmNw7Dc41Cpq z_nWYf<6e3ZJldUdWu|{(xVH3 zms&j|yzz)0-j%^`D(FuE>(Kf62HjhXx#@Cv#}|>l^(Kmn=gZ_xOq|grmx`xKS zuw#~I#p21Q@Lo-1V(RA8)Fb+@CG`lNKY+YGaI)tpi}QO{Fm`V8&`F`s#owJDMx2An72%7zd49Z#4eCdqf)TEyJY zv{T%lLw~?yoweDy0p`Z1YN7t>z}>e=(pdZg{mnq0BAOd&!^^Y2gQwv5(0ObvTg&79 zx_=1M^<1DWJjcfGf@Og_a8tRBvP~d+C#Ue6{3O?{dz&zMD zm=Ag7Q|Kd1YjWCBN~B5fN{fjq#lS0*QmkcVQg2$2(!wiY9?R#KGNLK;@xp1j#Z4#R z9ZMp8k5L8rqvqqWO6U%t4kPkQo}Xu+2Z-nVBHwMscObvu-Rm9XI|ciC59#3f{)ZdU zF10EaXlQ2@?M<`{&C%y9x_{Z|(`O5TpslBkLI)r%`<=dx)95`wqf$Z=nsr2>OBP0A5=5z5??F9~6?~@(*4>-;w7XFD3|4>EParzHRL5jKGTc}Ub35zURI~>fah80GYkDc!aH#e!S=Lr z|3gqNBCVTY{`gPizZgH8PFZm``BUP-C~ac!+aGRd(4JB?%oW{C>}S3hn%hr&M0_|c zFi&Ln69wjq{nzF1C%X0ue#HI6dTBp_{I~_)wYt724$K+LvvbB&&X3ix96wsbj~4AI_A#-isD{0Sy+QD!et7 z`T0VLE4>B7wf=MT4$Dfh;LgT zYevDB4AzQ#-OLbQ$P2ve=VgS43hV2ww4SU4zLnRLKY%_RiR($+6C}L!63AEEBKX+? z+L$+7lFi&N*LhwM@UYLzCh@Y)dASY7b%q$%;JgSt5=Iv39c9dVt9ZP18+Es1}(;94>ceUT7rNgL;T=!c@X?b-2o@@b@b zSr(k4F42Fqo@_!|Uz3{LBdsZ6zP-Y?J4&(v@1s5le+f<8qrxBhl)-QK`)EyxA7C2& zZx1j(N9eo2jMK=9Y4V(XduPUqfvj+atoUMIPFCy-$clGsIy0{}Zpq0ClVpWWvcjP658(o`!jfbK zKHGiBiV)hAi35MZUb_TgJN}=5a@~BtY?OuMkH4tv#pMC3|7jERCUq>m0!fP5{TzXQ&HEm((d!k4Aq0VmV`|5vd@LUvfxgoPJ%qadRg5#rX9%lNHb*ue;Xdsn zLhjZ}ayJIDv5wYJHm##>p>>pyyTiELJ=HqOggpu8lc4N%3-95l1@`d8J`ek{*DWA> zL54s)m%YWZ>~)3g6>BvwiT8qT8Q+>(KBSf7Y)i6|@8RQ{PbqiNM=6%Wda#a#&=+l-Kg*(Og`tU?#8}in~v~% zXHaGd<->GK*s4joQXDk3YJpT{ZlJupU-HY>2Flk-Woo2n-#!TT)mB#c*8@NK`DD+j zf0*und&lQ*PLm(g{qqKV{`xfeG2K5;z~>XE$&cy&84W)F^)&ha=AR<^34g)9^5I~g z_;9f8@)<_(HS!tFsm4SeOQpaV%I{DMjG6o%t-u(`XFGat$%cl^H7?{q|R z+Mm!YIZ6HFw^4?JIDS{URsv=HpI=Wf^qex zqJBW76o>X#%IMFJn*smMVdBq^Yf!{lsVT*EX}#ZpHhS@F)RN+sK)zBY{`@$+Cx~Z{ zwiMT>ksmrK?(d32H$>H914(?6=n zPyafQp1b%b(w#H&)5`ybdLyZKZA9AD>mtdqTq|Y{j5f!BC7J`Q{K}Sslqt8vGKfgZo zJJY54=j*-o%JT6ySu;KPK0j}yXCH25!Zwi)-vi-T34WA`??^uU924gcKKu?7cEEiY zeO#`WV2j%B!&S8B^x=H`zpGxI9e|5iJyn4`fqGvP1A1at2Hzzd$pCyX0DA%WPl{-(OL}>#D)@ah*>^fa75rUCdVE7v!Oy7ogz!jJ z@UQPPaw-a~GjlNBpDmZ*#j4;(hv0u#1^@Z!_p5^6e4nCp=bI~0%!|z%HF5;EmhHp! zjSYG5$Wf#7;gR|2BQ6>#;D5W}dQ?@*RZ}1ebN=IqU;>~}Ad2UW1QXzO1)_M~UM~bx z3q5X|ig zK|Ag=1>Uckq8)D${AW|NW058Bf58%Xayvoro*G-UCr0*uR+GJ-2W^2rt~K!eUADk~ zpDplwg5viRjP`*LpBE@zFA{i#iv%9AB7xVP1aB=8czsYL`qwEI_%)OWykaE+FRqse zc)d{~@IwEBm|hnW_~BXy-`5Zl_%((EK4U4qiQq{gf!`F0pGNTdkiZlD7k;FYt5w3;bU1FZvgCJ`v9;j=&SwT=?FoBk-I-@GM8* zH<#cRf)_Y~4$uw};(0Tr#|U2J2s&6oFxOv%cD(Bl-esa4jb#Gnehi@<_mzouyjmvO z@kF_3hh8Dt!F4L39aAbqJJwf-c05rb+JS3B#G|`HwBrSWU#bvzze@1y1iw)s+VNI} z!1G;7_XvK!LbT%pg1@G6xQ4{AhQbC4r%<@yOwq3O6uK0?N})#~t_6901I2sGA*_PM zT_uJ;5IUz-mjaz)I1%25p-|Xk>Oe8?VOUGySPG*QE}(D;h3hGFDcncl|I@C>-`xRZ zAwAPt$pBSfdZzoKPtoRDF*~)M>0H^E73*8iw6A==o~bJ3^Q2{?kKfq9d&dqIzHWl~ z8UyWkcbFJUY=$Tq5b>sNieqq zzV5=0`CTYS*gUTxe_+u#>R0BzQzE(IIlV!5-&DFMtI0a7(P-*R?mr+`?oT~E!Vj3N zgC=}Xfj6t?)$dn`wx(XYD^b(lhIdrkoq?@BJ5jPnB(W z{~U1-*!8{xZKb%|`1ftuE_V!Vmq|9IG43X^$%T3XFjz+wcWtUT@Q%OWT|6f49pl}t zYBzTWaE|W_&5fqe?l<1SmCxn&@IGix?`7=mFXq3NbZ)FXhG*!qP4t~O{*rBLjN8U- zv~1(|xA9ItrZws9ANc8;@UF9zANJK%MNYo0^`&?(Gqx?`;2rjGuAZ!o?uf9XhH=ZN z9m0+`*Bh&n>$(~HDb~!-4nG7sK-(Z_OCRqki#%6zy(=F+1K#QF=*(zc(BJM|UeETm zsnW!nlnVW;bNd_7^eBG!baoQ`d_LCoSB_s+XOYFyUvj^EUYfbNF`J2Jz5rwVd)yv@ zh+i@SzoSR-e(m$~dwLG~@fH1mz9DX|6t}p4G;PG(UCli`yZyL9rO;-GyHbj)r8+Gr zKTRrsIh3~{?_??OXlT!1$TuYB9-e^u;nbEaKhe3Z=y#y!i|u0vh<~zum=O2QeO~aN z9qAc)VI1oA5o{veLSMM3!$!E2P#oU-#(RW8JLo3f;fF13R=0X;%h2Sbqm`(*#_jwyYbxD@*8u2bQ;9bwy#b`UF z9Auk{{Ym~|QMT7&UxIuw*wx9$*pVda-9F}gnI~6{Bme9cb~1cgLR(sk`%h0#W^DJM z(&^~|3ft{AbaHxy=Yy9+S{1~NX4BI{fM$Ts-UM0k_F$s9`6QQ$VZOb&yfd?w=%<|e zJUA{v+6@)xj~4;YH}Gyaj7?p5852a z@0(`moBvI2<9&0ySKlO{Z=Qtrj&-(W+6A+RGwd z@<-%@L9XL1svmuk?8COy-M8P3G+0bs~%Ng z9AMqi3iWrOeS~>jPx0y``lNWw{VIw5fLrs&*m1H)6c=`z*c|Tfwe0L$lRM6Elkj}> zm&KZgI9Ad$u+bcp9G)bQ_0u10uq{3HK90wkU86e!=tc@vJg|Crz|XQamVcT~UWJ9Om)Fjf0Ni z9MApQpxr5w_jz?XQd)F{hwp$nsu+2xlGbE?Uc_A$`C)B(8*jsCZgb%(?qkA#e~5hY z;{ANo3!5N~eH|auHLx9-om>z7_u!-R4ww#pN_0nN#mp{_FFuY~i>BtO(JdN;-^Q#UW zu-+7RaVNpw0ob26KGoBM^Df$Fyc3w)_l3kH{WRbIQP3W&*Z^Gz`!9{3(3G%#uzwoB z2C}nY|9dVwA9ka|*(i1i8^@a(erCEmkk$u8GV<(uRSV}*ozfz$LQU)vMD)q`3V;~(dXajGIzhcmovt>kv(rmxdG^lkbs{a5<0_225x>Id}K z^h5fadQ$&T|3>f8HKWKVG0KbzW1unE7-pPr3^%45&BnFHLSwP9%(%nt9m#gZZBMnR(niVSZy4Sp%%Itzp&} z>r(47>k4bSHPdRg=3BQ|w^={4)>-Y=HmlS6jrFwky!Ep6nsvxJY`tS8tq-k_t>czq z_qQwTbL^q^F#CMF-X3pHu&3Ks*|YzTy)OZa>d4x@O*haR(9rB_v+vN&j%>{qHO7c> zk1;?L4dVg=Zc#@NaW~?wF$9b;#u!J9F>2JPF-DChY78-Ij7?()cjKsIj4}Lms_Me+ z5+=+ylmGkv=l^&ftv*$C>sHmRTlbt(?>R?0Oqwq(mX4QJOV>*4r5mJMr2C}@q=%&^ zq!*=^q)pOm(mT=z(nnHDnYGMI<}VABh0CI4@v>}LPg!qSAK3ue5Lv!#v~0X=lB`BH zPqtXLR<>TYNw!6HRCZq0EW07QBYPlwD0?DP%YEd&a-BR`o+j@pA0#i750{UUm&hl` zr^;u@tK>EE1@dL`Rq{3Rdif^#X8BI}e)%E!DfxN1N!}vACBG}bCl@R16;28dg})+H z5vj;fbXW9M3{nhJ6e&s+Qx)?RixkTgYZPl0yA*pAhZQFjrxceIO^R!Z2a3lEYo$VI zuMAX1D|O0DW&f)+u)=cPjTN_bQJno0TTz4dq?sL*-+o z$VO~qXQQ_9vGKJDw~4e#v&pdOV>7^}*k+1Nl}(M!9Ge9;i)_~0Y_{2Dv)|^Z&2gKr zY|h#=+FZ0T*|gZ)vw33EY9q6CvGua`wGFn7wvD$SkS4~k(Rn@5GsFtXfsp?egRU1@WRohj2Rfkl^RV}K!s(Y#@DocBX zy_0>geWZP|eXf0X`yuxE_QUOq?Mv*d?5ph;*{`)O4pSVaJ1lls<*?OZhr>>XJq`yP z4mo_~aK_=X!xINm0W`?sDAkc*3#KvDvZ3(b7rbWbfqS7smN)R z(`cvZPBWaUofbPSaa!fH)@i5HUZ+D&N1VQLI_LDz$=X@wZ0D?Yc5-%e_HYh#j&)9U zPIJz6?&Cbbx!8HEbBXf==Vi{Toa>!8Id65|>Ac_hu=6SBv(D$8FFH3lo1AYr-*SHF z{K#46V&~%T66_M|lIb$cWw^^Em#Hp`TvoWOb*XpR;IhMIkIMm<<1Qy$8eN)PuDRTE zdEnCOV(qGS^>U4OO>|9h&35hS+S_%2>nPXpu9IA+yUuW}ab4`X*0s)cqw5ye9j<#^ z54j$7J>z=L^}Oq4SCi`v*9WdoT%FuJ+;ncSZnb(`W=<+jXimD?J(^=_No zHoI+i+v#@H?UdU^w_9#c+{Esd?)L63?!NB+?vd`%?(yy!?!Ddnx)-~TcAx4#-F=Sx zJojbp8{Id%Z*kx5zQ_Hz`ziNE_jB&e?)TguySKVqdiZ+;dPIBZJQ6)pJTg7Hd-U-b z;4#Fb$YZoexyKBTYL6uzbsif%4tN~#IOB27_D%KJt`#d3gDH`Fq8CC3|IfWqS4U z>gP4gYq(dj*Lbf9UQ@lMd(H8h=e5FXjaR+b2Ct1?JG^#z9q~Hnbw(uJFOj#k zx58WP?cyEi9qt|No#x%sySH~A@3G$Fz2|u^_Fmz=&U=gZUhhNRN4$@EpYXoq-RynG z`=R#}Z;_9UkDZT)PqI%>pI$zFeMb3A@R{mUSTjj8T{BO!PP19FU9(4XSaU?v ztZC8Q(%jKJ*0gGDeAT}3zL~x~ef#(h@g3@$?_2IW-M7Yfh3_igb-r7Dclhq~J>+}T z_nhw~Uz6`O-@CpKd>{LY{bYW2eja|let~}BemcKozg~WQ{D%9D@+0Y{u}+b_;2?=;D5ybxPPPnCI4psJO20lANgAbC<0sp+yX)a(gHFAvIBYtpmm@+&@C`H zP#2gMm>t+FuuoulU{&Cvz-57}0_y_n12+e558NO4Rp6<>^MOr)4+9?sS_UbC?1G$v zf`dYX;)7CxGJ<*q^$QvjR1{PkR1!2bs4A#BXmQZmp!%RKK|6vD2OSSO5!4uTDdkyj|b%;lZPl#`bE+j1^Bcx|Y-;kjp!$Jx}MukiWsR~&YQXjH6WPiw^kj9Yn zAs0g~hujKz7$Od}47CsS4fPMr2<;v^EVL-JIJ6{mLTGj9oY2LgYeUzCZVcTVdLZ<0 zXk+NP(B{x_*t#u*YHQ@ZfM=cx-rDct&`y@IK)K!i&SlhL?m-4PO+#Dtt}&`tS|m zo5J^pe-+*mek1%|_@nSA;j#$32$u-|i0Fv;h~$XOh(QrUB8np>MN~!1iC7j)kH0eS`}3n zwLWS?)b^-dQHP=qM|~A_HmW7+MwDf=ELst59~~SW8l4%P8{I3qcXZ$ALD41A6QZX_ zS4CGxZ;0L;y(9W?^r`4G(HEmHMYlxXihdCNC|abo(fVscwTapk?Evi%?NIF~?Rf1J z?R0I8c8PX{cD;6!c8~Ue_NexR_KfzN_OjNby`z1seWDfXWI8vUuP#^@sY}*n>vDCy zbi;Iox>35Zx^mqN-4fjzUA=CTZoBTV?x?O&cTsms_fXfWlf^j2xWstHgvP|j42c;Q zGdyN=Oi9dym`O2HV;04%iK&m-7_%j2N6fC6<1r^Lq&z2YO| zW8+ieGvoWl7sZ#vkB^@eKQ(@Od{z9S_{H&S;@8Hni{BN$KmI`c;rPb*OYtr7cj7G* ztP|`L{1bEu@d=3uDG9w3`XuyA7?Mz!Fgjsu!h(dw30o4jChSSroA6b_nS|zqYY7h% zM2X@=>qL*ljKti;o{4=Dhb0y!PDq@QxF~Ub;-r~c zn-rgvnbbY0S5m*EK}q>ZMM)(|rspKih3!n^6Z_3GBQ+t6;qyN&8Lw%e3$bGmKlwyE2$ZeMk4 z?AFxH)a^#Md)=ON6Q|gxc%=BI_@_jsB&X!2^h+6%lAkg@Wok-Q%Dj}dDfKBEQZ}Y+ zN!gXMC*^R;iIg)b=Te$dno}O6w5EtsZBp%1T~d8gLsR2Zvs3$|_Dda_T9i6Eb$n_~ z>b%rdsT)&wrXEN=ntCSnQtIW@yQvRT#c4KaZfPEAK54#bk!i7MiD?;W*=Z$dMtmTs4BpYE0JpB|bXogSN>nLa3e zczRL#r1YxvIqB=t_oVMnKahSn{Yd)p^mFMK)0@&;((k0VrYkaRG7>XVGSV_KGlpd3 zXB1_O%9xTdJ!3(}l8h}G2Qt3OIGb@P<66dzjJp|6GCcIY`apfSUZ>B{XX|tI!}Nvv zQTnm^Ir=5~_4-Zv?fPB%{rZdg%laGohx$i)S*9Y>KGP}FH#0UfJ~JgVBXdY*QRe8( zNtshKmt}6w+?BaM^FZc_%$CeMnfEduWm;xgXSrqhWcg=>W<_UZXZ6b(kTo=`FsnRk zdRBE-P1d}uC0VPo>auob9mzV8bvEl#R&&^|9pvWv49 zWG~8Imc1c+Q})*Eo!R@dk7l3BZpv=SzL9+^`)>B5Y*~&X$0f%MGSDhQ4tIN&I?U~yz zcR+4o?xfu5xifMX=dRD)lDjo`Z|>3DKLe}#gCO>1TRa#Q!9KEBQ(K0H_O1%9ho5=| zyZfqu7XEdF1vM}KSFx5X*3_DzrJS1n=3^=P(%+LQ_ApscAI`FK82Vq0CsV@EuQBvY zhGu;zzRA$bvr`utME)pU=+9 zA6__S3`I^7oZra8@`{S0@{0VT$uysLRtP~IN8n!*3&%_-;v+m#jAe(RHib}1K9VD8 z>6PLNKF(wr2@%N#w(KAxCFjeo?&as>9WZ}F@rdGy058Ru$#F$xg%w3~D$xv@hhm~(BVQx2I%!-@ z{;p-zWh4a0B=beb$HwVwn*}E5CYeW_+0!fnJk@fJ_~6?|(~}(WEr_w1PQ<4v_$0)! zDqUy{C|3^0;Dt1Trb#r#v7y|Eza8*NIE~V^l*X~4JZNkvPZApnVo&}oB0dJeUn}?q z41rX`aXlbKo8Vwb{WuQ%r9gjbA@!#|Em8h%nh!C!AvWbrNCQ#-z?WqZ+9x3m=D3$2 z4dJ*7n$D%^n>5`<(_=LKfu`UK6MPs-0h=01V`91xd={PunkLaSho=2$tjt$w%KOs% zoTl(?6U#X6IU3V)AB|~=F|O!7-&>OVfNvvcZ;Tb8ag0ku&!w?157O9|1{(A77aHr* zlg6~fJ~Yud^fcDxENe0^IMyZV*KaiDr9?_-h-HcAPfD)GF)QO}%*r6@V-r1}n^Mvq zN3p20APwWV_hcmhq>R+V7}NmjYZHw-0I;YI==sVChHx^a-=Qh?wTbFom6LX+DM-5w z3PNMdDr)znf{ZIb0DM4$ze(E17*tDwL4_1!P-O(a4`~6-A3@W}N^-w{HzpYmCHyln zgR_+F<(**$E77|kr7H;95dKiJsvX{BdF&psK>@8WPN)zC-(02Ol2bJ|EY2{*rbrbmehqK(3CZ!6ngv(lDV;Y+5 zII*!@d*>&yetDBrlVt=vVfNDRTR83aE#L#+qV~`g)F${^YLaqg&A0Ix?;zv#*48jM zQ{+$?Ua{@5oxz4rQ!Uw-B-pS)Ym*U*yeK$4Qhxt z<14@|g3mBp!8tf!F0?e(!bIAhsNop+gER?oj#7TU7Vp0*tShWD*}fjf5`cMpO+zs? zl>~dmU)u|=1MHG!k?B=5k1(J7T(a%*xSXp3Y#3mC9M{#GtZNC*kF1{)j%W>B&l2@- zLZ68XJPtv^&AZ|!7&|^s*Yq^-xIxIj+uAAzA5v0PFM~wuhSoqX_6aD&4kA|K6G?4P zql_EW#BEz(0L;N^eZ5JdUkqkl*C8Ly@1YF^_N$PL+mUG6+kortx3<1NLPgdYKUU%V zfp3@|v>f8Oz`C@i^U2nI1C@^&IdbIL-mj>VUvEx&&Vu-c;l6w$1#De_$1326tj4cN zN4zHc8l`A0_r=@NcP8i?#P*9sDd4w6+;^7Zg4vvv9?el@1C1rS$P)c?QnmRt%QHTM zF|mU&CItMK0saSL)HE{<;5R4*+OJ)Pu`havo5eprT|?7MQF$}kFTV8ARvV?7HKxN3 zXpLLE&=df^;qd!^QndB(`YHnt3k|YgW9qb zaM;)vNZWHs!Ox|Cb2@&$1DhqK^9*tDd)rFf-`)b;t%N>{khW*Bw(VIUw(Jv)4z0z8 zT%)96zF}EFwZXBf-n1UQJJq^sv`^(2@Vx2aeI9NE{2|1xZUKFivNsNwN*v9MpUdNa%kk8qD|4bA6K3hy7hcNiNF?~V2h;F+2tVXUeKdy)2xrASp} zp@o=X==mVpbXhAl4u<%ZsLw(_M=HFYzBR$MB0L^~r-v9ZfSCvXFo5S4q5S~jf+;oC zCMdU1Rkiu(xJ~0>LO#Uu1b>b&7EvDmt>EPQV4tBFKeAuuHG5EBd3-xw@J%mRVGzR_rWVy+g)MEM8pFT}iR ziyMvF{~6=58k)f`AKc>_`aX*&9U$nR@R|#PYrqBv?6HAu!*icU;-x`do-cuT*;e>_ z)6o1E`knv=s?GPcmSCh{o$+NbZ+VCx04x&l0|bv$OX7Z`_L8X27KpvZW0OL#AFzgI z%|X};DzCsdM2~Tf?U>)6VSnccUk!X~2Y4pL#0|%MZr}v;Pn$dexUNXdmJ?v^{-Be0?zM z*D%*>MCRAe6}Qt+{*lkoJ?s1Gr5KCmV}b+IizQ~xg_vi|aD`9!9?>JRk0OBs4CqOVAa z%_7mS(qQfAKOHDrQ5pmP#lnAa@LxRFqi8Z$Jg&GRO2=WP0w3D_)iyorYei)xysmF7 zQ4vzAyrOKxgwm(ux}(cS6b)xJlu(sX8S*J(A3vgaB2_>3L^m0S_rsA;fI0<%TwIp`5L%KxObDviYtoRvhqikl0N5;EFM#Yf8y3rI=rH64F187xumqBxMUotWpJ!} zAM!oGdM%WCAz@7_BKk$xLm_rO=s(43;`b3~@DnSRP(3K<_eA9S)im8LB6_sXMWp?! zBBDRT`p#fC=T$2u&k5@=)9uvMj zh|%ZtG|hn&J}&kFp|+Tys*^@UK+QBaS+FRL$vdPz*Z zj`W2e$=dx=3EN}!g-1H+AFD5fdR@^M-j@*@XwVmWbDir8zePglyIs&1eqi6Jz7URU zx*QuPE?yVYRed4EW8wt*LIoPl&seT~!C2u}ZvsBMl=^=6q&{jz+lWtEM~eA(hZqOg zmad{1@!VpOszJ@E8pI$MC!uSH3GC`-uy%DwCUHggQ8NDJ9RXL&IQTSY76ZbE(%S%{uN?@1_@+b(1Y&dbq=Tx-p~isw;wvN%Hu20ntY=lwkb3$6KRjH0G)`ab{@#?O%NYc#MmIC z?eKzHbIDZ6IW*tGvffBkw+!|Bypu?C-03#R%BVkck&HB$#vl>K0A7k?6rg)+vm8X) z?<_ghcTVV;s;g5CB2{da{d$y&v|m`FIdTRa71&*!)_&LYAxc9*zEUm1zR5EM>jCS^ z&{}JvbMPvC(O}tW0g&vyja zEY*Uo5_escH^A6SpjXX7<@g_R|WnC8X3wDFyVLDr~#iMC3_q%i|7lu&o3-X|d{= z_WRZ58Jpp`70fomJ+0{dc>A72!F_`|xvy6T_idu~yAYd6!j%1l-ihD&Ylz;f&2|XK zgGYInP#xCh5A5Sk(3rG*zHnS3W}I4Wz3E+!yw9LxACI2leEJM6sVu)6Y_m|kHMHJ+ z9G`^jkG#E*g#pR%2hp?B5T65J(+_(B=n8)Pz1@cBkLrW|;hTSX<`?O(ZH z>VM&W7JMGMXDqrW?CZaicj`LtqXB#}Xkc$g?-9hFnR2-|wiO1uZ@j;AWPkrbNA`Ee zm^zcnU3#03D|<7u-gFu4boGmg-}Jm%qitSg){(ww{pQseg?`=h>layoZW*OUSVskB z=LLDq!|?it^O^zJ_6)ums<-P*zZEPpYV?bYp4%1~m8FYJ0a$NEbv~#Kq3u53CQpOi zp;SQknX$;IM|G?OdGN0C_Kluce+>S>a}9d5ui<^Wv_Yqb*dAQ9fscg-ahAoJIR;Da zg(gqUA`{}1!t{ceC9wZAqW4Y-_K|4+Jf^BQ!C8e+CsrV^)7*y2-ei0@|DbFC?s?Pi z-jvJjJ~E(fmA=k|<1lFi-!pV?*dH9JT`!#1Kn!-Fy|hTLH#+tyX?iECq3Il257+5l z3_cbnp!r!S*o(iW`>=}c!+;Swh*B#NIXh!@VVFS~{_ktHx}wVx*5;3%-?Sj{gW>vF z7VYISTE^3AN`tO|uisx{UjHQl4dsI^nR3Q2=t-vhG*hk==&po+|B3j<=Cn2TYsvos zFB;F#*$mBM0S7bm2!^g;=-CXtilMhK^Z|xG!O*O($iUDo9C<#lmxAu(4oVja=-*Mg zPe3a~#78)fmF!5D1kbaAmM4nH^OI2e8AN|8pofYG-sU-~TN}@`_g99V&(NzGdILkV zekyk}^f88R(;eZs%NW%~5(06Hg1E#{+KzE#R7vzg=JPrEuS@|^5_CevODXuhRy|8&O`EKdxll@FJ=+Mh^L+@ z9#=SqcNZyCs`1({n8b1Ayf%&Kasg8Z>mIOb5LX+pTrY_TmJ7~|0V{_wb4c(4Uno%S z3A!B*5;q*K$MKv!$zJH@#c^}N`8@idEJP_B{MFL@<&<7cQ(jkel49#_QfytSn9w7{ zgnpBz5c33dP$wyUh0<6Dg!-8(A^m|p7TO<7Qzg~;#95MZ*kd7o8qI%)=AWeb@J@R9 zlXuYzG0otI5&8*s=`c@NM-+kjWkqyHA5hHOKAN7Ssez_9sqV>nw-oyZiVWzNR7&O-_I8*@h%*iI>nJ7tb)~6>mjAoDpg&Uw6d-W#+nEk% zn+;iOQtqGYfCihp3hq<~)LA`N2L$zmF6meu5dT-wNqc=bbKMTq>xym%bbxw_k#piY z*XP5Vz6z-$ zH_NEf6ATr|D1hY}oL7(KDTrYKJZBv5dqRG z{z?Ilc^}6FQFkQc7-WIyTUXNW3q;Ter!AFijZ6)!oL2rB>ob# z;mFnC`HSFcOn}jX@(|4mla%pwrzUc2vv7<&hKv))NK&B=E-%jr^?AR3yiY*>I?&fa zUmP_xMj;mD3aSr%02$Ci)6*b~fpQ7(2B71i&%%;oR+P0Q2BD10Yx)+|QBWC??+fIA z4YFJ99FqiHZz(9Vr1}%IcYY$O9b#0I*m!N_*xwB*{}f#>?8~jzR`)#upK%!Lhn{b( z(61qS&I{o^Kyu&;DvyD@Uxe28O!}_K==0wQ@zDhB6Rg*2OQm*>Nr~nI{l-4kEVTXx zrFOPyEvgTAvNrrDT1z3qwFN$`YM)-)?s$z2!1x^SxdZqem;-D2JcQU68NcRLM9yqm z^Lg!SQi$E;LwSxL^{c`5|(@god zO!?0Y4&uIm7XJMivLG)TgYFGv6)!!&j1&oVT~iZCB~TECbnAJ5R! zIWj*zX#GV@`3i{Xo;7 zDRzLPa$Ovx=!MRsHcTTZ7GN5c13}({eypbX`zS`>ds_cD9#cTi`n6)xejcSqP@5)< z2|)K>NK-8Dq51e+Ot1w%(=&fZ3F)_|gvwoc>UBjP(g41SVgOX!Kaq#fN3?Gj z*YvQp z^-E6sx=Dg~H4#1=n`wZyK=$D8aaDi?23h6rARA;-`3B^Xg7&kOKzu$hut_6YdDRNr zGnk+4c~ILOh!-y6dKe{@vrL?BMGFV9$$cA|)&tLGzpzE3T8{nTid2~p#iqGB1Lk9v z39Ziy76%s^EHpI+i7E#C*fy=hRooX3cF%KQ1*$4py5a2Unu8 zz69rM7pxCGXH?o(p|MpNm4{{;ymz8@4XGw28fSX4)S$p)^)(^+%-!3B%9<)$FeU)z zT?6yF(qy5Zg>8M{c>*6U(a$jf_8iWjc=`GsJue427i3E*oO$WnvXfV2qD=-?S}npGxILr){ez1YkdR+|)umbNH-XBuRp znFl4D{xgZ{`Og4Xpxg%Uz=tMS@16;khLkv%!*zHLa_`NFdKVzO5LD|5D1FlS4?AFZWXh8m8h9+=cYdMu*|R429sus+Rjy-sVlqR+*%QnUfB!c) zqL=2cXZYta)V^I1$LT4ncbj1QYq`*)+7sCHRrZLfMr`~=W1bH zdD>Dw)0V_3ftWSlQX1RH+f%G4q9INg*x+M6&w=Rkfgj^-+hP4+%&C~vhgjrrJ+@g# z5AY!6-)@RYeOMMy|9>(+Cg}n{_K_8t!$0E3lDf!`bykn%$DrOnvuWo4^18^6<#mxC z157RWOMp4B;X3EXAdb~L9kj>tW2x4i^JC-7+SJp3%#S4{X}ijgZ6k;IZTy%V^&VqX z(6?yjHiGf8_U&cFJej_SQH=GK{jqOp3my6vgB}ax7iz9}gWvXsI`lp6fpL5AeYY#@ zVYDhlyo>gYD;OVx_IZc z!8$D(XF=3J+UM`Z-$x>vdHIXouTI`Ubj1UKebwgL^G9&C;RqRW&q0d20rH`o%gZ&0+Vp<4 z2EZ{{BOXc8cWsNrcYy)&B%%c^XuWomXg@TGa7+U5$vxBbE9hSZ@nAro4t$iApdZj* zYpU0UK5RmHM*(m4bli)l@_7BcFwQyX8^PWV^Pj%;rq6W*k0YsEg0WC@L2t?pCg&yE zWu{pa^O^|w%+mIDB;24TNkM~s0%IS@aRb$WKeYw@cmdIGV;m5=*I=DF_Ggv=^V%DH zv7@?kVU5k%e|2sH>9v!_1~*3m?~l?{yoLsY{qvWcY8B#{SK{@H)`^in`v7?X zbimHMYuA{B{erUd7<)+afNq5M1TePD^;m?mB6>c=pP}f5 z&y1Y={3-s(IP%Q~jFATY?!jP+`u~Z1oIm}|#|G62%HbQ4#n62i`Xz=g7K{hlWBq^4 zVQAL>*II_&#L&Aqf?F`q_E|k{BSRY)`UXR@_GG^^v>l^o)i87dL-!Do=cVCD|N3;G zrzPJ>&^b;hD=(f{MB>90jwvoI2Og9bk1Ht~TQ~+NP*9ph1T_y2it4rzyqQz^Q#Zkk z=h#wO+O839k1k5D7+x~=mBKhPDv77exYEv)w!DhgoOk9G!n_igu+0As7znhq3^z?M zXmFN|d1NG%tcKKv@XB6F>W|8n#t20Zq(9}Emy1Y!z*J#>L6r8SymJmpz2wAy6!>id z|509!)Q-x5Vqp-Ee9VII&Q~l579@aTTLwcaCvgx!*ZL0SvBCBc`>&$)Zqj-f6NB!f zl8}7t56X^WWE`o^)RXKLfEjsCLNFzHlzxd~X|SItSd9NF&;B1`(mUeOE4sj=_mC2s zIBV{o^XTt&kw@>Wo|ZG;RXeMv=gfKU_qxb?gANvOvCtPe*E#PEO1YQ2pbIUKk#h&O z9(h44{sp_AxGrNuqa(q4vHa{Z7zlf<$PBNGH`e}>fr)uYEsND*V zEho(LqrNR{C?=!ryUy@4=-Uj_A_qGV-rk3gSHxpZW{VIXr6;~5_;0wdo{(p-rS~lm z_%i|flAOePQ`kX*fdM^HsivWM3Zp0bQQ+@`#u#+x@5!gNa_MdUH(=-6ehc&EvbNt! zK^?$poW#Bn24deIwQ|?npmPHr)lzUiei-;O(EZkej+MKP?Rr!~|3M^PAlM4Mj^jEG z!Ex>&zKal_;!a})VmyECB9lc!L$m4)*V{_OccU%wI`GuOcz%HOumPPLw2w`)am*oi zh*$Um_hR!Cl;?y}iHQ4Bt}>4Ucmv}b1~x4h*q|)oZJW?Dd>!TUbL%AH+Vbb3{Lbd# zQ`9E-{HS$zy{(eF-4>QLL%c+EomfL~8+hK|AaRm@fU-&wzYKiDz#Kp3E{yg^ z^>q0M&<3N|-w1LNS7*YQ3~jp|1C%wu9HM)*#l=E-u_&*9T)nA(0ErI=wgIr-O9Z~0 zz@`i4&_Xze^T`}8z`Qq!y$H_+bhdokvvB`iZ}a1mF#Vr~%Flv)jMkH5ZJntEVm9Ju zh}sf7!!p_qw9ytfQqay{0J9^QgNtqLxZURS3h6W8=|k{d(K_sU8pzZ=jM{@a6F$G* zFwf}OsT%4`!gV3UJ>|fB(|*fonXuofg7S`@ZyfYvK3Z4$MJBo4{5j9Z?;FJF)tyK?Si>7&J*nzUd>s)}UWdx@`lru`eV10? zJ~cE=K>Y=~A#4W}NuHGq-d{v3nHYNa(U{kwvEp@%_LgYt&Co0!iZ z8$nk9XNe$tgKv7!82|fd5m_Dkc5$RWt<_Y=;6#17I8$5~N^@wMN7I*R z`YKHqiimDt8$HMSRz!3OuwQ~MKyN|%GnnGG@HrpqS1CR3n@s8DR2Q+0;=GPgJlHQ3 z4~FqvNC=CgcPZf6XG;{r(w?!S-LKTYj-ei4)LDyc3ag5tJdkB9avX#R3a16B(9 zx0~X;j!DS;U6GJ}-lw$Gl8i6HlH3pbd_nyfL({pIq@52ay^qpA)A|}Ksw1H(_6-w2 z?S2B${8$mZ8Jq*c{jj|cn&YMP#;_3m4fablm> z4#chp+JokSLYmjdq%IiUoX{EN6$WXqSt8c^)VxUD~~(+v%mX$ zgKns;r-|Q@MEUuhpFiujPj^3Qjx4HcKH=m%J)T9e3{PjHH;Ae~-F(eBgzb8rAE!pLDzN^8ugv zY47Jfn0VpHt_6+jtU{XC{{6Pos*Q%kO1p1fKI!;bQ!#(jNm=6$hO z*%_znF+bm2+UpC^m(@cKe6ui7bbohA*4A_3efw%+dj+2Ssk`HhiynKw{rZzJU+3I% zTzl%q#qBVi0!Aq=8 zd-f>xop{M(_tta9uS-Jcs`36`T!Rm<2^#vze$^Y}=brm%&PIpS zGIx>Gca!GK%wDgno)~@`dNEtK*;LWc$0#*EbN-Wqb7hmp3@dzXkW15ErDm#&Yq-w~ zBmQzMz1(8r_amh9&s+T(eRuTjZ`^~vY5uydFz{K!@`c{#Vr3T>{_Vl$qgR^Vt+89R zW4h96*r4&(=gaS094x7Md~?Jno|iZK4tOv2wLFQ9OYD}&&)=W_;_GEjD$8G2y9RyZ zWctgB>(hF!NOpA@yy71x!hbye!gD1*4?D7R@!Qrn->wc`E1qrZ8Zqgu=9I4!qMmuV zHfEZy>zC3o6?Y}8_dWZ3ymHjol`BiFLmS`P`$COo;JsHoS8iGVga5Fi>ikmyPu|OL zE`H;vlmwxNFBi;qVq-M+QU>i)+s zwJJv6N__WP)^qp2`0LBBOx#&>CHJ$wW5>of^+|Nh|NB^ZxYi@P+gCf>R@}KHiFM1$-W@x{!#pTb>+4|FKCP{5@LE+>b z(GM;@ju8KP^P365uWfvJdgHc$o&LG~drbOe?oTmwL*1WonPa`@o4Zvb#$>;y*+0>_ z^09xnk5+w|GRbjtj(3RH#`MFDnWu-8SG3;0D6g=2@A}KDZZ4bhXu$4eis+S_TtlzE z|7+u8OZSmGx;ek$vZCPBwo$uo6(>ut<~@;I*wkajS2j9vugwnrjcMyfO`2d>R&{Gl z!$&7V%h%@>>`Ys}RQH?p?Ky5mAD3;79z0L|@{c3$u6^U#n9Pf#KiK;y{><;q6HoQ_ zf1~FzJ6ETBUt|R=e7j}gTiutu-0EZTVpQnP-PfY$&)EIxrsijZrd9uK*U5*^mSW5}eto}Wz?~^y)AGhlMiEpD1jw!QD-FNHNn}cjej(iY$q{j8s)X=p9X7(Jmy?D$0 z*T4SwyZ(hAojW@0%!PRcUN7~0{mOzpdB*P>4Tdk4ey8q{l61D}`pSyS72i!P3Vic9 z-P>;|Qd=j?DJ(0vdSvmh@?T=#{BF^XEqml!Z}V~#FY{P%?ed{8PufAAlDQb?Nr>;O zgTi{&M1NW-%#)EA9tDEDg>oV*>xFp=BF}0Cc@Tq2!;yb{-ha?ddMCZa|;~dJLTx=seJsbWXsw5$NS~J~&FhN9W=oZGRt~he}Gb z`hD~9u=;unhGzBmf9!{yx8(-kwDzd}W%`?^VPS;`Xuy@F3TTLbmnWdzMFhLV(;*_F zcjRfnnJpC1phIjB&@YGx#)+pXkc>JdDk;m4A(0f#GvhnT)FlgY%StAc<|pZrW7xa| zU0fWS7pF~5VDn92h!x&~1Z2!%BZr*3}-kSH*yocs} zGw&6C_7);C-;jpTG?k`#G#x?H$uwO^(*~M;MALt!+v`NHN0fe8!v{agN(hedKd0vd z{T-|46aMjUl`m(mzw4|X_?weuBiFe;@OwMXEUrLj`oOs;V{`}i z1>j{#S=W6)$LPB519}@-3}$v11T)$F=k*BD$@5edU4#dPJ_VT3R#LUJg~w^&`Y6BFQf!cjX10j*IY#hNE#`XQ zct+5M)|!tk1~$AaP1g3=Mv?E*7O*`;eQ_0OvyBq%Y>Y`zYG+##8&*RzNgJ9W4ixb} z)Yk|u)_8xpc+5QJT$^VEU!F=8U>o{2qi?x;4!7c}h#T08H!kZ7|F zU<-k5Bfu6G=0@KDK7!=X7v5Il3hw_36Xc1weoY4KZx~`r)tCUg2JJa;+djE&SUG@o|{`|PbT3G0b4W~v51Bf#0?7*c>2E2uFY;q8+1=(uToCp<3&?c)aD z;7S??%39wb0-sTC5CR0vwM2)Wm3El{eAY;GGg~b6vkW&|Tf4z?k)SaFo&xNlMXFgw z;qwRoKc_WcVB2C6A8fWs(syPHjN6i83V+TdHXqmrFDh@lzSMjzDYYvt)fpwC^)U@1 zc)nl3Txn`?+$LC)V2kRFF(-W?-dYnpYYXZtye-y~s@BBYXXAGB@{ABi3~Zx`&ng=0 z3C#zpqdu+q+#aoUkw{hTAW_Y768D|y@@Lo~2><>Q_y%DO?C*sB20oCFLk_>cGYXdzQRhM`$|P{4-=Sdh5HaQ$+o9OBx5-;2q#d^1xH=k#r1na8w; zWfsPEZV!3F!rslyLHTOs>;ZU3Hm3BqFtSS90l7KgzAfU3nOOkH5uQ{Y0h~FG*Q_A; z3pfMsLC>7AuSoPAx+x-O$=H{OJw1E2rDw}%)A$^4@TlIm7Uay_K+82^a(xm#BgVc$ z{wr~D|4baZU|Dx|W(jeUVXpoVhi?9gbdE#sq~23F^iJyOIR=L=?jjCdOz;L$I+o6O zj}SE8-5s>|6b{|0Qylu_7M%C>_kjS9jIrPp zmz|`k!+r_d`c2mq(Dl5ZLoI1T3eO7Fk5LgUE%ZrqD(Ym49zvz z)($eZ&^VMd)7vVv_nN>03Hr}9^!a4XdF4{y@UCI>yrx5J6K8f6>`7+ z%}&%##FfT|1-x>R0Q(B}v0}=EJfwXG@0BO-?@;hA!@FuBjM<50AC%AaSKmfsy$tat zAr66lkx8XpWP*1RuUGUNo_{K$VXh=-&rb|9YCC&oMMw4A}3Q zfg}BA?O4SOz7*_O!DjG2*Y59xeyj6G6qO-XGQYG~2=Wx5P6YtW?zzurXXOts95aU2 zDJ?4=Ta-Vtu)G3$S|5aUQ(F>q0rKCY={{P1n%cBpp(*g5AVd65k`a;wTTy2+!fGk;RrE(P!Wi>yty3AH zvwEzI&{;h_XD%Z^y{^azZ(0&xl2WeyETb?a&s^a?pY<82~Bx_hCNWh>A2@o*i1|meIx&jI+ zYIvxqRKiO@5u%`gQr)}*B$xmeR4|KxB2pU^6|Ij|siLJ-Ds8bodT+ud_^9}-Z~cG2 z_ufrzE?J2E$^S$7e8Sv$+_^Jn&iS4*vomK5pBMvlKO|0>0Q+_w{gAv?Xxi_ov?20& zhq`g64!>odXVUMGkJ>|d)Z21dV_8v&!h7W${;~!S%DZ@9eIaZE`fKqQH+X61@s6IF zk_I>LT=OZ;<8_xhGuUw|OKpXweGqgqbfz5Tv`f#J;g>uEQj_Yfvw_~Nk^;=h<6Z)K^C39V`KAeGa z1D%28iYp+RD-gtc4};E^tGwe~Zzt+W3YREe-M`EbynE&$@A4f7eKQ8CG_KpG(ANrm z?iE%v(El{%H{DXrc}yN?=nPaUF8{~|m+#yLeN9;I*PNiMIkw#%x7>v#1!EfMJ13M^ zr;Wl`y9#RK7x@hvF$-fgE2~c9e5=_{OJIdI-1Gqxoqt)^7J07G%8Qv&t?B%j4}Q!q z8R`zKM0)Aa^G1hNJw1r@Ak$YNy<+I_9<9?qnO~<3XB_R13x<2Zp3QL=-l@21!|L8X zb(-69-ffo|r@x?dsn(+DQt_F`x=*0W_=vSO_1DSBr zKKGz}Z*!jQLLXhIe@2%&EnPn^d8bl;b)w4J7}|q8t5M$^vtR34SsO6jJNL7U9Rp#Upl~o zPCUc@By9Qg8q7xvd7s^tjh~~QH1Je9&*lY;xn$Fi#`nrrvO(KPm^XQ3aRcR*XVXihSP9fuYq@BL zGhD3j9DiIr&;5ZGeq#>dLmfPys^eL3pgsnid-##Q(&6>x8InL zIU-K9V?v`uYIOW0uB_a(40ux%?}`+~i&EU#6)EoQB!&Pph%2Ac4`jsXC)ehQO|C!y zaz8{%q1-EwdnG*30om1MUwH(we`BKrLb~kJ7gHH9h<$fQC>M(rkEI^{J>@=ySON^q z0G19+QC6UykS5CgdR=-wfqU@HuE2_NB`#2+q$v1b`FS zMcS7<3v6Oo$j@`nrcQN?;k}hl^Zo#)ThE3zm*w(qz>UTQt zU#71ac~Cx8#}bUG{{rwd`sjO1FX!^zud%FaV7=co+2^PdzYFzh{L0T*s+l)K_v&k;>ly9RX?J@4Vg!WI#;6 zaI~d-12k7S0C^a#IDFt=UpOO4=jZHZes)97fENmFfD-baZ$txm8F<%V<>w&Az!l!A zuY9ao0 zcHWu0GETI`nZGxw?b?sVd2GKW&N)!FEzaw`_*>Xs^SpLj_gz@yQ*CkkdecH%-0mmM ze!hCMEpDD0o;PRWteH1V7XGKNrC5C0HZOLV&cEi&ojYry?s0a~#Hn*8-aLEqteLZ? z+HQleUh~lUDnRq1y4J^t5B2ADi07`hkL0osocnWs6xR%*YbL2?SZe;hSYw83%+P19 zO=Owja5G$K`m)_%hTF|>ry1@w!+i71X~+x}bIsxl^Q`KOcq2}k;`b`uM!d$1>t{VX z8D}>6{ydMdrc&=Q%H<{*`9qS7@>7$H^jT(njTuJF^qppWPm-~wL!R$;0^|f?*Dh!u zLMPhvi5a?*jr7!HBYrHO5lu^O$&a;`viCUSH|Qgs5glczOC?yX^XJ2#i$2uc=^R*PwcJB$x$sN#c-@Fry z1~jj^q@X9cLZ2h2z90SF!=d|@3G;ro1L?>Uxl^~vQZ3KXd4n3?%aO?P(pWx24wE1ZK{xKv*uCon)BdzljZ21YzJB|%G zI8;BiF7IwW-|W}R+xIK~T>Y46izi8M^ErAS(^-~$(s#5iZu(Q_&;VK{-ZX8}+&Q!7 zHpj2MdG-`&A}Wtrb0$rlXr|AcJ>5uTzM)dgPh}KmMn+I(?z!L#lq8A4mf`8w4dc;{q%O9^)IyfSdXrq zpN%t{&Zq>+2r{fb@-XiY&A>N?+wKqbS?&*wGw-mB-WA%*c2_8WXLnof3MCf%%)36D_iqM6 zovB}}cZjlW%yXM}_;`HAJ)*o5G}m9J<O@6;ENA7Tt3o!7?qn z_lR}rceniXQ&oSt>q7AxrG4J8L z(9)0e{{x*I{$ZFZw5C%@dzc+@s3Xo_q&Nb*_c`=6m;!yR zi2IxG_t&d>89%@CrY4a$G)H9EK8G)|k9YZeW0te5FX*Uc2EWAe!Xf9qj^xiRdVD0h z{=oMOEcuCIoueE1+im&J*z)rnt@DRFTIbJ-Yn{I+u66z!&er*gC2sw9G}brl`)3B) z;%D08W5u9PbpQ55TmB8U_-$g)E6ps={WH|1g=Tz}t^9gh{BbepNgA{Ki?;mTwmA23 zlfF~U@^(M(Bq8ZLL8AL#TKuieoi=5nzNSGl)w=32m(J6t*%mxn=ULsKS*9U}y;8HK zoW3u{j2?2Urup2)7xc-HnH_C%^rE3d=SQvTi56$Mo2D5XY=DFMS_98halZ(0f%`=Y z#PB;6V!W&A_pF;C7fjdpjHj7?&rsfvuzPpI4x+y-jvdPlF~gu4ZeqUQytlH4$oFQL zYxQT=&G2W&^7G7aff?%l%X*l;)q9w0J{c%)?pIl3hR>M!dz{95d(H42=dtfkYe+kM zq|?6`&p#Y#t)#G>KGI{Av)lG8*UeHc)>_Gu81>oLN?5KvHa7PS<(vMrI!kODo9BwX z?bx54DwQrMtB_Ww#dM zp4mLu0YgeuC(^uu3?(JEME^a7zstv?tPAPUGH$a>0z>^Cy<#n~V7HdW{h%nDFe9uw zXO*kTcy<=_R+1P_Emu7S#oBQ}zKi-sqRgmLwFC2lnMw-7;ialKSgiGklcbLMyEPxu zBHSN79%Uz#shwCRzn5}6!*OM*KfhS(j5eeq?%jzpyQ z{gl4@xPLc8xm`K9AM|MGi$15I9Sae6Fuo?NE(jgmpQ6?_C8Dmls>+6W57lWYsGs$D zke-ZZ#`~eJepUEGA6OsD-WoRQNkU&eMMi&EmlOR+0?j<2VItC}@5t7l-S|tv*m@EA zUaix73+uFaj3evMF6Hl+84BBjacM{ZT|KC`PpQ#H2hu#C$D-&n`jL(PWbZQay)PK; zPey%7_|xA-z8C%IgEDcusv1Z~mLnab-*KaCBFm%h>vA&`FKCyDcNXj zSE|(YNZ+`ccVSj*A>?}zmb|i9^&z~_AxZwoaxFeTTggP2o?oq{)MqO-uheNZO~!a6 zqwef+VfE&G^mRg6Lp<8k-;6sLM?V)upV8jz@y0kXU++KCb(-Q&e|J3n&}S+A&2b?A zupD_M`i&Hn*GS% z%Q41<^d?3nA@27r*YPSduv}wZX;Q4M2Zk2SFHzSa%troszhnZLoO7FKIan`Nez z;(5abVRZ)L<%3GqUgv`DmzJti5zl@B^`edPdX?J0BXG!pz8?&!@h2DQd$^|NFV`Ld z&y-AGtn-g25LPQ5T%ta>0BQ3p)O6tfMzpN}Ui{vWlB+I7Si&4#o9RZi)HxE1n2>OLA73{5@lF7^H`Q?**RggH`<+qHdi6P z|7hUt#4_N0wblcD$U>eA?N2$UL`^tYrS?JGb7_^@kNYj>RH)XAAhc`!AdFKz#vjjRXOyT}cuvRj#>>H1cxKy+t}IgncwTn`@7Du= zjmB7m&q7^`^xyiuIVjh$SnG8T`D(2uIVEbKaIN+RXyQkj1Mzq8+yl>UJnzMGA3Vq7 zc@Lf+7>qXNm8kPDmJ$4Ic?FoWvq~+*IPSvVBTZq|hjx@6Sf=KppNlZ2^!L6K;f4`m zwR~i$It1;j!Pu{aY$&Mn0N%?(+0_`EHK5l5*bF=Hw-4`pgl}+PDbIrq!Fc(fM;o^; zRli3aJ@A}}=WMj^YsC8^&i;6jwheVBD*73*#(I2fJL-9+rday~-}?e(heyx1DGIPi z(QN_hdMEeo|q1B1)mrRoE)kt(K_s~fjNb{|{{87fA=dH8lFXto)6moO)!mRb0`@S!}T zElZv(Q6GS;R^WZ|C1rUr-W>_LP!`wWJ4Ib8)hc}F5xny-Y>hH^NG1P{E1*0AN2Zsk zTYv}axR6{4ft01ErM;PU|;9HweZyCyLx+1JD9bcxdgRGT9&K{T!8JkhAt_NR- zz}I0nXy6H{8#pc)yPfO@@`{jGPh7=w8J@qs;6-&K>MBKD{QgSNvtmK1x&z;dpv~N) zwF_+?kMWy;@tgq~&H|l7A%pjyL0#KH&uxV*ytuSx*I9R5dtW%fvmXa;ed5u%n@62C z_~6UW6<_-P#<4$*OP}-Rr{DI`eBDo-@!k(F?0@XV%FF9%+l#YUiHEK0f}QOsFl_w9 ze8cW_!S3}04g1^+`vI!d6aZNFB!z zc6bMbDL&2-vK8JN>VVxX&{Gc7;X;_=>j4`Np>E$T*D5n%N3yK{P6gAa4-#q;6?b8k zZmYb1au2jWMe&4+Fkk4ceEfR0;wxMU`C6$Zz_xLq9ki|IL+{dtb3o_zSrk^Oo1Lb; zl(cm@?6~E+&6l`txo)R=YM1MFR)5%xZrBC4T4%gZJHb=5 z9JYw@uE(@@X=A6MJ#240+B0(=>P4Gqmssm%+t{x3&`OPM)9a454RzD*NkqF+%r>&# ztbK+J$nVkqqb&zNka-k58~5PdnkPN z&On(4(jWi_vK3dX1Ix;N)x-pG(w9+q^^h4hl z;GL}P#@Ld6QNAPH*zYLa*iN=Tu4-vRwC|)H$FTo1=qu?*+M!>pgEZ0U7c%I_zHtu1 zdiYKf>!f{XnJdxW&x35}_Hkh^WeD2d2Q=t|`sbt0J_`(cm-RZgEYs&WI?Yfg$Bg`^ zj}>Ufy7`SP&^TC|Dv zUI%(|FK-IyY2d+1O@8{RTOu(5?dX-wb1W}uy|Ti383&%Tvbk3tVHWQ;1}@;acQ)^+ zL5Q>-oEzuAq`5EyDH?Y%HXyB@N3=DO9L#hX9#8l#_ zo0y8Yn|R8Qc*+oX%Ko{5(J7d-BrtS^)MSRlREES<#lTdKE%DT)|5k{3FGJFdA@UZb z*6C**bQ~>oD@oxhH3|7m9sEi>aMcqE={Wj6a5NN5eV-UA?ZQxFE^J_Et%;$vKLbOJ z_YDj+=5uxo^(m?C#!zo4)qP_zMkXpNz@ZD43^Qx<5ZV<_m@S_h>b9z#JleZ9z}8{647hSs(NLqTuO=XDG< z)=gqzXrM5)8AG$t_GSzXTQC%Db*_nGDC$D}Q4D4M?yW8L>lj*taDyE~(GI+;uZ^KT zD~9UhZMM;4)-x0I%R}3SVEl5?u3$~BM(l9O7?s!&7sXGEr4>KjCVuua@v~nR_e%H= z!B2m>iJxHuKl^Pk@l#sx)6a4)6F%O6azmOw1J;eSP1jIxLC5Yv*n|YK9a{C zdrbQD=_8RyL^Ms4ef#!FSy`D}am5vK`st_32OoSOUawakdE^l}_uO+OBO^l&9y}=5 zUw^%Z<$k}I~ErCEl-g)O8nK*Hx zTyxDe^2a~^QIeCBrCYad^1uTRh^ne`#u;bGYp=Z~fBW0tWWa?mOAF zYnRm5*UPD=o+|U^&6DGgKVD8b_ipLZrHi;+F8Rkl z{voNUsWN{2cscXTGiB1GN%G!%@5zZLo+uqVc9it=bQv&UfOP24LDsBUBPX12f{YzI zR?^baAz z95`@5Hf`D@1qB82{rBI?%$YMKKR;hKZ{93#yzz#F!(sW&Z+;`|)~%Dpix+&H=V>Z|3XlTMPm?z&49Md1Y>GIi=y`RiZ*Dn6f2cI+@*6^tA?Qr53uFJFE2 zmE3&u&GPo!Z_AP;OT_7P%KrWP<)8ojrz9pO$~otpBk#WZuB=?SQYtDcWWt0AQc_YP zadC0-=Rf~hF1+wUsi~=vfddE1+_`gQ`t<4Ypa1+vKL7l4S-Wlx#*&cq*JF( z^4xRJ$&@Kmq<{bZ^8Wkp%ff{V<-rFZl&Y#KsjI7#*|TTMGtWFD%a$#ZzJ2@3mtTG< z-+c3pJn_U6a@uL9$qzsLAltTWlPj;hQWh*&AgfoemV|@^x$e5_q^YS%&OiTrx$(vu z<>{xNmOXp+NUvVK60-@grfxiO6{s9F25eRt(1pImk_jwTPjS%X8L!d8(Fn5O_ zFNYA9Lx6t|;avp5JsCoKHU#z^2LCczZU~YKLX-*t zIupY49t5W&gk}H)W(|a8ECl5<2+5BSkk=s`FF`PFg-~QdAPztn3LpqGAq1Nt0AVoy zIxzlZF#R|%{7GPT7+=a%Fu4y5PS#!z#=aR$y#x%sAIzKxMt&DeTmc3y0rUPDj9UYy zoePHj514f=81;ED=@>BRUNGlQFy_l($|5l2957=iFya(2;rn2~2f=)GV7zC*bbY~a z-+#xv0l3ElZ2bV*tpM4(0kES0t}g;u zZwIKx15lp?nC1XTy8uN00RSBj@SFtTJQ1Lo4#4aHusi`knFf&T34k09aC{WN_&7kZ z7J%ph7;XX(eh(1L2LQeS@cRvbZ!th`6#(yQfZbgHx}N}Ye+9tp0Jx0=uzdwkdmDh} z1epC3Kz0s5Y$X6}0>CQ{z;z)&Yajq?I>7340M#7;sRjVly#S})0H(nJrLzD?zXcdw z1R#12Ak-fKv=HD^1>l(t&{+n+`4V9B1c2rTfXtNunAHH6>i{h015};{p!5Qm3O%oCcS5&=(N~(6Me8L~qBGo>jp5_2m7U7YwO<1PNrYfYWr3$CYBaBi# z5oQV7gkGvZnln@@glj?|p_kA~bwQO+RZKI2rVZ6TRTtGdRWVHls!gg;nhbm>k(KI_~`p~SSDx>PA%BRYq z%BLwubwss66-kv&RY#M7YL9A%CJS2h>J?PTS7|sNA=L)b- z804;n$b5yNeH>8nD#Yu4biWGh^;fV%4Vv^H46Pf@dl2x@2co$QWP21%zW_t>9@uRd zhG7Ck{vT-OD-ip$L6$Gk#5(~+(*X%518&kVq_ZI|zXwa62GTY_)XD%I$3d*dVhBC~ zw5$R9PXpV101;acxOxPTb05U;BakQr7C04RaXr|16WI6^h}a;A(gR?jKY~={fRT$K z&I2J%KY;D71sskCyZjb#btXhT5zulM#K;HoF93Ax1+)zXTdoBbkb%Yq)0jTH+(P#q8TnTtm0XhG~(EbV{ zx)b7dJjBWcc$o(l8VBeIK>Th89Bu>jWI_Bd17ytr3*`e|p971%0ny(9md*kE{|W5V z53u<=h-nAF_G@6hLWuuQfcOCrfti3Y4`6LFfmA-woJ*3K0GnRK;z8 zt#1GYe*xq^50&#TsEr!|A$q5huZubAgu==dpKZt zG~nlDz|e01>t{erO8`0VK>RmD-7SI$&xg9X9BMfpFmWOvZUbPnGt^x!RKZD5r|D3W zUjv%P0Dk>|iByR8`%oRv0y^FWr0fHvuY+n=ppKUSQa*)x{yWs+t$^J>06H3>%7TEr zOOM8xw@t|{@#a%!KE~qp^6wb`j;-^@ozOaeyd`e^cjPm5KX=dcp?jpG zKC9UW?iucFJ`agE;=|1LbJD>0l?byC519G4nC*||n`h#NncE8|jvadG_?Fmt*H2qG zecEhG!fbu_{Loo*rd&U9%$%ENPo3L#W=rbuX@_~mnkn@>FPvzM2g6K+-8&leXM8Hc z?!4ghzuO~wRDH6|usDN^wKsNCmT~UwNPA<~SRG4B?7gwE%0>6a##zhx4sBnawC3P7 zy`|N^Xx>O^y*HNS+Os!yS8v0=bD|vn{8@UIamL#!IKF4@~EpGRfe~m3Z#}>Ea5{7r;Gy`9n z4-ZV9k~1JLM(R`pSYmi~Xs)*TowWYuuOX zCkB~k?tE`l^X%w>==tVjt)8QSZ$V3(`6F%lmA3pETmBoCxb@#JiH}j=1Seba$)~5= z;uqQC6U=v`@7sOzEHV2N%@5o155doCC$|Nbty6z4Sn~B4Rpzu=I#vT((kTn{H4{YG zHO0W+ZXJxX`}C<$X{J}1>H2xEPR3X=eUF)bEaCEAOMWa|&UG4Nc_du++C(E3F2^co z$K_b%g3=n7S*|^}%yV@-Q^&S=B{nYeTx5Ih^jPFP94^nhslB*-#-M14Bja+Q4V&+O z1qZGFjs+Y$1c&C`)D{l4PHiU+=~$6yVnvdP6`b22L$coy1b&*>}*NF6MTe zv;BA!?y0c;`z7x=i>~o>wd6CcpDj)qZ4s_1lVi_{4iQ0poV$Artmk~`Sdu-?9}2gjcj@~BE$Kv`KfDtw7kv&9wg&S&#a_=xoX7`1dyRQqBCq zwtTJ`%(CQ9v*owK2Hx^_{j`OLSsy+GIS#vEd=jmwJ_IWXKe@WXuD8GztKc~$#hB^P$ z8Uxo~Y1^~^wC(9T?O3zzgO>bQG9PKDZ9mWUy;w3ItDIftW0ea^Ynf-c_Q?DOX`gLB zwVk&8-dOepmxKo89u=Rk-?y>es^6m$3ah(%)M?IeSlyXp+}okwy%$*CG`tWpA6TLL zH=&Re#fC;1gx^e&K8%UvMQ zzunT!vz!uGsozh*y90P%gDXHkh1DA4KJ!iuuYaw6=Y`Y1L+hE&dvkW6%v!ByT3GFw z$$MUQXc^gI)g3@OpR>YhMlSsW?$F{9PY5vF)8vS}z0VOSYlx5RXo{o12LA<$M{ZKx z`OXGMEnJcZ6oyUHKx(WzEn40+&{3^W37YlvtHKqtfij+wtq+Jn@hjP zrKNv+jd|LK5p(btIF1fBzvE~-_m{-x0}cb5t-g&YcYSQ}JX`z{TRe)@=Z+m`NLUL@ zGpQM!5Al`WCR`utL7j7d;&qk72=#m7k_;O0cQelA`McRZGkz@L_yNms#lmr}LlR?; zgyZQ}(3IFX9;=)k$77WXN^2Zvx%S{V@8^r!ZimBh-osbWMtyc1-x6zm*1AB`9vsgZ z-0t7xJ#FAP?Kb-1j03)G_t)w8ye(j2Z=^=|ed#kXJ<#F#{X<@Vem`;DYvMZZh@xEw zJZd=6SnK(aJ{EZ2jT4xX;RV*D8F(YIL&uvqq`QezNb8Aw;?!__n;4X5e%pnz@hBU` zey;`l(T#3V8(*-y&!(ZH=;F>-%N+=|{|Z@oiz?4uJm_(saHimPN$CGTzO~J19xNu3kfqxCda589$aVtv$avRmQas)BYEJ zb5f)on1+|GZ=*gtrfrWkA0>nR3`{#cXFzUyF>T)QdT@ws>hjlWu7Vm(g0-68Ke@pb zELZmB)@k#GhE*lAPWRRO&NyI=vZ874jyi2G(lQsoMvJUyiu12%iVv)4${ZI~=YOU)cbKVxfje*y8M?`v|MyP}Eyq@)Ho862Uwf@O+}XPiT|nit=t?=b)W^&4AWuj`s-3BKL^p%~^6V?^TYXW@d41C#x+*E<1*tfm%g;2&sVxvQzI7e^4f|L>8CA0Mae3&Ic-C!B< z9k_0`-UZ||d9yCN8_=2zcq+}PoEG@*cHyFYETajOu?3X5#ZKTm5bFc@3R<0S0X&-r z_RyPT%+VwN^1$YLjTYnu-+Ev;|~ z*m`7nYYy#gjy`UpF1s1jUo%>F_@-J0ydb@1@U8iHY^#g&v7Stz?aH8CnF2rZXnVaD zkQ)gR*6wi-h6CbC%#<$<;e`F_z>fggF`EWH`B%Jf3-k+icsz_pko?6mwhiUi*Blq19;*jq!pUeY%7r? zmKWuSOVn1*E*RbS1PTZHWv_>wDU>BraAW!r(gI9b^hkT>#{ z$5-c%htLkzcb0ZQuPuyK@U2Act(RaMgWp;vu>jY^cLerY@h=g^MB5Ii z2F6IKHp5VeHu)Xa8KkG-pC=g?83(+dNybAdjE6&@KCjL^o-K>^&|@1>WrXKoeNO~l z?c<2du@2&*i9HjPwQwv#9?hXnymTl(Qf9PWfPEohCFu)jE3sWT(}xS50)pH&QX8W z{do4|Eu^irfHvB~kMM+T4cjQTH5}_FLtn%3h#do9w4##d!J!>Na`#y|s6F}ZLPc;J`n^7LwF&gA9>_^C}RMUryM>w9* zxk-t06U-^r(5IRLy{3Ztn-ERLSg!tXoW=Sx3G5B?wxZodXCq4qT{&OtuweB2b~7`682?hJJOf2-C}<>Mg1m@ z^f6Ai!G6Zt8X8Y@8#A`ed9m#d`)M@f6#;$C1p0rRp#O(OllhvRtJm}A>M#2T+fQAg z?i0QA*r##c48*Y*`hN(}Z5~J3A~&8iPoQJVp4F@lGAWRZ0oX^(pdDeoo5K8Kf#bS% z7CZN_eFVP4c8qmCk@k5UN3g$PIoY<1b*t?O|591opr=2VJypuS96MQiMH#y6!_svI zQM8WrvAGZQNhkDW!7R{g1J@X&D)a%7kuTLQWA{Go=^EJ&{atI8v1{uN0mmLY`yP8N z*SY0*us#}wp`VaL+t=%|MTv8BY+)Up))wj_b9PQz(j1UYxKSGU>zGH<4fn@iI*xgGk#4*4=#!^2~WT+3a5A8a7{f^cut5pT_7V(BE*qZ<2xj zgWaLOLHzm@y_}hxdxG^HJU7QwH#dh-&C#1cSV z+DZe;8fDJ;Smw(G%VMiqV~}Ue$w9xcVs%UCLu(8&wUUcvI~egL7kOgYArcX4~`jN@f6U zCvKlS5oD6F(%`b-*yGOYjy*=XeoVSdePFLKVAD3+VC$7 z{#n95Skga5yIAPYx2&w}DemWp)IFiC_RMMQ!uyToQjy;x4GmR2bL`9U-E!<%d^dwV z$kz;g4{1o+LEJ+}pVLf#gGl9;!=MdyXx}0=fy?8a(Jw6h?E=45{S9Kog>qmyh5f-^ z4)O*ajgzhYB?t?3*=Xw<#|zZYVpK05qg`SglbNiu1`%`CAXc6=NHk}m4$eXyoHa=G zXQ3X>8knpzkPh_)^~2Va-ngEYKso*{Ps|VV<>&c?mi;0P&(QF=P$p2`{Se2=L;|$m zqOTv7E_(~z8{=de0`OL<8#;;qoqaN?tOCY#8<}ys#@smF&)hg2{T^0E{XeyW=H1ALt=~{RHqxN}GFE7)8s6`Z&Ug0{n#n*l3^b?~i^FWk5h&(8!2TwGq_L z2x@NxwKd-4Bj*~tH$q?G^p&mOy44s)4q-hQ2oy4McO2)!46BoRMm!I5g6CyU z(!I?IzSf-JDPSP6RY1`d)bC$45~2@Pj8|F_Kdjg;ke)q+^=dJUBZdDr?H@W>Risz7 zf4HPNd4upz`-g!%S`1V5{$W*k!u`Xl@HC9^{vqOh&HiDA6}j*BPj}Xfl*T@Rs@gxa zuTjUy?R z*F*hFs$OZpa}7M7_13L1pl|g$s>=pz=CYx&oSezl1{n1VJio24zHG2TnCcmfW|g59 z;MRq>dXUx$^(Yv>nM_?So*_fGd8$&U=SbBldQsM`JcVk2L8M8|0lh_!=gFo_Y__RJ z%AQBF{^JzwX!OByU(6+56^|J zYJOjJIUCQOC93lVMtPoh{(PREKc5F3`8@Az{XEQ(=V6XKZ$STiPTaYZW^Vbpk1bD^ z!J|-ns>`tc(`iNGotJ;*J^cTHbLxHn!Z~%+zi>{iXULh(MtVNIm7YiMr03I_{|nEj z_kG3rw3p=T-gOoTnoLz=aaDN2vA8Nc4P!hO7kuSej?Gt|PZx;4?tD7B%Jb>iv7wW` z-e+mscHCjkKaa&5Td~0x{*>Gw_04gO|(yJGcxSeJZ+ zwVk_O!t{gnaAMwHWn~w;j77LUc1q6H0^CiB_id81z2Ubx&}0I2CxM?E ziGfK53mE$(`;xP9?H`u4#t@+gT&JY$K!6kQJ9TolKhR~Au@ z<6!jCd_RDvCay7r;20RVG~Y&_4(rQwgD7!nemB60hVSDvFgNLz<|{Bi+oWua1%fPYGb z^GmwH1%8WR>~+dSxiYZE(B(?)mgVEy4pG|``C@ewJ)ZT8?N+hBo$xn=_B6z`8v1vL z&wTmV0p=M=^;P+}Hg=HevMpR!KHBc~FkR<_L5YdYqT-^yoXs4(3)z^iKl$M8oZ>$d~)R_c-qa9e_MFsPi4ba2&nG9g@!WH?^ z@LL6Qrnnz`2j8WAvYSDOe!EmK?}^(&9#BsFPSFqYL3$au&(K7Vzir{S;`lN) zemuMHx-1{}LeVD*hGwu1XAmF4SX_&$=CE_93}_)g;)ToeEucsnn3x{#)1N5 zB?XxKUJl<`LOoeS8sggP2~AEmmafb4+3$`o&&PQ5H-K??j~9Ql{IGFA7eYqkq*p@w z!gVYIGG(A1ETC;UX5xL{CVHIXam|ae#`=}?Tak}zel}0wB@TF3^dog4hC0GFB!T+2 z!2M*z*@JbzEMG#)#TDs@M9ofDb$Ao z+YI7B9yVGCv2B6OWN42c{)w`5*=A5jxbLb&U&Qzx0^75{F58LPIJbp(Mzky+*XnX? z3s6@wpvT$@hJPaX9@`s(G6y+eC9SRTgT9oo48$MPs(jQ7v0Nx8 zOTa^4TFjnRtdB7!6LqzD2J8#?oi+T9f&L_e_G$rjrGj+a=dgf(3fvopa@%O3%;@(G zXe*$m(eI9BrY>6z{a6ZRu*SN?_6;HS11aE30auB4G$9We@GpgW#`ezuoz~D_TZ1g* zAUo_^(}7QW=p&+RXsaL>k!V$ZOB=l7sM2FwLSHPR>ZDEJJwpc4mGCXLS&UPNi0ox} zFA{azEa=~ek9QJLelSiwjK>pZnoK{2qfIKDm1_j0r zYZyfRRZl;EBj&{XK5|FGjCv{|Uv*3gF7z6vtNc47f| zXk*)A&ocHm+rCkrz=ssZMU`s4!5Z5w#EGFF*uZZ`s5=F;2^rL{0)FH7=x@jZ(lH-v zh?8YHvA(X*Hqc=NFti@Z88rm1MwvD^tN(46>M{#<3UEVZO%s1+0vspUIYN(PIMflO;~X><$0zi81#L-uoYV*Q6YQJT z8R@J)EQHPApLGVAw;86n9{ZIX>PHNGG7DiWfIfsis89wLINm^8K)*S5z=NF)X8|A}2NWee4tPkL~YS1a?->H^}Imj(!kve39U}9LUcS+DH>` zUA85prlG0eS^9A9RjAhUB*M^EK*)Fy^J?G*U})lG`Tci&V)uN~VAA8(7EUT|q~+@t(J| zt?@e1{ZYj_(UI;i78*N}X8-VgL^XBooR4J>emipa*_Ne=Asr69n=`R~jpL7*+V+;e z6gbH;uWYMcz0FdC`O5slxNEZ(+b(u%Q9UVlLYJ4V_I!Nspwpg*s~$R?RG2r9u6tBt zrc1TzV*lHfPS^GoP4e0I>G@jeck63egte{aHSFPU^W2`V8P&F+@NE9l1>Nh{eH2rz zePVn+H`68WFHiFi9(Qy{h@ED;`AyN@3)k|+KQ$OUU_)}-$%PNL*7&ad_D==wQFU(i z_%Ob4Mh%yntCsw}E4=5yk#~kK8+f%OdGzuRqkJq5*3$JH-XKc<$@}1`?@vU=6b$~D zFge8IyICt2-sr?OY)xAFE^WRJKXNs#+>V~y17i=SJ!5OuUqH9 znH};U)>-qo!?R{n-t0bq@b#gNmq(v6+oPOwe9Ex~+3)IIHj$r+9`DzFM5|%F9^N#$ zdM3o_OxEGO)vkWyykJ4JTm4%Ttw#DbJ$C(|{n`V^YlS#$>fvL!{Vp)6)3=*0FHP7~ zYxT^u<(`iN&Q3Y0IAnTn$Bz?C8vf9D#+25nZEvr0u#YJ{`tIPzfRy6nrQ??FdA;mo zk@+X@nx8Yj8K=#8TH^;_hvHhrQL#-nIG)?S3-Ibz^Vc%5!hKJw8;pXq;&DgJ;RLYdw15wsl|Np-=HsGG19p za#d?j+_CREx^woy?weowsb`PxdZl>3TS&Wgmp0bADD|wDnLaJ=m7#v`)ccR}4@KX- z=w|-Yw9Au+7Yu$gtoyC`eXpn-Z*Kg3qx#yU56@;f-@kmL{eZ4BC;syBWv7JMCZ`|o zO*E7q4D*@3_uT0XYZI$YeYT+U65kdzEtei}x1IK)&5@cN8*jK^@}TA7N1f&*+n+l5 ztYqhbH?;;oNN|1`6*Rgy@1oc75lQ_sc38QN2(_QPZ{v)+t@AWf3Q~37KgoVFXUNBl z8!w07FnzFOkk`GR4YyrHqL{&lA6mxNY#TD%clyF5huU=0CfA4x>3zLP{L*C;^$R8+ z|I5y9_~c~=XJ6~vrD^j=ihhUIO)X7&9$bA_=+G%oBV(9)i`1Ix6UW_Uf*vS(OL{cg zmHB9RV93emYqvgr)_Csq{NHsQ^L4*&4jE|HZlSiMxK*io#wzP1*2s`r6m?p4QdqXO4c|vVGaHR-+aVI&&pz zMtX}H=YQC-ULN?c(OpMX{^4fg+Ra1yGAG~LB+q-Lt$y)mC;$9*KNm0U*yo|??4REp z4$wVGa=omu(`Fxe<&%?iGHlPB{Jc+hGAH@Ff11}&r0vz$)%~bTgBRTvc3-_^(a}-M z<)yn`9J+FJR*kwBpYCT)1UAh(5*k0bk$uRv`(DYTcI`bmE3l^H)<<_NifzYrI67yd z^7kh{?_IE6ENj>y%c8~Wv9*)SYCNkR?v-0RzVD5)E>nE!k9H5}q<^zIWoJ^a-$%K% zKj6A}ewN>zt7#EkE;e^O+_Tdmhn_93kJ;R?(I1oFAM#h2zUt&La%KMh+IOzIrPWww z9+|Z%d-U44n)T!#W8QYYw#T#6kiUjJs(WS7+mlDtha$7=>)V+&Pdni`qwR?E)lYwa z_(_k$F4pfKpXoiM;82glKD*{=7xV~lZ=TTA#{Sj1Lk*Hkt={Y&=i~G=WKnMIdA zo%H>Ic|L#hj)bW5t5zM@WD*%U^XF~d3hSlKN;&#ODz_YT^wq$w9S!;46|dhr^S6g@ zyGcF=wI3N*+tFLnElx4+Zp7*BlXhk2brYW}>Nl+9-HLS^o7QgL^3IFgHZBcT{pzbo z7~Jl+DSw{%cH8EOk^b{SFP>HRzd28Ht>uAlvjP*+O@1x)>-6KabLvuY%`NBJ%^&yc z(fpgwTCJIP)a1jYN%8@W^Cw=3pSLmnW%VKFQuI*?W79X+2_B|<>;JwzbN9Qw&)>dkVsiSAqFD@{&4pYhGhmgABq zq(1X;xRUNQ>el&=u0HvW7tDiBT-ny^n@7Lq9(tS}us>w?Pm3PkI@`rewsq|^QOn}! z?sYbvtljXzU$^zOW*^+6z2&>EW%8k6BS%gWjagkktM#_IUbQs&wQoBOlW7{Sy1sbT zu~xaidd`+Lyg$G>viZtCqRJwA&FZzz*7SMKqib$42Y;IWux`CU-^%7k?W$!PF>;jm z3BU0zTFRexZvJd>QRe+RKNa5`@?pt_wcdlS^dI#(Wkr(gRO^!iI!|aF{)b3@TzA8= zcK-U!kBakVUjH_HMURZQ2bL|p2Th3gO=#cwJKrYl)_-T)?wcjquTP2=H|^6#yzcDu z=_|WQm->!4^5K1V%Uy3LlrA3CJnHvG-yHR9c*c3hFE1&(#Gw)y7dVfC~uolCJv?N2}S3qF}~W6hAq?6{NteQwxCbT zONUN;x@N%OR#%3s8ni3ery!wz>mJkJC5Bp0N-8NGcz0omdV{9Jf)&@U+vM&)tbO0z zu1NFEPe-y&dY>55>*tOO`bFQ^lbZ7TbE~^|(`TQZw4u15zJ5ucsPP#;&h4{w&4TxD zvLkjnjrRJ{d+Ln#Ir;K?OZT~li4OZeFWfm`V7oxGb;q9Ptbep-a8b&yvz`YZ+`PYD z=c6MYwtKsv#AVN%IYGT%wLj9SZr0s1Ca7sr<)oxlMd7-M%2+N{Wp0)&zMDI?;PQ>5aoskx>g44y!&`Inm%Aq|4~^SWx3m1u z7l+nmUb)w6fMT?dX!SS#uD>1$853;jbotL|htguBo{l_gk$-PhafkE=-v0B`Zx-bY zNIE!b#d`U{wn^uwt0GSpy!`WW#=D=!HBS6- zqvoX9>gkV6w=UUyX~fjwoA)+o+(s9^%5QE`SXA?$fA5l-HDF-JIiKe~KIU+E?#O+| zS~tGp6}8i0_Tp{ZLhl_JkpCenKjG8pC&3-ISsZWa{QZ;q>kF#ICHDF_O@40WvAlGh zsQUW&NjGL>H63=>cV(l3IhP%mK3~`V(=q3SgX27<0V`%XH12xt)cK=>j-UY*wzsMkv2~f`;&-2`o!()3`(v9^c4og7J(`nn zG$)|F?*fnWHG9OTUzN3-dFxj%*Co>XKP)giH!J`7wAl8mPRbZpw|;fZ#%w-UV);i5 zGbee1%JJyiZQIt2Zg(tx`0$&|rvZD1VquwT$l}ulH zr0m0is3)UNtk{_sl~owKr^UX&!*^FWFRk&@`VSp%_i44`y@S=Wk0l$MEL@iJjbesF zuiajY4>Q&sdq*V|FvZp`?wY@TJmEKa^$2C+gyqNL#jAG{nxlir$?hKf2cmG{pPY6V-CI^a-)IAng<>;YPYTP{?!NhrRuNq znfkKp%%NeicN!mPaWN^_rHlQWos&Nu4WE&};qhhF*`nHBy-p<_e|S`LO?c`_HzA(#1b)bCsG8tamYV!J+)p zelZSpHZ8qfXmZ?q#ir%kf9-sF%;!mcgWr}EYZ7;d4O**sK5yy1>S@ECWoJG%n>}c} z{`Qv7&3#%KMg=oj*Tk~#cb*#mdg9_1on(sQ4l@Ufj|Me9uXFn&aYM~V*N6WSm$lL^ ztk7V0v!L6D0eheCI(L4?R>!23OTS_MYUIE2hRbJ*G9SfLQ+ty=zHPQo2>;brw)AX9 z+?K7GW$OpNJh5T5p;@c_n}glPJZ{8V zY{|PhiBCk;N(c7rf4c$zxIlH#jUM3z1545 zyL;|kR`xnCN<0>J@KZ_CYWdII7N?%rB;DU>ba3tcueG_OdKMq=c;WkdvdEeXQ(k@U zG&$|V&A0h~oS*jL?4yst>W6b)@4xHXym0BLyry+S1|2y4((lcIA?a5|ev7&}ekS1j zywcUPrbPw$)=aNHv2}LK`g>#cWo)l=dsDYV^=nU4X6#zMp=FQV*N5h<8|rz(v*z96 zKWF=$x##m&UP+YKxH?a3PBlBbsX=gZY=B2;%Ah*S$DFyGCO#S+bLH~hlI!0^P6}MC z+LZW%=e?Z3qoTGaw)wZ+^sLy;r*TXze_N;eF>gxBuGjO?ILvQz+o#>91EV$H*K8t7 zIQ*l3_x1+}Z$A~BXKR%z3Jwv^7?DghhowiwZoY~@YZk^G~57?{5 zZhO7d;qo8H8kU7Qc?^s{YO?Ots!;u34SN0h!p2nOeDH$$w_2YY7o?aCPU|(&GJfFn zk$1P2O&HO`bHEz!E5u#eZ{{^i z@%ysLj?;tpTRmRiA;3)a>e(dsHL_284lEA|ZWL1TsCPsAfEm_~mk)06)iCboRgF(7 z)D|~B`F7fr^(WJ+L9Hp)_VudweKES<1uO52vhkk->b439k=6VBxu)WRyuRE{R7b{$ z1|)tS@omAo(p5V}&GKgiyf}D0$nBBI+#lBd=J0fJ%%if@-$iNTUrYP^@lDsJQ5%@l zo6{dZo9?Ic9_R7;kCKQ-J9;&JeOC9=@%imLE}q_98>E_eBim(vb%~`m=C_|M-?i52 zcRZY!UYOKy`QRR-eX|+A_U9M0RfVtot$}=#^kkXLrbFLc|8HiT8Fg;+woAjmdm2@E zcVX0`b!{5=SU0W5!p`51JGRO9lzVk?%2M}dK6Y0Rrl`aUoBiDzE`De8ct~x>ZzH{$ zM$f%H?|9zCxT$)RUmVO^m5Aq=O-lOhX)r%d4qgJSb<`}&q08I->> zy#0d?nRN=AH5qJ@d2aX0g(uGqvP}vdTQ|shMEu2>k>V`VqZ0?VJ5gBLeNL=+ z{10y@-fY`8v+2TnQI|UQm}ha;Ys}+6Ck-U;9#)Ujr*{WiGP>7d(hc;ohsUDK(yPFAl^1pRfl zf9SimC$5KE)pXb}qxPM1#WvE-%jz*Zn%1gj=ifHCFvfhur_YCa{VAVhGwVpT^exfV z+bTkS`1ZE)+e`azT)35X@+U>MNd2~KZcN1boTuX>OI9^%bM4}lI;jtjk3DL-^UivY zi-W9wn0GR*v*<#b(|azrJ-W}tG2u?m#vg06Gfa!xKmF4X=1Ie=C(3r&pSD_AdU|{H z(6G3&DVA9d>nh$IV5C%);TdpKgy)`UlcavvV`FS47R8$Yj~ z$?3f>Pi{JRegBI)6OT4AH217^;?c|SHXkNG-u?5b=+fcO^d%EkT>3-NBd@c|%)5=+ zq|B~6I+nTc&ST-Wwav#iUwWcVuN_B6Eeeku{mb14H-2a%oBqf)+Tq@dek1BXwjb~& zBKPAM>809|Z|yv0T^)M=K%?fNliWZ3acNTLWY;&BTQ2TyLaC%?*>s4*0d( zXH~~eFO~y0R^U8v<8G$|fJLwT zetO^{&4$4>#3}WB%4X;f<@ZSLZ)rXF`(tlo+ovV1n_uAbar35MlFuJ#khL}B#Ja(4 zea%J(bU#vj{B4=$WBP$c$FGn5{s+Sp_ha8=uIzK>-Y<`{(t}s7Ng8N-Zu$DiR-)dH zz0*#Qk2-m&<_Y(MuiQ-|y3H|533%|_N?S0a&GZ>rw~x)xilwVQ47+;0=a$xW`_=yT zSoH41nkR>}R<8&+b#iKqc%PMd^2-@kyFM+O|5FR6?|Q9z^8LZIyINgov(${iI~(@e z7d5HY{r<^6Ogr*&+R&KIcQjjfNj+bCO5(nIFFW~dLB05ez9+&5EpmPGvPtV#l5NG+ zng#C9n;?!0{j}ugnUfY>yf-0vLefNW`oi7s*1q-fc{DC)fo1BNoRn#8QqLI_&dK|K zTjsTA-{KwJ)Gr-(`>yz5_qvJI2HjtzYS_1N+V)PV%kDRycK^npW-q2@%+p+N^h@s4 zto-%+Mj1u~Jvq5X^{!j)wIzYBKd!!P*<*r#*A-8hq=Y|iIt}jm>Cfj8Z&x?odSv^q zX*GOI=elQPEpIgUUVZt|>AJq{h1`{ zRpVxv;kz;Kra!RG*)SnYvhmjiOTSz6zN~YfCqH#QWO1WxW7oxR-#3}nYux+8-RgSA zk8C!%)IV7_??})!w}#hBrMrG#^yBmz2h!b?G23F2x1Ne#WMP%hdU&m+^SyfaQf%G->*3@D{+?rR%;~fwu*MlQ-Q~>XGyx>9QSgc_S*cOrfD{j%)su3 z4To<&{W*W;PN@6vwz zYpuv_PT8gqH@!G`X3IbC#e~;i{^DVq4UsK>k_U_nn!J1T?DK}}&l+7haqeZ%c!!;@ zPIOMbbkN&w^tBYjo{`_)Kk)kz;E;6uJ7r#KSLqo_*0iYu4UVXEkl@L>eXqF5BHxt zL{k6O%R_HhKX-Gj?%#If>2KSmbnNl+k?3^$^|k|UJW1));l&cAs?qQ*7B8n?$qi3* z@7{Uxm7vd)Ri|GpS)DeppUKCVNiVnT>JZbw=l9@Yw+?lbK76S@wQ`B6B4FCR`!*#n z|5!hz%`ZAx$AY0tGHbms`?KuHEXL#hP>&@q_g+dl^x^VJeUj_lYkOLlbv^!S`Cn76 zJl=G#nwx0&%ltgovh@xo?-H{Um8Tj_e(^({ZD%Im*r*=zdB(hlD}1^aOblPNXwsB* z@9l@5tC^j`oZ9KoL3=wO=;>R(w$>wq`wXmRS3T^zp(jV3i@g&3O_p=c@3W`hxoOvO zw0}}iBhwip-*!$_H|*GT@Uy&)A#DOzU+(fphaQLPw~{2SRlF88+c!CN<)lZsy7?s$ zhnA>Ql_R!wa`+q-RobX)+4?ab=Bwi7doF0ULI0<7m$N?MzG)vH74-c!jj?>09x}AU zd>7BTqudi4eEOk5%x|f)Hnk6SD5#O_le!`?y~e)oH$G>ZJN~}uhwNtkPi($*#BX`t zgMojX4v8N1tgl7tMA6p$>$f$%mufxw7ySYEBD=sLR}HD^BEOi@D3bx}>-{*e=l#3K zz8m?=-**zEsJ;(oI&6L)+6xUc;m=caUC+A?Q^bbw_* zP}K9J5chmlLd_BTeh9XC*m%8d+O?*me=XS3D{;!*1C1S84II(wdQ5-YrPn8UWs zyl=kO@Xs6Tk6o9!@k*_&wlcM1L-3>VX&2i({PoHCGl~IaFD&YK+U0g|^xN01dGNaC z`K~XfwrUVLtG`rRGILbJGa~1brA`~vTKSf?HLVs0)=B*plEK3b#!>*MD>S zvdgY@TIx3Zd1z{sB+*X3`cG|;SNDsdx5u8?@#e>n2~C|7r;OO*@bTK?yvNG3s>J<8 zm%H{&t<(42i?(_8^6AfdU3B`X>zLHEgtrUc1b+7-cF^0&_xvBUIo`JRdiSU|YQMtw zd*9!lQR~vAnJvktYdH%e|D>2LO- zc+tI!&7Y2Vy=aK_4BsIOr-Y~YUD`UjsaIg#nNN52nSV3TCFL)DlWKFPyF~Q5{@7d* znEG>r*baAkp4oH%=^*c&dy|fQo4LcX$?U@}A@BG6QnYu&hy`vvY(7pND=|MZC)0P% zrZpiWTs?nmk|Oc{wd;4^#w@+-@Sa&34~B_REpq(lRuXocLu|SLk)Y zk=KJx)@m>+|HSuYHycNF4RaiNbw<{?NlnUfv;h-uXimBhn$_k}y^T}TS{=E*;Sc@! z^>w`>oM(?a5Y)!XuW@#}qbo)z0oq zmftzPy!Qc*3H`TpD*ff zT%QdgziE}dMqO`}WH_49xcz~3wYom3p7;H}@0`o}Xij*3>b|_~uGUxIoC;0)IB?g6 zJ-2>pwR8Qr12;El6+av=)-+CY@r_j6K9;vJX2WpbHVwQ7)g7?b#a7jC%^fg!v9$9TeW!08?|tLfB4AoVmtS-fMn3wux$YR1+vHm} z|7yO_rv94|&0>BVouu8qP?_QKO!mu}`4`WoS`EA1qM3E?*<%-9+pv4q^%e`S5858+ zKIe9|cz?I1YxgcbpE};A)m|pT`nR0eZikQRCUqL=w_~kmNbLSE|`uxRq?`^2Y!D+3K}U&aJsN zzr)pqcYDstuqs{anzO}qWXHBkOB>3hT?cPZ|1PojRVKx+TZ?wgqst-3zTZ6fP|Hqd z;$3g-*2cFxzU8&Wu!TF0`HgLAZPD`fdG8&`msfj)7JqnKa(d>DCHDr_x6i$~z59`k zS8{%Q+DhD4wRWe&k#lc^Dj@9{o+uw{$ z*xs;vzs5go>9_ro$Bh<6uEVD6`Y`o|Y^h28g?WXpkK3KxcQdT91p4?eK>0-0N3tXSAPXA-4?XsyiTCBMkFET4z zbEYVC_}M#}dw))vXjypksYlPacD8%gUbKI3#Lss6Umn$q_E_3q|Fk!L*}zUaf85<{ z>dn3Z=lU&+&lvQ$)^D4Nq>>@8oTL|?cB&`3xBXap?{5!v(3LgqVAgla$v-32hetKt zw8G)7*Pu0nMwDF*ey+IkbMVw`PmAbR+NZZ-9JkHfa-`AjXs;$WODtw@nbfyys}-(# zxBTZ<6$h*JZ0enq^zQbUzCHa%D5egr+vM7cnEEdFO0T~jFe_z8lZI^~*RCF)(nG)S zNTlbWRSH$-`{PfaQ`z2axvXs2<81j3*FC?^xiB>)KfIp1*Cp|!({DFSD_!fhy7m;e zUPb4JF;)v691BhI|2OWAdkEe%Rs8)=eVcuGm)saOEx%hHVqW)gx&q#cuuTQLA7R@Hn18pOGo?E&bR7Kr z@84(VzlrK#1UVZ)9gUz)fAg}cU1jgA%HL;c7-9nn-$db?CJ*{H%8R~@@}{tszK!yu zZ>5INH&Xr>)?k=G^Z(|Xr~d=K+(OJN(yO{}|IvxuEB_~7ZYy}S7^dp`_Eq5teYvrC zsxSEy`f^*re*nl!6Z&#Pysx>Je-7}XMMMVXOx5@DU7Uzd79l<1y?pOhRrqoX3xfYz z1;vb;z&@1vXB$iEuePxiSc#1#{$FT9bWmi7s(rgwBPYd$s>TXONIg0pE>h)I`7djY z;jM40CrZ6<4%#MBTum207cu5p@Egj$N~LLNkZRWE!$_73@%4(B)diZXeRDK=pPVA5 z)srHt#RXQ(+JYi0v~}LLbqSvSrF7{g|z~?7(wQ`k<{X^oqnZeMoOzwt2=J zedsVi*M_>Ut9e*RQlc0oi_{gbK9^fKTR>6!qaSiV4@9Ry`F2Yf7#0`M`PFep-rb2n*@QoXMhxrvinVvviVNk9>D3>YlhxtW7xk57Vo?!}G z&XJIBFq94HV0kRzdz5P!lqno!%6+_ zE6NjP7@dWDr5h5UzBSBnXk+Q^QnS`LF#7a%iFybV@oxPZgQaeMwuGsdD~B?mOhrF?L1^kH;(~<#q=0EY7mbj)C$0PEK zd}=bi8Y7>`W0SJ7xu0ODE1`V}{|RM^yvh?D)yl;C_GTH))rt&%2jtyc*Fr7V!SBqL zYH6lFObL#9k+QkoDiPuUN33e0S2DlnNz^U%rhq2_9EH-ypprW38r%gYM*CV}J|aWwr#LvYQs6c^G( zkfx$##Wu;c!T;CmS53=|b<6da|Cj3+`#SdR)}PDfg3e{0igx@n(sN+^G>CM6=7}=z z=ZO-B(KX*BuQ(R-m<>%TYRk8I&2t|R#{ns}x z?r%4;*qk@tC&HLBRm7}9Je>Ox59fcxMI8X~Q4a_!Xj+WAfZuWZV$=uxTtkXcCkP9R zEavnAN!I)v5pO(0{lHDGD#kg#cba1{&Z9ZxbdxaFEKH-<-R)X~X*Y_^LdAIbq zTM5!nQ0*+id2zCCX9?;UQ&oi}sAnWN>}2Ur-n6p>=f$ZGg)DvK6X$8<6X!$Z6ZH>m zQjZeUL58gNDM3A?=y;D3oI4W&x0awjGFiX11a*t4fdwTvUnhiaEx~zpvSDip>L*h} z3rbMuK>o43$Uo{T$Uo{U$Uo{J$Uo{6$UpLvpxRP`^ZjJqmJ-xqrmFHwP>)G)*uv&F zdDE5>obRVP@vnT3@Ul1@(ox6!MAs6!MArA)i=2 zw!WaA*!qHUVSRkI3TtuH7qTVGIbZcaZJBmLex53%)=<5XOX z^m~NtFUI!jlLz$$_4GId>I>SX1B@lA%=DZE$>T~FoETeLm2QV)5{^ek+va6klC`}MCC6n#MB^Wo=Ra!C~+6o(=BrmBZwk(~z-khWzSCmw9vn-ts zZ3xrfi8LkY;+CbqF6>oe4ZOU!b1PZk*0S`Ys|Vm)mVSmdR_%mksUzf%yfRj6SiKH( zue@qvpHiGdK?gHbbMq->=Te=m0q0N9!$5~p=~2%B-A+&SIz8%i>^nh$UWfE>enML8 z9EEhyUKi5Q*z0i)M_YbW@6@9%hj#R;(mPptlsBC7Io@#Y=kurjO8%sYX*tLr;2V%O z@+i@y<{*8ldm1=iVJ@1<+8qQwkzeE!`4t`Z%Hed+M3|>$!8m;f#_bGfJ2#=7T!l8m zy}wq@`=Rh4#2v0=4R{IX2>2#lY*DI(Ic$NOMJbN`Q$ChG&4Rh4EyJ|HIi(JSsL#qE zMEP049L4A$bd#3`(K&)`L%jA=-gcnPU_1E^=2G_CWcuw1d8sC|PfouozOStPv273N z8MQrVTiC8VIr`$hlsB)^M4;dKze=BL+hXIkjrn1l#{AeeeS)@WXuH^E9dN6oyiH?U zAUYwo5l(+1ZI9?qSl2|COlNgxuDz-M;ahAg-1j&>lrd=qSl%ost0?hqo+tzK6PT;f z4znnc=qDLyH-6rlLT{?4xT=`)z2Dln$M9qRiOnTKliNmnE*yk=I`%vm z_g__@Fs+v3UeJHub_n_^quZ@ab*8useU)k8-%V4p-;0~2=zc6L2>xrQ4u}kmie!AX zo<0Kxcn1W|?5`y+UPG_LRm2 z1dR)g3;TNC>}|i=$AqoGo>DbN0rjTKZCKVO8+}c%_Bb@`yUeHVeO+0%1-{Xtks1Rm4(3#FFjGX!Z_wV00Bf>u{7mg8@^tY0!Y|N0c#~~GeLRfeP^j{rggx|{r zu-O+r&RPjz&Nn97HLH&FU;qjp7Ze;28$LOd{2CiNDPUAgXebU(o`bXj+CH8`14f6& zsTr*J0C&G3B$-7Hij8a=fNg;xM@{J49FL3|SQ1z)rLvNeekm4{eQ_}s^}lyEg4k_a zI};o>0)NCrwT_4i4vP4XjdGQro$&pARCRU|CnjY!Xa0Vi^A_Y+)i_rb9yiXls|nLD7spol)xJ5_nhXQT z)La2^(9X<75Q-RmO>?v%M}6jOxPrcT2IPve=0kD(b0vCRc0;y0bh#XkT%Ez%D+U>v z+b9>a{$N>~xr{cq6n&GSA08F4iHB(n^+N{fIU+_^lcU4F>(9zCh&jBh%JCFxUA86Q zyoPzfn#n+%r&4_@g96F~c-bObv=2bOwN|b(5Z`4B^aw*bo*Csxi`L|^$ISFQ*4~|p z=EvIhSYx2H%kwY)4&=vZD#{4ut7*-o8tm1nwGa<^jHWmuZ3Q0UL?<4RqdcsYOA+6Q zKKjt*%ENxIB{qv$9E~%=+>pby0+TW-SMKFE^lF5SjAJy65n!q7Cgf_Zfr-8RDZE?-2xtM~8*R zFbv)S2n$WXfYlQ!ad+^4^zYx&574F?>H{^I3j1yds~F;|$c4f2krV2d{Y{CW9R46Y(0e^jWSBnjYI3}1(n zG4mKQk8?0PpYiBrP`H4XpT5fc{-^rSKi#SMQXk^mGsQ7yj{yEn0G|=S_XKdM0G5l4 zVQT?wCxF`s;O+vrhXD2$z~cq*Yzn6`75ZE>YH+M}21~{WPQ4o)861~j1O$XfhR30n z7akcG5FZVyR4DITHjzgvDtZzhj0g>ijF0BUP*>Tlz1`LLzqa_lb^=}kKy|CoB3Y6w z)C==sfAt8D1RW05&b}Tj5?&-46%jN#Rv1Yb4j2{5PaxSrIay)@Ntwzsj0~M9&|%B< z+n`t!3IqHFkBhE|Mno9C+OdMfSkQw5$k;Y=QY5U-j8Wn9b`>&bnW4Hd{M1&m~W~Z3)h4IZvUzUI_pNF9pu}djY((y)4 zb$m0bHf|VGFAx7A=Ld5&@lh3`yZ@=KcViAcweLY7XV(SnCSlWYGI|i z{|aDg8IO^wbbpOmRl0u=-__ke>;5k4ueMeFj!c;4y1xQTlXcC2dZj4PEeGwI?i`@Y z2G;J1^qL$KO_F{-=+&aESB6;i?#Cj=FMDVElx&guh2dRUSv?cnpJn=kt{a+@pwdg~ zt8_KRYKeY+Rueswm4$jOtMj%49X=s&odMSuCV>m|=+hz%^gK?7HKTq%iQcTQi8?N{ z216?7&baPCeG>J2q}QIodsgWN6X44g>4M&8MQN3y&JVaQ5RZAVIxgt3FHm=e5_9@9 z>fj#C(mYI8DT!Xp{gyJRIoxl1-tQ3l8~DowzSy#%y;yEv3d;dHzMPa}3Gq|U(}i)* z%1_1e`4ro0s(PV8#Kb}#jCnNmp^EZhlnV`{{N?KmD6dX}Hpw^{NCR;#w2b8+%L!|w zY+Tz&nVBq)XgAPanVMtIgFOKD;K3mPhfp|Gy~KcPpj7ot1D6Lw_uPtHv2EZwZ5iPY zeZ2yIXrf<*c4f7T*|ndDuKkLV@Y?~oSf`9ipNs1|T-&6o5?PyV=p%$*HU-<22j#wC z*M$09lwE4Y8i-v#Vw_A_v1Y1E*9lyHsj${WS^fny6xG)mWNIRFw2g~v2h5NE{Y$_@ z8G29|%BT#>*Gec0ZK~k{)KykAtbw53OrwEbG--FpkC;dA9_wqH)H%{czZqDENFRAI z;o)8_E4wRAOwExqXvdVKH9#KVw;XQ9eGY z&s4)o>=V#<4zg{I$f+wU)Bd?dJzG6N+sD|p5Wi?P`gkGoLVrP|ZLFX^*LB%2-d&I~ z1G7B>)7rsU)&-{jrwF6Zvqr4zixmbeJHO261BOisUUY~r(~Z)ovLS4J@hk_ zdi0@^8=^dEkl>i9a?1&2QgW~^2#rCx(AV!a$x6);sXTK`l=Jnj`uhfv`lCUjZw;!D zf3`@UW59DItXFJX+*+3#8!gFNJQv3i(zgboPxreLq^Tw}gOH}`qd{CwGnb`_{$Frx zpw_&ZpN43G(iv3^fVCJ9y{;f4` zAVb!83ZrxxVwrGPEQaSr{_ro-}7MpAFct7`7tSfN1-r*K5g3N8P2f0QMHZ!vrwuPAD%er8iNKK3@Q@W{8hqImQ2lA$tbS z6h0}yzs(SNZ>8x5LHb_;xTavu34<7;0v+v=h)mQwn2 zAS{wrz)K)3l@a}voTCOYpxWM=Nm9CC;}D? z?Apz}m)6U(e>s9@|Na5d@o@p6F)>jw96GyHtXQ_vrLW-Ym{5G^!X4kTNU^-H@BoFb z94i*6M(a=>EkTC3P8MKP7B>>gG?pCXj0+tXY_zyWR8Qhu0^xwrgfA$P9XmlRea90J z5gIuf7P9m?4~sI@NUWZ}sV`xLh3W=1iee01u z72En`F9kyt4V`GH#SoN}As99!?=~9PkTC`6tQL_q^kKS&zDn27dKwnd5EmX8;u;v& z$ScHT&AgMYjSFc!4iQL4i6N|&yGcmAH;oUIkhQZ;LijsK)2~u|9LppYWNm|?t(1J{ zFD2h)NC_VYDSU^*=${GkhR6t=6*7`uAfs|Iq5S<%*V6ylCr6d8ot=5ZaFuIk3*x89 zjQRVubC~gvUgg@kDm>xZ8S!fK@VK>ecgA>qnrKpSw}D@do{aIVq^ly|!Hn^m81cSl zO?(ybvczO9PM_mdx+cyrBYVL@dcrkv227ZvE6F!_Tr7K@`_=aioI5+aeC3*0K^)_h zuZgV~OW;Y1HlW!x4eC1#+LFfeF=nxW(<|{jM+A7>nGT~}h&rG-errL$#dQ>?r`j+x z*>fJLvO-5keau?T#ur#KUq z$ca%eV%H2X9GHtCRIqXxkXxPNqU@xaMFwkbolHX;8kXZQiwsVxb%yI8tE-}{Y_!*n z>Cf49K70>pw*+aAAr0h3`vvc~DwXLu3Oox0 z*)3rpPP0Itf<9JN=+k6LzPlq?KcGJpl$D+8vccV6m)#h`FyK*|rOQP>0LY_Cf7#GP zec8b1b-Cz+OTnb;ZO|_%=-TLC0(}4=FV2{%H5BQ!EDJZ{oJ_IGT2bhI{pB>Nlj`-cXHhDXOS z6GCHR!=oaZ;Hbzr!1tRJ9m?Pib!dn?iRl%BWJZO<6dV;3J{l(7xTu%`fDsfI9}_w# zC?Y049I2`O3WrSaz9?}}rCEu7H;W4psewZN}N5Aq8 zN0RJ!uH|z@z@X5W@KKYvct5}Xz1#-+c@Fpj4;g0TxKg1ULdaYNAt|v33n3gDIUzJ6 zDw;+CUifGz5r={4Y-N6-QJVS^L}4jVW;g`{E)ySF5rcVp z8kL&mjlcr_;v=JDV2Tfh6pkGGy@!z$x%?SQn=kS5_EEtR{}+iu(8DF&0bZ zQ_)t+v-6E1jEA%ikByBFjd6_(835%EitvdaHxhD(eiA=&4Ag5MVdF=%=;l>P77YMS1>2Hn~ zzZh6`3FU9pM+AL>j<0{;Zs<4VJPG=9aA*uNja{DYQ^7G2<$Y@aZv+$cYZiwc;bO=@ zKx8x_Dwu8980Q`q6&@T)#weg_+=uvxJ)&a91;ugg5z_dP{5T`NDF3lgmt>?R!$K@K zi|}Mo9>xK0bp`QLX?#6F{CpaJSrCtV3b-FpLt-4CN%6V+lo)r4E8{=ZpI#sfl-)asdnc*< z+c~^%?V+*wNh;Qf$dWUE(Maie&^X+C5mB;!0z7M8zIfjUlkuKwhyV}IW20#t;vt(V zhTM1kn({6IJQtdWi1NJiE58-;Ea&meFYnF2q4(dBpO9xIpJyIDDdmHcGsY=+abg;0 z&5M)JI9pyE_g=&v;tmShhX=*uq7l!VEB`-~H%@R)kL8^&h{N)3{2O}w@?HWw1y2tD zNRRhdP)Rx73F72jLKVF5`NAthZ z4gxEomqqjC+Ah+&PvhqCaZM`Xxcac>CYnlk=0uHnPJ;b8{#e50p{OJeB`*(-9*&(F z0X@uv+k51<7n~Qi3gnPM@%Xae_&4R?=W)v9OWFJJ#%Hb^=LGyA*_(nmEJuN$yf_Dx z3h?kxMUTK1^5#Lc@v+!H_9G)a(=YL+^5Qu8E#bv+Dy;Bf`@Ao%kkNgb#@&4`63of`KTiG{sj-ZmBo%34(h5GX z-4EfV9TcSP&hdg3C=IG_gjQQWmOH-JT zG`u^9eBA$vyqryp>3!tU^EWXrm!qsAZH3@Fe_mRaNrkMi%xSWU`c#_o?lr={jlA!? zO^s<5@xF^Qtyn{#{B3#p7Ye@fHsOuca^tjUURpOfY2$hcS(ojrWSryvI6ltt=&|zy zFYh8jUfpr`{EBKgdX8hegUfM=M8kY6ny4&H%kh>jL^mhn_@r(|~t<_BB z?;RT(#mLyRIzEhM`tUuMj((5$S>k|ZY5GnGas0KW@CAn4L*U@Y3>jxR{7(!$*QWHU zi^zDom8Lt=btISWDk9@7M-R`oah&D&8$aH%3gxPLAGUF&Qs8c(<6$ z1>E$mE zj_Vd2+ccx68=5 z!okM+U;z^4T86#;x-06!MM?*yvMJfy<6u03tjukCoH6DiltW)B8CbzjCrC!@*f{@-~Ho zAJPjT9Q;;J_E$LA8rps|4{mBk)~g)s2Jk5!>}N*y5;!>0jEr|2JdbB$73IHD0RJR_ z4+!8h0{FH7ej>^eR0G<|7D?a|LM?=Sy6E z#*uvN$_{a>kgM?tZFy0uMBoYH%VXF|<;OpD{d+FIEaBgvR4`KcnXFNHg(t8AKE6D1 zlIS0q{V%CZu(`z`|`+C(Echx__8nphN|kMeNPg@iJ$1AN+&NhG{o$p66U0dGPyp5D00kedK1ncb?G*uJRT@+hOpr_yPnvT2|k zkv|25$5ql^!vAgzuNaOikFT_ieKF8h45C5~j9Q;C(Rj?`_bQ|Du7a0J#=7t?My2q- z8Ii)j7)Zja90tPwt`Bp~)z!PFZ-0c?rswPK2kmNrU;kb{Ju$MEk6-(C6{&8$J>C5f z?CI&_*&iCBk85Ag0Tr~kdwP}A^YQTPQJ&)JGqn6SrPgbhr-xs!z7=1)_2TowX5rr7 zyZkqYHo&vrKu;fc&+-K1syr2n;MvoYEh4+F@uhZ-wB-EAwjWt*+xBOeL<|Se_XU3B zo`UT_x_%i%?gHTV>>K*Q)DNX^=7*E@ zt*t-Vr>grM(1Ebt2!=U~``&J3PaEUexAHRjR(=B614S73@x&9U4G-e9y0n4YZ6(DAfB?wzn*q*|;$A$d4P#E7PVSZW);~ft1RI*M)80$qm9^^@3TrY}81OF7p z`Vo&JcP$V`TLL_?8LlLrr14 z2Y~cEDC|Y?hfvs`rbkd1??i~hNPA%Ur}UOkcm;*GQg|o*{xF5JX!=zO;~fU9uRIDD z(DZi{{z&=3`vq)!k`Q_-3gg`YtPf`j0x1!w7Qbf^aFNhv(l2%Q2h*<)AQo{^CZ? z(Ggag5PBL4dzg^?{3tvG!$@)_&h~eTKb6Axt_J0iLE$APL>?O{yw!x%??DP5HX-$U ziNg3k2IZxva31Bal)|{@hULM%Hn#tplKgEcjOW=%Urk|WQ-be7VJ{3}yc$Abe+=Wu zxi#DWF^qw7P&n0;@Uw)%D=59K6y9k{_&ZGDEK2Vxg>O=Nc@!?dFqUEW+SvY&Axw6- z*T(jLIiaVbu)Um=$C<(|aw0D;3VX{5fBqB>q~AwVI8ILZO{FmI!D0VhLE+UjeJ6!8 zoX#j}*p(V{9)up@>^P!0-r!!Q}jL18V0 z11TIr;W!E>n33|OQFy)?;b%34b!J39nH1iKVG5Lk!sjqdr8aM>k-h8kdAnN0yse?I zmcsrNj-YS?h0`d!gu)y7^Kvy}Lx4X&v-7f)&d1frc@&21JY20}9=4^h6NOzU?8TpV z0S=*XG=;f&cRq!e(D`&Vjc4awInj$S{VI*WPvJuTJX@_|p0ySe*q*{_3cJwxPebGJ zOo_D-MAPGFd?JN2D7>1&8|n8u`SYik&Y#tY%_6p)QTQW;TaN@IH+%!q9@wd(|rDJzEMpQP_pTUKIADa0rE? zDV#{*|FBJ^f8EGZW$#2!i7V2%(;neF(GVxnHx$g@zY~4Oqs1^)zZ0zrPxww0@fz@s ztfVyEU68L0)a@lRV?6v5+E}u0MaF&wV{Bu|Pe3ov+W6iS_fspNmj?pWi3vS9kKSlr zneZ>HZsp~j$QZv*MZB+hpSl9@v~psr)t9OIeQJRn%=x_Y1dg8YeQIhahM8SSzU=F| zxVUn&P5)@K$+=UzueI5fX~70YHk;6X3EF94ZMQ_D?HYY9zD<-c@Q<@C8mP^Ib?G6r z{lR|=T8G+Nk}`EzJ4pB@Q^nN5x0>}d5Y}VgOM2uet)jAZVqz}@ZLgqxmU22uZB9v9 z*&#mNm#hsOT`uC=sL{p`dH<;dnsU~LQyqLa3gXn&L>v*tg;vGBHD$kJ))+8UPg|>M^l&CwY zP%krxRK&g(+AqR4w7zIhMv3oOb-CoN@c=d*-w4|PK9iVk=uz=D+1!lr%a&`G)KVyy z)RJpbYdk3{V{H{#qwS}ZBL$9GsLETBD7y@-kN7qW)4xMD{l-c^?Zs zIG7z%Vad<5ry=&cAcok>$Fb!Rg};@O@Fs=-t@}@UHeZpEeQ#XQ)fd2+?ovKjeR&_r z_L3KWXAFM4fm?EvdZ}~jmKc6iB(cHjunc&Ji%DjUq)A&N2T(sJe89apT93< zg11(TJ5H>?LX--+n5`9=zigF67qP2JJJnmlRfnYN^+;P zL0z(zN83KmB4URZ-!-CLVD=7`9a)n%XhQA&0==CgLJ#-EksjXvM0%$vy{nX7DW!*d z-bjzVlWs@$vXLI{W!u)EcdP8l9Z}mhWUiB#khL+|R{Foe){qBp46JGmiT>J)n18l4 zlpuJoQ`H)>Dm>vDGC}gC4-rfk+8WA`5S>9-4xz1~b5-Okv^9iyU$d5D-;7C!tsyI> z>a`s1sRr^Qs#?o=SyXLnC^)3T0`EVq<(!>4jDKFs=}1t%mNSLc@n6*&dCmIYa-+rC zI2N{D{vTha{5Dw?9=?5+e+8Z`W88+S!gFGb+YsVa)rRf3*z&fB%? z==`;HPT!=J*SQ6>V0QgZ*H&S$cENQE+WN$`xW8(hLCh@9=X`CT{m;6btz@+>S0268 zV8^5uFzT6xS~T8-*x1dMGD{7NdbWXY>$BX~2dwq~g0)=;V zF`QwLF#WU9Ca_$YVdQ59_?A2ILPy!{xmK3ogyM6{QMv?ZzvJF9?O^E$}F18_AI z7wt==xdTNcUFc&&B3QGjDp^DFVRr%CTL2Fez+nP-k^sgv6o0*BbPp;x zCX|@IG`?Mhm)0t7gCPVuWk6(jNV}+!WB6Bh@G9H5kT1SxFPT+-U#zmLSYNP(-a7(D zhDY&I%B{InmPkJfft-aNIFR|ur~qOJYsyr3Hx6Nf%-**{Tcc=?bX+vO??bKxv35j_ zbm=ee%YAV(j-B6ZEy>v%U+?y&VIV{FZ}yEt4bo5R)+Bd>>RA(g9QEWZOt&F-UC@>& z>wlvjecMo}uWJ?bd>LaM2JyZ|561P7 zQcPsN{^_hlqhYnwTrlBN{)s1wbLi=@wLT92Q z6#2G#;zaciX-0+tbq{;0dnghCrheAgxjR~$a|q8F)af~Br$4FO_WF?A2sVC+L7`m2 zo=2chHibI17U(p&BDBAWb{Cm7tiJ%(hQ5~U8Os=LHuT54n8xKpXwETkad-wI)9J3Y zWta-YT~z2v?mE=K;Ht4u6&O^ zcMUrg4awb!`c5KOJ}rCZBL$wa*mIUO<@h%NU&^cmc@aDBm)J9%A-StzNSNPbonbAM z9p%jTF;EM1B5PfGjyX?%0$t~V4D?ig_~xqU6l=fo2do3?6OZ+qphb9%0qG!JrIz?^ zFy-`4&?{B&y|R7Pwk&@Gz7XGu`c!6scO$ujuDO+#5p`X;8f|Yv*%Q;*^8$|FMp$m; zN&{aO$-H`$>xf^5B|on-3KK@XyB}pFX^NI2uPm?^I@D-44&4%i8K6lB*0% zHCRJg2LkUbZIDw6=z-nZf&Pf+Aj))BuMed@O4u_AEvY+{={CScI(Q}1hPE5j|6|eD z1GLL`podSOXBr7S{qqO-P7i6#sUH+hPvzrmK))4s6?K+Cb#{(tc71@d(~EjtaV_O_ zJo@j0wvIj#kVgyP&w};~DXnu0%4Zj7r#AtA4(0VK=;yAL@am1e03l6F`Ni~60)DaV za%dy2RHtX{^s46=S_7O;=_de%ybIL_psJXt$uW^XR08_Zji6G~WN1 zPxsYv?!!49=Xbn^iu-POe;Dt|;-rCR@pw+hBod#E4D~B10sa-rL<63uP0d(m*n#Vv zj0Ji#dOln}c0{9GQ`t2XcfQN{y})x?YiKLLo;kP1#PubfyD?Z_^gL}RZG&czhUG(< z@1^4}_Gk3RgLi-x$Jdc@k?CI(*S?CZ1$y))1NAF`@vNo;yRFkNm!ap`;y`kajs4*! zu8*13`5fO;^r;E)^(bFbw(q3EIFQ1wXHBU85%dGX`iF5Pz92cDGxd;$dWozA)`e4n z_PH!lkCTD-DT1>@SRXz02b{wcC%FdlFjH7jldm7xDT%khW zsr_?tzL3LMiND1#M=+|CT3D7Z*ONF?|IZhc#xb%l){ACFFj-%gN-JPYca|Ba-;o)o z-xs78nHZ-_O)Fsj-@m#3M4oYd*MgUhDoQ5-++6^B31FeWjA#LzD1e3jGByd|V*>br z0DdNb`8IbDt}TG=1aJ!h+))6#3gAHkI7$Fd6TnLa@FoGgR{)<7z&8Z&69N1|MCuOT zg=78I5}c9ZnF6*qN4kFRBuMuZz{A9({c&fS<%?^2Rw;ZNSg~Gp>*X6Tit|)~0Ip3j zEceeyyi$C%i;FlkOUhrF3%&@SzW(co#v!M`Wjo-uoa@-gsvT3(tXL=QkQ1Bb-}^v`34%pEdh5ld zIpKyZ&W)Gq6{R8@7609rZdF!yg$I)_-z>+qWjQq7Ua9oenh|;9eG!JNHF2+i-9xBB z_Yi7QKPA@W{S5jo!S^%hw?v0w9ikiH-a!^a_73oV1nwQMc3rG1{FHdmJ%p)r4`Dvt zL&&7?B^q*j3b@`xza`f6Y|aM5#thRz*%Igs}x=(hyli=e*{ zyx)qU7Y*^e2J^%FtcZvASds2NntqdpMHEjVB_V77z?!_T!1PoaqHPoON0LS1JR0JA z2;1gl&Z^sj*a)oGlGvA$;8-9dA-In4oA;~oOPm|7)(d|Fb$lD?GUUFe3yXu`)H`AAa-rt>> za(@X`&=c+fA>P;Q0pWWMd{c!oP%>5T0i6S_I-d7oReM00R)4n#G~uiFfZDe0^woY> zoQTnBqdg!c^`*l4nWXx99aiUGA`?;nmv}!C&tK|MJ$o$Z*(p`%*@%nhFSrlF`e}hR zh@7`ps)6TDkYpDeqo7Yb|3K8a2eJ1LrF3ry))ToM%i3g%nP~QoEIsGC%lW+| z{!s?xn($~Mj+nRSV_^3VD)4c=(1(^IN=3@BLup)&61*o0cs(fIG{D1k6^akrEXs_d zFK04pDS3WL8CoVOyZ-~BIztA0;meYmn2vnf0H3U%PrOTuvZ42f3wh^~HYkTc*8gE{ z6Rte0FUtPxo&~!v$1Gyk@a1P}zz^rURYA}7Rs+qZf_;nWytOfRc8BZh1l~Gos<3{k zKSYkMH~-9(t`T$57ict;KQOWUoD_XIa(+7NaCxf8`tc&j6z5Ak?AhQQug__Drb2nXucWRaNxi6ny_1`P#%@fcQG5Rm)scC=0@69M( zu3uSGKS@&RSJqma!tNQUvF~QBGhlvG>Hf*WDtJd+^ykOrw=)atcmVF(W^Yvff6QHf za9qWGf0E9pyOXfbwqz@=fCn)t0e5o-L)6wOXKVs39(vRY5siyZ_Q}@8vUKPNHsGWJ z+gK0@&e+B>v2!AEs6*UFZk^U8X2uL=Ai+u8DVZ>grwG|^@KBNw8MmcLru6&S@AtiX zZ*?ckADLD&n$LZA_wDcf?(TQLyWfHX83OXZ5ORe0{&!$Ew~=l1oUKQG;wUrq6RP(R z`880VtF?x;eO+y9CqcBxp<1Z_q>S_FMxT7LhT>Z&K}8Sk^cv&9FN6=b@i^u zclJv+g03s}=`1C6%_wSN9xXE$lN zKivtym0>In{Jsvo!((t zk8NK(`u3X7EuV-dmQPe!cfGxq(mzD;AdVWVcusyJrN0~Q)`&OWAWOoSqq!@#RCNC< z6D%u&?th1b4-M>|2 zr3%h7gewUPgwSh`N;?h_-9&VgRq)#U9JxVO_#1P9VDWhp&cz^Om`9U+eHE`?VwF5+ z=y`BF@8CW_rFGBQy72?f)fhKP=5sXXI+5h=dMnAi&hqiP5b-+HD|i%aokfJd%+dN7 zXw!SBj)$Dtx(#A-0g>Sa?Ih`MRY0NQpTwLTP-kB*>>ii;H~f zh6>iBx%n;X%R62;A4 zoOpcMlQcfI(wgDzyQscu6MOU-+$;+BJkC=FsBWQyLv#H@vx~gp6$zW!U2wkt8H|Bl z4rI>}D>)I37tdIBQeL3L6B4;I_XGS7I}w-Hq*4>FVSSZ^eYaP%?dN~K$vwx|;nbye zI2Wjn-0=T|`D|dPE?-@GM3+z7v!}EgiI2$q7i*B;Uq#={GY=@*YlfY^LFXW?9bDQ* z9(*?9k^fI=-aO*C^8AMMpaVO6xlb0Hmg~#6&yS%OMeVEG=T??WCtoT!$?J<}Jv&OY z`+&YPG~%6^KsMfx7<6jqQlFzVVe8z@Uq58sY4ouXCx&yrMc`lKXG1CKdufg{Y^C1b zLglKpsh&oMClD6A6MdBJ$qzd*-#RWsVGpe-IESt*IN|gY0qFv}b(8(P`@T z-05gL_OQS1fh8iaJKd8 ziv{OZn!ozXwNDy01BWMQz4`9Jih^@+alvudw+|cnnMq*=Da@cROs^3J@tvk{9Tcv^ z7j8R+8}fzw&Hohpgxd!nz}diMJYS(s4j6GikA6gPenN3s-1yMmT{ed?<7~&enAS(< zi6gxJpQib_sn}M zShogx?vPWDTP)AUd&Z||jGel^sGG3-Emt>kr{n5Ih8DAaLKUsuX1xDI{tWR0T35Zh zjQ%eXyPPRFm5r2c#2T20_{Qurh|AvR#1p*kLfzxcZiYMV?bHPAh2F&Y zt+k%wahIaKC$*J(w*M-Pt)thKFHZ;M1NqbCp|#b13cqcd@T)0&pzJy3=MA)$s~uo| z-i$dj&TA=m%-#)No*dR|tSHtI!t*q5PY#PdZ0ha5pz_oS&xBjD! zUC{j|Pw7O-4ZNyB_}sHp2U~~_Y%%!7pr2n*Txp5}^}PBD=5?<7F03W#+h=AK_uLN~ z-w(^!s-ygQJbg4ZvBNh9UPk4J(O%`-m|L)xp)&l0eqW&R4ZeXhamWjXef$CC#P=sD z+_RT6PlDb?!ASq7{$Yv>=^vrr^(v@qFzgEpor+Fczd_&kY2nx3 zG5IysONzps6ag5{-f?&B@k)NL0Wxdo$iX%|?zXhj|D%=sHW%Kh_*NyqoBsIULxOfi<=}R;+>4#FdyQW&LrvUK>B9`)niyd%YIx8yGsz4H$#&K1KAr zY3-=TeMNHH#QPt9_8R`p4SI zJ&(@cS#Zz?XQUwWq4iFJ$HrHv{`%}<9bsQTUti3>g*!18~iKm4`}ULiG4_7`_F!feyI0T*1I9!H;{2% zre#i3@mAT(Cn_NCv8<=jc2%ytL-wz~ zz}@%scIV&qx&q^2yK_MQ-s9bauN3#a@$Uj0p`A3yc_KN+_uY_l~y5$MKY=inf?h?R%?4g1)wpe5vH0OK~?U^5WC+d?Y zK%VIJcj7yDy6dm8)WlUFIBY2YPkj>fcq&pFa_&^;g~Kpi{5ye^1?~yy)Z6r>IZAfWAfb3)#=}a+l#H zUpekI%5g;7(Jq$#2UPaMAv>^gnI*j~o6=s)PO=)j|K}NYHPu=$8!oUk&=4LI2vIYePZ$(*}KmLEmc7 zEe5^Tpf?-z<00lBcrO#>KVRb<(03bjhe6*v zgZb5{#qm8dgZa}r8K2)U!hg@8Uo&XOpx-y>3d8n_sduKI0BCm~v<@g+1+|}KN@l$( zPxZ`uhFr&!+aMO#H5GU>+O_oUe@0p>qD^)7qS6WETUKdpNufD(Ksga1`nxvu zwB`JsJ|#)FI^fjR+a4T;uEM2ua1+rZV5-#s^p0RIbDPk6bVemz?{cH}X>jbj<@y+! znLb6)srA&}6|5(rxm%E%S1(gDuWj=fIW13?ndr{r5!6Yvua-SFk(wKw9rm5bHkZKSHL^cpbkN1sd-@HD1p$a(oxU zx#kkS;|Ln>p*G$i_U~?_`4ae9*0}?Xz0@a~_<|nA@1l5tv?&<_ev$G|cqwgk;x!z< zUBm84;N6y$0sbZJ6@Xvjw?zp#&euF(P+G*iT?l+5O z%J>oYB|u1|+?k{;@hQ^urPYTB8FfJ8L3qn?a`pV7WdhHW>^Bk!Up|=SRihEvgzu>6gA;B@h zy>--2<`S-_asgW{tvlb?LG?J7`{U?b89zdw1ZFMkJqZ`81Bd7Q5m^O$dR05{UO zn8*C*p!gr1$KOu^yDWVXy|J6(0n#RW;e2kVCG)wRI_FC}&gcFC*$VPAG@toUe!lcS z!4bIMOZ6`F|7ZR1QQsx}a{A$`uVNd^AEh7uy8lAIRbD^5TsTud9N{iTKm5Hi@tOMJ z2zN31;kW~WcZX01pSH^DhcBfHz09}wETe^}a!+UxzT0i`=P1iMD>>B|?T4}4O zAAX5dOYL27{QAxn`g{QT7cZP|OcorxPwUFFubH`Rg7q=kpC)gGwxAWA&3iMO_sno} z`=U6lE!sS?+d63{o4MbHEh<`s1^hbNdhM=mo7R9?{q&6A?>=qzs+a~Zv~4U3vu&N( zUTuKMO^^NbEvWXsa-np6+Zb5-B^2A+^4p!+HP%{Q{ARrcroA!SyRofrE%7VyqrCRn z>$>~9kXhFaOI=^kpgTTT>)GMbXtGMw`Llsckai%aR>*pc@4w8KyK zFkWDdHZ8;ZvR&)#oVK%tmasfOv>W_3Qs&#%w`cUjFLv>zQ8!$##K9j2-MpoLmKNVu zwu`nA*^J%p?l|y`E!!mebN%5eVbFYDyP9F#FE?gv+OFtU_sUl$*Y>8KZpv3N&0x5o z@5bKsRP*8`5eM9K17z74n6I(}VT=U3+Ns#xUF|Z6EHrPM+rT|+3=a9?ZsFDww>;cv zdoFmt-3AM48!@UZ&#e!8o?Lg2>xSK~8((jK4n7p$I%=c7j%+V~g54U8L%Bt1>EGPN z{J*_$V&&B40&;jMEZ+XnwC)UyHl?LEexUU*LC4f7q@S(%S%~)c^P=93*^TWsRs+R2d;7Y3+Jjx&nvS=4e&SH1G$it|d zM6r~+H#R(JOwizDSt*U@E__PAC+*TSD4VIPT9OyH?c6nuVR}+z*(Ln~lBl)ax%+*^ zF1iVmULicgHSX!AU$g*Pq_yEsmlpY*?|1RGaWD+Kd$m@|k3X9B;j}XT{yWO$Uucsw zwO=bw@s{_V6!9Ck1v#86`YV4bV10y?&nj`n+O4rter}4T{ED?&_DI9aPf3BKdj!LN z{BE-+2n-(0a2jr%dFMPnZri$+EpEDP%>Kdt#b-m}$b!pKXGTT09?$%z7rp1l)3~N- zl3JBr&n>J6j^x3x_sW%$uv{UiHVx}GdU*+ZDZgU7e1zDzl+eG;cX%_cKXv8xWlm+h zFg@)XygDxtMxxN?C#CYL4Q0|;e3*j??p;r{lXWM!MwCv`}3+_2Dd_L6IMsr=p%{$8K z#U<~^dkjF_eHM1zL90JA&3Kn^3-D5|*KE;UCfAbR7;`bcZS}3y%e^S?vqvp&(idk+ z&~ApPNBU30AShQE=p!X#s!wBB!S9B%rK`R|ruq9~a!V0zot%0zD z?LaFZRH}aZ zqU!J%w;fXpfs;|S6lld%3oso~oxprd4eD?;>Yxr6QzwC^W9l66L`+4Xjg)BPs!$jHqT{E~d5st*F`$9Eho7 zz<5->0UV8~3);U%LFR77Yg7Z!j;gDHiKv3?+)74O3osQ`8DKi9wg7WcrS;ARqUta( zA5|xTqfs@kzmF;X7<{fq)d9z%Y7y{6R5bxlN7Ze>WQ}SD%HLYcg2tlt3?X#1zDEeX z#ZkhfrFFR#ba!YfRagwIBZN)F^@PU>X&1%+j1+XKh)F2iJ)z}-!WkT~afNr@?j^)o9PtmHe ztL0Hb+y{CL=@5R6aODhr_f_F+ZWY_Yn>e{sZct@axEDSSyPwoB{4fI}%?zfOf3b zcZw8jx_$%k5f%trd~byOE^J9HE#q5NLf=f)1+#$XvXQk5DdC~cvcZt0fo(w5z_ zTi2y+x@F(GUAjwK+On42olJNNrL1MU?3V3P`JT@`=g!>cW(Lw$cYWXA51ZaO_y4`m z{eRB+9C3GuyGz_X;_el9pSb(QJt*#BagT_5Lflj0o)H&v6srB=mWvw^w@Tb!*)X#*@DtJ*K_v*HejyF=Vv;_eZ5uekff-7oGzaSw}o zMBEeNo)Y(rxRCQu?H0FO+=#eU;#P}WEACuz7l^x9+y-%%i`yb@ySP2#J|=FTxX+3^ zAnrDCcZj=7+&$v%6?dPw`^7yd?qP9{h5cbm97#N8$C9&z`IyHDKx;vN+Du((IWJt6KX zanFdW?92xkH%+x)+=#eU;#P}WEAE?YC-}`a3!K}glM$Ye|GbYj$f(1%i_XP=UT2k; zkN=!6p3Q$oycNc|SM#6eTjd_6IQ0eIc=%`A1wL{CW7we`rd{B>9q*|ktLx#d-%7{J z&qlnl@Us_BVZCfzWhOZJ`Ps`aP-pW|`PuyJ@`yS+kBX<>=(@Yd%+H=Pt9HzGfWpk< z_u!p{G%i^N^K7%L^ylJPe0$ajji+M{bby~r`JAK=xMaX7DUA-e(4Q}L`j2YtdTrBH zV~kZx`K2-7lQ4EnX-r{d9S@peQ4aG*&k^RFBP{F)D>MnbVZ zSK(YmlVxZ_uYY~0&skrgF|v_98qMnM6)L>60XM3!OEzb80DpS+?00}S4UC_-g^@Ox z10&6&<4EP~mA2x0`-Y&8%j2q);tUNjrV;RI!b)#++fmj9-IRv@XsPIrhBPkwP&`fZ z`W4t%3RAp;qHk~54Tf!{GKy0PUF$XjtL@V{@Jkkl2VLOwg`Djf{q(ssZmKPv(^5m{ z)Gs!n!lHMIG%rgVaqg{mw0*EUM}Xbqr23}>VcR(M_!Ncxm3m&`5CNME`nAFE^BMdi zN3gVkUqo|oiW7Tw3h;as&QU5==A<;v(en!D2zYzI9ZOL^^0;O!^@Gx!>WDcTr2aT0xH8$kVmnmjz;37h={bkZta9m}xXg3lmwnn8H@hhR zKQzV-Fv+0f`&nyjnQ?|iuXn%nGwu`E-*8VkbJ?JKMq^;yeAUmEY5n6SwgKUDxXle( z7g^S${hJ3B1`x)g)^UY>jRL1(k}r`AIEiFg8L-AE?*d?=^?%}Msn}~MHf%EhJL546 zJEOtqj~`JU(2-qsfU%FDkIQR9e8fqzed)!N{_BEKup`QPv1ofz^lGapyokPSk<{Zb zum_y%TD+g~YNT>Z{rnHAHKSi)J+7kPsTx|hC~Za;IUY|JH6gC=&taSVh`aZ$H2xZrT;X`_TkhRsbrV4IbIgQV-j8EVaeBcen z`M68jNr5er0cA^Vv9hH$;1(L`fdf?|Wg!1T=&(yZ8ZV?7y7ytxn{SGzmx^7FQd(zp z`yLPspb*+_tbdQ6JzKJSI_ryT+X-KY^0&b^Dok=Yz~t$tIQ!%~eh#=YtXM*%Dj={VJ$f$Y5$PIU0_s@-}lAukI63utPENg_QbM% z^d~vm30oBdwB`br&MfzfR=HKM&milJ&TnUqKChH@;glSrv~ABmxvN3n6z`bOE6_L@ zxZC3Z1WZk=C)l4R`1q}%$j4aLQ}}JvsiN;*C3XjaLt2$xTQYl8MAj9oy&=J|I+dXL zngF&_e3;g#*Yx--H0D5oF@`T!wl222z&P`%@A$l1LHX{EL9USTAj^T{Li>Io{L)e2 zMe6w#k~L`~_1C&{U`0-mb-7BgNte^PZMlJig#P`E?%#T@+fmp^p?GtRcwaQ)A4n2K3)xOz!`dNE|>ivo6DUNA_PxQnMdWvJ(KoC7| zD}0+3zQYQC*$N-B!Vg;Ehpq4?pO?=5EAYtbf=1NUR{X{OERCRLes6dz;0<38@P=;< zdc$`Gz2T-_KEgxA-tgLzEN%XKEA-;08MM+9w&+@mzQv-?XAk3J4v!DKhvHo~X@!5u zqVXQN(TM*OEBt2;j~5+ob_v3<{tOx6@36vW`*^%OE8(|U;rCkfT8nT_(MK)%v_%&Oy!2Fyj#~5#i@r9%^Cd3zz10fW`qZXuOGelFJSHqy zo=FrXv%0S$#hG98IHRrF-FP?i;@f(hmYTN4(ZV!lwl!_Jq-`i^TTRQklTw6SNk`lL z%n`{_xX!2*uYw?iRideht%VA5nXAucx3(jJ_qTIM5sgwBHnoa76Gr_ETb+hI60bJ= z%?oc|xP(5+?elJ1xHQ|Cd{Ttc02PeqK{Vj@TNZefY{#fBZ8cBziPAvr&LdluLwYJB z@RgP3=(ZJDGjUo)Dy~5*u6jhpM(M$tT?io|v4Vp z#N8F*e!}?*{Q})t^wY~iLrw^~a6>}x3vqw#7x#d;2gN-k?qP9L;vNzAn7AjzJtgiL zaf8M3or}4h4aM9KtBa+7;4X$p3xA(r3Aba0xUmv$56*+AFLZj*p1DHTmxzwB(2GmB zyasXK&N_X|O0wbkcq-8G+#NW-PT#XeUXGKWr;;z8%~L_VvFP;e$djL~(}#Ft;ei}1 zVZC2PfQNNKFkb)rwYn%|MZ;{z_Qv%&qhvu7)T z=YEOx?)fR}OS~6~Zg=0q1gP^HC?vZ|UH1grz-WjaNU!dW(1~_NNpM<1qL+ z3d51=iMQoa@Cm}W|8F~rH*$q%o8os4jlEc>%YBRTRPX4N&)gKhMNCLNmx>@i=hH`# zUp4X?N{%2e*w84YzYzTe=}*65M+6&R-wmtY!hFxgw~A8#C`^Cqp9(4${GSO!+m7n| zG={&P%hg8wDkDDXfNuact6inDqFZB(5zi4=q8s`;)uNs$noU1=Z zzX{PSU*X`a_rHSQ*l*AXAF#srSmArEaPwUU@>SV2naew3#kcVw>wMnuUMu`rEBufZ ze#8oo`Mu$Dt#_ih6~4?0xA7=9TH$-G@K>#H^PLIdi>-I4CWAKry%q09`rQNH6!-8+ zMmW~QOD($2qHmJ4+79OuE4;;`KV;FH93C$^{U@yOKXG_`G)VYfEBx;*I_>ay(eZy_ zh5yFk@uKk~CtB|`ms&LJtR6AqFR;Rw`oy-Pgg08@kaxrQiAnfJt?(ZzlqU%Gx3);o@gUq|lD(Kpi3U}Opt%X59#k0em5^1O^FpZqT_ zCW4&tVB*aNzVL}JybS`Oia<2wp`vNuOg)Tzv&lv!QPd+{RLJNoGv1j>*?Dq|(TTK8 zSrw#z-@ahsO=k17t_HkRQp0alt#SvbWD>IUwp(snXw=AjAj|#mV5UG$;*sengH*u^ z$Lcb9BdFrH0N4w~-Vk6pclAbqy+30HVnvw5vYIOH`Ej<*1W<1Jv?wY=6N z9B%=r7vwS{*9lJ643^7;|8993*em`+;=fPwgPbMqc0X?t0t2TUXw@UcPwtU2mJLac2E;-SuA1 zlb?OpL%gxv^^Q@zsND4~b@Jc!LewY|OE=$LZ|k`HcRgq{>f7FV<=y67H)pJOy#yb~ zJa;`{#QS@b!^`QMym306Q|T<+Cw$-D`t7dbrzVoa70!T?gEQqxaNe!Qy)K?yUrP5l zYSJecQyvRu=9dXsB=$#^3l4mFa#OmT@-B^TN|zXz?^7M;wLZu{K+Xx}g*U-=64yi9 z1?i8cA#+n2-tKCQ_5*ZpIUMDCO9sZf-f5(l{v{geIFBc$==8Wl&mH1+>%RD+ zX%l7$_E@v!X`6ioL&-^yXZs0c!HoVc)pkK)&qCSnTLPO8B|dg5GxkEog?y#`U7C&W z@}=|H4;|9>L$|r5_1l%~RG7X?neIo2#+XBW;YdFc9aQwk`CBLiH*J@Yev4e+Maur5 z*b$7bce$_QBV~I0#wp+UmMQu>2E&xEAr}~oZkrN{z96;@8%9`mRo8*-+`>L#OzdEA z{_H9ntI~Ht>ARrn*UehaX;lB=?F;V&!8q&%i`-Ng^=}w14{vbCi~U?Jzfxe>684Mj z;qrQxgU!l^u?%cR7FND7Z2wl6wtu59xR8rg_N)3|a6^d~+(Xep_mIf8L<|`m$kO?= z3@EjyT+8@1X`4E6*i5N}+}<`?r_RWlh_HUjv(J*3DL2;W3R$BgBF7pG4=CGZuxWLp zv*{RQaKhnD>CQK}9GBL!V0fzw`O*JG9h9BL=Tcb94*2$|J~$lxu6i#){Z3BbH~f#X z#vPEo33p4#5nv8rPl6rRSBD2(*m}d;)V_!C-t+0BL$tS)!-fyuZHBg~cPY#*mj8U| zsINac?C&+~MmZZ?I;NF83wx*NgCxaGZlH0|>jrke1iPQZ8C$Bp<2KX=3nGn56OPE$2gQ|S)=HsgKo4YYtIexW={X}Zg|O{I)nI}dq(&RX=Hj~B=o)pB&@&WkQ& zOLdxqXU{78ng^zTdl+>MhS^RfzHQfUIL_apY?>z(G=|FQ4C9M$NSkAl_TXU}53j&x zZj)MTu>Lvs+Ce!&OE{jkWHsAS#nZ}gk0 zI!*uHf_A^{d#biy0(((@dGFh1y;ttG-YX;4dt%gjPi(is6IS@M)_Y^W_1+i^dc#AO z+*@yFc_+Pcc z|Hh(idoXE>{*^@+S#oAmEc$YbuC?g-7Tsjge_+v{3h;bb_|)}o)Z=$9<|k1g7^hXTAA!Rj+i7QpU-VYa}qe$dtSuoyQ` zi2$wE*t%|%jRxDAf{`!4UvpV!$Yy$1qD2`z@tBDiEiRVx)jF>bSzd%I?@Rf4v8;X% zKr9pNiu?XiP$s-oTXT0FdnFlgwfC`Yh0%yso*3sadZOx*!}3WEa~k;CS$i`1nyt;& z&CcVo@$Im*O{@{sD< zb2=mHXb%kFb|qS=xO2&xLbg(uHuJigY-!4)rY`HzLLRLxt+UuinW>=()7H`4)7ceQ z*saoy!1RbzGH)Yt5%V%45!Q`~OyfN}axu$tM=lZh#i-cwn8v&e$Wq72>MwG z-|Ayt3eJ5>ZqmncmOF%oT%*FM7dHf6!aMmKsB9Jp-7YkE2cU7zQyBXJ=D7eH0dkn9 zBpl~FghTfq;n4j@cvR#C>k63XqHJx<6!}8(KUlzWmM06i-AXQy4pxs9ayxK7RC1w( z+>VWfVk<%X`-N8Wg4a@eir9^aJRop&z;jt%Byx3d>7w&k5%XN0E#i7?mGB)!TtDEF zK|XV+h%S51?Q|}JdxzNmpabBp5^l$CksI7o!tHoj=)EP}??XZZx2zHXJa1zj z(}6L_6UL^njPCh(Ou=#9^78SR^2M`xOo%tth?kGYG%ZhlHjfGM#)PRbj^`-)pwh|D zV`>jG9*CWv&0`8z>asOof@T8WAWwKaZ)3fuTG+rmzQ3f%R%D>Hbec;5$=< z*K%6$-&Dq_x7-1@)YimF@@u$X8Zk)=6-ucx|wvBz^{N;vol#92S|4{Ec*|0BD9cXF$AIGeiVaNbR zQ~dU;WCmlK+)~I>O1sKUo|XF7ulQ8?#vYBG@09bq?Q2tL>=nq^qwV`RO`GH6>5t~q zaoWa@(>OhTfsDTy)W^GJ{9Ry-zo6BRp&Wi{oSJ@4-{^1d2_JB^-F3>#|5N%V7{7lx z#A8W+-^Z=*dsx{y=do+n0pqt?#*u#8CjXGOnT|Ki(&%P4D!is}{nw5;;mxjKVyO2w z_}RZ%Uii+m>SJIE#FLY*Q9AhirfBvreV^krP9cYT{6hMRNPnNT>|U3uCGc*=*YgWM zLg^95`F&Z-2AgSqN&TbVHW4Ou)8Rieep5dlUrhb@0mBaWHtNStgI}lLtico5fA(x; zl;YPL^2#5z;)9=2@37qI>FI;+X{|dw@2a1LHQ$Enu6gLC1`i#4q-wz+ahTUyLUW70 zLm9>!^~09TxV=!uZG|;%sa(vN<8+KT?w9_(QTi2pr*^7S%gp@P9(2s842$gQ0(rAO zN&PvOes7lb5`EN>V;zI8bj75>EH*(Rp00sjcWhG{yoG?gNBbkpx60rXfZrTV4r2}0 zexWFM*Wf?OdvuiMP3#31^5vzmC)~fHvUby2vGp7{CDVWhA$$ncrg=OT!2f7EN4kmV zYpQRqv{=j68Q&j$&?fzXdZPZHlQDBrHbm$#Va`F;$rn_ZnF_z7PUc|U8zL9)^iRS3 zRJ=zTBQ~tc^Z#FV{R$FYP`=)2>;XMGn#gJR2dUHCnfz!TGmnll$YVd z$FuxQnj^lDlkk?K4X);8XUd2}&brS%Mf0WJq4{Fy&O^R` zvoT)^3|@GFF&-F4s4tWERA08gbRB`$Gez^&F^6NAQ+=+U-wLBf+K2CXiuR3DL(E6S z{sBHB()bl0gvO}BM>P3n`VW0RUHEXi4wLXrVoJtetpmCb#(~$EgTGp8@mGVwYeO5G z93GdtuO6X1d-C85VXoCt{5m8479)Ov;1~RxjKveO8$z!1Dv@u6?QxuALf;&8Pl~RG z<70XHh}tK&9ra7wuxFN2IT%}tA4dJ<4{IJ6_ft3lSsChcoY^GZI_Zv%*kArN2N za7Q0upTvAz|C_TvUl$*A74MPiQf92vICtT@1)8=TEi~q5iOkOo54m1pH>o-7(TAbA z@;c;9;*2?zkh!4ot%B62_>MhNFYG01k4^ACvivt(9`#vSl`50>3Fs!!?Q(dEf!z?i=YSr6yEjru<Ewe4EBOYl>t3loceBlPXl*UsT_$Kz+x%z&rke_5+_)eMhBBwRGg)m7LN&BSi}5 zXTZ(wt=#6I>=QrJ=Na0Y@%@#o0>#BQsi1j+-@sq<3znJ=+iHxxfBwI$&KeiYX@a~B z%^Cge!dcuaj05^675Fy6ct+kw?f?1=sqYXOUAS-@>?gHm=`%3ohw%Mhrt$c)+-1;b zn5+J#%||hBQKmUI3eA1p#D=0ejh8cw_FGN+*J{dpp4=yQ=ebX!{Yw6#cOcW2kdZIs zHXP@r%-mP|ZT(ZOP5|OS9lyI<`tDjk%Nxd|4@*s5K$%baci32aIKS-K0Q(g7E7Um| z<@Nhdt-VdhEvLBU5*O|Kcae#k=V!d;oBX->`*_KCvvG9B&fkaplIHKfYJ?X{n5Hii zdYuu!-jWAcWYG0O_Zji`S@HK<@&hR={FoJf#tIKw@&grqZ@4XgPJ2Y1^@a~w;jdcZd&hgj_l@_4!)CD=zHWjyeAh&8_}+=$@VVhEZT@>JTnq z(ZG6Dx*ZPl%b&Kww_3D~JM>M5$G1-ZLo1vX8pel^@LyTs1tMRe!z(R$i7R|OzUw4> zz7-CAupW7vMtHN2F}}-1o}o+RGqn816IS{cEP9(o@33gRzoi0IfOw^f~*Y1k4pUM0p>I961v8UzsRB+1H7JSd6`Zt{KEm}Cu#YdtycI>i~fp5 zziQFn3-J1+^LyP2KV#9MpqHL%(U)5E>>#gi{nDO0t?=ga$csD|dM=?_}rz(lDl&DQS?E4;FR*H4}PokX87=-L8reDFG9FK3U0FSWu~S@c5% zy#DI=AGN|a74Z7INWwpDg@47OziH7wwrD!EIYSfp-iiGF+X^o)^wL)nePRNiDG>i! zD}13v-)+&|g}guL{(ZsF!vNM?p@mkJQg~5KMYA`4zv7@Tp+9MJr zqhvm;nz1p){H86(2>+EEl1mBacziK7Th=B-8~Vmu7tU|Ue&@FCtbMsr2Iq3?PmlJ^ zbiZlA(nM}Mr}MhO_HPSQea z3Or6FQ{Ez)mC0~PW_`l+ zb!W56j24eXp9;peZtLz|+uGHrKDF`$u%0(ccWYN$bNlUU*F2yltZuozVd2dSm*kTl z)G1?)-D@BCVCxEH3AnK>*9g}+WQ69jb%1cD2h)zmh-&05s><^^T9z6W({gLJw45Zy zjFfZg9Ok1yUIu3m$i1Y*U*XDK%leY=U*==~jpF~R_(O+$+I4(Jo_amwK2*M&&&bnm z;CnLsA)En!*a({TUcMj0KO+9nIfrg{t@zi8f0Lha*4o8CA^x!WgZe9TpmAKjZ^jHIO4(6l6J~rXb4?t%Kax9gYM0ZIB;Q zxO8_q&h8+W`*M)wmQq3=6M3-{;zk5dPRWN+Ifn{3-U+x%>0Sl-t_D7nMDC(=J!I~+)$b%`lum@N!Or;n3Fxab%w6I)Qq)Bk-Rx zIe)yWMIIEKxpwMrp)2675!|^B#+k!+X%aayB~RALIA*8^-ZfAz85^z|5|DnT)N0_gZl`L-*H_2A-FVQ!=*Bw z$c^;Sc!m2YjsH?E@3B%Y4|Xn5-kwq}5AP%>Zy$sJK1}lf{vV?80{1cM4?34eK0@^y z&-GX|p6ihq&-wL^=W;fV=X`g=?dA6s^XY^8amru9@xFudcY{&=9F5b7+`s!Layf_KK2PnM$m6~u%=zKn2k{n& z`*xNKTpZ4Z=aUP3EK1+J;QVrdy+&S+lV2_{Up!kb5b?%>Gq^QRezsg7;*CWv@KuTz zE#^L$MWNu9nzb^4rLr>5jc6_p>hY^hWkyz0La8z3`cF;G-c!0_l)Ot4*`=pY!(tbhl3koj4rIcpR z%t4}`Q1)E{;cVF#DH*0uDtk+cKd7)6){9Pc3O0)rKhhue>gBQw!_zKwejB2&t77mQ zRU5XrY>(*aY@dR^N$FtMag&BHO~mT574;-26M#1_JRG4l*9OPuoD0rYw#Nu z==lvyfM>4y*I@7SZuL6`-Re0!&%*JoZq-WGFIW2J&RrwQ_L-q$j=3DF->Ps)z^@G| z8HxcHdf}+wvDqvu2Rl-!zZrB>M1Mtiw4b-Vu;&G(Il3|M9^&>VV6uLHC$eHwET;|!PpG@oHZCD_lp+$v5oo^F>o z85spCCnIY~?OI3k7}yTw(XDQ=^Zca8mM1aDGLUpTDusG1?)7%SUyDA2;0r}X>^>~N`97YiJ5V9_*F`V zZma5FU}J=u*1OK`^{zR;9)^5lV9?z?D=UZdXEdHZZ_JxD6i0uf66)uTWrOa<%eW7- zaxh8o`j7WeKDQZiQ@f44f+CA>FU5;f`uMD@&7qZ6nYz3Y*j}P|cNlpN7;y_F&jql( zMC}O8^2z|3dA?h%Nn71xe>mtKyL8YkNz&YNHoF-dC>je-8-3>VZ>F_-b7np7rGD;p z?j43qDKK5>3Uod8qYUm-?9Md&>1tN_jVSMnnXujQG>z?&jmlOd?G-W9@s?D8;@~?2 zPsJC0hURE*O38v=8G2oR$41!g!v4~WG#gXlD=S_P^zvRAh&_|a$aGNL2FSZnIQAZm zje&GJe6iU1f!ISf}sLhR?;C6#WT}@1M8@;h)gBzAWVj9lbQ=j|FJ% z{lpCz^jSJf&4)~9?@!!dY*9Ku{YU4nDeF ztSuiPy7n{lt*QR_hQaVN8GI9+C-N(y@KQNP9PoX67<1(V@6C>{aMM$6dGAvh9IWdo z-&B;=RKpe)_M2tE(1kGXEdPu|WJ@gGP9t6<%e@ZM0Z&8>U=^`R}ceJJ2E( z8b`I-2*;gimPOB#xZtfL{`;-)ghhYUqF=D+&sp@>E&6XQ`o|W1(xMA3zWoIjoeRg- zq-`AA28&){(LEO3XVII_Bd73+Menz0+kVm;7F}xb@ooD_@AC8bdbyCl*CH#t!J<1X z`caGixJ4%|`mZedphesElYACmexgNRV9~a{BpZj~CX2q;qD?;SXx(|%&TsDSex$Rj z#p&+pYKPAHD4+tx&gjx9vk9_{U(EGjW~W&xEFLZjGumW@DoZ-?_sw5AyD{;=gDop-6kEe) zWmz2IHxXe%*iCWyAfp0}niJL7sP=`%wQVhJ4|}OKoh>k$d_o)DSD`y$>`6>dP8;{<)|k2N<^ z&Nk0rPhH^xTtN3yxO7?Xhg-q;Kk$bvS7a)!EpRKD9|b%DCEpeidq>llKLx#fg%fZI z^P%7>T=J;n)cd(yVDTV6_)o}pE8OW+PlDOI7Tkkn1^j&;6T3hvDU8UK=|C%HGuXoY9EBq|Q_agtfh3pSJ1ZCf-kiW08pL894M{yM%!o|$b zLjTnkaXstBf0^JR^ojpYad%7jA)(KR8xcO&T;W?O+=FVCLxVr;DgjsFu()T$jex&2 zSNK;%C*j^e{a4K8q>8y6r^P=cc8wI@>0-ezAiB4N>!EN9E*HFl8-(BV4&gVwQ*aC3 zVc`@Iy|0A7_p9Q6P~1b}ro=ra?g?>EiF;aHXB@jhaYN#U#jP0k_UC&<3@m|se2*iO zsqX^k=X=D*h=-6bp3V1&j}Z@{ZH#ybZDYhk*gB5S<>-TnPJX@z_+<|o9{KnlJI-NW zsk>$D_LXMNuBjU<9>SUHlwb((qRRwfc ztv!3qu-{3HOj2)t${xAGnKgJI3cD7%7C)Ca-%h&ce&HOvk1)kODslh7(6zw$*s5>~ zSPw(%(4bzTw=a648{A3CE;{Ak)aODUGgLnyn4eqJI}P6Zp%;R8F8E)dbv~5+s12@Z zCCezUS0t|&DX$mu;9--DezLogSr&Vjwa-vfCJD4dCMr+

    +u!y( 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 z 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

    n`>E*#(L(qoiY|PzcT{Y3Ep1p|V6l1pHyK zxEUuDaW7S5D4@(o4AkxY2bsp6++P_38UWh%ES0d*s{6be^J1@@INo6})mDN__Ukmj z+)$fcIGp(;nG#91-Ofbk zF+EEoiq&GG+jv$wV#rxv%vg3AT}Vp8hatl~Iyk)`{lliKGPuamT&1*RVA6-T>B1|r zbp-TomYH1$|D#2Ut$gm3PDO^T3VK|R|0uIO_+GOfT;L-2PUPPo(xNv?ho%;Q48wI8 zOY3hHNMNsy4(q-qqrED%ZHv*N500v;xQ&G8Wk6s(S+^YT2o7%{wB``n zS(*>#y8Z2c!Q*C^m9uD2B?s)Ep$=w7P)s$gP)fc^ciWy~ojm918}g=R@&vl{eN z$6bGO;L^w#m0~YkmiuEBMh9O$QW*GMMGLm~#XT0P3y+-^$deP&x_VL?dUM3e`vk9V zKKlPD3F34$!Tcf5U-yDlbtlP@#lPlgwhs^vkubLf%2`T(u=cODi_WBRKi}l z&(;OvtUu0B51~G1XhsaHq1-AaA0iv4$vbS9U0i({MoO8Ig@-k4c%860);m&Qw7SeL z>EV%4!BWRvgq2h!-OWHXp~!y!f>obn@2A0eSI0!C90sZ?1!Zj9kbN#wJXYZ_-^+D9 zHi-RlEys>85n3*();CKrPHvz|*{MhvP_ZcXbOS`E233X{8wYvBCAwYCqLzJPry=mC z!jS_wHwz#@hwIeZ>g zd~#R0-bIxH7sho=s24#TRgR7GWW2*Or@_9WiFMZ6 ztwhz`7~=dLp}ZQnKpIa(OIGRhs21kQK#-eBu3{2BP%e95Zv+7>gDTRJW1`|Yn#3Mj zQ$I~<`U)ZwfTV8w3ClIt5~R)i3kIfM#!4bqU4L-U_~w>(&e>^f2CS--ZC4v8ved!AX{p{7#8j|`c^eQQ=TJp?1#btOm=!#kM{)^4_F-F{S8TWInT=C=mg;DI1I`h}El~Z|0 z(K@u|^7V?Sa&%hdMO$C_uvT1!_hXG6zv+CJ z={K`mOIQ5#ScE232geHjT3FE78T10JEKa@Q2S_`_)+0J&!}S+N1EiV|`K)l96A+Zz z7sb1W51UU^Rclu*5(}rCcWq~|1K-A?c9smRFubBwBLS{aP}Hl??sf5&b@eW;QZWk( zirY+0Oo05?Xa!V9rNpsgheI3T3s3(H0`~yajc%bKkz(vs)pBrIdZZ3`dWRozH9Pko zW{i;eZ69<;LG>Y+4MJfd3u0NvURXE#?z^BxsD!RTDanXrX?4M0V|)M{HDR8u8Q0kH zWLFS}bEu0sn;aU#8vz1s-1{VzCXz25h$18ixs?PNbJUThvzrYHwmr- zma(mmEpprs-`V@jxJY96tp!cS<&xu-pmchsgcE2Ns*gY_jVor{`1Wmlen27x79hOk z!iVD3y+_q)l@cW!s-3lS2(SA_r4cNX#{9ru4w)Q)U**VBb2Ux8aO*(HM~aUb*{SSH zh*MSwN}@6!;=5=y{qhbEf5`D!te203X|}mvgz5763HI?Z*BYoLGGDQ$gcbS+s8h}= zCg4p6Peo&P{o}MwfnMeTob`e&aSxcZat6GKiC|yZlFzw8;}fdnfVFk>nj6BSq^S=T z+$P;15GTC`cM_$u-k-n3wjFld762~d08O)ES92ba;;KwX-)k*qFM?H(CNsG@$MFi< zn`;Gv4-PPN3+fL^RXgWGNXmjfjJ)EALqrWXo6-F7MMe(c?2$Q^eZ1AN5mi)4PZ;g& zSlhFGWR3h~AMoJ7LBJX778Y?fh|hIBoaUf}R3>DVn&Ask&ze`_aI!c2Nj1_xkVym zbK5jE$`wwcqT+gP6h`&&aML<2IGy&!Db;_+cPuki9K!Lhj!hdPHZ_w+rCGF;4=rXS zZ&#$1^VjoPoxs|s_wtRC$&itr5qxq>FMoW(v;999vDQ3ObPryYFs|0KlP%sl8e)_Xtm6DPZp7<{bI9-6MV~KZR9viZaeqmjZ%bR84^*x?J_-g-J!!-WqF}% zyk%_Teqc$x&u?Lun7(v4(Fws><$wOa{a_oG07h0#flP2&Ic5`B=VChi8|C=})Jv-! zRw7CXrQNJKz9%`e=&j%fh7iuFPK!s4xMMG28jIA^{&Rv_)~QBTr-A9(xfiF%4r@v{Ri}Ss^M`nbd%hWQ#nVB>KNf_?Tl6kFQFeI^1u?!x8Jrr)*@_M z01w+{TU0ejH;j37(XbT(B078*j35S^6K?Lmw5$PPxhQ3hQxZ?$U;NuZY@!)tNqWb{ z$Ww0BXyIYyhr*dzOee_D&=Ag_3IFw|_<8mZbJ~hFj!1sJn08nCTY?f56mR9o3~Oq45IVe64p~ zg?u?-$Jak_9N`X0^v$dNO)tl{Q~d4gK`MYi^GEm7Y7F>?x!g(+xB2( zErq@(P4_Fbf%Q5at|g0L!gV_R`Av{|iaPeG@Db9v+ao}oGsC}Nm7H&>fay2_c;+jXx8F5mT#Hy()*6M zJ@Sf9K2+Hb(aA9cr0%{^&tK8V6PBHu-!iE|1&S@|#GCSQJZsO(qNA@M)tqbv|?KQy?{t`atl_aN*=+i4;}WAn3iqg{VzY zmM2tD$Dy1xQXh+NqGC3b0#^m8LXQv1hR?7U1_nlq zN`&4hgd~46Na%@&9|8wYHmTf-Lbk`8N7U3Uz!$LQBe$*%pEwHNe{;W+83iN1z*8;F zK63$hr`&9X9VNw7XqCYrW!V9ReaX6GLwX~-l{$7Ubsnr{J|HlSH)K4=Z#K9zVA8Zy z2U2YM1pX^=0|S@*O8HGh8u`~t<>C3cL7-crX-K;3%uZEHy+H}6kX**&APkTEehyDP zM`b`Bd79vlkLk;*1i0CeFe*$JM8E?~H`Ci7<&JQNS5J!1?qt0051!AJUJ*}vHB*d* zYX5Q@HBpK9lx3If{=EPxJ?NGrq~p(lvX6621;flYBA1}rNwU2MD<+_CYagZ+<8Bh9 zQ5$k12>$nz8lPek)Da)ifa|2O^fx@x_X zK_ri|bwReGA^wzp9_9mX^y?#8Q6-rfZTI6M*g+4~uMNgOjyCR)BWDB4-f8>{-h3hYqhtU#l0Hy@wnf0M@cuR^3S;wUPJ?@qorRlGiK(4M!Ur zYd>JnA)84@JgS348v%9>m6)P&+h zi|ctLYDl{bXC!A=U~O$Gj)bZAO7@D-|i{s>31 zgk>EKpXcBN`5dffvvrn_CAKM3Qc!wO(TT?e`#P}tYnGk-u+3;8VfJNYr^Z&j@S94S z;*8jcaDohnQFkNQ*!W8R^ld*XHVkKlLlcA5=`#O}?9_kKGEGkGc2)Tew^wY$)*{nx zaU*C&12-o zq(`ZaP}2DI4G+l&*cz0C&$CujUxOVmwv;yX7IKVjN#t#lv7wi6bw@gWra2w;%r~l# zQ{kJ-nwNbTN<1G0^y6=O8HBuOq;6WZdQsZ-bsqk(b3-|pw;mTZH5Fu#wu3{D&9;oh z$R7`LyRR515{ZKq_GdM8Zd} zL{HtLu(FMegO<}OINyg|9AwB(JVD3!WLTz~%guM&IOj07k%-uxDRB~>-yqo(qoBPC znP&>&>AKJxy2y=o5a=+Wlk}Bp64jM4f7>(szk0sp`cNArb({EsG|2nfJa0RivTu|O z*~10amJlb~)`)WCkNne8_+zrp@hZF=Bc1yv-HqhTW6!PT=})+dAV+m`KPLxWjEru2 zk>d8%aM%Fl58im#w9^<`Btwx5zKa`(NPd(B(CpH2CdY8=ih}`C+vRpK859$cTUwNf z;!Z6W_Pl5AQPco~7G@$jNX~_!!f%F4j@3fuLdw76BP+D9bf|H+5VU{TjC9~%LhLCs(1o5T}ImgOv0G+7}8sH#>aq{yyl7oMc_LS$Dxe)busugM#$7h zormi{l^G;_Zt2w&s>8k_7{Q=lG?>CoKc)#+8+FEs?(n_58q?}-f(DRJyJ4)mK8%#= zX;>yM7kI4{qOobzg|X9%DN?3bAkejL7pwC z2((}-5iBn~UTD~lJg zN0El!sR^})%pHGAeHQj1(D?N&UG4t|03s$ut2-58Ul+Vdg{G~mH)@N}W+k>)auoun~8+wGuuW5eptHq2r5<@dz zs@~5`bAM}q7$C<4{Ft^hY43r6NZ@!X*Nt^|L!Mx!EmlG=%&IPjuM zc6kg&O{wN}{{ge)6T(yJZ6Do;&k*$%?*&;=6-gJ`{|>Xcu6f3>ln4nhpDO)Tz(I`V z4(`IB1vX}ln#{@T9W=liDZaWFqDbvlxzTgf3;0vr=66{ z6pwWu?<>AH-5EhHkTxf!scdzOE#sGTk@L9A%XGT%S8$2$O~w8`VB=dTHj!`5Y%{h* z49{ho$gSge8D^Jn34eu1NO4fH))?7`1(2oG9gatlUXIDqQ65?#(WCaTj7 zlS(uo)lpO&UQ?;CC!@rD*!!U`jsVyiqeQSgxBJPZW1idCwCUbTLH4N1b!W4VW1+r`!4OLg&KbIh?M$+mR@Oh$CT8ZDBcc_-gL+sVsO6TJ5n2k0j-{ zq~B$laymJj^h4Z=qdm1Ad*y=>2diR!tgBL#3$Nc)81cZL95VvhV#Ca@G6^+zt#a12 zEwA}G`<1)-gn)#|#%;BECG>1@Cfam{tx`veMl$v+iQAJOMuU+V(hMn=U#TmH42Oy4 zW1%+t`n0%?U+Irx(t#a&#uPVJb7VaV{pFMa3CInolG;PWLCQQQEW5?4%tTeA$0Ql*8yLYDUm<66 zGEOmR1weUx1ryy+>iWVa;xqp=4cd1n;OC}$j?LSWNIET2^;yctN7}c?j5l5>F!fh- zixA5oF-sETgwnFZ(BqUCqTG*7!wN$OENekzz%`FIo7Z@&(A*z)RjYBxnV0}69IHHE zf|kO_o78AOJ?2_W9D7ltdFT#`c-)v{fOImQd^&klr%a?y{H2Pd8P0^ein)_YErMMg z_%DUCkTPLy6Em#5T{=`n1W+(A$5_7Y-Jz~+<=*XMpVTXjA8Fp&RX;oD$aBDG7;3Zo z&aXxa%z>zuZMLLz^Nf`(q5cWD+dR$OTMjM%iHiMt>1hkyPMefuI7V=E&9tRIWU~f9otA)T^{GUjzUf_LZjz;mr zu)JP75PbZjC%uoZ*GN-$ygo^jvwz^l0QQq262u9EI^J`Ex|P(^pRq&CEdSoi&c%9ZF%)X0;a1 zMZmuSepGf&vYR$6lM!>&>RveU&;m8?OfMZ7y$Aur zk9Fenle>0n*N1k0J1GdCI;BLGTC;emzV}_)1~-^pzHaPTQ_-c@dKR%+m_j#p8`dtT z+(o9GoS7sJBCeEW>rQ0Du&fzD@nU7NHT5ss((rg0H3V;*^sj+vPm6{6F(U@+H-3lYt8~Tx>m!dpT^x}|>povvrdr;q zU-XZa#w9$}ul-FkXyKz#8wahq?D0@hzw78&Do_rc!is~8u(;kGdk_Eo{4wQXK*%UC zG0B3fzEI`LbT(jbp=bKr#zRdf5X~8}yLYZPNVgB!3|;)U{wl*@Lh?b&6^U ztEf*%JS&MXDcc&3Xyl3Q5a>py`ColP;ElXJP1puX`MZ~=+awG8Ns?WF`0ZE69)j&b zxczatr4dry9;ERC$5edWH=q7D=%vrFJtaRR{_bFM3Ip^A32i3>-ySXeu}INN?FP_x#+x;ZMRUU4T)jX!A5G3VTm)tRjIovwtBxEB+ z*Z~{%IB1??QvjE71hqR<+qKIukrd99)!>$JnWgP%GRL)bY6(Lj_$EHiFT=7KuoM@XTDhb$)TcbJ6txIDZhB z*0~hw(?1X6VH#c`&=FW@NAl(AWtzeCpqP%If4+Xuwuny7!Hd+>legIO3-fcy2rtjr zy`~*}`b2`C1IkuAzfB&z2EsS{-qWe$u3_&ceZ4R4E#YNxcdMM1PXOZH*9BAnwyzQl zc^d5?a>qM55EXR)z4)q+I)gbR`co~#Vf+$nR5QPW$yosK+^A%{wZoQRK6~N_8Il)( zn+yp_Xp0r&&J6kti}2i7uzP}q(pDV%wa^X?QyLLPZtEAmDS>xhJptzc!bS8q zT4VbdX8{W6;z9QnN2lq?1-!?P@WaKLP7QnTAdDeNnGN?egf;sSGuB2FJn^$PtaW4O z8X2TX@>m^KRb)mb|9y}&Azc8WsH)(T=h_i89it3quJvGY6yFH;az02wJpfqy3>APa z*>XpwAZm1;2Dz+gI_L2jxC>?nO`x@_>TsdoL3V{0TW@$;T?pVC*amYR7R%cL_?Bd4 z#lce4*s~tg3H_%qL0ZmJO%cf56HwJIpyo(PF-slJ{6g)I$g>Kr9ZViiIixqpRNp|R zLScP0)O8A<@H15N#N19Vfx;lFPoS=*xKQ{-@$%8q&&crfR5#rF6W=((Kj2xlnhz(3 z)YIJk+8z4c65xL{0p2L}4*yxly=vqPg?oqj*W&H6_qP-K{7ofN{Ud7-(16&zz3_+p zIE8~CUwkGMlhfhcq}r$X{cM^mb%ODq8w^9#En5s!eqkhzP$k$@{V?oIsJ8GmXZPeh z{m4M{EyYd4DZ+?gtJx~(G0Y5~m#8kPrMB)oajE~1v_mcyfQs zh;OL%TmQxNv^x@YAirUXv91A|7xeQj`$XuhDS^UmPz4d$L-@y7UA!&8gVKRP97bYb zZe>zS9Efro4vu}IwN5B9^0E5yOT~qiEC_goXyqUyi^TP^+ri!CX{48_Y%V$_93)hi zK8;~vqhR&YPLL%A2{C(?&dj&GW1j?l?4e3!(^4sep2Qb;2(PD763Yr!?&`M1|(Vq+jCbtI=8KG9b~+ z?{DD{iEIq>U+BjiJL^8BQ!PkjlA!GKbz58FgNFpC*c(LqLhjh|nEx=y3HTb7MlI(< z5RmjNj|*UJ>%SSnom{~Gv4*?>b2T5>^r6&$EgSi!cp5G&;>?R*ySacdGNoN`S+3)( ztKK*X2UlOe=%BdJ1FQlKe|`+o&qe_UpBDBjl&`L43Cc`gH&doif;tL&vr;Y(GRaOg zEKw$CAC(}2R0^$L;=1{xR3wv1V*e-w(jsDl)GgxdX2tGf(Dsw4H^1HBl?xf6otpTR z8ahy(W_64uy%?}qc7H8B1_HCsqZDrE$j#=yu9DWG{u1!tpPsFEcl-P{w}PDUQNmcD z^PtvlWGJi&k9+SKhjzJ6${z)`07?{tum9Ph#t)b}&xIK3rkWnEe5$VRiCz?4FLRI4 z@ORBcl7H^Ge8N2cR#v5@N#r2bqPFDxhEvX-D|^e&YSm)5G!8l9svIORag?)H*yz;7 zPctlYen`5rRfKI2&yVu9+fL6(2=-b$e(yhA5efb#`~NV@ffDK-sc!h{Pm(MnnKh+Z z${l8Mty_GvN_=)q_QC9EUo02ClHsB5KqMt=#S3o+zy*vWBzEfQxuHE-73XC~T6LSq zdxn?vN~7#l4rcG#LAX1nhGXGQiIPC{i!w($OSIicj~I55+IV87AHH=`dRORIG;l3w5v}hn&%4z1N!0NJw>d2 zK+G@luropwy^%iQw-+VDlFOb%9ut$8AC_b~s#BU6z%)EZloW7hb}E9?Y8Vse0gabZ zQH}`SM6h%RST({awbuYfF*Mg7-KtzC$2W24zO9Mzve8B}3@;`jUdh@e&CnMkhZVx0 z8lP_aW9ez%c^ES!tA2Mf55_$6-b1hx zJH046i`8o#T_74?rUC7Ue}`2#!VLp#L25(a3G-NK6QR~Q433XLXXp-_n?f+xt^{>v z`%0(a>FRALPrzK%)VE+-HNog#!%uo$jlwBzHc(rFWAfUK0$~Yh3P+t-^Y-At3E4Z1 z^}6=N@MVd6-IHr>A)zW};E!UkNnoQ$LSoD&A6Ytkr-Be)#`vPZ2YV(a5#n38hPP(V z3e0cl0wPQkb=+w1d<;gKqD=KrKl!`?v7soC2WuB;iF@S=_j}=#F#OYv)0sDs)!$hv zPyTE-OL#1)C~92R;R~&kUuFmdwK3v0tN3VqQ?g-=BtODk4z82bzO*mCg2|U zKeZ}s=6{8*ReM-8Oo0Un(cF*2wp?2zJATKW4h23*Q_~7F@9R3_zC9Xns0C7C*{5*RP>_o@xn}+p0M-}iTxLiS#E`Cf9oa86Rg(LnMI(x5hylIXyr=iUX8m8?lGB;Zo4(yAC&U z<=%$C-&?}Q3TLNG1GdpX`-e4fO1Lr?PIkC7IQ(DHL7>*9M{eI_uit9`@N)J+jXcFW`E+Di) zS+f=bpWEo_{o`E}(3etX%d|+N1*>z8EJnhV)yP-G6Qm1#G{3Y5D3anG0H*M;zujFR z1)o#T2lwcC1Xt0loZYU?iv$WS3J)O($PM}+6^~NWVQ>d%_V**0zci zv-QIm|K4JlN*!xX08f1{XY?W-+iBH@{qC1m)Bp^gA`#k-JCUvNwhFrE#uN_3g?ga_ zCw#|B`v+o~HvevMFTExIjh7TwBYzW#5ZEankycWLr_%(<;t8S;swlB}`3UqBSHx7; zX|Ol&fRN60f#-D(e{onlcvG87Ud$r|o($L6X^`JGnUK@^HTM%_u)uH()Q7;z*vg8- zn)-7z80k8NLdD^#wTG-jocewRHSK;4ID!{{BBDLh4blG!Xj0M_S*lmP)9MW9up$## zLPj)LsTRebh>vvwzhl}cx|+|sb6Bwx@@Uv={Yg;I3F>P9AUVaZaCz$NoeB6+AN>K< zpX@0%{5iBtALg{omm%dD>3U#oIXp4cLw33l**|r9rX2!^J*A(KVNNpa_0!~K_YOOX zk0LQ~kMUo71c>>FXB35Wo>U9%dPvRB(girxSfa0LC?WkgPJ;#&VEc(Ga{hJ4q^{&! zyWUjZce|a^2L)kMWiiw`ihZ3h2_-0U8RV`Vzo+kY(Bs?-(HES?-({M7oeP>hkFfPo zMr#rc=BYZh=~9KIB_GL)|Km3$m)MaP?awN0Pw#YIlAt{Z#a*O4dfjBZUQ$Atvn&h_x>0b5`rdB!a z`v%aGxwy4EIqaptv3`F0iH4|fN2Oql_(w4WR4MbKd8{PbGi)3*!l0HXyD`7P*~m4# zvlEK8C6e6CJBnrV`OzxPu=LkNpeOteX+{fHTUbEZ#5bEwp+(rMO#(4blUC3snH|zTv5J)!V3^%snmZGh!cT|28w^kiRZn}QQVUQ z2XBn#%ne1T`~u=Wr^thMn2}B&jqRm4R1Qv491KShSyUCZN3bEGJ*w~So+dmwb^jWS zyBbMiA$I6J5LNYBMDVkL&^4BoWA{+8pztY|$tQEzDerXBZS?zcsZSEs{mHuUz5QNX z`cvbUt5?Ie=ng~Vcb5cd|8mgStGO8^iTAq!1MEa}h>|Fnx6gZ1dzKWh>su`rLCkw);d6mbkHf9uKMbe7IPC&w_4*yK)lA{o zV#&4mgv3!-6cPA+9p{0CVX2?UMY%1C$)o;S`QRLY*mtUh%9ENd5Cd^Nu_|OPtz^1f zs*1uYfG95cli_i4uG-j7OzgGe1rT3M=Hc`QV|8!E`OWwii(E=P_>#&i zu+G^_@I=c_gY%uR)T_EGcB+TXqR~H~x!ZncEs{*@Jf@6dfEaiyosV9_r8^L_iaP+7 zva8OG|2*#NPy4HuI7}68eDg71kC}*4NQ-eBF+Y6duAF=8WaEI;R+?(r%Ej=8=h}de z7h%dI3%21J>fd$NA9(ZYR$scY{xEVqN1D#ff4w^DLy15Z%pH0!Q5x++K&*ks&Ve73id<|v| zsUPI|fS#_NG#-^~qIYS8cem)uAL_w0<_%A}cPos(|HKBPwi>=wnOARAB$Ij#Qr5fD zoYi(}B4*q}ntK}&fC3DU#>N6dDFDQ>p=y7rV2wdf)7~>$L@}`3zzi`1mT?Ns;|s^* zUEqjI{Y)Cps)@##sM8+lY%0Abfdgvt@(&)JidjX!ZMVqmcd&38F(KMPjraF*HEaj`=A~ zKa-j+q;+QYVvqTac}r1#`|Fy9mH~Ob&j|rw!m<1>K}^90a?IW(;EXi5;IiulU7mjT9wf~h z6h9jiz+^Z>mAZRjpxof~fD@s`NRq|X(1#3juHv)ka7uol6T`-wcTviS&)^>aY~sT+ z&VN3A)i)yvx4pOQnkm*#>Uktg33Qy7I|v_w%&x>g&&JEIjENO_>xj7ntH$_lRNhc- z&x%VwWYEiL!s9?nl;Q;Xn@aX8lMD{{XuNK0=GnFcK=fd|!5(KH&Rk8pc&PQ30J&j; zWwISYxQJ#$+5X^$?_EvI>sOj9C-To|!v&H}O}9pV{kKX@Z>-3T^377uk#{X<271Z! z7sii|wU2cAkj(tAPSPpaS8?f5*iiaS6MRAyB4s8B+Nx8}>(PhRd`@N@eaQn^_z6B6 zluS8R>;1OS`F4w4ZbTK&Cx2<00G7UhUb|Wa>J{BYX`W=wy7F&l5XGT&gcaErOm#fAwTw{u(##r?!Q)PB0NUOX*h_IxK6sj#^Zy zG56u%$5Q@wo5ud$;OY7H5Zdo@B%HoVx>z9_;~jtyc5vR0^9Pdw;I!F&U=0Wdq-~ok zjG9arMNFSz{u!mC<9U7}CAleKie48%wQgj}D_dYe2K(*+II$mAh_{_G7c&+rn@D0P$0n zH>4*>KKU{ukdWX3-UzM4Y$bYsFSobGuj`SVqMY}CpLCmjRA%|$bF>vJs&P=zbY0CH z2~2>@+z{E8v)l=DA2v)Ih9whx(#D-y&ekHAW_x>tx@ppfn+V!>W{|vmJY7SQUj_Rk zMu_*M5WRf>DJ`XHtIXZc9xIz)sgx9%ZaYCzVHOue&s#KHCn-w9x|$3i7b}w4@pe(z zrAkXm3*>8)VaKy0dL;T@?tlA>fam&GZma#<1;DFKufWi z;DBxnp-eOZArii4Cg$6#jYZuOU;-N5h1N58BjH#oM)1N-aami9Wv7?)afp3j;4Li) zFD4bIzc34#2-k)D3>h)rUR9A3MLKQJVFH1mLVehMK@rj=w-uolK5%y%fVc4^j&O~0 zZMyMBGGOG$O=-vw_$^c6ftDbhS#++Gl0;ewc_7RGvIu&C?wErMq!qBLRj{>S2_fs^ zn^!@(wAsFWU|l8NpPpNM?(2tCYQxX$SI(C%M}$lj^!4e~MI#{a589W)xw7I!>b;e7 z%#;HQm+J$;QOFdgeHbvI+M~^QXRyI+h~3swONAXuszmHN{{YyY82GGDb8W{XOza4vKluAJt6WQwCBq0d3J|!N>$XOgR=zfsn&#j8f0DfPgR#*CY%F83KrAf$OpKy>8z=l zgECu#-hV4_$`uJS4k*cq#->NnRs=v@(ia^vA#Jk9i4>T|Sj5WO4>H~1;hH50F&6Pi zKpevP(7Nmgl;#M^%GV5OMzXh@;}$7=Fb=JA8{L!XVcQ{i4>2G zgYSRJWo>yAUvBwbnrKvUT%7}ab9%xTn_7KEBn&|l?}cA~ z*3`Q5nNiYshm%ZasbhH^M9Ts2`5l0$Q$Cfh$HEeaa^v zNSP#5SqS~N-wJo{PKhCLaX*aLQi*yEXbH%OoD5*fcb4k~>+4`!Xi+T7kIFk3IVg|X z7n@UYCaD?w9!k&@lN|2hA?mXhN3T%j<t@6HJ3TC4?j4LHqGS!Hrl9UUdmH zSe7zWw%Dpj!R^43TI}nwby@Z`_5FR8n}awp5Tw`pDDcVTGhJqZoH3f|nqF}CVfmA< zE4_H0++pCU(V&hxU$JrLp8bLD@YZAOO3E;=wk@hHp;|b##y?+UNYfbW<2K5XhD&c( zW=Aj1sSCi#TyjG%E~Cbw_|TRq+%lu)dY47=;~w; zI(7rr>SH8X0<%Mx62nWOIG#LQINYw0KoH$*AO#p=;Y7VzcqD@ zKvmaWut4^e`x=)dxWSQMW^BjX7P`0x`1p~sj3&9FS+L$oW6XR^NVVkofhb6yprrT> z%zN|B2!(S>Wc;YWu6nQJ_V z_zNwQV}gJyVLlhp*le-1z5T5m5HP`b)L$VoX-E8vR8e!AJ2W35gV*Oo*f$xVnVZ zeRHy$NS}l;(=6eXVLo3sCC9fd!>O;#4(7+2rv~FexTdI@=XURdT7abE3FlPvSVEF;^$k+Z9xc=;4`?;nq(BKU>ASCYV^6wPm*|qr zy4&R7-x(osBR$ZuCDZs*0Wg~Cgc}%WO1Yk3#QgVGw@KkpIDI{bqOCYw3{xSXRyvf| zs3VrCW=@E%OK!`?rrRGbuID7;9``yFNM8Do3WTN{s$jq@Y3Mey?o}%HkfD-KDIQ5~ zb0Qy$47T(8(c&rKroSA$``z57u6mr%!}@P>qoRw44Vr5FQ7!(CpImVt|I0xH-w4_u9)_Hgi$hlV92rhRf%=e`&*pa6E zB@U6@Jg5|Ul?7WBeI1)TjQ!lD&Ft!WD}H$)RO)|Hw7X9Sl{reUF0>w=#tH{m)TKmo zsDvhGxDUr`rzrLXkv<~L*Y*6aB0`osSCP&{(EYDso5FyR^S>79(>0V! z{iC7on5~)W2jLH=y$k!m)v!r7#$V?c!C>hfUJmMW@}YHpIZ+25%PLzYb-n^#-fTCq zJRc=xeV$#R3z#QUxx2zXjV)eWzwKt|(S54#$_~(YD~Show!@M_I)oj8!RQ*e=J7+! zdRa}M0RC#_LUBCH$VC|N@`0UNw~f9r%YDkX8CS7kHjqN%!}X<0!_?<&whRUtOH_|7 z+{G*Z9APQil~17|B_zJbG%vq1ZGw;Ssh3(we_#OeK6_-}$kShLANz5D^HoB=GJC1( z_A%n{(doXH4zu4rvMj^Sz32WM?JNtbGIoNrYZ#|!8Mh}POt2!DSk;h#tD*jb#b)+C zqN#6l&VLN%-tMsGD$LBVQ;g2Y-0Fk04rz^$hN|N``-tM`CuKGVvyl#3Yo8CjfwNR) zailDc+D8P={s2<(--0J_DzSMlCx5Pu> zP`k77PAwgIX$qpL-5XV6YYohtv0lhbPhvd8qqO8S?41l7Mqp^w1lJopdYBB^hbvE) zTUKST25z1PHxAOFQuV4U?7a`Rx=3SJQR4tp^>?zQu|xqr&*>_9^%-76HsClxyxO(U zY2VTW)c80z9FQ>bJp<%Fge;n*iU3YYBw_}%oimD#(!Cp@gA1jiT+gv(g_0kzE<3XG zfd8FAP0b|<0)?}vNfHp}T$B?_Qd2dYyn3w!8B~hALUZv7B6Ytz5~!p{T$M;*aa@`? zT-!wGbdo+RI~%tlnBj*quS(MVj?$ARK!h0+#p zgQ$XEkM%lGx&+I1O@I~>a;R*qc=3#rtKhZV(WO8RP>9lnpc4Gts-QeI(wldYw&ey8VUu34(p2e3it?`17d>HI79WA!oGiB}KB-pwNA9*Sq z;Z6jmbaXdDl*E}1_&@Xi>wlG;M^Z5t@9^{1L2F z)x!hP^-ZjT6j;F2y`i@QWcW2mH0&F{zFg}Sa$EAL%SXm|Z|5lnxaKDHk zjZt0*6DBNOh!Z|CdiEDw+r-UU7G>l#>mhBqFkQf+>Q7@1zA}-CZ0NC^z-J79w>+oelnWNdkO_iGpfD{Gq9&Q)&Rt1i4JI*g@_@Ku2fZjIQ|Bwj97 zs2-e{h||81R&eFaNh%YVnZ3!MQ-ueOlrYwx7_I=<0r>c|L4)_ zV5n9ITqz0!4BC2dj6GF9&z|aZi@fmhI))(vJp3T^zIon` zJSK(SbkXD0k2!HhjJ;?mEjA5i!3&l?5iUd*)&31|gq9#QFTU{go3vV~P-@!SY3lE( zss_Mlq+uIN_zPB%$Zt6n;>^D*HkJ&8`#nnu}#Vkv?&?LGdZ{TSa#k z0G&B195`(@mzYe1{Ppo!dW<3ayqgS-V7A2LOEXUdOefNy;c*LXK#jVy5t|2>f&oXW zD%8vZ(}sq%*Im5O50_l!tj1HbISFVk%=1IK?G=!|o$I9gN+8b=xks6>i6*(dOZKI` zFRdDN=>NuRYB9d-(!@EQI|V-^y75g63E0KJk zB6NF0l~z@{g(I`W?rO|QENq5tU|oa^Jcwaf9sB?+gh@@6b-)7xS-|Op>drrlcE1^_ z6B~`6vg@+~hwlD!+r$|suN95-SeHY&MKWI`EeQVX^6@m8dS*mD)c1nV%5PJj3Uqm! zMOz1wHOBPOkSk*BlY0e_b+@+JY%txMDl)OUs~T)8*!ww-fS7F1v-5W1=3eg5Dc4tz z!xPIzkUpYRh}BTjb?q&tP!Vgew(uRvJsm3AUPDCm?>@(=;B7oOzl3tH>R=TE6nD3? z6->OpRfZ&Umm-~6pgDFp(H|wSWb+Vb@_Z5U&c3f+F2%tC`hfX|{9OHJPyxaKl7tW3 z{1ByXMfLw!ctu`NzT?Lw9^ZdxZ>6~SX^C1@W%QQidZvkKjonWqS}Bqq%5-IaxH5IM zdll;I%<&^>$5A`Q;Mfcn%usA4W5U*Z z@7ba6Vu6=YNonH~Y#PvH8$L)STQuqJK{Wf0ojknA&*%7ZArF<_SJy9qm%WcH-Wj91 zT#}=8gX76FKaUCe3t$Q_vqhbboug(g+Be`KQgoeJq@L;(BCx+a)LHK*VE4aW9X6V! z`%{oWgE}BRYe&4p<_G@JjzB_XD<`^XVFNZ;HGYCqwc;f)!*{`v6Qq(->YqbIYeVS~ z)IC$WYnHW^DWfV1^0f?IVmIK{qQA}gSbw7ZRBEA+rD2|?!nuHYxUXPnrzdwo15Uw~ zMk5>lp*JWqC&HQc%YPdCLBm-Q_&HF$kYP$^N(W!D2WAUWt&qakPC}l{T2byP2NUVNE-eh!uT)4_!jU)hVLFz9+=2rC$@ll4pD59> z<10Od?-hbOniuYW)1`btC&^6ddN7~GG_S}UihQQ@+@{-T!J!kdR7oX6z|@o0a8o{C z)k1D~xD^L#k?8R+&j*Ahvmm)OCA%h;0wvXhBXMnFrqzIqF32iZ6HS>K%~L z?Bp>F-fvYGTpk&btTq597pBdN;&N|~JjQlzJx-Iei>)LVIl%t9kj1@A4gno_>=2G-jG)PM`k@c!(EKZ!KUh%_iy05j`P`rFtE z*;Z4*0>G$D-2EoARUJnSXi&JLwDB?e7BiGUl~bIbTO~)aC%ia)BLRPKdSMPmT+G+Z z6%`8YOqpgn!@-(*IBE>u6K7@o2AO=NkcyC4&hsO7`~&;5HdLuIHU8s0Q-0vC&%mPL zD7lxGhmfHY&>dSt@um8KgXeppU!V2Uwt$O~?J*Puc!iznS!tyy#qrmjql=y3jl)hY z`W$T1*UQ?5U9%dr662`X;e^f(2GE}Hv0E>95N|;*TS!Cm@7oF60g3#2O2C#g-@ZwO zr-+jU@)Z(=1R#Bk;tJ~i?Z24Yogc3czxNVPX--Y%E6o*z@8f^D1lX;GZ2s>m#& zXjeR65TbkT$(M~>K?n^n)hl0(-P|mwQf}&Dcgl6cR&Ezj4gj_opyi!OP9S(5Uq0-^ zNwmG4RW&jO)@pCX-yVN)loCS z;ED8ZIgosp8>d?<$g_~1wQgXS%}DtVnmf7mMEz| zeC>q;Wl8sxJyGJ0AsKsBUsZ7U@Xpy95WD_ma*A7QC(~D92YZFqA<_qdsuy#$?=6IT zkKnVOpD}_n2NZ;P7{u%#wd^5FQFxZd=kRKJwdKO^BurPf_3q_qD|4P^k)6~eSW9wf z_F}XQ;>pDM``t9`KS6OJh2m`_Zh7@IJQ-(o$mB zH=6Hx+{=d^J)Myfx_F8aSC&MME7IdlC+xcK90ZM4RuOc-%pW~^t$9gVqjBK%iHVQT z9%FZrk?{;1%y$6SD4%G&ww*IWvPyD3S0>N^lgUiUQ&QRDF~xuOw!#omXRc#4bj;KR zvj`(KH;(bU9ajHhq7!=ob#1efQQU3_b|q53n(B!Umdtt7a?fRXPC%osXAFXc1^q*W z7bJP9r+*}NS7s5_GcZD1Uwi~hMZ;+z0+Qou29Mt`_7{)Fj9xDCJXFaPj z?(hgGbFdK_l1QdaY}~+ZJ8+eY9lV< zxSSXQ0G?a$Z<{jQYhml_>HO|Qyh?}!z!1n~Y^1K5HnwQhzis1hU9Hyn$ObkgWXW4` zf4}2aF)EWsMmdpy#ogVZC571qNlzQ=FK*f7Wj)QkM>i+1(VL`+)u0cH#r=_}h9YJ0 z;U2d4Q05-+E1d$#=qk9Ii6|vQ&m#!pr*JWet6SMFspRP$V}HiM`(s6%@zldswym=O z&JvzuGbPiq?x$;^{UD>V2O6F2;2m&VpTN9>36w{S1@Ci~F~>E+EC4EK(ZsVoghY&! z{bT@lMi{Jvo#o|5VK;K04t7=d$~IRd7W@$>szju)SV2aV$2**(B`crHvUNas!d@G+=PPs^6+48Xp>s zuzT+iLun**@^W#ZkA-zUjP?$Q;vHPL@x+QDb|6qcvx}dkMV<#>A}pq{!qUse*y)sJ z`w(-IC4MnN^h$NCctgv;$*`}qB9Bov4_dgpL<0%X`2@&wj-hrZ-g5Lhd1Hk#39S<3 z*N=`Q15e&Ltbyoc2^ZIy=3=)~AfLH9xv_!zZZqdf%Gam*dcO+_nLNk6@5hkZVlc*( zlxyz2w+)8Hv6{JtG{S)^33`YLWzSJ~868?;FSA39^_?a!N+*^g8%)UAb4xM-5qnvM z+E9`gMbsb9nD#7bxJXZEa7v8x!sWTXi`oF_3Uc5OIfgFsz8M z_H3_P^z-K>b&R9*r`VKu$ald=Y-08-wLw|-)24cwa2LZO3$gB z<}Ni+I|V^Qd><|rT`#EV&TR&#TI~lpA+wG%8Byg_bX*ec#Y( zN(`IXY5vzY|hD@faQ%jPr|4$oqDr<3F-T zoKTlCWu8KTA<>f3fl8BTqQQ2^K+gV7#iz1&pUCAwvDy{(`XN6NT*hMB0e=slo|yhP zQ*3{_i&X(OGeKUdc)u=SPs`~i35)?Pf_*=)j~+L9HT%wk#rVZk{ZQ;N(Y1oM{UfY0m@9!;|3kw@#$omf$Y< zJH}@)241oh2E*>9AD748+wleCwzNs^KqUu9NyZI?Tdz*>IsTj_(jXD{Il&*@Pn zMFR$7q_PI!j-$Wzd>P;VI4GE*BNl1IpCIHyCr*3GLSE9jzcT~7mTiD#kUN4~Xhu)t z;10!*k%0L0oc;r^F+{igA7#j8Xv`Kd`+!ZyqB^gkT~0nMz?oi3xqR7rJ^tbQKDkgHX&G!537wLse4Fjrv} z?2nAc+WhqjhS+NlZV9+hD(X>$GkTW{R$(mJgSoL&F>)Yzg@4?*{`#8KYCOlxbs)N& zxf=hRzWs-^*XUj%Y8#PKYFNDV-pm*`4)r3D(oYDHVjQ=ierA{2pghRv++n0-iwZ-K z40zd=im(?CG8YkoX`Otj{hQl{pYkxo&H_z?xLpun zxV#0T)bXxHR=KQbc|nlwdD=RWdiLAn>XAryN~5csnMohX&F3!T=Xh-g8^#Do*dYHI zigO*tq12T#_eHDf1wP;2XtK8NakSA1OG6el8n(-LW6GYImG+N2aC8})2(wA)SJcj1-K&)* z>2M+M7Tq?9<({v_C=AnQWW4(r7Zo0-w{kuG5L}|czhZeSr@0?<<`LMubZkYH@1oj} z!vFxg$0#;})LvW>>U{QjX8)6SNd?Klja>Hsx%thr0fU1>EuoNe$uh3n(DPwqat7WZ zdpj-c(;fc3ZrI~F;Gv&qud8{yS^1~mD0S*IYbD-E8wZscM}|gwXfSZ>oCUVY0B8_P z#O3j4Kh!+dNtkgX{|9aX0SE{^ilK4eB^vKwhHV_#kSUe&OqSCZ=$0}ijAb~yL~2^^ zT5=LYE3d=cvuvMNVJ{Ltau!cC$kP63`&%N~ z?{>Mv-4jwIwj&>DBDVF0|1NIO;32#*fwjfkHdsXGxcB|jH14bBnev2&n`cD`2WGBn zRibo8+5&=SlVD_BAFSs#w#_jpc$3Xk=|a1-vlVg@F@+UqIx#iY$qhfCOEv@A{d(5U z4D}QvaYuz@=jV%#B?B^FsXN;%Cwv0?dDnV&b!q){B+_z>-Nm6rkWginL9TvWgFYlY z0;xAiHe)Xsk~)gzDr#J(KA0r^**h&v;*JGd-A4}@T4I#eZbjIcLwKOE-)M?fsQWHb zPWmD2Vifg|+l&II|0yE4K!;e`PW`LvqCn34SVlKL1Wq<~L_GXdxNl2lYD##IGlmD` zJhcyoE+KwI&KP&#%-4U(thXzawUNy%`p1~PuquUj&w|;mb~7}Q?Z8!5K&~Y6ejmD> z#L;Qfx*>!0#J_l!bfMUcHM*am54CEta#H#Nal3h8y{YaZe8sG5QfI5g(v2n@E3XWs z&b1MlO(JP%-~c&5#=pisggxu6$y>Fwx(1sHvQ&xkuwOy6iy<*7lu{n2#KM=kbd{qp z`vP|XPcj2Ek5MfUVeZ8*i9$TS?PjKW5XJ_6vOV(2(Fm7pZ*2yha?oCRJEf)2+{W5- z((?-al){`#g*c6}`zhU!!ncnsQlc_mp$}p>So%gzhfB}J>HPASV%?I`L3^Squ5L}d z??V`FyX6K&Q=tIw^-Hb2r7U*}Hv}S;>wCdGAPPj^q9e)th4WjR4?rs8$6BHG1~ROBq$wz zMH>ZLYnL1)6@*+rl|7I@doGDA6t^j;<@=aUE-6o`*hkrTF8 z(TCKX_w1V@Yms@KY%)hmc~yO^hnQKHrE$E!8}M(tfsVrOd}A+wH@3yDSzcFOA3Ei0 zYhK%q(uenydl3#xrk_d1Xvb1tD2FqIQeVhlfrSw0lFk!s0VrNskGrXy+=bI~pjy9M z1b~^4$QDEm2?5O{=UL}M;N1a}AD$mJ+s#Sa>ZxOKxh&1f8f4*>zTspp$5|C&;rT@m zDmsPudQn{4Pm#SAN>{1cIJrO#V`zhY>>TEQoa`1( z3(%3YECgk^65l`nY6wnc-wCDav8<_Q(z|m}(l>JaKWXAW0hBuTBMx07M1sdSHJ|zu z6-Nyis{%Q0vV18Knpy!ZvJmQ>w>L}jD|RCHuZmwCYqArbg!Q~`t!7*sT@tY2NR)%1 zCREb>JdJ^@F`5;|*wqIcAwL8_I$(fbA3{FYD|%mXleC@ii(c`@849UGtg^v#4Mb{^ zBGB4E6n%)%UJ%KoP1xXlbQ>$nP$a!jO62Vh=)hZ0ZS`nb(Zg>thAH4OKLv)Of0Nj^ z0%)@qstnUj?3!iYjvMLm_NlY{=HO^7yZYIi!D;hak7w1K z5gq~{ApT3qi((g=`bU4Fi@`ercsT#g;Z#6BCtRJ~RyauNIliEFLxS&gP~X~2%11_i z#sAJ`-|Jg$MRysj0UscttpQ($K045Y6BkoJ{wA41`c$Zbd8b6so>^pf>~Idvxv|3H zIg_{1)n}iuTiH;H7wo))WHqWoKTM2PgLa~FFz4u>h&7Il5iyRnadk1sC>2oV3II)Y zQG7ELmjCGUps}3mor*=zVuI_=>ZQ35gH(C=&u4-i>duDG!<6vo^niRhzg?W%~;Iww2RGVJf*PI$;?6>?>j;woBKs1%x2eT zbFvZx_M--2;;WI4GYg+;fL;%1L0W#;;xbUeh+xX&BZmLn3>mw+0i!{Q@Of2vrle+j zM&7KhvEJbxjbg#p>p3qSP$yH#>bBF7o87@^jJDxkpX$Dti0i0#_Gx6Rne%!G|BR*= z8!m3l*fTKiT80Nnr4tCe)`)%?;2a~VCB;>e=-dUDw6-iIQX71ct^iKCvo9=Gf0u>jv8jqp<%*eFnnn>A!nPX z9C8NOD3oq(@FIn@Y>DVDI>p@M;|z+r99M?#Rca(zUT#OYt;G=kzAcjp{uculO-h;- zIFvXTMW@f?7SkqjN#}{0w8wbE7(2)aon{Y6$Rm+gVe#_;J_@5uY&2V#Ku1Es>sVO- zA4^~HDK36?6;>CV=T!+J3ZmG**+faHB-c+81fm!m+*H6KCp_yP0hgtQkJcl!UoZH0M-^Eu$~6!5k#svU;m=eX^qt!vUifhkqcQQgn2mOD&P?sofos)` zb6<>Re1~)TAimAIL5N1fIKbHmc`(|4D!_6_u*pGavFNmh%U!TrdM5qy8%`fgYKnx^ z2QTynb3GhY1Zt&QS~< z;4HFt8-SZbr?Q$rkJ~L#dn}3l6!mw%Ks_?x5HiigKjP@L-@-$B3m0;s4%TKhkMtq& z<3ijcJQx*v>UYIgP5Cv7!L@i@!#>shwNUOvB%)Qe*gai!DkeAQg|q)~ zU73xUx}6D^@@M6vh6W+D1Dj)ZANUE_P9n=l&v|mmKZIVmIfo~b9$DYQ=F-Gpt!|v8 z?A3ktE1Q#6&7;Nof;mPhS3?Nm;XZ&njtJ$&{skAW;PAIwG&5QHlfP=SY`mFbG%WHv zV)7hF2gzUqrQv3cgf_?d#yhF*4@m$2t(76Q5@UT24GCjWWNEaO()k_EO7>8aoz9%<72SMZK=pn>aNWmZAzC2FaS)f) z68bTg`#2Y7b2569@PfJVB*kP4sU%8Ato!j**Rz^rV+$@5o*M2U&~RJ~CFeLJ*Dq=y zS?_SNj7pPwUnCoO4z{1{M$;!s!6uZILDjQ_bYe%vO|;_Y+rz_qGb+Dn_Fmln zq=Aw1s3w9$H|pUa|57ts3vc){P6AChlg+^Va~k>fpHuwz;!7pnr%fJQ7sH@I9cM-V z%Rx!R7-)Wfwr|_Kb1%o5MvMzrqtgmBwyNn2oV6q98QmjV0vLUgtv_rhJjkt_AFPU6-iS3!Eq-L{sC{6ZNa ze?c-mX`AlamqjLV4HEDme?$*V($h4P$K~gPViOB}#^lnjQ!ddMfzW~Wz?iQcvWm;o z-W^kMk`7CZe2!)I;=)=_j9CvgUgSeitGC_!X8Ez3Ya6dFh2}m?qSRGKxn^d2qq5bC zj))HXPObDg($+ce;O-Celt-sBB$}{-2xkC4=9PAY3S75z!DL>1x(hCECc_{umBH|g zRZ2xB=JQo}z0}(Wz5iiR-hbOxb8DQWuyUu+lT95ul6c;CW#|V%lEJ%ac_2fpAues- zL+f)%5+74_0qxH81qCa|DrG6?&s*@pTiB?qyW^}W3Xpj(Tapy=$1UYEZeB6stKT7U zN<^y0>9|oH;`0X?p_rbE?c|LU8C-Ie7vM6UcxcJu+^#l1+Z98lKBnms|D(SAb!{ZMoaaF|wVDv`; zqiM33bz6{1`C&=c#M`R`3`ZU@b^4;k`eqDDlc4HW`AO8r%q->Ht6`1|^DniZHlYQp zw!BJhm**Mf{*tAX)xh;J_KNo;V%=5}g!RUtH>3Q$7AmGTj?^js#Kp_q-HU#vod_rp zYya1INHs6MG@AR+eO#^Z-j?bu#fzzWi@ulMdX>kO*@oUcjTMeXX(!YtsN<;~pp|s- zi@rPmqxK_s*}cLT33prN@0)~vJJm> zV-@&Ni%%$X13EF~4OWk9OP<Q;;h|0 z@dQ72OOH8y0k?5Wd*N@?C>mFmP2z<1WDvU#yx>4w(!H+}r7{c{5)0T5s&_hVm?iGIO+c4vEj`Y52M zf*UA;YEO7!NWT*caSW~Q*Z9m2cXWL#iV4_9p!`KZB=gSOfuT^I3eZ^huki^Is3fv9 znP(SMADsz%%Y$xe3%`$UJ=-be&<5MM^7BJZr}J}@=&LWtb)(qxMLlXjYRv>F3 zDCXh|!Ft08V1nRJzaBsEsj@B%zt#gC4b~hswXzgeA;RDu|L%%#u0I~-tOZ7Efilie zyMksilScRMcO058F<_zzd=%M%=@}<6UZ9w9LN73J)-jaeVeL?6`nnQc#>i*oj{P*L z>)s_pzvs0U^4FB~yM+-z_zt}pdfj;W!baq^C;PU#ynVPQ=fD1r{(ggFLy{)&yWX~wPgM*DnLL=G zjB~+EnhB`t77Ix#L++4aZwVX2En-XjkfDx*E|861fd6VdVGr`Ga3I0*h0m18l;@xX z->>f`(!2+7!ARDJ*1qbMYR;latVTw$ba0kzCRlZygxA>@nnvl@J>LC(^w@s*j~uo8 z>BP#qQePMDb$uOB7M~U#0E@vdt8-zxzrj5 zeIBN1LY?~T0^GzIYn-uOqqJ?brX{H4KRs?NmG{l1PBO}V;Cm7I3gKZvVC=K8l8G}H z>925q90#=|IyZzk^|lfG3=+Zose)%s7wGEHVi&TUSpkRpRF0>mg{g|{{wct7#-P7u zn#K6;q{|6JbpA5+LhA6Q>~!iBTtiX%I!GR%E?F+yIvM^Sl_l;cE19(6(B?Cdp|3EY zi2ytkF&J==LRAXM#SBBG?$(e8uEdPoLqTIz~4d(u^ojdTS8H|J>Yn~jnduDggg|s4*ZiFeDV30SRjFgp? zJMk@6TT@^y<$r*2R;O-eb|0z@y88~#AZT1Dq>S zSCd4}bjcFu%P(FVmGVt4QH@g7A>$h}=kgd~qEp**s%mWc^zS`DqX2+{?dE3D*6EYj zlKS8IoO=5!$ER~=EA4i%QmW51grRL#h40I}qsH@Ehy>Nx@q73YZ=gh_4|eU>nB&^G z8^`8}2pJ#v<1#X(JaL~9-cTv9RDL^N>-Vdupf#4q#p+f^I6B{9oJxusx+52wh65&^Y_NyMH(P?7(7qs^0x0oJ>mLNI*8g3U5QV ztxWb-wM6uC%@Yvn4J5&-Haj-*Zutsl%>rk$Tw^+X!8`IDJ@Nm)_m`hAAF9~fQ=1CC zIM}xFN_ZN=M$!zZ&3ie=LRH8<0v+_NRRL-dE&69ju0=7M34Ph!uUyqYQ7WMivu?ta z{@!q}Z0cWqJaUDGBp?cU=V>x#eKZqbV#jDQ-ZU^a$d0!YX7SEndOH4M)^fHRfJ2Ht z6BXnfdkAm~ZnOReks~2jNXDt7sK*(Jib)bBh|&?IsGbg|asID}?x!3(CntV+lI?eN zWxYm0CE1H1bDyd;pdz!#aVT=q%k(M+3S*X4E`tl`2brB&-ri!pv_5bZ?Ukr6x1zRX zR-NU6paomb0MxlCTEUuh#@8p^e7@)nNBn^WYfos$%w5%HgEVqAt2tyDXTARwL?9R{ zue?~4+Za$)5o*L8?&9EebMc{r^vgh&^mj#JiyDj!{L+349vIS57KH9!ZujTq;R%h~|Q_7>L1!%O*UfP%$E!k3}oq@P>{PhQvynH7An`6UgtDbeDw* zz@Rez!{d@J=v2MJBkM~7^;!!JSWrdh0n`@&+YQt%px!w2u^!OyqiZ1yy{?3QLN-q> zMy?}~xdyC;e<{lQ>E7Fr{%^e7JV-rm253pULyeupUq#DzDBv~Gv?a1dZM2C~CNr3W zMVUf^yqA3e-vu-;52giRK2gk}AxCr6Nl(J%c1eT`S zWhnjma3Z%M)228N9*BN(>;##MBasv4PjrFIAKkB5#DD-z9lnHp{)OjvAUtJG4lZ56 z*~|C4$c?`is2*eW5MHI6W44;>n5eN?f@V>!Jp8B;lJrH0d;@RhhvcYvzq1Y23yq@| zD*Ji4=i8^-l+)%q-RWO2eQ1z1{TPvs#S+%}n4nwDfp&xl(5qQ3JBs<)mEMcHlJ7Oc zK1Or%I|;ucE1VB-nidXAry7=+w^~33f%L4V=Bx7BhZteY!DqcMZw4W+bC}^jdKZ60 z%N^Zdvn*I3iUf34nn1B!yrWlbDQw{rYr1{GN~U=9yoX^(tI6>`Eb1N33tZ61b) zg2OSFAl=>0VPu*1zQ61@REv@++QoZK#jm(dOG+y>F!dRpGT1J`8UE2>u&M=E71<6> z%1#i2=H^%EjT5wQg<0~0$TzB@<;Zsif+1amSZABeoctRoFRaLxktRk$D`;1+c^M9^`d>|A!Ks`_OIv)z-nn=vDeePRGkPQ-d*wePxW}Zm9lEHjV zkWJN=nd*iuxmr*K!fU4tLqsA8xxiLOU*E55k>b}ycNcnbZ6}lB#t<*aJ9T&wt_SA2 zWYh-w`5kngPi+GHK!BPq)Di>Mm2r99bm7`sw>WTN7(ZwTG}!*T(Z5&i!cuKiKFbU;#|f!1 z5a_*d;|}Rhde%ZkE*m@z6>YSw>BXWFcQivFmBF`Ba_xAat~{=^)Y@2sVyVUfJro%S z06c;geu|NJp=AqOIzV#e@YI3?#cKzyy20mcd2XwW@=?)Y*1o@gYOnkA^j+hQf6CkkG_o;xz(BXInlhLL)Qv`>O%N9 zQgy5?D&^(X7yT!tl~wzwRWn5B7w zPTvVXFN#hZTC3FNA+rJ*IjJrVu7)tjpaAW1+;Mk8c2sDxja|cBhWN2;XWt}1YAYSQ z4JCb^9K9HY+Iogw?GS*2rtcn;tj^{#HQ%_$oA3{vH@(?dGZ~>HQD3K8nSLt$g*+y$ z%zg(ldn8(GOL0!hi^Kyab^>S(p$XtFq=JYATExpSomScn00g`T${*jJMC~@cpd$Zx z(aU1s4KCa+HYkqMso*%&#VhWwqzHZOEQVtK4!S(I5wo{<-)psY&e2<1|w)PszJSLfyY zn?rcWew4&DKCDWOV1V3m;}r=iz08{A8W^bYDVxez3vGKdkp(AeNWQEHr7C3gt}2l6 zEkCIL7m~#6Itjn7)8ZUF$ZwHynd5>mPe5D@js3n5yv`m93BTUla(+y_>@?z*ipK64 zY^d?2vT=wL=n>jGcMzPPbV(r=&u<0A!A<_>O=sv56!))Yja4)N7!Yo1(oEUY6+pkv zdk|^86%Jr4c2Izl0z41uM+4dRECAjqxi0pA5P>LfLHvki>9y2&Q~w0S-PbiVS%otJ zGiTvW!mQ>bB({S{2n=)OoS5WHJRfd%>Wp?HyFp&Tin>1KK8A^_GG!Y8l2+hi!H??| ze}>=9hH{SjE6Lm@~zo;9ieAf z`uhgRf1dXO64{Jsyere;4j#QTdi?`IxpFRJ{;@g351^@bPkXDb+9K?OGO+B?{lX|7 zLMbuTvop;T9iOaoC->^cX3{c|U^Vn8G(&21j0Gx$H2s97%N;90D5e?h&8_Ng_Y@ec zR+{yyeNKv*(b_?;==|?yh|ul*t}2DVV4^Tx8%Xv(&sN`4+%5I}e<~_XPZ7-Oa@)D< zk`AB2Ch5N=YA4}io84kKt<$n~Vnx5d^y1c+>Fev5t8?6n-l{uk{b6sr{3yWxH!kZj zSy%hh4($PEP-9b~(Ikw>jer9x9uYdZZ1i7PqSjbzg<;X>+>i$}Zn+B$%hlt?ow!jq zVbE+Y?clssbYmMr7iyf5836q6FtSLO6rM#{)_S-sHYa5be${Z!(S zb`Iv_yD?=;7^t-RrhR&;M?EVpW~^I2UW_LUGXw)pU(IWrsuP1a={U~=a9R%-_3UKx z4Ct|`2XOb^3^n!Q&F$t*tXxe#1D23`Og1A9SvJN~F8oz-SMH zyxPv9=e`)ti!}wKOKnjvnvnw&>JB!X^-;GBr0eiOg;WLYdcwLpxEw(Z zyR8;}WKW2C7p`_yc$4Uvp>QAein;4Sxd^zOcvqp}BcKD*7Q7ZD?Zpxn3W_5UBEtSj zojnjeOY5u4A)TGEWQXNVx#tiCpj9v5Q(gn?e&ipU77YB^`DQtjo=G&9)}>Ye@vjK$ zSkH$7#PKBPDTB#fR%oKCy0eBOS&cH5O1tUuQ?iFj_1q)WFE((s9qva_Y&9yaHFO)8 zwKFVjO!zqml5EFi5M<3$qsz?XvPQGFL;;dS5+fx(0ORXl<#tF;N`E%+3tqsN4am>c zxWEyOxgIeOgf=eVLgoUUq}!r){h^iMN#^m=t|w~|1xK)|VFA?U&7FQTUF;&3S+~Vd zgQR9w$3v#~U9v^MmY`d>?XD@aeZ2wRz~{{ARWdTa-2)r2PXALMOUCRLom+Tx#C1`M z9Ik84oOegR(KtSyz^xXy6>HqB=@Yz2!LMn~#LSy>`PF=@@bR1K#tRuoLcyQ{WEake zPZp-_+x}tG*1!=k7Scttep1ksckm9s7Qjj>T&z`Bu|c+@Xp9PG6}0u)nyF~=yavU5 z(HK>lEtD<u_D%P4U`QwoV(7 zn=P}=m$>U#BPOx5l@T890Nx}hrau*;n#-Nz1YN^mmlQ07D`}i6zX-a-I5w(@5i(-| zqC{U%@LLfT$biYF=Us59G(wjuV3{DMV8C#VFF%SDfQr5avrrbVGPE^59Wg@PQ0oMO zOoKg-wjRQ%(lEL++m~%EmsvBw_@!CJM4a&k#PTsd|F{wWO>8Zp$AjuslMab?zg8-l zVpOdx=>f@IF4OawDrST z!Ypl_D19@{$JXBv+I-M#k%-o7=W=xzv4`Zl5a6Q^>SYRW$nH`F3MV;c+SaE2mm~?D zAa+idovEL8ASYK_^mZCc&l7B(V^f#P5Y~`={Nn>GY3E{X_L+^lva0; z8F7DlnZ2BOVgN}xVo6JqWsBmdJ*9~wtm^T0ulY=OjkJ6HdN@lV;AY# zf>7~{G);9XWJ#}>!yTv2(Zp(hZR6CXm6-KhA-V35i# z$giJeA0!GeU%5>C%cGWFt*fjFk@c1z;Fc7{peCX5k$^aVINX?)AVyFi(jd-x)q!A_ zvast|bv&8y;QjoN#nC-z2mPTx>qpR7BqSR*6OH(twFKrR}l2@#dVZxFB*B1mU+Y%fnr zv;pO#cdM>(X z+ecr6ZqZs5qPzFIgjmjX=+ctaw|4TBXD44Wkvgx`G5C_sW4A>ow77a6PTtGNUemi$ zk=1gonCu6kREgaz@`}ZoQGcZItQPDR^|+OYhubGE zV-hiaYb&6ewfTZV1eESzZcbF2{p$fbtwQSvJZhuzM*A39ETan|9bHGJj0T*c%phmI z@LQCh#t@9i6;k6Fp^5#Otxph93gk-ydku3fuB2!kw$E5+z|hJOG%=o|yb>rLj#^{& z+VVD(V=Os6`GB9ljqe{7$lGFAecTFvhp%letddVr-(GcI4}4Kch!1;7rF=n7Di6>P zc3gif5mRWs~R&&-J-ctzLS@idhjBs#R0~4Z?-H6 zfMQ#O1%G)c1ZScT{JR(8h2x;}L`RlrfU>{3V^cV6JIy?#5J-U7Nm}q+I06D&@^_^D z7X1^*i<`~ijc~R-BxTwfLT`{{nw#2GM8M$J`*RRH&!z;K*+u1a{@;PD$S~z*MFPsr z*XDZ-+Toy~p@%eM3#WoPgSr?C7Bvx5jlO-Ten0ea$v1Br**}iN!P9`>?hb1s_q;-r zdNywZB($7jpzZAoW~e3R8q^cf78EOxeJ-ThviUPq`3eLl0~FHN;=!u6=;ciE+f2x_ zr0c&DT%BdAnr3%GwAEW5JJ*qkS1-_pnX<^iG2SynHi6rhdlx&UncHJ4NKB?&Y}m-uCfk$sUtWsXb?vU$S+X#Cc@ zP|OFV_AFiy)}vw=?4t5(t<3@fb-OkvRu5GJ;8)>t>t+BU|C+qP9xRjQO_L}PeP?X( zq~%m=sR!gl8&WtP6WbK(j4+!6V#a_>Pcw)cF8QX9RNi$t*1nJUsLK zsNzYa0D_ousqE^#I!@Oz@e%M1o}%df%xCQU*8YQDd@Hj;^evjcAkl$h=~)>l-JsDmK%oOSf(#4QDgG5tu&Y=5h? zcat55!%n!Zv^_U^(s%#D%bdr*0s?T!pLdFVr7@}&ks0)408kb6E;XHg!t&q2r?Ti93f12K8#7>PC}=jhT0TRBnz21S+Z&?A4lT7>r8eike#3UH9<@or z)uL&aMMFO8&EHR}v*<~xoSax5$Q*g;79IXntW_8gbC=w{MPvI&L5Q>qIvr&|8xUse zzU{|s)eIShBe6KMOU=i{1{grS@1HI@aWe%lamwj>Ea!txJxj>P%XFNg$GeO~4cM~* zeBXKQN6V3K!~GBY_-mU0ul8wzrC3xVe@pJC1KYM=65#|x7wpvCIVk$030@ShYyJ#n z0TXpS=WW^A9TUM;Kv0!skF8NZ69q4W&B}{)qB&vS*EabbLtDg}$^5AFc94CgCBn}*YJv>%C56eQIq>GLkjK&h3vTTbFpPN1&(O!*xf0g$Tjl-SbiJX- z_dAT?9YVARBp8I0ip~JN*RcJM3e$C@===$(qgQBo4(~Tc)jNz0XS^(3KKIO=o`GV> z>^L9?X5@nv6FWyE*+#Er;Xo}EgmS^ZzJbKc1QuQ|U^mftm0X1;<&Ny@!_^g>H%{|Bmy&gvNJvsk zdy`bxPk-f6+;oMSuD$r?=sz3oi}cuBY({P!uV*KkIZkVt%)n+;zhL$xWc?PuR3 zrNMGGH3ByrII4km#vH>e#YL|J>gE(tM1-C){!2&cv=WvRZ%pmvuz7psz&D3@C_?wt z-u%)d5ecOc!^HLAD@Ori@`WEoD6Z2f1R6_&tMWBEKrD^3$43p(JkT6#U7JqdHjTzq zo?%h((xf@9N)(|B)2@*kDeJ{`h!Y-`-EW4Hsy7yuhP<&gzh8AgM&WNSGc6M}@5%`T z1+__HxE`~jUdj+MQ1EXTkB6th_2|i+z8E!{?l1dA% z%QsD0I(lf{vVPavTt(6k9Rs}W*yS!J5Q0Wp;1e?VxfqPSw6lyauw4uDk6PM`+ByE_ zW|cqwNr}HhHh0Hu>XKY>rYTUcFYSq+EY@rKzG9zZ$Kx*4@+_Q~H#lB_Y@W>j5BmD( z-7YP)l0;rF;m~Zuxe=9cO97z4Ug#Y_7MpG<8xy(OIlVwa;s!NWFR1mW0AL3uA#3I% zqAGCfh0CdPF=F?ykk=-`)7Q)ua)zc4N+c9r&AzRD@C7*x5WW$bHlJWY#(b+(imb%y zy+i96T8#zKgIJQd#&uRKShy|WLbMCXmwC8i&=t{L4OfHYx^to@xK?B+uhLlK<`MyJ zYWsm#%`(D5D&kQ}O!#RIz~-OyMoVDKK?9@|@D;V?IsIww&LcWdX4~}SF(c^)lifJH zExGb!IWHTjaSUkpuv>KH2*zE$xc5K~wB~jU6px)Hm23rgXo{wQB#H}a*Zk}@x>wyv zf;?ZH^jEAFneB6?uq-3n_(dZ0f)>PRFXxPfZahQXJ?fC_^zZRDNEImumQl5Mp`%$C zO%YvQXK5%Y%j>+47ZO*m+O>x7S>Lg1YBOeM(L(O*W%BEn7ft~`O|%A%`KWoe@n?(w z3ty9Pc2ur=4NAQC+!186YSC#$8&~|b{)V#F-hv~BYaw`xE|&NNi@FuAGm$5V2*JP$eRI06`xuD6c`5j+Z+&fEMOa zj|UUkfq9gYs=2l~@=YXbcO`yDY{z0fmLaSf@Lye8eiz+G)fozfKGkbd65_l=c_gHqcJm?U=60Al~F*Aec|``u;Ib+9E)c0ZyT zuI|BE8w^5lLIwb2G2qTRkzSovgR7@=!WAjH`5nkh#yXy>*ZmibfbgM;T6%!kAE{gH zXqh@@%v#@H>D6lTEPnW>-wav3h49IdM4W~sVEgI+Jg28^OBb^wnH0Qa&_SxrCex!) z>m?nseev`2@H*hNsU*XAO7ELAllTmA5L@u)2u)vfzZi>1jn^ZA?V>5E+mZHRe99i+ zry-sE1UiSi`J_9$#EGGQ+=KdF56|EBLDQplc0@h_?#}kZxk*EA?g>nX=A-qXjhVAM z*6SzvG-${KfU-@t-k5>l?8ye+hVJl(xMvQ|_{Fi(;p0;szHHrIg97!)+Rr@8h%Cxw zWu9?P@|c#gRJ4}dgta+zn_TJ(FqP zubSrL{khWwf*7G&o8T!-Wc8I-CDR0pO6gZ`(TqTINivf^CbVnQ)R4E~ub`U1hPPhq zDXIfbR<+GWec&cNU(;2FXiCLGU0^ynZ$-Ik>huN3B>2tc>&iOkv{{2%r5)sIsx<&Y zGNo)j!NSCyE<;y&QuZDKxSP%JJy)Y|_YzcWV-2#p|DpEv-=*!^-&S%S#Xqgk`8bqO zUu{2brjg(Tv#w^`$0qSBxgMKdfxWV&r(Ge7SJur&R%(*a7eNU~hv%YfPfZ1T=7Pp_$32R33+!v+VxqEjjORO^otO z(h^;^rwar-Y2BI_E!iRfI2D?1Yz%}lGP3z$?{qcn%-6h~>CNMyr48dKN3F?EJ;%Dz@_L{+zTx_V+IcB|O!^L+SssaY z1z>~|V-XY}sVmUKSj=-iEU2fY&@Ti0sa@U8K#`Ci+6r@6*n|Z|0fbVEn-`d<#KX0e z)iQ?Z5ap$rjaNQ0CHk~nI$x$OeNNMR?m;77!pV{JA>dC9hcHv|4rAzBJ?=`#csQb@ z=yE~vH!$Nplg3LB4fY@v3U@)(eardUI~S($ZpW-(>!L}Q>6^`2)yL3Ym5){(I~2md%8uT@u7 z(UfD;ffiY40qAXgdZ9VFqPlsVX#f*hFFo%N=R5*^qp0pN8m*?=8{L;Md(T@=M=Sipg1e zvTuIC0Hh5GocAIksW;v`;dF)1#kYG3{cHM|NvVHQBM!hyvw^0TMkMC}(ekV@vR+~1 ztN0rz%A$BY--^K(MxDKBF+q#L$f{`7HDyWu#-y3dVoQM>A*9&jv*~>JGcO^fi1Lno z?f?K{&j&XEXl%7D#v9Craqv%QuMCl#JhmZo({JUeR@e@zlacNu&{Xc9k3r`1f zEiZS=q~yIY$K=`hT`y(iL0r58q5=8(1{6{VL}ztQy=F=#I!VO30<(^yLg$hLZD9~| z&~M>xXaBG%VsJd`fb9_m))-*nJt3*-(vkq46Y^a+ao_V_ijYO{M;{9<688`XYEtOp zCYwuoUzfJ=ZAUsD$Q^iF3b+HY;pfQk=K!qy*Gk^1%@42lR*voazJC51+cgEO-7I)t2^c5C$rIR3}DHGbLHC`iDze+I*l)1FOQz6t7@huw)KF} zedkOVT5^v*kmaEkGiJkk_O{&>lx@n3*QEvfUrv;2%v9~`z_H=Uf5eb4P&9!&gGW4C zvNL10r~6rOhw6VYBD7RU`#{or$w`11C&G0J1`2~7dhwT`!Zbf^)-C>Bi~VF>Z-2i| z0k=l`_tkG-bjZq@E_4X#J&XO9IC5vZiZt%^IxAjeWCna-4|<428)=ZZuuXA*cc`kL z)5iO_5*`fnlxIZTv6T){(FSRfzvgG-%?pPE5-z7Bauex^&lN+!r)a&->?pQ{Hoj6_ zSNeT&(MyQQWW0J9IxlNu3W8|{s8g0l-dbM zQl0#?vVa*D_|0e#>>L(9yF#=OJ9=+0ffBVb@VJNjmTfMx@)pZeCMiRr40B2bL;WP@e^P*nxB_E?|4OO^3|3$*uR>Tmb3AXEO8 zyu{-Fb-k-|XhazIYXnB#Y-O?{P9=`Id^&G{nY8EmmuNt#mi%ka2zBJzZ?ADrF=8d8F87m4^t+WOxSOcNvC!;c7LDgyt zybr4N!KVo?hT2xJ1WD8&#-eN|R!+?#MywChnQ4Q>uAhkQoVxq-X2(5cRnX%E18M?X zzn>#CdEvf&t+dCUO@MQ4Ogf{{LYK>QG!UoZb+n~%b z`Om-dDSRfZ9|IsJb^~vvwKMwy==SMeu{L~`R^m-IGdqtZLDS8@+5VuC^F=kiC9yTyguX3|7t%S_Ab_3y7mxng>s+XBKrS-Q$hC45cxsGZJ z;L5yAY{5UiMeovA%_;l)rFrtPyGvCjFI zx%0&4(z!ZB7BY=jc&zo?qLY_o2%Bb0pk%9{)($M8B^F$7g8z<*`^p2R_@VZP0$h;G zGoZkb!XMpSP?PeDhoN)YU!x$@=SA1*OI_B0{4Lx?YWAdHCP^aUx@)Qi$FMNv^VVL?EG`VHPT>hhgZ<*}+1|o5(5Gv0Svlr!AI;zW6Uqzki zDONNQVr~kR5?$1yCZm;IsO3&3M$~1 z9^S2%sz~dK=2W{~dcI1(mi85TaBuGC^;56myRTSL)S$*h14lE?SYp$YA*H3X?;VrO zjQ14d>_AdS=Dg;GY0wp|Bq2{G0bmag%YGCA;gAsq7dS20CgOb7@8DVRj4Hjl<%1eXgPys$>xcvY6!VJ|)1<5j*>(0Toqx!k5j)Xw6iX^?AcHbBejgyFX}`}R*`ilV zX0e}IsH(UT9YrXth6y+6AMdo_Z*{HZ*d+!Tw5SJoc|&7BCcP+7Eh#4X{9JL5{bCY) z&}Xw{?=irc!BqS*ZY5TCZoz92P<){amx}%mTNaap;MljnsE}>ei%Z!TyXe`wUpY!M ze6tdn9{jQek!RnH3sw8kjSQQBf$od|ML@d01>zPktsjU4b;0HAGWeidA@ur2D?lwDqc-jx8S&jbgSw&9H6ElGspEL zeH6%xosTgiDV-5Q2k0#9eQ=Gnq{=fe19can5a(-$`3vtu@AxFb@p>9?elOJPmq4cG zEy373?xfn&P(c{Kt!4s+U=+{JP>5c(a}m30%CSa{yI3q^VIb_um<-!ocww>$Ci*t}|2FXIt(iMO$IqPwQCk=)9qt5RsI}o?&(1vb^9y{HyQr@DZ z6FeWwgme-FkwCZ`75&@jbj(dbwOW$XaralH_ZV7NHr|i2fmz60BG24?|AX?ajC<7t z#JPD&5~s0#SEXxKiV>Dq-ZkCtn*%}KVOw*IY7B3#MN3vqzKTEFd02?8%lc*p4s;R{r1aL=hv=9{`1acPuhTDVCJ(VE0xr}UmZp2FA;(rw1bnZsT?x!6d+I7pR= z|9%~SQ>=T}{BK<)FV7X*8f3v1k!6IL3cECNOzg0J!3JsfFFxg@ zMN_Ms^*{wvWX7lY#MX(~M{fz*QXDL$NN zajGB+QC)yc$^;`L;%T$>WtbW6sZH4621^(1`tsw`X-MQwz*-iN+qN=pYu9?V)Pm&9 zF)>^igzt5q+V)~CmQA5NwMx6cowG9l!E?p3>xFc%Xzdneamjvfbf)L+@cOGk-4OZz zOtw!I<99I^Tw3q4sYJe)sdZYn;1(A*8*u}6>V_RSWVLoeqE5CJX`pmEi@q zzYTX1mIHAC$(Xc6l)$Y${BPw9q-EFVc6_*?3eD&NCXSTslx2qQO*_T4E>X9sXw4Lm z*f#r81X8rho4>}CD8+s!bFOW==T^=x0P8jx*H0i`{SVYk0KAOkD&=5Zs+9Y|z9eo~A1mNPND zy>l<7fVc}f*Z2kS2FIn&YHDJx29u;;3tdgPr1~!WwpffRMx<3Ao0}8e7b-mjQMSIE z%JU<(dwP<$FZpSj?z5p_R?nTQ{)T!%loh_CrG*Lmdx^cn2@t0IL5^1^+*tGhd@)|i zuQ4x}i@q5ap@Dw0sl^r2tx=(KR?}(~HRG|{gG}Oui~|zHWNE-77JFz$L?VQywd9bI z!QCZ%R1VHt5#&%DJPpUe3=l1gx8CAUej-!+2UQsqJ2Un3*n3_;qJeEd4Inm4p)@qC zLs_-4kc&vq6;~Gy(7!0nq<2lO2~1~E7^<|%H9-qBO|$PmMwf1`b=PVuBAoAzuX3;y zki8L*-kJi8Aoa&HO_Ye5Y#6k_sgwtrOAwFfCC!`l`jLvjlVKbH(KMPVqcu3>r=PZn z53PunpmXOC>NescunYZ;A((8f>-rbFSo0ZL)(P`u%yPY^s}kEdFD2yoc0Q(XsIlax5?K@KfS!84aAR z$O>X|%fk|oOeW)?2Q}`mfi(xIF`!wWeUX0e{%n3219+J)%*P7%Z^2UMVEa~q^!6h0N zHjJRg@2#Pg1RzF2sF~83#tM=V(S%CBCFRd}n1O(*F$$#n?=||QtX~0Zw~mIvMwf`< zsyiRG_^FWaMQz=q-PrFoZ--U(md%Jm1DAs8S{$JodmE?xs)n?2-VF*;n9^lkr0S8$ znG~`r5^a1T}P2(Si6ubePe*z3;l+uta8>>Nxtyr~j)7IfX z^E|ezA_y19^?o!~t{onJ+a%(`IP*v8R2V8++=K#f5KFUpr zGXkc_6}KeaTvb*PTo)VK#??k{pK#|MLGFKaJWxRGY2&+)W(+GUQ zPj}54#_cugPKs(cqodfVqG+es;K)Dz=X1tu*bLQ=f#){}lPGiV#F0{^F#kuD0u7A) z$tYl{tuO%0Ism&RtI+r*Mj*gvo7NDfgl}eGW_7mvIXa(xGJg_pjO}?qN24bfsH6gu zxiSMZ26k~kkSpiVKUt^yI(x{ah9M31#z_ZO>HMr=Fr|Kw5XjpVD|IJ#Q9{p9=xhtO z7aECu{?c{&x8-OM94Cc~QhkS4P7aY^Br``aQW z7#1-IkTTVJz}V}qlRZZ34&4Z%-cG@YCc^l}UW%IypDWK!6GKX6_D32DuKAyQ#TaD? z%Y}V2HbWViAfjZl^&rx8#w;mD-ls}`I|V)ww!xVD^_`QIb`gLzxYTEQOmvf{jc9o7$jJh3t`L`eRuL*Ghg>1-b(|L$jYSACHe|r}m9}!v ztk_ZlZ#xt;Fw8l8iUDph@7}W^7n*wso57wig^SM zAmKSl*)W_^`4o@bol?~_s#yMAXv?cRJf0=qZQWQ68=0xhCQiUSiyo>{Jx#bL!O^0s zc)9LhCPrUD4dHNo%|A-YvdS+uA86)giuU7_<*ShEFX*ytscF*SUrJ@DYr*>iQ5QS- z;Ag{H=osI@_r43c7Ww7xg#tgqtbf zL}1XY&n1UuWFHN>v)dH59{_NegB_l|`4{w8uBfhir`{hi#woJ*?=~^#P%~*xjJ4$5 zbce|GA{$HW#Nk?fTIjhi^wEQ&x0m1^_d0a%U$ZdI`XkktRv9F<$6$*Cj_2PjHcUG^ zghiJ?DKMDBz@qTqX-AbN+icALEi$U7xN9Wr|CCKO-Fr>Njp95MS~qnc)|h?M+^{5R z=Vf3j=Kx!1nV_B!@LZ@lg<0O@M)Tg7Ny$HdEumLWoX z3f)3<&%{@cC<=T}Nm|AsveeLFr8T`u3>Ih~!)#?L^~>A_T-;%?Rc`%0sjWABFGV=yvWz0`c@zVYp% z8JNg4WlZlHv%H7CYjrH;RLtdRvdG(4_@{yg_aK#jZF@6jXQ4O_oI4_`!PD)BGW$wh ztvcb5rsGX(0OVX|S&(BSTj4utw#fy9I(zh`$Vd5(tCGFbLC0bA&M@1qCutANX;A1G z@;L-rDH35uTpKPGoceQ6<5Aj8h$XWBRVU7d%H}G#45!crzi_o{fJ9|oBl-<=T>Qs| zUL%U}|A6ors+g`;&k}TOaI!hRW^wN0Lp%C;seWl{8A8<#9+Liw56X1%XmRz+1(d-C z8?II976Yl5tx2zlj3zTpMs>5C?r`B(L+e@pcZrI3KYr%Eq6M+zL0-qy6xzUq&;?N4 zV%tnR7O>#;EAfNsdx^W=SKGk3ecw|LY{gm&HtVCq=EZ=R^M@&y$u8DALn<~qxS`NynQ7Kr5L%HVwZr*p zy2R!fC{UdV6u#|q(oz5T6f29}DjJSRuGrtXTg#8ajMlUJKrgUtn(ELLjK#;t`1McX z*Fnv;G(LeD38GQ7VEgiH=B{W*xs!Xz=K#c~^6!h-{Ga7*NaAcVb6hZd5);b9F~g#? z_ans6M>&(opnJ3lKj;+|oTuifzPe!fD2QfzVlo#PXopkvy!Ce%p+92L5DHY5oH4_< z9k0i;C!R+Bew&E$o^BZrv54ddc6@h638$X)*}rG#ooaQb_3kOUgrG!5HB2J!v+A(& z)_5S7C|rJ!27no3q>Aqf>WHbLt|Kl$&c(vN{#uU|xMmtH+e10M=X<iE^NOHlYyqx0QEN)OmETEOk9V{KDzMRx-2QFU7 z!3AM`L@4I1@SQC^&X9e1F7pV!dZ}0hr#S`Pw1;ea)w3;YL-aS&a7?jpk}`EqS8I6S#hdU5Squ6tdA{C6 zJ!N+%JK5@0c_QfdVzJ{gP6HQZ(_QYMo5H=H&D`GCY?XTP2}~5ZHyzO3OD>ZO`UpS) zHs6Uxrn?#0Px;|{1Ih0JCFyOIgn#&s0{w3=?HicTv)z(qMoD4bcJ)gs18;fmzIq34 zs42Juv}{lxN}{j5-WYO_q7tcDd-j$s+C7lEe0e@@@02&CVt!zc^%rFv_EAlVN`743^W8!>SX zl3Dlfmi+`C9dvyN1|d{A)1NS*8Gpzb7SSOp^C8X|8s2alhXtPK^ZgyFVQl-~P@14J zPUL9X4lgQj;ZbI^EnBuSKg+c z4$`V{CL{b6?Pn2(X>Z0%;Y$RD=~h7M2eEJIkKHfMjmipG0Yofx&=d!nw_3sKsokWM zgu)S};sDtEce!5Xa__}B;E)j({ik=mOdaboRNE>;p@+LJc_K}--x}_9u-) zhQ{Y$D(<#buH4S}`;UB6~;*omK4z8hJaI;})A1 zEV2W^^=zyFYPP|`Nll&g4trn;%%?TGa5NZ6Eb&24&S(Q*6sIm(52C~`Gw`;gF?HOu zh~(oAOFvl9r0SaQN4NJNaZ=NY9N8_11p|s4J!$Yc@FlI4)JHtK`bgJ~_1rT<3cK*r zvBQ*s%_4c+(2|SO!>n_1gQ}uZ6DJPEjHPKIZk(N)>v|dfw%bm@rtCtnhG}5lHW6xu z*S{4=qH~XE-qx~h;Kd&w)y415p^#kWY!!2-%q{58Ovo2J`d-^lRP&?%P(DIn4fFTA z#`Wi8P)Wg*U}(K!z=41k{blEH%6um5C>_k@w zhdeJL&q&iCQT$!ipJ$w}K!*iw!dpD`#uT9T+1nRPF~Ty`7=2g?bO= zUH6O1**%X>%+AAZIzR~06!0jUz)E^N!tLN71oipuxHXi#Q%3R{tLfg@Cua>~DtkIO zk-SJOI0>NRX5;I*0xf`tflpi<{VNZdH#+(cj*N$Pa?{Vq9*R4cpyo1WJXI|FsX_hu z)KK7C1GgSY`dRB$u_4e>>*fz3flq>(k7qp%9T6XJSqX=k z))>)Y0$PB~?(;j2b1JbS+8xTW)_>Q1uOp5BB#vGZ#=2l% zuQGWwzyXu49&&%uGNIfH%I;cv379HI3g`U~cAz^eJ>|^=qF$T2O4SO^ly|!%q!PR& zwqQVF(r^wxVsbB`$r1w(IahalTrm^Nj4Xuxdv9R78VhBN!RAX~jORX-D$H=7(ER`o zHnQb3?urGKAeCb=8{tlQ>l`q%=%3!`FPDLNA~8Cy*6u z6i>WM2SOy6ro>DyBH4hMmur3KD=|0z(Tlym2ufBx|Fsfr#Lv8S>E>@5Ff>~%9gl^e z=>7fT=M!_yV`Gq|q(6~mchv0+9R`(?bcO6O-pRQxd~GZZ6}M{)(&(tmkX#W{#hq02 zkoU+JWxzk`_9%|<98t{unP)wvf*-SW2q%T9dsLBbfSTKE7fLpB4q+|R!R6 zFtC5%Oz-96pe-Y|2qETYHG&6O0@P@w@j`I4UR3QKnX*;!X)b)KY+ls*7N;NEMsQ^& znDMqkvq+hI2fi!rw88MVNBGa=jWnj^7n-*$E(0+A`$FfT;512O*bLRUcb^X8a}6L2 zAqTGyla$un7)`ix)*K*Kb zPLK)AQSMuv_Li755D=3mJC?n5MV+m|d8Y|z3cM8QV)kB6ZTs>iAd6F`!ZC5G z`l1}6v!d4`T5@4rFm=p=u+RsLl@V8@;zIL1hgL;6)*CA(5t+`+E|wdi2BE2C;U81B z9$Z8~qVfahIV4$M-7xTYpEU8)U7Fqb0v1d9jjJz~+h_J3didDpaoifeMAE&pZAWR~ z6z}C;3h5e)KW82sH-;qwQa|V6;*%CTX0aVitOufnVX2eUPXfd&!N7yLNYvf;ebs~L42yy$^ zpl;ELP}qNJ!5|`)CAA1!7lVo1iyAcjSFOHrmzwXh)1_ zVNB{cAf@CIZq*bw0nfeNRhP6d@MRV5AIMt=g04fY5!w!6BMM(q`JKg>POo<%$e(Q` zluwZwv@PQm<>n>0j@UEf1p#|&s5wMSSUeX~7hJs6&D1I97kX)?b&d)ELB6z&c^#}O z51V^h=ojL%9RuE4d@&e|9Qhf0Hy=}X&L6F|4I$yM`9e~e0;pK4l=z>SEgoo3T9GT? z=;k!GfSa)nhwLdAO_(h}(D%w!omLsud`>QPUFSaSN8GUl z7ql1`NnsbgzDooE&P&WkVRnVwD}_jc^qaI|Q^y8S@;yot78Wm&s8opAYqYvvMw2y>tNFLF*8|7EMDs(A6F=^A~d z-H~^xqx0kCTA~d)DoPQWjl%$L)K9tRIe3X=vdH}(=BZQ{gFow+cdW>|4tD~uDZWu+ z@1@nNd6=tu9uw$?Phq1s-{#u6^=bEd?$%#wvz1j@(QI4Qx{Gv$zaHhYBq&SBO6;K> z+L=G!{N^qjq`DddiMNwZuRySVKQs-Lb&QJph|SZ&VU=T1s(2qHm0Go!n$);7;YS>3 z4+!yT=G3nv_A+r%0jct#MGJL;3FMqA9ShddCg?Ck-=iy+5y$68{l~vq^C&V#z{Gl% z2n{Tuf@}&if>qbgJ_x%a6_yfa(yk#oRBGTe45XGzUGj#xaO7o0Q%IT>|#l~Ydncyb_mV0xIHr|PVFf0I+3;Q2A;O(|CA&~y0 z?xp2>nh`-!tBF+Ljb9KOb|-f|uvmtBw?+HVfvC;E$Y1;Zj0!znVN07bb!MroGBxQM z-v*5nT|moyP(D5WlB8kC<0?*p|+#djGhsJm=aIyw&gmOp%D{zUk@2T6e!H`J{cW&F|1t9*D3k} zJ1N*YH_Mc=ILCiFM$)(b`&4`ZqMK7!1anRXkmYy^MQ0L&*D+b7cB*{da*`30Olssc zH99Wue8G`+(Efzcq4@*Dx(T~1;ef83Ty*Mdgou z&Mb#6_En7eu+nlZlWiz&jymmV0@{GkaY~N{SvZM-%NleZVxy(Fg~QSWJv);&3=&oQ zd&r|Dr<1TgNru06kHk#=&Pw(#6*C4pHA&|P|Cy}zAZ@F%1*zl)SDf^AjDlpU0 z@`XJ$ zx_l?XPSsn;6P}_FYP&i4r&S7Nh&-GBgHHFHnQ6Ub4bi}`L)K$Z6KIXmepZvUj*k-M zHxE3Ok>at=6QeqErnkvt0VV>mk+?BBhmHC35)~A5dfjTj&lSY~fDzfAAAe+I6d1 zn9~O`mMX^{4;0&$U#!9JTP@aY69L%-oXzE4zB)$~3BD1nhd&PC%a_n@A4vz%j4R;q zvZ{-47=J0u#3N=%#I;`W5Z4_ly5tgM9rXh>aaUdRq_vp~On_wvFTD3Ci@=YBV}A7k zBaE+cHPqp7$L}9zsWexrn!A8Q6_&&U*{;5c9HF0OVdapYrt~}Bq0#aAE!U@hJwiPj zne9m~l50l}3y@ae8*BcT{+psu%)jcc=;0IfuExY{ZGHAWf*lg)&C=EWLHMMzY6KG~5I`r%Lv+utn_w41kJqBP1_*iIIYyRD=RU)djg^*P{08oc5j#qJx1-jD>-y9&OU9Cp^1auX_Fch zp|Gleup9zE6Pgu&v;vPX3+~x+%XF1WIkQf960vuQz^@nm!DwaC2(DIK;OB3%9uUsh z-|lCv*a+ZxHsn_vMIxs}s1i4lg`YY|(EYPOAQ0^l;F_ihTj?Q{;YAra%!Oa2YeqNW zp%*{4>9Bz%_R+W^S|><@0XEdQz@Il#BGPGlJ^L}iqqLzN4kVh|0u@U}Bo488!dn4g zN+gQAaH{l0!3_i^KXkp_Gpg2NRp$z)if6DemHwjL1FpdOg;kE!_FC>5=9J|vDxdnDn9JvkeH zh^LQXF#czCHPJzk+H-)<$@WA5XU)p5ZG<_Rvb+4Oo3fQJ$p$*4dzvN2<=cA3i-c;o z9wMp`meW;Gq5=eIlGke=n?(n4+T3=6FtJ&>^nA}j;>POT&gK+F*HbFG-6iHC8)q+ z^)*;cj$zcUB~b1~-4uOBfk(qXKY`ntSyoR)?VRHLmj7KhaIMno*)1xsxfV)!U3RsP zbQy1SeM;_wrGfL+phI3py1q%M&T=*~&5WC%`CYfU$BeRBv_!#>Q)b

    pBRhSc$~sxO?pQoElQ9CRJuEgF+N;_Le4I+J`3?Jel3}G6_<0)uQdV9kA~*EDQa01vnltr84pR&Eo3OSKGF~m$^c=(~o>0(WvHy&SmSBI1yYLoiruF@H4wC$&Ctnv5=o6?KM1p zae@*$c`3-JKB7)g1C)A{w<}?lJziiOXi`oL{yPa>x1kBbA_>}L~^HhuMFT)Wd!Fc?hGJvS1*mrYvPl!ZmcF8>OEqe{V)QAndpi=ZI(PAp8;z2J`&X#>;n{Sf40P}n)U%#> zh-RgZFs{meD>9;q>3&%u)tSc-sBe{xgtX6L=UfT%yzBN1gc^+#_(>w`303b_5+TcB zhrjPbTY{*uW|f=^dm}Rc-E0^hcL6+ts+TAalcj8h<&;3+ zqXAb|LqOfBuOWF5i~mbeYEtB#N|d(wKD!mJEAAxAz~-s2@bJ?c5H_l%Tj`XWS&- zp6Q9rl6;XB|GoY0_$k-<=uzj$A;lo%xTf3t31u8Ro;gI%^}>7e6n$y((3fNJr-4@c z-%W#6Pu@^{{;M^F#215pWo?BfX2A5N z9B#TY`ayduWSCft&M)&;@MrIHdBnm8*M%K*I35*(o%7u~a-$;-N*X$s$no)5T_3J( z$OGVe5A|;~L5A)~x z&<4K3tvSrH?2yd8e6?2~G%ozk|Kr)LxMFKXA~fHQOYwjxjStm3VLp+ev4tmih`hwH zp}tSU{U5n7F)zWLqqZreV1jd#bn2I9@}rkfFQ0oO7`C+T6`-UC8tJxUeuzLBVN&eYLHETn~8IZB@?u2?*`lZ&`$?%Hj6NLOIRIZu* zk-m{9m?+%WF2NzoG5AO3cWv%_66Btp2S%wC3p~EZE#=lFOzL6W7_yzPn*XC;E2~m{ z8^fbPk6J0c#8y;8Enx@k(Sb$VVGr8srXKm6z>LI>$I3Y3Mc&-S#;`*di?LYI5RRo6 zH-*Y!XI#*7B5Tt-Q@s1(9;6%7!-`M?IrTQ*Q7#2asN0c;uKqt`zJ01UPp)wfJd`yRtAcLQQ>aR^0TD{J2xa5f>wr`E76R?lHg45sRMJ zr3p5PawQla6Q|$Bk$%=IxCopQgCUnat*jhW^JN4t3DMH!&Lpwfj`Of*Qu;OUeLfS2XZfYRgbvjrz@rL6IYvA5R#z(w*KfT;>RNm%1cQgxBkU z-t6vCS`J^gH`=9?D{XVPY$BC9s$6f0knFQD05>QYyO<;VJ|TRnL{xj`pch#05kE+{ zs1-_*sX5}NI0L5Bd5bs(uS?Xc3KRb=`m0N1p0GW-7yX2oKn7Wlpt6>wFt?;N~m?(ke3lrvq&`M^;NE3psO# zOQjNddGm-j;!n80a1Qs3=A*nd5Y2K{)`P(dTl1)N$GL>aZ2xAhm2bR)genn0`KUhy z`KuXK&VZ0*Nj>3IEE?PMsxRyH5Fd;+Mrg52Qs?Pdcr*RR6Ao#v^csqHkIa~Kh#-Vp zgH);R!Vii@vB&%U@+`0Vax{8*P!Prh{R$pRwe{ysn1Edifg{6izsSusmReZs?`n-m z+DN;P%pZpTdMEGZx5p|+jciKB3NmLMFM~ZsK}H;Pdnt*cWt&qy*^qk2*M>@5Mi59w0vMz@wsjF9u3}`^g~-~{)@jT) zS4ndk5PR2H-3#*;=VB}}k`8chcXSQ^!i_sWDJYMKxB^=`i)D$DFUK%*!yvq*Eu!M^%NBIP|O&FnxC)J=NDDzT!@8OAfvrLwPin zZ#bc|pzZb-^dxX57(N`{+^J_0)5zCw6Q#2y;R@;YPF8{!+`N`ASp%E&!)DTul^x)j z``D-y$EFf%2x`a=G%B{ln$2PNVWU|o5ioYOm%J~B*b=iHnT=gn4BD`tQ5v}TiRHp05gLvq!%s#O`3i01gMMhe7W0H&tnu)TY2`0T>6TIh=0oM`(nHu4UkbM zMRcH5{9x+uZb0h`VMRplwjfO52x2MS(4p0TLb(qT@B0&?@r9~Z7;JEQO;UEwsqG5P zRqz3sMCrtIGb+wg^G}Z)XE`elM&d~ZQoqDMlY~YJo68-gW}H4f(URKY5nG9R#5207 z8T~xt@SvN6P|m4hf+kXhKl#apMkMJEmW;asgJ5Y%j5yIw&|@sN60(j<)8pSD0GaUy zlfNfIi{p(2+|{I5p*#+kPrtN)%C&&VP|ETI`f`N*rBpe^at>JiET}4 z)Ks}V)lM7|4;hk|A!UOzAd;2T4tVsE7B<)$*uO#K`?-N8a()|F1x1%;@*@_?PL;xJ zV1!?HfDi-&z;!KjG-yLwNFR7(N#N6$<9pAE*YSe=F2vh zJ|n&f{WwPe1MEq=NgqXo4<2-@$p-)gaOu5av6Aj*nqH9H=b1LpSQX_Jq8(uvIbUqY zzR)8cLCC6(zqs3wj9>>I>1^cQ>~l1fRHSQ3&xf^$nC2|bw|1o|@hMQno)hF@YE-f& z`ua*iI|}uhV7vStT_JvvQPy6P`5}O)HNd-i>vgoF(~YkOQ)M<0`L<~(9$k-DUF|~o z)%L?4Da1(x=6_xS?XtRukXn!_*BxUyoo z+ew9YlBVS(Lro25lmRK$F7DYbmkuC*gc&?KZ>6w{kByeQc43#oWrNt-t3vwa9zv6+ zW&;L0zPPS&?%Q48Kx7veE(6LpDshqu0izEK3Gxn0O&IgU@=<$JFsme@M>xqW-wiT8 zKUXdML>Xj@>6*gw_AM|B55vR$!qL3ozpv{+WGLd=Dp}pzecqYqy+-L@v_K-wC z@Lu5-^<7Pyh2|!`tIS4X7*W+cpKki+l>EXPontKA!-;2SI@4puWCZle}#-d#+s8?RQ0rj zS+yBbe2aGOHITo)Wmr2UQW)5OALek*RvJa1f$o)WRYR(7hpDT5>7k=z=NoILEaghF z9&=u`u7l=O3&m@;#B*XAzZzwtUOGkDx?At}6AXN4p_p;EIeffn+tjQ-f@7O;B{~Lk zU~$7R)0P5DM!nQ-#wpCu;R|MB>M6(1HJ&dYp!|$VgN{4(dT&y%nVg2Jgy~ zo=pQVx?bswArj^~)=U_rC&~3ty}s~<*Z@}W5P|IL{97rA|8rn#^^6NJ1XG1!4E+C( zA=07C_mERN^zbZwwuI+Qi0V(@{|{Xc(=AVa+|eR&4X&uq1$+Gdhd`z1{Xg3;03o?> z0xXs!uQlR_%TUjL`mZ^H4ArJToOfUrze3EH(HRYjP?&ugdq5l_-cXP-nmq?Kz(`CA z&5^73^9FZ-mU~$<=D(A;SRqs_MPTOoRWGRt6Z7I@}y)2ozmv-)b{j!Y|PYHpuXg+x; zR!~2NOqFb{_QgXb>b&B*cNoTkWpg-?oyk|tqI}aPSwz`+uHA3x+!k}X1&TnQ=g%pm z-${QRbH|H~t+hQHWU-HU2EM8+2y@3Q+}J;#8`9xVork!o-0!?GpZA0b=e+DH%R4$S z;#<7UsM|(>?5YNi*laarQn{GP5U*)EGl##D8F7*<;Zt$#pwKw2ms1`AkoKYnaIz zf>D~XfN(DV8D<-{ycLe-4~_&7ltgSYOJTf97JDTsCzh$HVGzMa-Sy+>A6uYz;X<0! z1T$!I97HM3nBd1co;ODJjHxcV}_#!nHM<>TTX1HMYF zU}(q_fKIl+#mARi3l3yMcyN`Gjy7wpSH~WlRLP0(6W$8Gz^OHN3ITtZQG${b;F<`d z3~Xoe1*)k|&O5QTjDlSe^5xgsI%S~aqzM5WvIvlrwNX|iBK;DrdUeka4z9~J>mkli)>GN>ZYKKVaWvRCtT|5t zHk0xQh}fgP^2q#qW-S`I5YXKhB1xH24;$+n35zL)iQ=-gD4-8mwUzy*Ok$ z#1{9&d~xly{+1E~w=bAy*1vc`3wQknc4CwdUU?QkJWlcXT+7Iv3{t0DfRKiR#U zFBw9-vrk0xp3vvXele=NOS*I6!@pgk*tfR)3NpF>fVL`&>Vcr^9-e94zQKxJ`(`1S zTyNO4&x#sL-l8DrT3YTD3Cm*~6n&dV`1;zn4#vH*6SxrKaRfGaZ90oZ(*^8`E&7Fg z-F34q(XRa60xrx$#qH!j?kv|gfjUIy?TnqB`nBBNvV2ikq#>;0d6tmk3%^ljcLM)s z3UD6;Y_MA`CL>yfb7pRQv+Fi<56!X%?s(C?UivZif@hSf|L@Wx{DP|D@56L?Z0SFZ z;Ty=b{}uFL=!8G2##2!mn*^Jy#qOGzfQgM;?+R&g((_5d!H2ULOM(K2u<>?FKCP8e zv1G9WThb7FPSgna{p#??7DI-n+s$>(sr{&YQTL@OZw>BdkH*cOKL8{l%4vBg17hE; zxs2Edhz$&(DfzmRwcWTHyQ@U9+8c|(JYk=Di;0;D!?ffi^*c7EVG8Uj9FlyoeAo+^ zNR3jWSHUQWyI@>cVnH0%`)LW2kTJw^aqv&i5WBZ*J5!?z|3pkT)h2^}T)Cy_i^LW7 z$I&@w0k_Jhai|295Bv(AQnLFv{D+U19+fM+J?E<#%bnZE@GBo4R+jJ4yf~n-p@HS$7$`$n)SoZZKZ#%bGfF! zh?6T0i%X^JV#XTBV`EriH^#gGa;!akBBfdJZeT?|nRY;er&9Asj!L89JL-3*eG})z z#-?uC=upTxFsq8;2&v<}jlF|Bylj%~@u|~L@+pfsMz08d0Aw$y7^{8<(_<>r)4TcI zE9`JJ8kB2?e_zTa~@$*+kVFGuOv1EvFPbE#z@HtKKZmV5z+ zupzp%?H_?j{moKd4uj-pHJd9T>%^f>E1q#<&U7>eS<`(GG~(-#AkghbV6oHQWB7=R>suMhF@) z4W2zazq^8*3G>+^3W5jI=oxMy(xRvoyh66?j6j>RsTZv*8LCSn`A*)CzIdCfx%#m_ zXoea}VY*{cB@tQBw+y$;MFnkI;@1#>8Pln&wx3gasu^?v$<{?eEakw@V&;`^Ops8Q zVS{tf!qxBv%IR-ssZqGJoiL8$Dfv4+7bUH`urz2X2CWX?L8kGhCB*OzSA;YyJF!95 zg8({_n2yj{3oXfI1Ch%Emsj2qi%T1Y3$|z_JQ@Uj)hiZhA7qP1U>B-rMe+;yq9^!A zhC!4+lEVV4fGZ9>&=Xk*{1Y=$yDe)XqKlt=jzBe}$3?gJ5F(YuNWoA|N;cu_9tZbm zW6Ki*aAUEX^I!pPV_Z!vTwGX-yKd2G-5LJ-*4${9oJI}ZO}$d&*}?-(OyXv1O}=aM z*1j#e-5XC}0(7D9(E1}YDSiM6M-`~p+qmH%)#8!R==z}zfW3WUo$|Z`p+ujJJ>#_O zGTCOZ$8`~^tG9$5TTdliQ|-HL1|Je%!0S~$&|6Ih_k$kg;{+i{8jSoMVe!@Lu2 zU#!-08!fvh{f*37dAVcg3iE%YpG-BH-2V|m^BjiIB7T8+1e09ytKYXBguUMT(se_) z8Dp3J4w0QWP{n%IQPlj6?A782&LIN=p<#{j^&hjZpA*=D()a1&VoAyz6$d%y0qksj zJMmK5@|RWbZ-{~p=JY-@_F--Bzo?p#FSF7;s(Ma<;RL?N_`}KAq5)knu5CF|V4Vlm zqK5E@0PGkMEop|ST65Y?Oxw5utyAnE4>jOVZq)?Lp!JM>F$AQ~@F&u?eT*hw8M=9Z z>}~$9!8*b{6g;5Ccl4H4QHy+j))*%OjOUU0%*wUxe1fm8h5CnlFd0qh8oxbUx6RC7{~-pcJ8%8i#E+~@jeuQ#w_ zqo2I2pC}%;s<4sdm96>Z)XXCfN-f>$WYGjX(8~VP8J#Gl9REJ9>H87`ZO^=4CjdP_ z!oSWMxxg`qZZ5AiU+~V7_;1!{9L8MkS*q5Is1xS*;({-N9!w3ZKHE*jkTT@(bitAv z-SemRYXO~&cs*K1bQY6D%Dyq`v}Mia-Bz7rNbvI&k!&aV_dA~iZA~5Ve9?gh?*vDf zT_N<~pcAhhu+>Fml17>Tw7)h1EhB2gYd?_?1Fvp*hT9e1`VDae77{U=X}RF%PW=wm zaiGW7M%&uG&D?k8ah;*`0uaV0c21@>=APQV*uWtb)XddO-#En;OZrZO^e5Yju?`=} zJRCMPREt#jF&+*aPDCk?ttf@GaxEK+CNH+a6b^yVHz`~X+#zBRdh4TZ)LXO@F4m9P zR}Gu}9%i?0tcbaFU}A%Emc}mJ{)p}enqnE=@umCB+e+&eU^GVj@@?+?pgOS;1B}%bN+zsfF&lvwi9oZ?aGlTw(=5V`r zNUG+%QKuyV&xpi}{IPKX$NbioW9}^k9e^azfCJeWmh5Vn?QWXjLRy{d?M6Jc@YaZu zi8x=@y8SXSf$8y9iKAAg0{~#*s9D>-aye`WO(s{S_7i9R8741)Hrtd90ReIrP(F%#+Edn)H$eK&UMyFQ;iG4qCM*{}gfgg>2Hk1*WqDnf-2XaO?`~qF{m&%UD#ADiAiNkEM8uco?F5}HF;*l2llnxu_0drv< z&t4&L%Ug%RMmbrgbb^1PY)VjHy&M{4fB{4q&&}^WUK??OPm95zns@H32Ha|(G*G1A z+C8B6nz+?j;C}@K)iR7p^$HG|gY`!%e0yaj0>p^BYEOwVV8Ta>BK%6;v$^fK-`CX% zn9N_Vy8^>vV(ytj8@^kpTtEYcDJcdEQ${HRI}v2@3Cd|{`pktR6dV0zx%F$iDxGd* z8sRrN)P+E!`2_5qJ9L+%WQs_#V!<5kXKy2}4`eRolD4&RB*0*r5F=|!BIydM31T_m zK<*PK6m#o!drIL#WjC&X2qyVcm{{HQ?It%jg|uX=o)ux!R4ogf^c6qIW#TL5Q zAYV)!X`tZgytrHtN($3boGu$|_d|nksmuWF7EHq=d0A-t`S!510m9w)$akpt1BceCD@&6rp7rwA*KRv4}jvUJmLq7024-nnd1VkUN(V zTWzJ9FB^;3-E-u_8C_`44&-U2>riib|B=R(gXnT<0ZA**kCPtnF&B@hD_=`_i{)&$ zVbz}C)Fs1{F^^_!NHAse*WP6Ay}hk6{PWm+g?uDHw$$KDz^lybcYL=qJdgyhg{riw zkY_2`g=#eP#B98@hE#*qUh~}=R$isp2EeB$)D2q*^t2WEv{oE|n90rXI2gW}?0yVA z!I&nUP~-s54-0pOF;zIEF;G!th=Bz%nK;p6Adc`UG4*-79uK-vl&MtBL(b(aDWy>( zmt8jhQCw!XVjExAFBu2_3jFqNwf(7*K-1=#6>lDqtshMs;@QsuaqJqSx=29%7_lTP z(l@nq-G25Mw0jPHWk!4BQ3I~w&~(Ph+75Yd^!ZMNp#Jc$G2ElnAxbhGR3$sek7&)A&UQIM*vj%oa{f}nkb~`@Z5#Em39c(N zPt$lAf31yBCY&~-wLJ@_hh?DKh#>#A{l&csD9D^be8dI@178OaG4^kDqnr>YcgmkEttgsLKe2(~n)pNoLHbZM^}L)#i0BB?noCKSYZQ zwK&zi&q#{*an@@tlRXQ-b?&HLoIR!6#f_}m^vz!+{)QApWj~33(_u)kL+~Enyth#g z{4(+c;!DfBV|`niv5{Aa-0j|xE!{%MJh0cLyr(l1GOzdPCDIyX_ZzDYyimM_MBt|J zN-{4nCrD5HiduN*7FEy66{x79@0u0@^sq$z#8ClH9i0hcLg|Ybq-F1m{&Am6M@T_) zTntiudAySvt&jqq8sx|@N z)K1|7Lr$zvWX&(asj%|$Oy3r!RQJCcY3rd{$InJB!a9FS6e1NyMlHdZ_Y3nU{CH;t z*x>fKwhP-$Ykr%*Toso|pKlRtZXF(FTe)D5qp6t}lDK6h#P8~+z*nhEc;nXc8Jvcm zMLzu@Oe$7+1xcB)YbCgq7#bj5~uN9vcqd~)ap+m|7cNB+1D>9*WbJ~Pf32~A%fQo zeDsEfV$Jm@8TEJJ68sQuh7jnahn_00r@Jk$z&Mx4 zZMb`N#qxP#++Z-1pj6(m?^i6gEO;Ui zyZ%derCRN~_?MO-sf>8FxmC%pxuG8uQu({8m;qO&h^k!hVp~4pU-X!ji;qaw)r-O% zMSbD}W`z8`j+LKSnR`sO<>L7+l32>AB%FLk-|Dn(4yBwFza?8aO0O}h(JbZ$J;Y$g z%}I^nP*vn|XzfF|g`>mq_0iAESF8jUBINu^6JS3Hr1G0I>kJTA!#@?Z8)~zCHv&u# zZ;MS`SAD+#igdJ0Uyvtu35*y&COQcQ-HZPYq^|D_f%5Yo;4?Ef#>4v*-JuqQfXg@M zmu(@ZaTzNuF!hNoBbfYPWJ)GwO79X`qm0?T+_z`RjZtz|YUNp?Q%|;2|FF~se|aPM z%fFhF-iR|;aHq@%u|&C<9}T(57~DQkBKuB;4Z!Dr#-UOTjW_16;}gE7`B^y+7iP|} zeSaAm_%J%9axRK#MqWfUP*vR3 zF!_|`UrKVqcUfqYNw~VSXV@p&YL|;Qe(70>$6nbo;;PZ1=nL23i-}h<+oOfL`Mgq+AP{C$f#FF%;fyW|JLgHXU|JwE#7=?l zmfCZWE3}58T+N`z^9BtnT0xfeKpP4=w>aUu<3jmT<(T3Z)UXM2b-@4_52p)Q!FXH@ z1=#z={aj|gRA|u_RQtu3*8_q_yGQDti;xGh(gqZH>$c$#RjCu|WqQ!%W?kod5MO*H zeNK0?83M6I92!w1a@48=3^yuxfd$HQxwY8!B`Yvl0A{^xIHJS*LxpXt%Gh(LcG_+L z8PK^Nn=|BBX!N)>MCNqTGi0GbrdsxnDYd2{j>HTM7J&%w3PA`o$KlwhXtDE}da97X zcQSBLT$21aF|R(@*h`R^#24PCwJE@YMIZ|2HPvUi=}cL1jT zO)zV1u!cA+$agHYwOrY|>3y=dYtbCj&}LC%jPnz%&+CE`8g2D;W};OvWF2TmP+F7m z!V_qfRZ-wpI+lix`(HaBNt`|9@{B*$Z5PKFSsIFRsIxiR#QMd`-$+Q&(z+B~C_7BE z%Z(e6Mr$o0A(@zM%xjL39YVr#8phJE?GXPSrLGph1>ZN~9xfFj3^Q;2?LU6kY5R70 zW$D?C_P}iS6@LWRam=cPb0hJ{V$hBOHUCdf6Xq!K!FmM1dv&ZFRkVBnx!@FMw#8{; z3+A;(hHnYqge<5qiCZK``a>ku2lu+G8~tFdiCujS@F;>wPF~72ebPKv#1r|noOl6%|~hZ_u8rd;o?K% zY4Ikk#zOKkLi>E8l*X#r1u4coMMND^en>}?Gzg%R!d{yq!78f$q9y>SyUBJ@wBNP+hi@>(%(5e2*hFIWWlfbO*#t&*9rXp9=j*o>t_i zqXz~UyHIsANV%a-V`bCyK*KFknyprU|FA|Rp?F`msB$Q?Y+-Jo{LAS62Pl^u+IGj7 zF903#7rsb8)5pGviQ$%Y9`5;gXTK62;uOEs{Yk37#<)*}&0Rc2v#=CzEpIjYV1Hyl z$ljhFVNTsv9vgEA!NxL*F#7zd|)(hQLHs-Hn>V)#8XyZ;ycSdX%ZU~u{8?{6`lr0a< zkQa}D%fc|e;WpeJNaHN!$-KS)Kb2WESTCHW97U&L-twHI1P`E-uW2g{9Q^;69BF%a+#ky`dgt4zEtctQ=z8ebI01trhe+42XR=#ydSQ>nE|f*_U%>my)TpTVro=R;hzd4gS^7Jv~Ssn#RhtV)!XYyNY=?kqR_+! z*#Yb=ZNZgkBG9ggb&Jvhn1k66Cyi*AUTfV^yOKDl65bD_9xm+Fb5&nPY7fa!4YO4s z-~sQFDFirRy~hh4ln^(!0+hVo+q2^y)#9k_M(EUvY?Ds6bEY7M(t-g;>m9NH%E`>DEeladDs&E_xWQ4bkIoN6*Seeeu; zfwx?~JoLa4S5b;-gbf}F?RDOsF^ZcQf&p6Y4l_KAm9Yy2JF6c_rq?0zL;E}R zYIPv{2zzD~M=`dxddcSX)(RA}XbLhDwdPj6r6l5`dp!Xo?Fb3A+kB}@Bio`S4927l zyBb{^LJNj+!^&F}1f$mh=NEX;RNmBfJ>h*OKD2Q{gf>`sA~Gfp$e9J>N~T&G1`3Er zQCz5Ua13H!^Bw|FAjVu!Fdt4@Is(bcKcw+y1@jr@s@ru@!MK$AEw+n7+?t~k>y?f0 zWrxY3L$v;O^vJszmV})zlIS9u@YVFhjTu=XzZLza#NKyUu9LI>L`iNplmyb2>44bd zli0L>U(e;8QIf1(pCdXx-;9%>uM^ct994oYH~^|R|3br)iuuF?(xRvX}5NnBCxsP7?GMa**Y1= zck(T+1TMEVm!-y+PF=pKH7ipZYfh-9B1kNOC9E)sd$Tw+LJOr|0_g4%o6hGD`DM1n zbO!Ipm9s!F>>rNnlQEH6FT#+8s!UPD&_2k?VMdnJU{-yR#3Xk62?$6pN^_8oow7Kn zl|F3{SCNfXVjm&L0Tr-u3nsh}Gd_7YWQCYQA^|;#iGS&E|E;d0WZq%w zal$v4iZjt)0|+A|$82IGZ9hbge4k{8rFjb$eonv+xOY~ZrF|`3eSs4!2-SmxIQ~#? zr!XG|?yD2R8RpM>h5RvF-?$Fwa=MoTCA_V4eBoL52*Kkmu?6QJxep1==1*Go%<9LI zHTAqd@5w8)*Jez{ldhegA1=K75gsuA~YjqaX9eh}wdzydRBI*8MA7 zGi?KCk~jzJg@09v7g&GCw0i#)<>2bhtUnJ4CHjEl2Z>IQf-VAdmnw9!djJtF(Q%;%_+DY(>w9Uckuhv;vs3erxOZXsppm_lZgqsBC z+6E#%g(D29<;~N?CyY*YxQ14jv{>=8!@j46gJ{-;>eux~8 znrU{a@EE9ee+;JmXG6zBXP9m`)PgBurq3&U?i5We8vMquTk49anmem~b`?lc~1>1x*gK;gHVYJmKNT81nQi1mwwkQ?t*;33vmTm*AZ}zL1K|OI%z*W z-i`wb>ig<8@Laavnr5D4L$9}k(eHe=n?TqP4hyHyn97bj?YE^F%4aZ#7Y@YXs&3n7 zz4h%7Xt6_cs+k&t4H{aD$p7i!92MHInUKyO{TI^uk2>fZ)0Qd?sZDKnQ|OP!YqUFU z430Ht32s9+Qa%p$PZmH$f2%q2Bl}M3ZqJ~$>}``9Z=crA%^r|kePP6|0kiTT2pz<(Iia4_U90t z=FZH=1o(Q?FAA43uQ+#?c*v=m%4O~=2M)2SJnjR9_X*JhV_`;-DD$fL@4{N=)yRbRxa3nI>^R`k6L-W;UK@iso@F?Xa=B%3LG&cLh zrkc?jQ4^>oca)E){av#<*xiUqBoMvc#tFy}WPu;Zcy9IE*Pthj<%zRHr zRnC!fZ~u+_3{0y0&Y$y2Mv{qyk4=;}+T#%oV_?fbkLVBip^0QfMRdiYb2A8{S+MJS zd#5N&6KVt%6wp3i?BPGLC55Ul|00&RT!P*qP7RIOf0uccZ07y3e1ynzyihNP16I%Kf9)52 z_hD;qThru&y^GV1QCB_8!SUFY{PcIQBYgT-9x5KCQRM@Ax zbG4;|9%X)~g><9@4kiJJyo;C&n{*;X78dQ4fq@ey$tS6&;6-U}Gt8qE5SfGql>ocO zj!rilBeM5R*Cc3bsxDWJ)=r{n*o}m;g^@77L95SPrcoaCd0HU)JqpFwKc>v~n|9#r zo(m)7bT$-kB%SB7LDy(EXWRj?Ao+_6^{171plnOPM3PE%4`JTJ!aWzDO;?K%HN8oP z1-e}ycGM5BTVJ>M&k|G_nn`Lxt&@cd=Zr8}f`TpCiS>**ob}BmB6QDIq7^`qi8*uTJ zkM`7WRg$7O(?e{2q9I!t5#0k)=H7Cu7r z#2<*1{A*LO)feWgcQcC6lXL>$fnL5ZP0b|>&<5Da`Ol8{!{Rew<@$+e__yz!{GE0a zEu9#B1+=H5ijm-RWr?)GQ+(X=t2va%J@_1tNjL7n8H5!(HnPIu7UKKf9}cAIS!XTk zN98QhwW+;ak~E9Ot4}Ly#EaXz%Z4;Hpj;^$i_YBFMk^ql>Zq4!H&z3EPqzFpf@ri> z^S)TjAi-0gy9+CBpe?{*0t@*LRk*k#ZcxBBxb|MNr;qr5&|M0|E2-0V&rK)Vtn;wS z0HBl-Ac-QWiR2@3;M?r5sV+tKjUG!5$M`_j^e=!u`1{X$RJFZT@;64GGDU}@YEdOa zRHBl`zN9l$g2(aXQuzt2!?7iX$BBb1_BEc-*s*c#`mPCsv+&*|?L5X{=oLwmDT2Qp zBNq|sr&EKa!JYya2$`mGuYx}dY09{AsWEXNGFOS47LruJnQf%MTD4QBOljEn=S;wM zwl&@7ve_Kf_c<~|NCJgv0wa2u($C{Ja|2vN#x6bXn-k+XUmSwudK;1wdUDpN8KZU{~%!f_NJ?yw7B?^LcV;2kim4LXw*3g;B_8fURXg}nw- zt8HE918@~mtClTmh!?|d}=|5VVD9UAC zW=>G4fw2(5ETv+|QU|4>nHGBpq#pE8kFr9g8KN|i&!=8;V-FzlSH0E7YQsW-M z5H@_t(jLm|fW$!Hfjyv-Cc{4=DmZxU-?;uRd46;BI&OrVKC zvkruiv&KH93xQ@7Uyb0u_no9pY1OO`Y$=qFJ4@|bZsAy6G8yK*`_;D(at>P*)AI?a z>i3v0LI;j=CsU0J>U21~uef$)m)fTWHz^6es^<5$v0rf?T@**(AfuF%eIe!|emEK? zgU2%8Sk0?vu9T%{%i~dSz{EkH$_2?$Dvnc$KT>NcWT?2`0)B2X;qoG zcu#p-#jD2c6^Lt;3f<~Q`3|^=6=*)dQ23!ODiMmsunA9nkY7nP>D~OS>Kh=f(&o0m zJPv!`FI89$B~xP4bGSYdk;^0Uz+H4JQOu&TTGI5J$)$rUT8azl30bv7$yLm~wrOUX z3>B_+HNNRLaGu>pV_urdddK7rtRn#qyMgz@1r zR|-6uAgI2Ckwj*J-4duq6&U=kUYhgSxoW$R9AST9ET=! z{=&F}s0h1mWUel|lds(CqYQYPfI-}+$J?p|LJdL#7syxF2sAhK znvgI<)LA0D)ou+i8Pio6! zQL&Ps1Dl8g)q*hlX-OckH&EmPo)HNk8bOWSpcu4t1Rgr-v?u|83v(x5b}n)HP#Z&y z4Qz+Q5PL#>7p4GPgKT?$a@defe^hc8A>>7LAX6|#kiRdi0lh$}#lF3pSz-e)fa8uM zdo5#6FypvZhgU<~YCIF|lgSzz>NsaJe-T`kAWimyQANri{aNo!w6L-d+e`p@7+A2= zWphI11$Nh>9-mpj&~_;s5-FSHeMJ+rSMm$dwP0-(3(8*PfZA4-K^Ro0tw811r#|;{ zKyb4o+ahAeMEhhI7zKm}Xa;||!}(R>4L4CV!&N*fT^%QP)8T4hpAhe{lfNpr=t52X zsi`lv2V3a(*APc>R=N=pGQ=YE5^uxrfyIKf8=1QrV{1BaPwXuD-Wo1qCmewxFbFXA z^}%fm>onOqqN2tZV1;Ev);)2o!mlfyV)6(_Z$hXctwNZf@88(k5Uuy+`67u#zlMe@ z0@0q2w-95o%s^|SjtRKqcRb!UdbfB_nO&Nm87;NhoRuG;%waJcmgUOpCN!jDTzn5B z(cua7tM+p*iem87j6~(>omg@D^*N<)_XI-+6!*4qD1GM#{HckSA*yq@ZJ z1oKCh(L|2Q{;;E_9gU>5j5R3ps;ej-$&?ZY#_A*aX~PsJ%vC1~CuAcy1=+Os0P_N_ z$XdNGAk{^t32zz+x94y6rf~!#kN{}RG@?k(OJ5%bxJ{)PUUj((iJwzPFNoDqU z{b@;+mZ6}jcpCN(=~9!NmTX>UV=WjWD$IW`({h5CuCObnmO&*)Jd(u^EYw2@3zTzn zDZ+}Q%#H0pZ9o`&G9KYhCa0v;%leSIvIHGnzA|ia2~peQu;Daxqbu^up1`h%K#;Z~ z$gKZG#`?|=1D(LKF54b#rM|VZs?{w9O(~7QSmjY_i+nT=bVi;4^ai$of#=M)7!o?`rVNmfR#%nnCbO_VcDBP2vpE*SSy zYb0m5h(>V8%V4Zb=^_wWoL)CBvXXtjfnV1UJC+YiQ|7J(`zFsw; zJ62eDo19yp^=lex{f^_=qKhu?$J2DN>S_av;&{ICb$)o%KvHIXdUa@b{j%M$+j3AbQzX5xZXdm-kY@)SG4qUq z_fv&bWPEmeU#j5XHl!1?3z-T2?Yn_Xvlc}zI{sjs+{*x3LBxPOofQT8UN>ZkoGrNb zyfutT{t81HS+5>Y2oEi|U1NWhYUZto!>k1qM6hs!Uz9~X<-NF)C?M6KgVN$baVIGo zDmnO&pGYBDkhtwCdP^K#*Ilj{@NB)RepbEWzrVi;-iLoX@Z$pMHWnH|8nxY zyWQcmR?onM)-2f0^k>)|w|8xp^eB2W1DA3)*)`7~D8j_*`I(8}%suQZzXo`b8&w-o zo4)r-_@73r?p;nPHuJfsg`4-Go+`Vu3SqSmdGzAaEEV@p%Be;r9NhX3p8EB;RC?o> zJUfN>vwUzshAL*!O@lbXB#tn^o>WYFVwzQs5d!Zp2wd}5r@k@-tFby!Zq0AO*wB4X z8>t==l9bm#shMyF%SNT(cfM^%t7lH94*nlgB)o~saI6KQ^rJ-H%NUh-a1UwlUcKl1 z(-%G%urH?mf`R9BMEgTt+VuPWT(mqL>J2U1?jOayvY=9So~`;BH?S2`f+dF8&Y6T~ zUstkknWR>=0Z-qe%8VmX)5mg~ka8&u%7*{L*`9~wjmE6H`*W$yT^9Tj>M+%PdlsfF z>pVV2eC`9*dgzE87pVy8)i_yH3$S+^b_r9;I1Xoa(^q8+2=PMqK zbwfj+LR>7R*P6}3tJxnP-iki>4_1l+-bX>$pvW>!qji9Xu^t5mUM!k>(<0zgk54sU zMmn9&dxXMN8(m~ROwOL?A7MMX>LqClH8zC;w!k8 zw$9%*+tAcPzb;*Dc$C$-PbE6fi_Xt?NCB>0#G^6VB7Voj9s1B~?!!NT2 z)J*K|p8tq~OSr}Fl;UVeBcjOU+cWv@bHe7KWcl{1THK|x*sk;+NB*Gh_3lA1roJ>R z8YGH-b5}YF#b`fk)x*lJ_LU{ zsT?7gtExGj2_4T3-HY9)N%xC*7Zo(EvLBeCI5QuolHLP!DhM|^~QinR1F?71OChq9KWI}MX23CQ{=Zwa>p zg=Qfw@v1)ySrz_^E8u}RV{i#KIAR2jKO<%M_rNf?1hf~|A#Zdv1U*q#0 z4M9>W{U-tdSPdT**H8)SKc8~0dU;D}Vf?IIO=@LbrDt~JBtM{U_F&R*jxN6Wi>o5Y zoLeTvb?cU*Pv2|>nicQAJ~eHPNTf5u=sStz46>TWiBh`9Dy947-UhwHPw1(gHl@sv z&1gwdZkmPE8|6P#$h3NPzo_&j1?xi$D;!x{QGeAFR5wI7FL@jv7*}Ac_sm_@-D#_T z$DM!IYTPtEZY&kMc(58a~NswVY&Kx&Zq2rTH2 zj=}P4hwFL*o+dtPxp6LUCLpQ6eC2Fw)WmyKj!Js(`~w0VY8II_5=pv-H+ry*SZ^G< zsa=!bm{5M|3WyriW0506Ll-XQ1eKPkE3}@GY!gz9zkN{rdGDb4r=aFH1UO$F z3;Q?*x#9&95~3*o60Stp4J=xZ`yH5Rg=v|l0*J1RUrk+aY z=lQ=M34*>hpxu#2JG@fz!UUFuwv()xl)nAuQ=fIKINgE3c_qzra$8Nnx~Ukdc2O1Y zxgy4KU%`~CY$B|q|1gfvpm)1e7j!$JeC?wEqKmMVDtFv z*%^tjgTUlM2wWN*Q}Z5LD3T2_zDk;Kc$2|qf!gOi1;wh|QERVfO&I;nkr%QacjNYwgjMQdgsxE@7??-V68NKjexb_p`|32$p0xBo`*!ora4WPfS*Jwqq+OxHt+N7V}f8y9STV;F~vk01@C z9;G|+T;<3k)b4pc(Y%8&17v`H+7o`0S7Obs7`Fj#EzM?>Ek&RLact0Vyhv036=UUL*^plnjci zR33mT4TLyM7wUuuoV|tE8&_)HvxcMiZ38~>H?DmGGsS!MWZOQlNZLv4iL`_N;cuFL z3fgY$a0^kaf~>gC7UxH+=*L#kt8;Q(=bpHCr@=>@P8Mj+AtNnp&w19=-*hmz4`AxQ zId?_euO+?3jSEB}!ND$6Dl%;9QQ&>336w8R{wL^T`*F$veaUdxSSK2gdHsX zihVP4T!OxyM*KNmI4U67sUSup!HWu3G_xC?+eRJa-nFN1Na4L*LfTPeQ zb=bDZkfJ8;y2TpYAAx05##e5l12Xj#vfWJ~s=0VicB{VrUB<8NZ~3Y*Aqb>y`*_^# z3#_C8JXRq8@M84lXiB8cKC2A=91CzgLYiYPCA zqGdO(wrW{H8FP_jcTQRwg`dT+g~66bX`D82Hlr>YeGyqz>`Y0ahCDqaxO#-jOh0~s zl{rEF#B}9}e%yxA+l#oOyA@=K`2c-Z?2E_+&30_duYS_qXS10WWAbfDed}i=nDHp1 zvK0K4_En&@j3zI}2uW8=AiPMcB=6~~Yj;gMVT5RBa>CE6j;uNCLj$`{GJSnfX)x|j z(7B}|-A4wktYRSldyJ5gv)GB#>Ve$KF~eobOi&7i_pjq;S5qBMLKiI*fGG(pNJT@r ztsl27tNLmiJbiZ8HXyLmz+T571`MfcvS+Q4N&HQ+6=7leGi;QSSno!HoCH!KCQ4QWu&-}uXF=+wf!3nKvFDGCaX8SJSP@3 zr2~K8Gi+_pQ>yP&!D67+I%kLDb{9pye^tnf=p3l#rn0}~MZkz6HlXYw+&*j4$KP9| zGAPO;;ZRj_StuO$laU8JPJtj;n+G^IXe=qZAQ7pcDP>o&u6PTrhhp{lPr^#1GwDJ{xjz zrsE$iQQ~1epkTslL64M!DWkTh#}SVlwHana#|2n}C}|9~W#y9`vG)1u+Jq*KqPv?_ z)4W1fkIrs%K=J~nA|h@e4oiy_r3CQcdnXDwM#+o?<9bCnS0^anAVlsdMpOY~PgFS;W( zG*V*sWXDJAL9GY-J_vM@SPygJQ#IPIWIRWSug+ODb&F8$zrNA*FA<~u&ixe)G8X{* ztpadd>skAU01J5#o##-%?SvpB#n8i3%WU`dSQBVSql zDi7#Puh%xmB_XXrR40`cLcFtgPM7pNRLmJ;VrQ3H&{_M=&EY(@$w|cj-ua1#oZn82 zo#nj^3~rPC>X7n)egK*V0*9{3^hUe2QtlYA@nOs1Wt;8X`dpnWT;ZUVKg( zs;kqBnSZ)#?B_bxRYo8i5IF|SkilxDCw$7KSO?dyZ4#zv#|{}&L?VT|a-2>3>y$d6 zWc4NJ?s_|M4!c02v&m}|55sk=8(i@;QOoOJKo5fH(ruN9gzD+zpy}^BGyXDClg^b( zWIUI4k=V|)!rE9PC*nQ6_Az;L4l&oGs;tj39(7n0AyOPZPbw>#BD_LqTZ6)k)acm) z4HGX2(-F5zgl9wAAlr%6o)lC)N!qMGkM$S+xL?B{I42=8zw?_c3!Cd?hxeWB6$K$_ z&n@`-@_|tZ#~6X%?WAzC2l@cjWw}~{XKxVvMg^LMWE5azO&=3iyL?Izgp5}<6CwZv zEzc)C^d~+tvxnM(s;$?j#XLpqD1v#Fk5*7$!WF9+SWjt2&M7lOQ|apEs9TW4^uLNA zVy&g>YEHBuEy|GJy{lWpGkjAY^)A4_8R4N?WAhVDZtqVzlo-Bw+rPprvo=|)Asv`t zaNU(r83)#A4l2}4Fc=)<;fWO`>L<@pR>`TSF$fp?{Pql8@q9MRt?w!IE+YxgF>iDd z(!$3XrEVYzXWT()z7^K%d%9z;W!PuzZpX0A9bX<%958az3`k%-Qm*EBL~Xc0 z2)BBv{Gf*aGS*r#PU2;&u!OdBKBi}Uq7Ne?t8#wpMX4&Z)66Ga65AALa}PGZpA-`5 z?*uAiK}!UiOvJLe9Kg8DBVoktCmtP_JwQw!>u$vRTrKtSy9Gz08k{SS&X@^nh9dYn zuJg2JFWVrc#DuCX%y#RUhpah$_F6ZEFf(O8+13~M@P_VOFR-&u zgKSEV{D6_KOCg+_L_F`)N^vV!7-Lcj+u^P8vVzeFHG9yt{1jIU!JJ)%-7XG zGl*AXo8E$PX#TG$Y{4uq0z%iNCzjHc6m9|BjM+}8D$!@yIRjfa3&fr zVR&^2u!!YKMhg7PMi{V-o9p%Ag9L<6)}T+aOhgFY)`GQ~MHRRq$Mxd<0w)*~Bp3nF zD*t2hq?gD`JFZDbROof*7Iz{Ldpz*lNp99vokMSKEtl#RE)0cZrOu&FJL$zRex=9a zvvE5mxnJd+_(mheqQM_OL#C?nHYdiY?F~?I@i-^97DIf0cUvV{w+Ic$gyg~lmF4pa zoFCqpe5vAap5FF=(D;T*dJPt;-ns%Xisl0kq4DU}_|AK}bf_h;Ng}3!esRT@o~TBr z>5F`POvOBt2)TISt1b2#Zo_bZ~)?>_jY1(28+8^4~%Co zQa?M~6?s*OY+)nJYe9GA#E?X1 zgyk_359l6PP0$;+&vn%-$}<<0oG{0K>E5mxasVpz-jhG|MCJI$>l$5K5ikk$NoH^w z)#m)>Sv?nQwG7`LoJi5$64uB_$a8J{><7*Q<+B*MRZV0E^Qt`YaUwChbS$5HXq|vb zGDw?L^K>_?PO_HYo$Ev3(4gaYy^E5SonwutBL}d{osB=@)q!f|!v5MEB_&&^tp@B3 zOK8JIfjF=tailBrrUkAHNR$mdNKtm*d1ekMz`<|GkN15y2BL~OYpdW@j_irz%ESQ! zU7>^_M7wJ?V^>d9h=IQNQ}+RZ61$z@ zV8mMxGHr=evHmu`vS6B#bDT0^t(x~%Us_bJvJ6`q=9rZ2X$y@8hshw^k}Y1kyA|)j z2N*;@^hiE?q@EOOm)&SH7~Mw*raO&!Sd!V=f;6X@yhEc3euWFG4#JcoEd`mKT*tC| z7(TMyN3++@8OK##WY~MO_7QUJz5LkepUs-_dule0QEDj6+KS030%xkT)ZPGqvj)HJO)~c7QIC$h@d|x7?9`bDdJgK!; z&6W|+i_dKa&wSb)<56Oz^#_oM5c z@YQLPdD^!w;sM>7!+Sdyh~?>AEXG76bQ6)GIG8{PjHaufZhG4>J!;r zu9GAy9>?^7yI-)m zs}5H&OWKe+=5Aip7Y2h*kTV?R>i3P6q8@!G6-j71RO23CazdtzTVox4vEqJ<8T2J= zcVrb_O!_rw(o_*ztc9?%=%{cusO1FFg4|+b=0Ao;cL-~!3D8yvhn!`OQ?8~fStH8n z$HcX{${_a4V*RydW`>!vTI;O;BJ`A;ZOP8_jV6BvKyiycTt4OzWRZEkW`NT6EhtND zjhtH^(G{@Y`&X|@tKJgnxIsO`|FXe`um)SNq%dO8*pW6U!NTugT|xxaYY?eyh+I}# zN{8HhK?AdVHH=aY!PVcJiY~%y27k1Z$x_h8S9qTJ2sXS+!M$-c_;aqDyz$Yx<9|t; zo3rA5eHd*Gmg7(;@_c%6Co??L>pfa|wQ21@IKIiP5qq8Hama~OQSgu4t(L?CE_WK8K3%fkxNm{cGi!gKzYNvVXwq#^^1zOEG4--juWI0QgRVX ziDLl;RMib%ky(UftoNLwNHIz->iGB$@YziUU=d1kZfuP5p@v{bdWJA+y1r_gH1tvqu7ADY z^k4nFR|1u9t#~e{MjS1eK)3p}2=28dQK}jgK}g__=P&&IM-Wwz0_%SbK&VTC+^a)1 zVxb*cu)vdbJ!skZF(Umi+Un|jF!*nEUk5q|k5`K6u$Mbg63dAFUVo;>0MI%*aGq8upa^zYS4t26F+>P#BYCYgmBq*= z53?5p9o`mQxN&xg3)a}3q2J539EQUcjjAPrziS5j8}$*Y6b@&9y+$FAZM|dW6a}L0 zR_^9E`M@v9;Evn(_RE8L%;WYjomMeG(oiL~i58b-*o_*c^hf+ma|c44kAD!fmNOpW z{|qxVyf* z(uV4r!%Q1>%@qvbDCyyun3gjJ>ZnfM!5sc<%B*d~I(^bX^|O5(D3fyd2*Gi3OhITk zfG$g>#mBTHDbUWcJ;Y#J>n(s2?v+9QNo|S1dSOvNyCi&MUZ3}1h?VGI#rDI))I-ks zPBrrSuq)&KxQH3@iNLi0VaVmG-WashA->FE$I1R8%w_g@=uI3jF?!Uzm@RT%Tu**d zoRuIcqzm`5eayp*l&}f(Z&_oJDh7ZF0X%bqLZMs_LiBVzB#ge1bKmjmA-%~q7s`Ps z*-FGN-%L-v9=z!?64Q@Jn@>q2R23Bzix^~m2!F&@0hCTq=<-UnJfO9&Rd7T>T;^d~ zyYlAA1v5pDyyaPcX2oGm8^`WNeOHfcor{N#m(O5`VpGcX9-v+@o57@A&`rI*g z(XB^-%iH+O2NGO5!Ex?L5}`j^NfH;Mgg9wEG^QIf6jbac&fXWQVR1J9}7Xj)e;mM z;ZCghDpvkxG$G#~Y66AQIKVkvr$5XqZ)xYJ3O4bncUde%i*m`rbTUYG86HUx=0@Lw zo2^bjAlK9NT)ZWe(P*zJ#myUrONb$E>jo6H73U@a1x`9DB);-wZuFdB_|*MNzz%2) z877CNzu^|}Cs?RD?I&AvPZ>a1Hun8sHwkn+xptZ|{g`=Ev+6pSA^EbGY)nS>{-)ET%*hjQ>e{<@b}- zcpBgq*dz!ZaLyWphim2XLtnErs)atDE`}m>;u?qh8*B)SL$rl;EEB>Ql;{l!)sNK5 zP?MjdpF2EMXsNN?3Jc;DlALe)MQ;kUYs=iE^YEq|ieN%DsN>(r+`GP&r>qDFyjB{d z!qQXvvWZW)0W=8aj95cgK>2us>XLA%rO2N6#yL#1>AT<3QbTKO zi!4Xi5kfu3KhDwFC|l0#wlO-%tqhZbEB4&f&4A%`Me94i5<=^7!HXx=%h|T*>Bf2g zIEaUG@^MN;RS%q#&lMO%Vk*T7fN7C#OE6A9FSTW!Wd}B!x?h3WV7?#qm>~I{Q};9KI#hla$L>Xq(4@fHIi)T@>vhaY9X?pqW1F}>^PO}?*IuFZuj^a?+q*O zgNni;Jy4@?Qs}J_M1z3-h<-o)M$n_HF==CW7w+u+-aBM&H~cuc&IDxA{Q_UMXr3C$Y{JG!O<81=tmiDgmcpZ>!h8Op@YaLQD zTfi}|36Nfabr(l#UYbCqsDxnL+tP{6vU2ov+FdmG#v3L+i^p^M43qRg0^sfCpstYv zg{Uv$a{d>6E2cRRfJ|7b*dl!|zYIV~`o2I+GL~tjs|y}8o6qJ>D?wTzBcZ#~*3tdF zRs52Y%S&=Oi|caT_jf~5FPRi>LCzglED6#W>>G)o>HZ!;+7EUDIP97*w^~$iyy*G{ z(180V(%1liWt}7attPnK2~Yb{&E!F7%H6ywV(X?daj*!spFVW{u+*KNY*3bp-qz{f zET-_EX8=$NjOD^N%h-NYCgGXc=u68 z;&jve?^t6~9oNu`YSXZ}yShWai)#eZvFOoqKin$;rZ1d*Wuc#&O>ia=OtRqt4tVl1 zhb@kk0(Q#fabGqyxx2&P^Bx*{qR`8**j451)RPtL<10ZmaIA#aT*WQ*ug6_44Du(Q43AalAPLhcS-ZjK- zktO{Y`q_`Crl)H%y3-txxr%I`KeZNtf$rB;!9imE=}cYMaNWjAD?)eYbnOCw4SaKs z@YAp$yU2nD-Bx7FoZ6wOlDv#%1I=iMqLe{AHkKcx(rsOzEe!dJf*F%XVsVaHs_}>5 zxT_F&y-p) zjrNgAp4Eg74C6-#f1y-(8>lS25>I|p8_N3$HQ<~T>h?aeAf7CNZ=|(r1Z~lfW=7mg zaehlb*5RnYr$vS1m@wKSXjRyjvCQk?l?x#AO!CG04$Ae(v)8m6pYMWJOvR-k+RPbg zXv;UU40Ev)VQb3T{P!HZ(Q;O}eWCBfGB~5#i!BPy%Z5>4(ns)@SlFc!clm)YV*d=esU26c{dg3?a6VR$$Rx+3X zONxQ9`snle4ZCJ;B>g;4V>eYRq^ZukMM+Q3#k6Amvh%7;n4WvS<|1+vrv#&@`*2`u=iS8KATA9G09B35&s=>3PUGb>J&DG3RXJJTT!I-l1air%={V7`0`^OlLmX+Qnf2R7V(MvH2rU5-?QjENRJt-O z4n!Y~?k+j2z|@El?*}?Zxo!+1n)8VP`?xJqph8Tup7l!%cnUiyu1dhNqreR~2T7dT zd`N-1Qw)3jQ11j)i^;}~?= zMszXs^sB@Qyk<@#Uy^(Zf!)k`?z26>k%7>8Inrre+`(L7xn74db8yh8eYMd1^tM%> zABybsut)1VXn+fO&~cfu(Cr_eY%QIx9AZ1{J}w+Pw6QqW`y`Q1L~ znH5dTdnHqh6S2Ry$}P@uNbXxY0bBhd`fnG@Qp^e~yKT-b0QH^?Sn>3vpA-3L71shm zY#D22nsn~8v&ZaU8NIugB79Ef#sCAQHd5>%&&b*=6sg#CtLg=eBq(FOST)y`%M^(O znSN&t2Q}Lld~z+kiJP<*Auo{VkL$yYh7FSTOMo_(YuJ^0t?0jZp7fK=r3Sn>@>eBP z`i>cnC5}8Os`3Ib)JKUnxwipd4AG5~e(>=BFORjCrvm7k@=@kdVx=c0#(Ft=sjnUb z)g`_=8Ain2)?R8Dz(HSAiPIMQIuOncj08;2tGNwqMQkbUcQ+r{5Q_c>y?)oGYk=N9 zEU2+K%&QP-k61kt$DF|m>pfoaWyT51O9>>WRvjHo*9N`fa&WGG>8h7)?8{XPT}3)2 zJyH)yFXYf(g@?AMa4(!0iIqim0RAWc_;Xphn37I*8ju~l8?HpQV}?vTijO`6l~6F) z4}WjA>Ao(>Rp)cocig;ZR53>|M&YFR^N>$yT2kWY+u8m0HY&l}HG*mX`RY^u%^@r% zdDe_{=bnWc1oiES7WLQhiSe^rM?%YzE^zif{2+&L>@XJ0h-4F3@eLP{#M=k;<1{f4 zoTO?>=4^mT2O}ZY@ykllwYOuGObX7B@?$YimTX-GiXY zGBM432$=Q{ijTqAo&~ma5tQ#wkDs@x2#iO|fhEE`?ea?M& zS;Zf7f%a{*%n-#*f0yKccESk5TPRrZj4d7@Iap zKs2M|*gT5-LCH1#Zayiz1YxfP14DVWcPAyjg~?W=7sMw$QyWQ!vbIuU5?$#>Z)JG+ zm1DefZKk=+7dck2t2z~5nwa~|QA#x-uLnv!{W%dr?fiZaB&7_pPbcJfGXDIIMrgY( zg@+FT-W&qmf}ox-F^z^cGp1Pu_v+AZXW0XC?Ns5b zKcL)88Rp~uUu9%Wsa#Svvt=kg@UgxM<@3FxL{yX#i-;6Z3t;K;<1ARH_HD8H-Oa|#WDPvu!>7q^8iky{+{4q$2}}kwK?#l zh&eZbb|AvQm^sN>?@FW4WjvfyXq%P!n=7{GB zSYDT{4RQs)FF%>?iiE>rLsD(4X3w8a@(mLeNrYZ(jVKa>H!Uu6R3~yQcev|(Io2+ zE3vA@$Wpn!AxJ7ynB)L*WZOhYHelz0+$Duguc!_C2X;?tFg$!aHmUfofdlCo-`m-L zS>V@D6rX23$KR+#hADw%M4mS%-7hm)_o^2(j2x$q21fBihpD%P?2--LcvYGE9A=NQbeuA8X>2}de(dX5YW1SK1Zdlg@C3T|_fXJax z%%Z;jUim}=i;u zQ11zE?ImmVZ^4)9)k*b0V8go&f)%Cr{-Gcfp1VU>y3**0s{G(bYpjur>reIC^~e!q ze2qhr@`UUATpMd>jyz_v(T-?nj}HXDuyr^x@B2G09n610g6%O(vXaO*R65fMPiltpXgO>NFp-JnpI`x z-|ixrk=N}r1aim4vQ}P?I7`rBOG^YyOe-S*)Lh+!jdS$(@73EY>{Ny~0c2M1bDy-f z()2Tvz1P`pk6?worWQXx!`J+_g7D=YEKx`&P(It{hC8A92{{CBv4y?bO)t?vZ!RnH znoW+f#cm<_-N`P27<-7hhBoE0-5IohUBDnT++%%=fDh({6uXCPJ~>lhJ;g>5qn2QA z>Hb;F?M>UGq49AGupfy`GFhd4_dZ+7q~XI*tlt2wMOmIBUsAm`cva7kkwFyOb~-~j ziQ$vTW|7B225-AH9tTodybPSY9H3d*H(?w zb{w7UJ+_i@Zga-Cq8uf;Jk&w=k2zrGC-TN$RtS947M%vb%00$mupv=x%7R(uyP2X- z{BdS0PC#OpG;>Pj@J~!OOxpvghbbXlkz0wlJ!QvO3W2pgBB586zF_t1vq|6CqA@mK zvZnTCh=`7(EUaFkGZthBN65Uw>DzNxP}C#=CME<=iokl86!Or%1L}$;%b4fAXa6$& zihU8`vXQl*wmdAeZZ%jV~ddFxnE2taEG&0;x=?| zJ|eL@8@k&Zy_{`?y)*m3m`u-l=t0{uAGc%~+lS}*Qb3HxiI^2>-ufw;53jJTaW$)} zG~o0DByW^Bhq`FhA6SS!5+GMY-q5_NF^{~6px;`|(Yl-%fKHESk(^gMT4Kj3K1s(T zCjFk!uus&osi;h5R8n8|@Qq{$lVQX@ z+u9Ye)+~f7*wUe9PRZ84ezqd|HD27nF#@<3MxJNWYL7F8!~QnBZy1X2NAdD*6>PCe zOB00E8~LJX&Bsa3Hjd@hH3fWs#uX_pWTugw<(-h{ChTRH6+cmFFl!uQ0^eZU=MWYJY6F3P*ELNA-?j%Fe zAniEbQWH1^)0f9ci*gFZ%5^LRp6(Rq+F8O>dU91Y0F$@5fdNLYLxD8cw0ivfm@6se zUY(Bb)I^<{zT1N{7@oZkG)>tw;4B{yss>(EQF83RYd*Q%H^bq+z8!|%63T+bJZTCwKGXQGTaIb}viFg~a3Yr+TvjDo&oO#8&d2hY+_hM7T43V-PxIm`$|61o z!s;W)1ebrx_V^M8h=K06-Bz9tHq!~HbWMwoF=W1-duUL|WI#I%1i4F||9)NASD@u> z!Z+v9*_NsnKBAp6=N=JmLE@m{Z!&GwI1DEzslo)t=Z$@PFuqi4f;Tx+RLQK%(J^_{ zdxZz|^j$N@kxu0kcnLx&NUwsih}6?pP&lV%L1Q#3JYJ8)!2qEmzFuUoV4Fgn##vm< zDQ{$3To1CYAq<*o8M&r1_}qf(x+l*<)82UgoTWClB(@D(XKeKrzh_iWOc+C#a@6e?n`1*KXB9 zm#tNJKIcj8uu!8Sy+$ zXlAhHbwT8={g&tfObcARPepJDp7&^;yX83`KfcrBmAaVK5-x|-^b(@W)KOEDX9n>h z4w8?bo@^GREN!sR;fF5?ERRcxAFF2Z&2DF}2bmZf+Q?;rqc@7JX!e2;Fy{`iWSE+O0M;40?ZqIp7B&^>{FXF924v(#Xxt5im?zOaLG>_-pB z(d`zNyrp6|+lVVACMQ;ZMmgwCz-(gpfM`~VlTnp-;}LtW#LqNZ)h7I+7_h*lizRSC za4EyufIZ?_Nq^m9Zd&FibI!IhH(zrSD>Q`vh2yo8K}W^)4k7y_58r#VDSu}wIC=0? zij-*L3$WC(q~!ZM=I2>Kz3pPoP#Q;)l8u0-1$wZ~NSv@_>foh!|G|Qpd;4@#`azm3 zw7b$Kp@3)?s*e;>wq4+9TQSA;G7bNvdnh=YVf@gNxNakg^T&k|xX>A|%BQr<>s|Jt z99G|WdgE%O2XW5vveh^|`uBRjOGBo!P5*+HMz!qPv|crM>q>sbdj-_p_qX%#1SWo7 z!6V9l6eq0O=i4y_oz&VO$8DNN6ga(n{=SVue7ZK`EGE)MrE9%vb!$?L5XKI6U`EW7 zRK#w_a9gy2QC0&YGa=+Q?g7IM7b6v}dA#Nm5Bztc2JLoA_br*oT>Y%j5$O6=7gl3F z)gy8E=^tX1u6!-cxNfli^U%6-j^mYr0EOEnU)E$J>C~Y6LAFG$i4IhIv7^5I#U~Kc)vI^NXrp`oA#`BTmm5E82 zG9B_%ISIXc%VUGldHP?;n{Ejbhs_>2)<6gk{dvgC zcaM5fLcp@fpep~oU=zGSO2&%_u%C5@p>;w_I`{X4Ii-jtEl+b!Fej4glW<(M>#z{o z52~|2wN+$guC@3IJ_h9!5s^2k{|NOMdy!a|JAOsc~=j$TTC8h((o}S8jMtsYPV@mKF&@{ul ziO&`|%izw{3;j;K&blaQ04gz3=C6S~5(zI64VK3TLe``%!KH$wzO!d~IPhNB{|WZ1 zYhCOspY-O;n{jf{&nXb*<0l+J$RXcVC#G|^MWG31F9;^_P~hyH@(A-uR#4!&b2^jt zt7NuN^!mVj%uf3@C#)uqS1e@GWwfF4-yC>jE5CV#BGK9Y5vapv_5;! zh^LoQR?wfcGx-S8C`Mky>>|b>RIDJ#v+1s>|I0s)I)YH9-%<)`1CpOlh8Ebtnq z!omCz^8n%edY8cAfsjAy9)I|-*i0?lgZd|n^6xu3MBJsR)g)iB-5oyzb~};{w1lKv z@1KdC@Bh!jC_A0fD0M#E6nT6Ms%-KFohJbm8DL0SeFP^hf|5Q9V%J*xa{Jvw`t)xW z(vOcJ+<{1X=sDHjR1e|4WywjHvHy)yF>1|TgBL-Q%_DXg>4I?;x7gkdSBsG5^jE?C zJq>EE(bfV!xq`BSfJmeiDS%ZSuRi#GHeYv4b8I!zd^XB2>>fXK?xp8_&VljO!LzTB zEXG@bH-(sZJWncB>IN`(bC)@F&q|wBxMW<0{U$z*Xwq=#*O^yumPc);8V-fc0Hr<= z>~_*eCUM$a^s&_=;bL(^FsGIF!Ow~tbyGQQofv%MNGCHKI7Q!eA_Z-MUHt0~1tvv- zpUw}--IWmpM?jV=`}3*prLIyll=Nf(+}k5cvY zdmmV+z#K+6rsRFhh?fv6USaifeR&&G@vO8zy`8qgSJi|~VAe%ep*AS>bjB&y$zxu8 zmqy-JKiH0SDTn9LBzPv=7njNKj+MOS{^L4za8ju;mw7tEQ)+YKcKC(n4TYQhatJD= zjzY40jf*Ht^~+{}Mze?CIHw56>V*z(6W~{w#<=aAu&IgH#i?=qiXpM=$YpqP#&>Dd z3r9_Plg}zN@f!|r4bi*j*R^j~0o2V_zVO-Njt;mz+xC|^3qInqqmv7CA*qs{#uMu!F$ZFr6hdk6*_*eE~T};1?b&h zsja@U4hw0vjneYob}}Z}1|32sjQ7e+;aP%g>XvXhxDE4XonDlgck&9F-lfvvs*#1q z50W(m(|Qy7@`hSC(SH!wFb*^0po*zC?e>}($4#B%!pr(&R4 zxfhBvGFiC3gw$fU_BGV;9NT`Tyew2a^vV(Bt=FZ zta+sO!4og_Yx;0dD}(NQ!Br=FPo@fTCW6gP;^;-@*ExReHQ%1GK~WWC$QcMtXVsyPXb$dK6!?a~2wGPDYPYO-jdwX-u>nmnfmYmG z5ZX=B{rscjszxaq>YPU#(58eRaT?P#S%2^MfjN4gN&!tq&!lRceg}(~7fD=3tjPTw zuaPheHe>H;(?|4n!S_G|n4ZgFWky9D8~qbD=^4K(SyAW&MB~CsW_W34qZ;KZt@yWv#ix$VH|$n3Fq5Av#aD$S^Hx7uJyvjLUJtZ{14NoOhq{8cyz zH`yUK&OxtyGu_SLk|#K0cj|@&nNDGgkN!@<7Ho;E9}8!7N%Y%o)3_a+vgVk9u0P=2 z+WJh3Fc^rT9ibSwD8dA4fC5_P1cmxkI7MXPP)ayVh4%mq(-zD6IJ6sTl|92Gc@92- z=EK?B1sQy_P496cQ)|Qy<6CY9@zq1|z57 z7+v~|rzGz7bgLKBMlYi@d{O8S2ygbmMv27I038$siDXjEAD0Uql)jyyYXh5uhd2*q-D#K;S5x!FQqGS-4#p`O{?>tP%oHf5pLABm_EEaXy)uAb+bBOuRRQ5S^_<$XLheHs!^ic}+R`po z&vy>pa?jtqEzm4sQX5KA2$JD?@r)lI-yHqfMwxL1SaE_} z1)#EOP||KS^lbZ3nX|zw1+N0-82WSx%%vDSb&Woi!bwH@z~lA*N4>*ye>q&l3_)Ur=!YIsa~;2mkP!1l@E)|ixK)GFIIvD1U{s-*z=2<9RO8ZqQ@>U&85+WQ_(qm`(>05L=IQF20V;n(hZE+p3IVgyh zo19RdS5Eb(^cTj;LWG0Lq$qL-bYkJ~bH#>icAe9vxLtD_0^(_lS=MS(=@W=SmFbsX z2V$immwsNtEXS`3+n!HEOV&NWBsfqd=R(n-cm53D; zYAl!6d?sbDjnM^HvSZOA_kDL%Y=;9M4lAyvq_x(SG|*yB`^)YD!S^@m ziI#{mkS9pRmhgUmX_?=9`zw1m5NpvI`|`~#i$@{Kl4K~+QyuN6eWZx1(Egh%_(+tE z)%slT5<}m%TZ2RM_Z@FOMCKsayh@twpm}DEBg!=uhY?VuKyLc?ucEg*n~Ovlkh4dU z2rmrcm0f_flU^LP&Qc2!!E!EUjL{R6Huo3ft!c?EA!N;uEsqR^r6Y@M02B9Wv}hX4 z!~DieCMUH&AO z(PdR&vxxgy$tiI0MZLaF0Zv|DnNs*)IM1kEX;g=DLCVBq!BSXei`t7}(W`xp{!xwg z&>>9=#p-m;A(Kjxl_g;g=`y1%4voPFy{8*}PTxnfK?{{HjZ z!*?-sw3M|8v9%LNE>Al+gZ2FB%th*3h#|UD#rD)=#Wx+HLN1(|P%Qr2_AxeJo$X^n zAl)bBG&~Omi;pt4N6rV!C&9P=vT1>NAlFB`t6^UWnctt@Su=If%>I&jA1zsk?@NFT zIHR%eu2Y0Bz0(ZO)%Uk=ry?KmR(1?{Qlr4oFTCF=4X9KND`gvh>meeq5W&_ks4uZ4 zzufNWH9%>gpeKbc7{N9q#g>|dKfC?)0-~XkJQhfw#Q=PJuQ9r44p@lv5V@w{Mk=_C z$BwOh?9z0S{u|J9c?(X=EnMe-Eg>A2{3^+u2M}b7_w^i9wrq18P}3SJW6QeJmExwu z%0>NN8@p>wv-ka~`E$5*O*Veasvh$oXr&AT()Tb0Ve*FTb^unw15Ws`-p@*2dX#Q1)1N2}hS9LhvwL zJnUd+-(J$~533~+yZAI7y?@418TC^rMs<_U!VBO1rSY($3zxNaw2CW&RU;|F(=wDV z@aA_cO>F$hq>R!+X)hOs@E6dss{LZiwEem%_=QD;ov!$tk7U)jwy9+KIfiH{tTKm$ zY_JK^-SX_AI{gthRA(t-C1U7mOJ3fZ@RxIDX(}PUm4?ds7wJnQgC2}GN09?RpSWNw z-GaR;!{;!Mkt?Jl0Rys&z0w^P_iBcynG(>?s7M}|ALpPU3YJ}!>4n)e=m9& zGKW^8iL$gvzAi9{3XgRqC0s`60!7OM(WC2daR?kn@hn5)L@;V$F-1PRM>8ZP6YKXR zW))2H2QUBJHxF@z$X2H+B8Q8vH8Clc@PYd*zeH(ins@!cD?%1$*1gd2xj>K7nj0)+ zcx&1>Caz5GHm`(X%+K@a1!TagcN61Pm=&u#GRV{nk$_kC9wg9Kcq!5Y=fu(p z%I=$BU!nb~g-d8Pq&x2^xJiZXyE(wdnU}_fY$y2|8E$;8bwLdX9`Llop4&i%l%0?X zKis+eSwY2HiI+)A8U|>C*nSC7;fC0VzKS+Tvx9qA-H#Y&N_}Xnxdrg#Cr&d&rxOH} z#aFL31Lz4hTj-Ll3V8w~Lt@9J&U~zG3i5+91v_t@+LoV-c|L5`x_|0~*dw-*!<>H& z%X^=_!u4-)XI~4^bG3{{2hG^-D9?|MT;3FT$6(d;NtZ@nNlLVj$|ae|9NuSuRP=-i?E zBW>HYl)4p*yw>7D#NH?Pq z4q-NIuWa%^)>X5m;I9~J^?0>>$4De>>K=bmwCHk1(!Cs>=373TY;0>q3_7=GJdSsF zzfiU?Zs8~mDuqP&yYUo>s=_Drj-k^By66MXCmg24V{ky{${q!`x7@%7ObsCtU;^WY zW#=41hTYqLCSU5htj$>bsh4Y79_4-gu|j{D-C(adMmF8TaD!9P!)eHn4l6XW z47qx^Ykb1+LhW#fwuUmjdjzi%`JNUh;0Vdk}|^>h-9bUIqM5toc8vUkR%MA3yB=d z$d+;!u0~;LZoX{sq@+-(Do59V&$X$(Mg}!_> z-H6lm_nYxrPH;#uTB?>F_;a)mvV!yzthzfAHXb9t35pz+M^-HsB*PE`J5MWSJs5T$;4Nb@75h`_|hJ6sI> z;Phx@#-G_2%jLRCq~Is`D^6z}kd~hqE*i2;4yh(V=k`Liv5>Ap0ucRN~Dy3=0p^;qOxUTxexfn5l zGH#bdD%x1ho()&v<6nCO*vM%0qO1%l@P7X?`%@aiSV+ToA;`ueMZx{h)BUtKT%A zT>svVpbH_-D8%1qwZI@r41VVKL2Gr{9Xm<4PXW;&Vig{r7O=fA2?YQTC0aM>rpE8( zIV;y|#g@{oMz7Pr{p$ZrszgYd+nGc^O7K5{b@F*JJndG-@bQe>9c*mO`@%5xBot}H ziA~TDcQ*j&$?`JsLyO@F#90^lAS(fOAh-jB_SY`&Ag20@bbm?|Gp(#eW*94L(zqZR z|H>t4{kltAoH#X1Frp+qyVUb%B&Cza^|G|31XH%;$K3JdL$`CPt6b{K@#|XzdjAhz zw9_g%P#aVqGS__SKklW-Mo4~jV0uDA(-jA4MjYZHF_bYRo;j;}`N9h=YgMB*vSsoY z2;LQg2R0H^j|(HHezjK|o+;PVo5A*!BlD8Ky2$l-K4(Z2JJjiqc)?4WSqQ37^qs`R zEWB1M6EJoA?MSPT>|_|V)wO@Ye}nSduHN;vDFeQMv0azbdZf_c&IH=3f*HvIw;bVS z**=DCwb3B+=oiMO1Wd@qP1SO#{7(1e&N+^OicU#2M!SaU_D=NsWaN&YDoD!hKo@=Z#is>~u=&%Agdf@-k2 zNpaiWi;xL|O?emS&)87I2nT^*nsUwx@ETsK{>E^b@l^qmpq&<(fsk#T|lRI+ybgmLWamZu^~^>lMPL zb9VC0?EMe)KSs%%bD<#!+%W*El?U)WQCIqF!;a~sW!(P&RY0o0SGgZg#%%)trx~~; zwLOQ+gtztnQ5IXr?MwfoW*3uSX@rcGbf+*a3HMG8FPYj#G!K)ImrSN56}WF5)#1T8 zQ2o_;PXsS^P(No@tpb8D8meyM1!|MGzTSk+*i-GTm8(S`OgkbrCq)^ge-a>Uo$J#6 z)_o=U<79VMi4s?U)j+!{%&atyhy7T?y^=`Sf5bUzP8?8^@2-K9P?c^*eDxELl!F6M@*1s9Bfp9bu(h0t8p)q&Tn_1k;XSEc~ zH!&=x7g#GK{3@Q0I5>KM+KW^j$(hF=n0g;FBOJAEQ`2GRco}TfFNNOGXKS0G35Q;Z zUWp+#@|3~-Y);v0(y<#zH=29NQvKp>2O09EY1|7^kF$pZ8rVZ1XwL}D&Nud!e*^$yjeRjy(9@oRa=F1)4h)UkvN6~yhc7%A2HNE|#i<1p>5H$aT4GEbBq z6j?|e)31Q%&ugb}=a?c(KmrUY2?|s9)a$!-v@jfJ>e979{#PtKM2(FCc<^?QlB=}I zz%TXQrwJ7C zv{whHd3Pc7f~T=I?iP-&&l6VXY=e>FeF?~ln`rVM8@Tt+Kd@47wBXKmKWBZU>> zbNX{Mx6_K!x!&SpRTpas%+Vz^`mbb%Ck4xC+f?$HnI`gp9grVY^`IPNo)QelY(=}$o6Pd zP4b9{H8Z5TxPGVrJuhhn42X;ND1b4xd`z$&xXn0 zsPl*~!7~+=S@)arNOsH6G2|z7EpF1oDeCY4PCeQ_@0654&6Cf#Rw?)(Afsl3y2wxF zB9^tXM0Hh|?GeyeP$uz8_JCX$lDfjyXw-U7-xim>t=Ah2p&+ZiR*dq6+#l*C&h=~$5r_fwe;zKM5UtH#3-URk_$~8OoXj5Ro?XVT0q=_Rm zNj1`VOOJewSt6gUaz|U<@(Bo_jbBZ}(SsMfIgX`Ms^c*OlN7X9@ZVG&c}*c2k3Vh) zLCh_bRZdX-tmE&eP&+)@G9~WW0XL4bBgI}4h;7wD{XhD3xXXeNfjAowEi7wdp?uMG z*o+J>=$HQ(vl623Y{hw>Wb?<|+f<|_$1wYBT2LhUw#nz(c+4AK^XD?yHb)TUEbHp` zB`}j@h{##^TmnN(BaR$G^T zMj4CHQ84q$QYse2kih^@hl@;cVMuAR@iVGlxU0^nTX~M@SZ!4Gk zvs;4bnl?n&ADbJ{6y($mHO=mbP&ZtN38fZI;k@X1kEs~AL$ud2%_iM=DOs&4w>t3B z7qP=|Duv$gmwT474n==s)RfqG1<&fU`+9k`kyE&gq6759?eMEJTyD+1bK~jIgDNU$ zGVy6o(2MAyeOnJ{!ZW|YR4GU8pW8JGJu97yk+$HxQ@Xd!EZOUUpe__{+~JTm6rHGx zYa6Ug35yzs^k(+^Vv-ZjvX=14ixQdf7Oq?;B&hwfOv2_r(#8z(WlrDm2Dq8TIyJiO z7(V&~QH@1t5_`6+W0^plqwcurO$Yg#EIp>z!G-ECrCxudDjzs%m z%6a9HnS(9hCIqcRCh~q(t~F}=?8YbLI-_f2a=MrUpDIkTGa{W+_5h+BCt~kC9Z`#8=HiO&dP|VQpOLhWcx9a33VKjBGcY@}-)Fu3ar{6@ zssZ0lz&8gl45I}5(a;Xd+>{1?gXKj>&~XNoBYXpZMx_u4 z(cAl-)CB=vW=;7rn_K>RU}JZhF*N0xf-Nhz-~^9`^RgC(P&b``=VIS?+VuRlLY=J3 z5&sNtP8JMV1O<p z4f4gW9WkAm1$Sx7eLQzw4 zJ*j9^wAj~jgpJK@9}Wx;GCF#sWy^dMfXc-@KTSZ&}1gcP>RmBS&5M zP(g%sisw!H>sSEKLdpUK$NCZbZjgObpcx}1kBJvRz7hM`1o(vOkbHzioKr5B~y<&Ee)^poC9mzd(4BHj}ii^bt@ZL}Fi|GlOz=19$EB7O7@>vSbt zkPar!7wOwDxh0vFMml(-b}gPl28teHMx@Xk;!HfWOu*6i1zY6QadR>Jm?~AUc#>I* zC6^WQ`B#g4)WkU$NiZuocxx(p0TpGW1#+jp(3CP6XWD(pTjqOOsLW)Ve=+ z{0dxsZl{Ljh|#QcMw&Ucm_4qEPU?-^$GHsGrUp$YD^V-kFiMdeK#AnKa@Fk?X1MQx z*-`i#|Er5qxbqRSYM^yoA*ovE)7Oe0;G4G|eZGOJR$ACCehgr;XeMo{ zi_8%~CF;qP`#YPbI^Nm-YfJn;zui%rp~^4qGuSO=mNO6C3faAe+a*&D zJTa{Wxg-++-@<%71Dp13liA;M_#BwxcSc6s2j;uMr?$Q!2d>-7X)TH?StsCp@bNTh zmqfa|NT1o*78>snXtu995XOAb632;0M4x_YW-@82fi$rOklwPy)BV((cp6-fWeUBB<&_4tl;yvWF}uso8BrxlM>Qss+v1ZpM}6H+ z)$8439K0=c+tm7COHoBK4*~geo7lYWs(mj0znw*{;sN8oSlqz4=tLGf*s41d%KKl9 zvnqpTNv>1^PS|MfAsV~%_uuhRdt#`v+)LC1(gjJ~q;=+Gg6AbwU@V`CdJ*aJa{h_o zugVO$^~}!)?~l9hQy${`nCJzoPJTF6B-L~rl^PCWl7JRPcrUTxgCr{!Tm@1W>FAza zV~=;li6VAGG5ra^QE*o+QIX|gKX?BZ{H7V5f<_SxcNr@68MHm7)Qbm8#-kpL_2@0< z%%M0}W_BIjWOjqeyY6E3~2S9|9og#`oCpiV( z_aLpp11E=$!6U8ZSr+Hse82uk{O8Vfb(ZYk4w8GyN8Hta(sJiO0v496_I zE^idKS;ghSsXteL8cuYxo8 z3cp_c@{#c2T~4JIZnQaznKEn07QuuN!gN&p0&ztAQu@L?jrWTl%w9ugDJhfZ_MfPu zHObzvzD?hZVOnbzh%;qJn9}%h37-JAo*_3X3$%1=4sJ~pMQj_WOsB@fHg29{S~hkB z27ldjBpKMZylhf}XKU5Vf#28U-4ru%&uf3LW+#epxJ9mv8fDWKTm@y&`v>Egsgx&d zto-g9HAB`rK^%s_PxE8+x=r6|+RwG0X+J#-fO!HmN}Bz)>I-2qXDw=L%g?6nYLgKF zxNawiV)smY;S=FUua+b`MvU~n6YXW;@-{7`ZC<96`(zHiP~0g`kdEPhUPlO<9JkH} zfKtGy@fdoxwththP#NhUP}sooC38)<;OTy9^~iuh^%#* zm#7uCEzc@hWexzJj;WL*96;o`YtQggf0)sya@XS`{a-yl)*-Zn3>zH)Est^{Ei8;w zM&(n=W@lcrVsEcAfx-57l0MsXXp_n~_!v;{HaBfc0UroUB8=pz3Cz1}QxJPJ0|e(h@GV-#Sc3U36EWWmvRRIV9x;_B>%BFEeI zNDg&F1ovX*sYYeYrKB3NlzV!Nnt)<57l6aYR1Qw#Ms%pI&CtOagkiKD7)^lja6rDI zqd3eV94~T(atfghquiqOWQ06JZTm$L`9Rzyg@8eXrv@&0zYM337wV$KZ`j73tK3Cz zs@m!1gz#UAkeM@^&jaSp84Rs2t%0}~|7-uq6sGhyRouG?JUr(ql_OkOk}j3#JBxuW z{aA`0-|7Hlu-`sidI>9lw_Yi1PG-J{vXa(6*ML_Z~^N=p%0h ze4oTqv18l^yVh5QO?NXPzjyxrG*SZRqhq@~+^b-8ggn^-1+~X5bp6XXtJA83#|LOSbKIgt=N8xXed^&gAbxK1+IXALs z7INE3_Y*t`lkdSQng7Sz*?`jid^u1d+#Rl){M27ou4>sep)1x?km!sta}T4Qp)Yo* za!p=X%Ab$l@cC*~v0pg@dL{;ObZ@dHig$g;q>G!_Tr<5P2ZRo`t1Sj;|!cCeEo9OITnUG(LC<>CV3!&15h9<{~0i*~i zGJQRzbexc5gCErHnDrF@IqSbbq{(nkfdd)u_22gZb;~Y+LB-npXKL`H(^_D!awk5m zH$FY)g-oHa>8r)liv$g1f1R`eDC_uKT*YQ_&+z}{<)f*v-XxwWp^RZuje2J!jr`=z<=cmLam8p zZk4~49VAEi)$Dv9m}i_{Zm)EeWlXl<)3;m4cIJl}^~bO+;ES(qJTY#B$c9n>DJmc8 z)GZp11hl|vlfuR4n2g8&zqpQDDO;w8#{!5^DQ4L7XgJiSy`o$jw-eW7?Pwv0D_Odg zgxbzwJ5dBa!rkl)YrZv6V<|w(rDEhKH5DIxi{;QR-i$q<(!NnWW$l!Ui~!mWcughm zjyzR&>sSVT(fo6*b_#*M+E6g_{a7UmOB)^w(3`xuF($lBdj;2A6r=F9dJGSTU9$eO_ z*&p}O3NMFa^!bd`d;+h2w#WR*O*IURJQJPf*Dgzu%!u3kXgbjq2Nq@#n`WOsH?9%7 zU&lHcNxT#xb==ImT#;VaU(1)5L!`F>BM|;ptnddh8~FMANxr>Nl7b zLGht-hs--_C!2++d$uL{)Pz1}63!&aYj)HCS4nL`w1VGvxz`H{5eeHO#Pn5b1zhJ%zXF|n6On{9r5UJ7! zjHp1y;s`>2nXScUAN|tBd3T#&eI+ev6qKd#3KX{azB)=h#zihCO3@w92=0?SwTkZD z^E0M3o!GQ!#h3|r%(Lqc%_aC3oJDuX20%oR>}uKd1=NTaN9!qtGIo2U<7F^Vnwch% zV6eF5jWwQ{6J>(3OY!X=x3s_m<{c}l9s!aI%QSa@l)_FsbJ#A}l`{opqM0dr(X;JZ zjZJE8X)9WxI>3RJ@xrH02ap7raOxU5CbjL7y;pz{2J*B8X?njWp}pG)<+Ck@LCjrf zH=dZKN+60~RIX2R&>|R(E0N2)gB}Ev$is;>{o3nNQ=i;3@Zgyl5$jdcJYUKwQs-;Y zt?TWMXo8W2foO1eVIB+#I)dEWCT1L?fSDtTMc23xmw<3dc&SYA?92twYHAvw_uhEM zLcYDABppB9`;x-F)?6iYJuA3iXWMWd=%3BBf{>mnl%=B7(j=$Kzu}9k^U{2#$mXx_ zXK5-jg z-v8G6Z{!fL6a}gUa9+JwpFEvUr&?uK0tH0im7Sts2Fv}Cj(^UffRww4@}}p5`8baW zj^wGr$Aq0F2~vwPsyp7cf}d@~-x2q68sq>`K>@lU>I&NOyJoIYE3j=H=t~|#XgclY zSrbABRIV-+lh^;PNeMwxYdf4lLk;ciZ@B3V%-#*Ek?)BwEtJp$D^Y)in|lbzw&H>! zsaJ9Gt_b>U*vfc9m?Ql-OKj7h#-p;qj7p=;pH}C>2G_;H*_c1XEvP$a)SOPpIQu=w$cfUFx+h+j|b%ku=qZPv&#(J$Za*NVU5{mJOY~Xqe?$ zQ5HoNtbjqTMcd>mL=EI6$I}M)m=)p0n=}rGxR4tpga#zAfW8Bul@K0s-Vf9jd!Y3~ z!*3Ajc0}W;x9|4CfZ_}-t>C#u%4cu3#lVdv?ojJd#)pStI(3ynM<)F>_D1ySLdNem zm$M2T$lnJfu?+`9XMKSQSl#x|@kc_vJfqj>bNO5UEY0T8=|^oDn`&ce^h)1ptEso; zjT}T$41S^hoz($nd13267$3IJj8^Uy&`6vp*QvEC5VAdCKZYKM6h(?bBh zaX_ld8f4>^G%xB55kVpNr)8UU;vR2a;Ale1(-}4Bhz|v$v2*HEc|arph9J8oScz-i z*3dS)GcZ|8Fp{k0m6Rf@!4Nf^p&$`mPgh)=xH{aIrSl8*mgnz~asg6sTJYI^Sp|&I zMMu9D6{5-A=U+>dU*`FEyN~&KpI+A=3-68ob<~OyD5d#s6g*OZSH_mEXD7iNw$>bU zjBm$rKGk#bJ^~eI7`R(up89^e0O{FLkz;=MtH8$YX5D6pdWyxu2t{Jv;rlgZGyt9* zCEsbbXk@X^H@!gT8rv0iN=*T~^@cN#)^oS9*o4 zlosP?1r)V2RAgGdqXdv#9Qc~MCuq}#Tt%aWb|JlHUV9q z#4KkVNR$??s1sc77>HryRBNK>fAAH<&b(WZC3_vypA#DaT|?-hcUp&ei`8lO-)5qO z3}&nqWxsU5(s*h*oS0$+L6x#2S;nx%BEb5p$dq%DTVxw_zRRJ3vB4&xlvM4wRlfPh^{I4^};D{kL8cBQ^MBNW{=Y!L4R-Ya-+5qVn!C~CZ5`1 z6AKsj;q1UT{y;HnI4Vn}j{BKl)=Itmbt^&CGhH?QE&Lj+0<-XZ7Swl(%jf+J2>Qxc@nUefsLw#u9`IiWc6Prsv>ycDSQ&`?Qb4 zy^z={1nokRn}k3u0hHUjSS9VF-Kq@EB@)dMztC6dQFNz8Y*^<_L&nevlP%!mu6vJD zpn#$8%k};^O);We(@JIVpiDm;?P8^o6 zD~(NW_HxD(#}v*P3=5+H5`2yjxMM+Ax&CfG+93Oq2z_QToln~o^tQO_?omG`shGfq&18Y7h=b9FN;vp|z z;)Z|tA~q12$R|Ugx%9NS;iDaZoPm<>A1G8&^J1KOB-C2mI8UWl-A$oV^V6N7lU}}? z(JE5$yQM!mlze?bVKx*cU#ssj^ABvr^rs?&E0!*C zsf-d8&V5;H^2#i|h5I9`%ZZ8FC`o?&kSdxzZ6sH^*)ZS`1hIdL&&VfZ*1hWQkZ;2L zc>Tm%MGQfTQ(L@ZGKPN{w~GT%?aGdepyR?0f#r(m@oEbir?%~MBQ&jB*!LHWr3Lc< zO09m8$!in;c6jj7^qE6**}7T2wWGaW#nXreoQ5*A)i8H>j@4=%>-}ry_)F;Xy1Rys z=3|4BBL!G4Vcj^bjTwFVd_KsZuIg8V^0HsW`JA&O5d(hhEya|&(G3{-f?1_F{M3X2ca`b zy9S8Zq1_HgjdB1jQKL0(wjaOcLTGPd`tuU08olVG@ofj^aEC;df>xs3~eJb6+vvjkvhnPA}tsiJoG~@BLt|SgECps5{tS)CvD#j3wTLe>KTm zE9ge#9KCl@F;zXZHS^r1Rny}n`9b3Fz4iZrhh)j;X(@)Um@_kCoO>3#{tKs)rF((A zP>A;D{BzbfLkXqE)=en2s#8*oECyt3A#3MnN~O%9GVs}Wxx?E*?hp}y1OszO#y((e zg74y}=-1lWwKPJ=9H*p?h%UUA3j9DefZt&00|E?D_k97!=|W zbbOgrWb{bZ(MKOmzYHKg z3>mdV0{XY8KbwZ^(eE97vw~cbNCGTY5C8WWbiz0!jk1Tf>q$^*GjvWW%k&d0`a2Fi zm_9SvRr+9^|WY_-_K* zYdjP#$=k3_IknFPuAE~pY9(QvK_XV@3*1V>;FbdUW=+Rf?y z^>0JXr3Ioccw7ItZ!Y~lOYhI%23IKbE<3fJJiTM4c#hS$fllzh6lKT&pAIEzdSh7R z=c$4g$%Y`5aa*68!^JYNY%7dZXEv0vM>GAOe1CojQ6GN430L!~ca-xW8zUs+Z%{#cB*C5S5kgAI+ zLhY-;Q-jC2KcTtw^swL`uID8bB8OnPk&menqi9|!Awi=82!hZn zW^Fu1$0G@be^U`YgHj4gR9u!CTX zQ=NB)9%aW@F)7)pIWcTi_U0(j09l6G=aS-qoU;CEhV!5pV1%()r=YXKS%7rjmb9n zQB0~p<7y$*=BCe#*$_u&{W3^4j}+|*fKVr@)gNiNrI zIAwQrNvul-+H?_yysZK911Vjnbkjtx9l!a-TZ4?&go=*orU>+i6U!Dw6P(9&si##0 zk=E(dZ^IY+H>0& z+F%>Rw@2UP;<#*uSnd6?G~@SvR_@=VyRpoFfk5fsKZ3*!6q`GM6(nw?&iW{)e~J6XgXk)7`_9*W0~I9w_gVV z-?8OY^@nNFdU3oCON*HI_6h;JpBojncHqEET?o3mb3Zm(Dc5|$hXZy0CLylAW^kgJ z5j9D8dpiD<_c!2aRyp@SaXEB#G9UXohX|XvM3n;}(_UrLHem zU`r8@s^>_`$KEbh5RStNeP;2)_buJ}Ub4%XO{z|j!IWSpGlHfnx&<;b3NS~otZ04j zB?-?_^adytva}zsH{5WDO}YFnVI>nEGDvOfMg?X>q3q7-QK`E4-jy35;URTPC4W2? z_u{_m$}4x07Bb>=AyPjGxS(6^z7buPd;j{n=$ahfDHW*Vwu5cxNz7Y?8$u z75Le78&nJs8?_M=*0jJ=11wAKtoer*Q8l<4+!kS5=~_dSLXT*@wo(9Nqf%~Nj_FVJ zY1mKoT#0@hx!ik_iYxrMt-JqWF2LOTFL)|YGMh~Oo~wXdeaLIGEeifaq7V>yyo{iR z35<&<^NPvq%e4+x8Lr@=B67v80TOApQn=kVB_XbJ2EpJXAmNVWnazgE^^*>3xVjih zHto{IBo{@nu*Xq_p|NVx z>PX^h11jIB4q~Dn;-?^2y>sjDhRM|Wmj3#)#=m+NqC<05adpJO9^D zpl7BDnt}c7G;__66^lMCZ+*=5AQcCwFQr>6Pq;4>Y*Ki=l9<|Q8CXnqFY-@;vPXhfGMv zV|gUA62DbDGQUzz9>E{-{pPqL4_ z8?DM?b?`$$76M6XI^(&i-20ilU=7=#&nK$Y#X<`L07I{}PvMB6oyz;49|)XgBg#vN zv_Ra`(;8&sn?vd#kT5R9b;VM}d=52M^&e0x_9&BY-Vnw~D|fMN&iyykjS=mL9?ED8 zPB?!Lt8a}Qebjx}sazylee_$lwcFuiVOTN5=T$;!RdmgNq zXBRS*+R=+xt&S!GfqY2Og9Ey3QRyXu(z$=wGe37JMM0?-Jz~bv`_UxF?hwFa!$!s{ z=$y!zBX_B_62w#9ydbi~65GNHV{mFgF=a@g{JhpbRTvj#HCW>_tgp(lQNhe$IFyeQ zxXg^i66)1C(?(SnCwd0~0(7S5$fwYD$X-`a#ToIHqf!u~9?h`@WaVkuUi@+I;r6qq z_xW-CyIU)I(sF#qbBXuj3(W;j3=r+i+O~li<|_tf@5TMq#%3UfDo+JT@l;A~9nGy6 z>ugxobv|N9tLh(r1x0RkDqfdc-ddSL!D6e&0Zpvd+{tky(KF&p9|;5>4MIiFe4y?~ z{fi%}P)Kc#GArsC;e2#T3J2D5wQ9!rBu2 zz73A_^+;NdiqcSgz`Wkun)aZQjTyXJ+`s!1k3k8#*$a_++maBh^0jugeSiek?3f8( zV+~jUiU)T3=5F7=bLGk95=2<}F0tk;^$nD%Q4IkDt9kkEeh^E`AiDt>))LXImgx@O zzkY!BSjp_K_br&bE3xr3lsS&P6B9v?^G;4{Dyr&mT=+uwy#2jzVjIi3IoxVTfv^8t zet@~Z`W1fB!?J*xy}2-rwL@!qF|S4U-64-uvGy%}mSakjm^Xh&0!t5Z5=!kWnw87s*`A?enJvGHLzC+Lr zaZVw>F`08(gheg@i$W_DhEdC)n5k%u+gLqwo%t<>c4EtuDM*qy`|BOr!w?@bXzt6m zip$I3H!k|yCq%bz{;H(rur1bbimFSNs!Ziv+VaH&_JH#Dl{j3Wv zNn+|3JoQVyy1GIF4HNMd|1L*H(I;fLH7GH)-X8fTYy>BH z1Rah031=aAC&`Nvm+CavQ8JKD*O5Uw?!1?< zN;%u(!^7>lZuvqw-b z97(H2IDbC#@wl@@svmZ=XaCQ9OR3>vlTALB+gA1_U6oAwUj{2&GcEY~Xo~G9DsGjA zS#j@%M_}wVd8wE!ZXhN)A?#TdWzD&f8E$lmI}iY?ibW$Jlmh~Hx6hn$7z|rLVmpt? z_DqjpW(9`@kkklv*Q2&KsDlNvNa-ZR`+1?xtSJ*ta^J0?ue? zW+4l!&Old8r`=ZGtXyL1#8gV)ja0~GM9gn0%?`7zqcG988E{e4n$P7zY@xvq;@XC^ zQBY#sQ9siE-hVh+|1BXMS-IWbgA=eN^}7*AO0R(xpgCJG7c0M717NnhhM!PH6$`pT z^gRV)&6|wRlUy@w>Pyfakv_kqoqQ~ZoI;-im;)P_W>1eP62-Obf%vjsL{G3WgPx)R_{uK|tqU?X{ofXp&6X@jd%gkg80Q5rxCgv|Wp@Zs_FWzCj;l2p?)B$c` zm;pIUlQ}pvSR}#q0|gbF@Gu}G)P8faUsBLgoN)s=_K^O!}djVPP>NF zf2$eeV`qW37|3{z_=8{jE=iXwG`n^#lkDk#O@2}glDus?G@D*wZ|X(oTr_LTz%gND zovnYE%KkmA_aXL7Dj^4eC8Fvkg~PFQ1b@*S8>N1hMgDK5G~bjTe*4e;DeF40{I=DA zi#Kuma2YVfZ=hSQTXj*GiJtKFd9NG?wqDOPa1RT-3Z_8E_~h^Ax0G&}bs}?I%FO!- zFP#2K57TA-)bN|@jMt3+OBg=$oj>)1)*U?~c@M4^VlxNBte6LKo7pB@8lCYqlKD6n z`0gh8Wc*%>fX>@w_ue2GTqgD-)|rlVcRd#m>M>enG8=e!WP4%$y`;B^dMI%Ion$QO#K|wnKKgLZ zA)BZWY8Krn&Q8>9j7NhLi|ibB+)aTc2-x}FW*y@5=iEL#YnUZfi0ImX7Ug}diZ%f7 zXvO6G>El%EOws=b8eCq~vHyMiTQowIi2)>zFzo(k_~NS7YM?^q8eb5v{E1 zx*#+IWsK~$qbv54;mEBtFpmy9_G89a!|0=h_#}=Yxi);@5o9rHYND;vl)DpKwCU~% z-2Sqys6zs}g&$VSwUsi=e&7J!`ah9z-XR(P$(KWw*bTyp-pn*e^ff`aR#MIev(MD7 zddunUH`aEbcLe*lK_xih(Y4+o;AGf?6MIXz$r1ulR#NF4Nl1gxnR8%DOnr?*`39uB z4z}H2$6htdpuuKJ<4URQq*~Y3>ZKs2!yCY;(eVfct3VVr^4$-`+Owvy2R0X7>6Rmw zM%1TqWD{~ugWzRAL9e!30HUi2f2GyJ*}!NjMBGC+fw+iFg&VPhvR0#!52kQGz3JLO zoAsVk?O%j&(o7t-ht|70?Bz8ZESvG=_znk{%vd;AblVOH*;eQXV?C5g+*$lR!K60B z=)OzWM`<&-axhsaPVvDdqom^!&oTD+t}zC7l4OUDu%8|DR&&K&yMPTDKUR$7){2^9 zCQgbWeR4F1wth+6{U?>WCEfH`D-l1@*ApGE?BZ2ka_Tn8*kQo##Gf^ObJOijDpmrclJ4_3QzcDZ3OAm+^s*Co zyOXJf@)iAY7XXRpd#T(I*~dhsdp|E8ZKwVW4ud{8C#Oz@JcRz0NQDV?VjZMpRPBk? zC}L6=@9NK=Ie z$45yu*lMd!b3fDGhbgSQH43zJ!lw(dlaeVrKB(RjNp;#5CsHqyBi8p2{vMN;2A%Iu z9Na!06PSqFQ-_HyDHx%Eu7ZTBzly=Xtg)vyg1Xq?sFq zDR>JBcLnq#d0dh6;N)+DGftu|qGfrlm^L(Wm)ALoUV@)Y;_6X_VhlO<+{S=N&zO^B zefeYb^%yF)1h+)QV8G8kh^V%KqnRr38jW@!9n26=SPA*sWnjax!N5&oQX)k%yb+TX zwk3~cpiGF#gW+7e`x>j;H9%&-FWmK#2u#PgNd@5HVJqIpY6JyMpXpg5R%3Ua;~qBO zhf3?ZLOmO$(8WXtzBe~;5G?|m_EPgyZBX0WR{;x~)tQ-Tn-F$L>L@1$Fk?I41~4}$ zbX z*_`{T;@)2#86IsQ9s%*ndyoc}9eOkjW>?o?A9MrDuIjI4jxxTu=GblbQElvg#+@Jm zzt_CWG<@YBl5#I^qaWx^ex-#w?Z+ik<9CoB+P6r3z92}Sv;|#3#YoV}gTDCKYYir5 zF7Im-SZiMni%wUvYduZP%bJ_oO@Hba27k@37}`DXFfO~o9naqP?%LKpbDBSjK)tTD7WM@my3H7XO0$QPYZ?4Us6|V4=mx;OhWF1bImQb=GR+(U>`T>< zgs;h5o@9a(fdvEP?MNfTQ!H8{3MYP5dE7?zO9aeAhitW^EqHJOLk@OgnT9|rdJ7ut+~gDV-htMa(9lC{MJ zKGGWe{UN5Zy@~FeJdoM((&)n>xeg4Nyk4OViT)2fMe5;h23h}k^nUb$z-!r4FqrL) zZ#@qCLT0N$s7mj1q*R$twq->5**t4mHn8J-N`-)IcW*RKo>ct|C33@BwmYpW)hSgm zt77C8?e7-vL)G0qWQ-HM%}x7HhC6c}#OPGC%t^XWfz9YJl1o?7d<5GW7^@7U~xG{8WZ61%FNTdYtUq> z4#0_{#uQfMM3#U_B{e)M>ZD}gEjeB=C$Yutpn|3f!d&Ocsb)4sM!F~7KUWSsNGimvEiXwT07 zKgG5E69^HV8V?2tO?>&(Am4)dhWztqpcI+qNALoB7Rt}L4`>Ko9cAt#R_hU4%JHYI zP_ofCW$AwQ>10AlfIt`HLjc<0dK|Of+4nQ+v$*v@8_jW-F!`KKtbUH!Hj+8DRu50V zSh%{wy?L47HTNRW&&m5n&;j;CJ3HpbDs znzOpwEaae2bnlo*6v;SEsYnyl2w?rsUI0Bn!oTv($Lp_%GB9r$0qlpaZDXUSd(Bg) z^75ZU%L+e?(p8P8mCswQWFy+Ny)rbFiUa3v??1H9#)>b-S()FhJQeMO3t5ZJ4W`dJ z!`s6FsWLc4T*y{|?;(Fu#I;D5YG8Fb8q=^~nuB4jJ4bqnF7k1BD9)+dOk0Egs{ffU0~E60U#evl zAJQ?Ea6F4uj)(2L43USbzDe5{dmSdH-vIw}a4rz9M(Tgc`Uw*PtUsUvrf0Y87;}*& zpeWnqPgy;+@zw_Trd;-0(Xg!x1#voi?hfJP2%p;DSyxX5 zT2Y!{{e>aX3OfoFVJMgij~+7>vtRSOi6RWIkxhtUeTA)?eNp&<3Y)f{E6tlmWzh4mwA=nX;y7v3OCC(;gyK>@ZwyuM047Mr`(G* zKEc(97dz|EHk|K3vU1)}7Ho{270!JP!!PQqP~QUJB+T@uCAx(TVwyR}TG5)(r$t}F zMD?kS*u2xobqxl_OC-kSa`)AT$~?dv7^y(Ud*w{#w*gnd&trpFV@J=V)Ak)KzZd2T zdo8InBm5r*F-LnbG>kkIo2>mf=A)jc-=;TV{e`$LXLEmG5{qK6IMAxZtvjg%X5G7- zv6bPR3UYuFs;CSPp-wM`ku#P+)#Olk@2}F*`#>g|n%$6qS}q z`6}n0IzHGE_q;=lH8Dg8eXUNc0L|v1-O(89r{PL_IC66-Mx(POBW)(DhTLaT{ML#{~)DaK$&(PS}0h`Wz_y5Gv?m7zvl)m@c0k82=*aT{qHn@qq}6 zRlqLhk$GNX)*D)RpoOpC*fW~ybhFJ^} z-18f+V|30Qtng)^tACaVGP25?`<-TEcKK-tZqr8I-&6(-C4t#mMeF6j_J$AXCw$ye zD7Dai2f?^NbIYis)Vu_b&(GB(|&qw|}Ty301rR4A-v*nz9TYEhRVX)r!ir%yzjj(&9!jwo%XGBzMW1S3IMHU|A?%BXk#eLcU-> z32H-*KqYy#ySs8wtxu|=)Uw8XP)uuZRu^5pG zOJGGp zx?TGHt1*<3f zUxUq2&^v%i&cEsYM$%vEjEP9dW!);I+El_fe%JqiTkyj`L_l(5BgUFY^GlIrCj9fD zxdse~yTFrkWA>a4;`Z*~QT?t>A-QXP9yFj$ zMAGlU`^0-EAVkvE`SWMNzpB-o*(}xZLM`k$wZRzeY)63jZ)Ong9oBgPPAbrYr=1;v z&h>95hSJwo6=g_F@%bTl(S#z-O^t)vj8Im{JH@uoJ`GUiooux#T39Ic3I+2HK#sPw z-w_)$fyE99ru;?}0(g|WW1T)FEXW&N?__rj1VG_Mh4=@*6lLlBy5yXvkya`?jx(-S z1f4a!tz-_Fji+wxLyz56h%{YQ4?;~Hlg^fB_%p}KG!ZDW?GFGzgzix$ZhI|v>lnD2tthr^UfvK) zDIIw{E*$X4v+fys5aHtm9%JbM*Zza&RR3j~C!>aPT(UY*9NT5Bi4B-}$}6K!7N)bg%tu=jmM%GXB*ppluG};{_ec$OuTgbK zzD;w9TKv~h)03#Y!=`Iv!NYq<@mJ)u&R-a=++mrjeo5A~EMmBfN|eFE6C>PwNW!Yb z*}w@AZZ!uKcYf=K00l7Yvc}$SLJKOk%CX#OR~xNTs3IP!Hdk2QO4@S}qf$(5xYRM8KG=trE0-p#u z#vm;S0bTJ&-iWP93HKTdrzw%;{Kekal0W4Pk?JJ-0-6&#VF8_foe}uG;03!A@Bn9! zZ-D16{NU<}oh%bqOn4kb;-+E&M)@%oP6#Y z)+i&T-(l_<8eXvH7ptta1MT*ny5CZWx5|7c&3pJBR7>Wgk@xE&Wt9`*thOS6amLJBg!paDYa5bm=Ztj@VC4o6myb<8$K`XBpzbS3Fv%U z2-L>e(N$&`ENYp<4jy~A)V&TLB83So&;NO!lfLZjW4D*P>(+2~sZY0>U|Ue%g+^(h znug=z6Dy1lJFBS9qp)~#K>N`qa>0=$dnw+}ne&c^Ie`SNI}vKy0Nv?9!Pw0CS*)9} zWN*0ipCI062^-%LrIc2ycDlvdY=-8fb6Kh5Xt}^p8&x5m>W$1@?ug+Se9e#l7`+qf zmBfzIlKwr~Z>7ATcUm1`|9f}D1|`L(b~aV#!0<+X%ICr8h-Lq$ww!|a>(D3Q?RBx78B=tufORq1A}-@`+YM}s5@R9_S)Q?nu!QiLyIi~*iBVLx_69Ljkt zKu(}G5#S>ar+E4KDlC{>sV!!?heOr8WaUyX9o!xCvlwg9JBy=7Wn#?AUjVy!oi)^X zMKi~!DVCU|BP=6-S{(_r&#Y9WMgX?}LJo_jHypK;jrHwDuM7j$`41ReNP{GZgQ7d@ZRL8&e+mm>a&x z0QJ9l=Z^B*Sq(<3L(t@)>nUoB!I`Gaj^*BG-$@B#jZ2{%I|1UAWe}y2&`33w&*V|k zf0Ie88oCHGR5aHB$)E=V$BJd5gka68qDG8o*}>;n#T~%2#saIO}?#;2mws;)l-dqC6&bN+o&i#4M8t=hSNKkNd(OsNPMQgHwziDw~BNM*-v}Bj>enaykY&_hJJ7^0m^|<2j$`m0})4r9DQV=1Fx%o zGNi2T*p&-q5WKS$NcooT-d*-MUovpfDX1OX^fWCDlSqs}?KMIEORFG#m2?8-adv)L zHM7@4AJpaohPqEv*~5K*J+|1A2Z@>A`yRO{JV2*rfKn%}V0nr)(!QqoOnh{!-KCcSz$1R>#_$?e*DOc6;GLZRDk?Hf10 z39L2RfcACNGE}t~=y@f?mFRTOZLBmO>eMdLcV3W>5>dN6-{$m5j1QWQso#05Q-yI>zgO?2k!X=!di&ENiG`zF=$H3I>nQMs5!b- z&B5A9!c6p&0MB8hyNqNrkQ8klt#y~#9s4I|Lj>GT-wq6Z9Etuhx0?rEnFu29ZcU5U zC*~v?D6k@>X%~V<Z7Gol)6&VbOfU(~<;2*6#>PD{_7AmbVPlBaxY~&+hiPs< zRWH1ekwbm6dzo5VuzirH=P(U2vO`07S#G=6cGO!<#HpFx)6P8;K{zw*xON)5{U*(6 zi6R!7AvHL66c9oeAECchv{7Oetk9xym^K4{Obr*+K6hb9`=M9M)cP7ND%#lasRIVBg1exnr z*tBiy>@RG^j&|Xor$PqF!vpTAn;!fT^)D+FxSxk%o5wf~SnniY`cSrPW(xEO-3=G313~%TY#bTHb|Fh{WQxUr~xGe z=Mxf#W+#?5x{|%xlo9Wcy}X|JW3#5gyGH1SKP~wYIwPohGeJHsUEaXbBg#Rbxo~YU z^NEL>a@iG4i&5QHp|ogHWq}e#>Lj!@%%IzSmf4PEVGw*O08N5nRHSe{oTUrW2yv*C zv9vH@S)X!YRxP9SogDm0ojE7VRI<)Iwsz%?rRNCL$1J8O$UY(z26bo+Yqg_j6c_%u z%qCl%3(P*(Y~bu(9v--wzLB-2wSy?aHD%M@-Ek=s!R~k>Gz)h+NSG!=!p;Neb@+6-7;gYN5aQ%8vP|5R0n|!pw1reD82Nu=bG5Yb?x* zDj|3ypmB_Q!4D^LK;)h^^9sOzf90FolSv4}Yq#p^)LNF8Jm1FxIiI7--D2}-$F4WQ zHFL6EndGh~RbFbYZyJ?Ru{+naO+AQI0lq8}7b#Ek?OGqH@Mpbzt>su!)Y1MP8`jQz z!=kBjn`-6xIU=fmE}~O)@R=?)hJz4e*Q}#brXzqxZ2r+Ek;V{rO!o4uK zh4a!Zkb3abfaUSRSUvYVy#E7D_pL9c{&U`_VAqLC&pq?2V)~s%tD*_oqcThP zPE$-(eZ}3>U6Z(&KGPLtQg~X@A%tBhuS9(q_}yH?ZpZ}%Fwlbd01rJ}Vt%I8d!MmH zw+$kJ;YurcemPBwn*Edv_ahLQ($b5V<(;s<&jtv9GHLyRU$bTrW9u*-&0AaS49YM! zpn`dIhr#+=I7{Q|MoBi3lJLnDrR<&|wx6_Z_U~LTeWbG``x8Lnpdd;*;y(4@XkihW z`yS{U;1`^VwcT6@btWHhRCU7~Hv=JIx%s;u$4FpvM0A`P=>@np> zOq;iErRSb-^a3h8q5}jlo4y%OI zHhad06*Op|xRkb5GxV#3?Ho3dryB#xNMsgUOKC^0*U8Ux(!F2=d}iqql0bMP3tOPt z_vD(boH6IBty;rH0BqAuli#*z5kD%tnvLrZwW*G!ESZ#zA*n%Y5iSBD*ulK;Du=E$ zsd!vNxVK|12{Ep3{5g9V!_MRSQE@t{zbH+uYm(}zYJweFelYCDJ_Gj zxgGct2~ph+W|i}ls)IUjihMdLg1dmBH;j4oN~q))a(C{;swLBpF0Z+=Jf_uhmj@#_ zuAU+Yg>d@+IOdk0ED;nIn)+IM1u;G#zfFg#x|Ge^>+C65PD2vt`2kSKQF>t#>FvVS zF4OH)a`zhyjI?FyA~7QC%~NLar8Mb*bj!7CMR93ugy_bG$=wX3FcJtt5Npk4Z2g?x zcRVr)@0lkKN^%2@wP>Jc_rk8Y&OvZa-4n|{1y1z5h?p-zPG3IDEYmNzlDpWI$ginH zi|{Xj)aM2n_ooVTJ}}t;0Z@J z#lIZPrE!!gJr_eKW5mag8@FH2-;Qc&hrubUdyU@*l~AJ`j@*BWAvYc-r)Ma#$Rk=E zltvpqa))yW;yXrBzFVnsx%fqG42ggZdizX2e?2Mx@3QP8!Y0p*E-1gnXh+#emGNhO zI!NL}T*#GMvhyOzbCI=pmK&ONU$SeAW{i%(;ojC{G2wd>(uI#wXzYL{JK7_^CF&6vxh;1%B5!H#NyOi=W0V{{TV%8wu*tumni|{IzFP zIXxq23w@XKWgw#XGnUcx$GUF-l{WS>(T(2j9{OO8UQBSiQ+dgn-#k;~HHX9Ijt&~o z_*W}4QB8eA7d~$e%7(JW3G*+(Uk;jHeVzCJ2}b2GkztdyFU5Ze3ssaCB^jHk4rp;0 z8%rCbb&yt;UNdp>25AQb%Zz!Or5D)PcgM<=SQ6~rLCF#pn#AxYdXDQEc_(`=RhCy7 zzEzBWmym!!U0U`B*0jaw4piQFBg#3DLbi((A_j4H)%T4BoxlD#k-B+>Fh?cuqHlDSW`98wibcHB^cRpk;H*M^w)=;S9te*OS1iupQsLZv&ZFXl^4VG z%YT-^sP1YHA6+0QljZXhMpH+hn8x)fX}N~aBT=NyvbJf2fJQLE1GQyZ>++=w+7a zP#Awqvf`B{_BRQPje7#He=@ajBG!Wog(U#RRX_TA$*+gO91)gr7}(pZhF1y}BQikK zuNPJ0=K*COhuXD9XBafhD0jmWVr+|w5qetD-?I~IDbDx{|J@b zDhI6UM0rvN^YH_JaZ}iZ+i>1q)tVTe%s;@uNrG?*&M5>68G+e>B5_ELc?cY|$bMi$ zO5@dQ30whgAfD2%fVZLwTP6rHbG*j=2C6Pqpj)Xtg|YQmTNx4UN@U>@401C zLL8@r2@FX(l?Ig8$3XS*X+it=SG67t^VzX3STnw{Jz>Vm0p;3YkB9e~wUV@qDzDTY zoR>L+nMwy#b$#A)2EmWC-V#IdVy@@x{<7s`HMj1n@CmqK@FUD+lst;T16_;XCJa0?$%z%3z9a#AO!HhfO*9F7YsoKwl9B9^UD+VxM zSUKwYnW_X31+TF8GGLA5(LgfRqw>01d03Kl@H)=2?q)vYzD-k|yW2(Z3G3B^IUok9 zL^bPeo7tq=OP$_5CZa9Rjy}A!j5@zod`M8W#lD1IV}f6YcS@)BW(PuQu+JHkK=$p@H$D^$G1`=eYuStyL8-thVbfCJwhDu5bsHYhe|9V~TsRVo|{zR1Gu^`=+>^c~8TtjiAhcmWDUhfSf&%(C6Z`0J?_h z>xSxtc!3}(C<0B`8(vRA{1ZM*e`HlPK@D`pYJ4|>LA_@=*@OCjjNzXn4dfj_=5CZ- z?05HGw4nq0pbonDC{R2&NB8Y53Stl> zu%T%JGkL$zwaXVERRVBCTdW{8>IE%mQRiP9%}g^*1R?zfXM$juA9?gB;HVhu^@dpH zpS39u=r`*L^u`GC-c6QBVm4zCKV$1egvj-D83R!Fe9{IWZkwVvuxNNOwgvjh%^gg= zGq>}TIDKCLgW}7zxjSW68 z#TROaeMqFtRUmtJ2hT(=h>FbeU8X)?+Tz~Qe}A`-Q)Fv-LQ-kQ)~cWR8NOu@nMtoU zNgIkif7_xOwbnFI$jAoD_PBPaokOnhQRSLKqJ!-qjp!W86;3xl*ao2C_yzq!^Lmy< zHUi2KD#IjL-Gx=MR2VUN z@`KN`M?mG8)|nEsVLcCC$d#jC!_{kUpz<&4X3%J75QStX*tlo_Hl?dR;X`GCEx8Ye z{-ILojeqEm{-7F?2Z&dm*Iu8hObYwMC z#rE1ZM7rYo9>UKl0XAmC%0TzTx@WBR7tEuUEeKx<>Ry-J$oW|$Zp8}%xf&cY zT5sW|-*v^Cx89`p_N3glmI&S|4zr#0b$x!8k$KdoFqb;LeBi~0B(P<-SOD1$m!_m@ z=nAZ?pE~0znnOX7ZVtp_!x*;V?>*=Xu%9j&pqQ96_=au8j6o*1@*UK2IqP`_uT!ct zfK&Jz^ivu77K=IRV}CfrW*yOxf2(ZENnPem%aT#ZYIIuYrF90aq-V;fBNV97w3T)n)3FBB2Qvj*1|Kr z5$6Xw{&Wra+1tGZ^z^Yo$+P%sdzc7p&P*u3jCW|X@pP`>76)rP)-0hOM5YO*Y|o^5Y6r z8dsfSUJd}rrXKS{<{%Afj*uNqtS_G&w=nEnnRstI=in&0m&I!G7s3&&8sf@VNo2^! zW3Z;nhYPtDMLmhSn5eh6-JI@FdsfDpcwrq#WHtwtZrR7sYK5H zIi~F1Y2NzCKtwbt6&a_#)?57_d%1}56wgA%r>72UZicdaBaZg2-aOYPUfwa3!5L*$ z19Q6EX_cIvj#=1IVV{rmDML$Yb+b#I%tq2~07*~qtbkR(48Issb#^o6*@VH? zaggMVI((eO^`Ibrq7HR@C3=1yG} zxlzc;medB+2_CC$8zy0ZTd2!ol3E&$q_-s*mSfe!$=n2kVlm@(08RL_n66D4IOCxw zQir}QlSR?1R>t+bwdS1R8#h7-=W(^9XZc9f#1JJ@oCExAUSeKtR=ju-3T+#D++wv@ zh81!D5i^u4rpIX2XxdxVw>WGZ^anZbA94z_5)&nc)A*mYKv?HqpP;11P^^O6_neP{4VZW3_pUe_vM$2FLZZl}`EUjQ zdyTnh!}fFl&7E-iVl?Fi=wDNtX#s}09cX}wQBn=)D52eAEAvxys+ik;bVTbV=ibCA z6cKyI?-b+&(DV_?+}!3rn#tl0XE1nWlv<%A){rk86Grv5`E*u3)f)-31ipy?z2LeN z1<{zNlL^I^MzvZ6tR7|q@Txt+RHMWz4Ws0e*3d+zH!!8F!pK-EEEs5wn@^e0RYCNG zJZaiIZ9}t#6(8GJ3vn|fyCFjv7VCrF<+o^_x!LwEuHA?%7}vvci@vXo{mn0qJ}aak z5uE@}>hP*9^PT+>d#o&GbOYtgaG(3PS{B0vVSJ2p%$#a3?yiN&QIQj*34%>x2_z*o zAA*Rx-4((f(rfQWLbY&cxsecu3X8T{at#I%+&2 zQ1A-rMkGbSUUsATaJXVk4w8pTYWYTyWd46sB!0&}us0gW)-wxVr7!~yg*|)0p%}A_ z{>naa*pfy`U{u+A_rsYlwRy}kKz%(6#%~1K@!e$g#9$NzTsoxM+^vtoL^qaW0Qa%L ze2fycI_$s}*d*z|#QL#?TjOFRp>>ji1X&PJOHoliIl672Wi9+89Gz(*=N0<8cE3|% zuni4eRRFwL0qr+axuy+qtL#F*J5v3w>1|~P^Vu?H!Dbp5bA|!09nsLwvFcPK-||g; z$Nv6(9V{ERr+S`{IyzTZ(|HE~L zRNeNE~8SQx{mj)#~t z%z)Q$2Wm=}?^g4j`R{M2aerY49i34IhDtS_t1M=XVPd`QY<7HS;NfnNbg`x;4FMnN zv@n`}H__^snh&0hQq-0(So!E2PRV@DHER=_umaHm~^sfzGt&as86`RxZQHi&V(RKzSzy7)pDHKh{swW*#4! z6dDSc$JQl-H;P@dh&gC`A5IGG5gK=!%eH-7HmnB+p9Rm5k3UPr`9hGiLMEXsX2!Uf zfQq}DLF7b?URE>Xy3a9=DA&c&J7`A`uG+)BAm^;&;oy2pgR!e}^u*^40zg@umNSsFMD?#Ws;E>@2PDti`aciVbSnDSEJn46oXC zgeAW24gE~@Ek@}Vxt%>rk2>i|wlbL~;!XcAF4bvLRv-$$ViYv{zm@3zv7bXQ0t23? z;%H>uH~hH|g^V^u+kA_cP{Vbh&-B7Z*rAN8jXK`vp3)vIi{vrm5sk5^&%KI+#;`_S5|1o;NLqkw3Muzwiy((CR>Flr>oaYMsU%!O$6 z%0x6nsxD6g8zWEY2Lht{Bv)`w8h8Vu-BVD41+fpyo-SAfV#usxGV3|3x@XM1nOKO#U=0hw`fosm1m9m5Y4N>8m&jrnNgpH2Jr@M>y;v2dH#$Fe zm@+_$a_~V5t%$nQuG~ACH;JVQ<-79uU8U{lIeYCZkk)L|`e~=i34;}l*yDi%PA%A$ zJ4+amtY0GLol6O?@ZT1q@hsUjif)B^yvGpHUy4(lj=YQ&vx=|_bU-R!MsXYq?g*|I zCLYhfJ)mAX+c|4Wf>Q$Yq7_`W+aNHaeao8nh-tOR=FOg@uO|*+LCubFgAvYNY_FtH zfwuBC1-mv*N*f8E${P}}0l>e0KY?JM{Z@fgnJa%DvA@)};e2uV0CHatt6&426Y;u8 zlN=op1VHxQJMU~qvklKvy+wZ4j5N^8g-agM#r<0v7>Xz37Ca>q^6{ZSQ)nkFfes)N zlB*Q=ZL;a%xSy1XB;FA5})#NxNi1VWSZBYk^M#>Y=|YVB7TKKC)^`=SDxu^K!N5209z-%YjBj_Lrb}F6!&h7wBni>2MNz`!f!@oDz_TQ~2^w$}% z@~6~sywjrzTpC3%EmNf#FR)#%1SsYfK6RhWcErM<`~Tfcql>Ktb0+ZbNYy1Gka!{e z#v>HG?Gq_>7p9X5Zl^-s5uOF3w@8GA_)hIT+x%$K?CfbPU^vm4MEPLTTAt)4of7zJ zQ{HxXr)aRW+%tQMbD*VPkAV(R1`}&m7LgzIYiQ<+Y1#^$v2>3dv%gYVsZIvALuq#hri2x25i;IPRd6|5s`Wn6J1?K)jH&)O_@4WnPRkXp!JH{1$FVI zp`FKCP^D4EBAKK^QtYE)Emb=w8oL1-kmB&pop$9a)naa#U zar^9C9{G#jLuG^(QVL#@AlN2LmvP?GA1=|dwkc|DK>#2<1NcLGSBFmVj&IZSY-}m^ znd8$P(&*RYvm^--{ z@|#_dnLRLPRWwj|JJN8m(veR3n0=G2f3bP+yK;jBxx2WH>1@#Cp6k4Bv2FB z0!S3s$tWATW~j)Td+m~2ct&|z(eichnA4w3a#8K&&V#Js$SZ$$YH;0vh+?KF zNEfo`N~(Hv80)vTdmd_?Sla?I#&U1uL{^cpB2UgL3uuKuABx)#EG(Tns9(j%nuKOG=fTOkN2j z!_Z7144TfBQ)+uu3Vw5^mFIkCodvumI-x_EdhP_zlma#;vw?IX?jD}vg4E#3^6b){ z_&=Y-Tj>0~ld8in&hj#;BlF<~u6>8;=itG7be!LYZCr{`MV&)QhedV!Kvk=nQt>ig z-iuODpSXJE96(RUu_=(un3MPrfasO2*5j7F-u$QuxtLsWY9FoKnHgr>XQEbXL%i3ORjA_Nt`P;tPFhUm3 zKx2mxD1EM}j1-ySPZ5}Y=@gtU;_V<6e6{q+7BG!`VMX*K_1l4uYeBE6_))&ZU5wgZ z2V#82(XNqR1*pcw{I?P=e}(V8D^^!Btv#1pCePF7NKTs-n0K^3@je_Ou$ba$?${|1 zlhw&_k0`z4*;Mm#wuR{{+?G;PTwzp}O7%bPQ^RA)XB6K${zi(X>;??#xPnQw?UeGY zcGZV@6;GiwkdAu-_X4m7rF?6`G=$WuzcPXM?H^&+Za|>iOtrT*I)@2@)mq3O=69y5 z&pdl0Mp%`9gt|%v3pU$W`J^i()QY^aw*;(Y4u~b1Y3GM<3#LQqvb#=95E(>G5=>Ys zN#X(0Wh3tC$_t?=0*%i%2E1K0y6l&g%>H}H91V$1;fYr)Dce3TCWmM=Mpv=5)l@+ ztxG6`d)P1yg}8R!qj-$Be7u)4QU-%8dYk8tVNvp1oc=J&K%pf*^y)p|M(#fOI}o^~t&YF^ zjD_)bT<1-!=OMF2hdTwxLG+n}C_rm4H!;$)-9tM+>`+*JAjYz(JR&&i7BT_Eu%CSr zk%A1^HvFXQ2C~A=o1{~5ciqGMG;i5l{5IWV83y}2^FM+pZ2{xU);08Dk(7%oMCYkEp``=s4 z@jaV_i(BZ##y$#F>~r^UvB>)P)^aRujlrX`n&*IR38r=iaru>+sV_@>tz-cKk>rOR z;YRgJmd04w;HfWw9@gom>;77{)97~6QO0OA$?bM2eq*r--y=ssFK|XpyEe##=~y6Z z1hS+YP2i=XS;_5VC0{=z7+ERfdA&7I?txkV)DB|PQZRv{=AOgJdJtDOC$~IdIEiqe z#ou@ZrGes#liGZ*zimPZ8$+G>CWpg>Y_}ybPypByz`$%Ff^GRP??CAIu1%gDO4^_e zcQ%XduWl+ms5a#PH^QS4UT9dTqZ#wTGwL}>{!xdvF=}B)=dniKTKAI%r*f$-o-WCM z;j)Ib$^Sd|ukVpZ!umoAPP-5d>ZB1=sZ-v>0p7I{TX~TV2V+~)@g-#5b z4IEA1KbjUVG(*Gi@fH@+pDuXVJY%4K0*+`f-Sgj=9z2npN8#|6SlC2k#|Y}SvK zPH!nXjG}mJZ?!jltEgRj?^S!o2%)W_c5837_ugVuZ9&W+k)&pj#3qRF^-rAZI_Esk zeN&F(yC4QVst+Q!xLcPv-(T-F;q6gy`BHxWY7}GcF-e4uX@*7<`Gu|HVte6{nJglKJHaD{J zXChZa@_XlNHpXAji|;1koHm<}Kh9N~Jred*`jq(RinH-g)b4W`1y=lQT5j8KCpZqs zD!fkq_EB#L2-2x>m*1~JEXt4R#p4p34Atvb|6K@;CRu91+0+v%h_Qh}D&IY@KEa+| z`_DYOx)L!b+R;O%>PF?!4Knv$T~#3UK81K%l$lbKOj;thDLHXQtK{B2AW|9~(zcLK z)8orM&3E9htB}kjQ_Mg})rmnc>yaWdjl;8PF1x3w4}`p}w3_?fU1#h5#ws*>ZcoMe zlZVYxMW<=pf9NJXl zgMD*ozJ=I*bccRg&Z{Oi{W_5$<1+r#oqvW&&#wKxhgln^e3KSwIycBa%~H=-el;5V z4zgYlAXe?O#Ura1V6aXMQwwB6tCruPKlrAss>I0kxb9TM=om9D;T`talB8Vf;Sq3H zierw<9Pr3n%KRQi_U%_|hyXAzwV2jpu1ckBfbPcc^p5DJ9uo0k!%l~nh5N~_^=tmu z+}XD&?DrFSKJv^{MnZ!+zP1YNc>!% zFR!TZpJDRK@2u0W*w=0kU7iXy8u@Uc8Eii9%DBqH9+kdfW3}x6Yg};6rrh(v-^g~e z$JhLGL)>Nj_UXAw-h>cbW6X+HKSnY)CN9uQghK;zHa|arL$SQiUWB9)CIm~JtxKUJ zwEsS7T2JbFS21Wv6a7y|tp1|ku)X$|40foOzHExY`{J_v(n(PjbM$)OG|yO52eq3t z4~?@CutvgoW^}74?ki!q3c;tcD=v79=dUVIXJUVXrMjc)4gB!PX0V+_m{BoSwA#G- zfL&#^E&Oi`0rjD#zreoo&*7NHkxs+rhq#;Zk4OgQ5yLHMa&O7asGXXjhX-W5(|h?s zY?&{@&0PD-$NSCA9tR%fAzL z5DBY>Zeu219E0+#V#trZz>=P-oSo~;vOY6n&=lKtlKNfLi900;n%pnp4$!PfgNEOWp%kty{MH!s<6g0L_I6z2Fy%KICd6tSpn&}I zTPI&A{*xs6t!8k~K=)7c)I&j!SYr{U7~&FjHC`6lpx&a4x>tuJ#AcB&i$CHQEFL(edE`!{CgMF zzbffb5$CT{=_A+yoO{6`1RUEz^bhjyPkiM%lRCy{xw1fvH35n?Pb$^WtOM| z7)HhCnu|z-4yRT+>3Px})J`|Em_=xAArX`KAC)!jWnbBsK?@FF6^NiF)MQui_&xX4}_LCxb#YCoQcwJUdX|fqsGrpF>GH{iqKg< zYixiYz5;+g<#PA`#aDjxR8qd^tQ{5od8@YAlHx{deiSD98qQ)>GdUx>3iVmmF8NUV z;jO{QA6c%|pw}X1Mc3Nz|3~@9xg~dAS_S;rWH@*~A!DzYuH;$M!x=sw!t~pEddL3f zKX>g-n2mz3{1{AW_qNb}R3r1P3by;AeABCSGveE=&}nmx!iGC7e0bHLb=CIqr3B4s zQ3+laAUxn=3$E|npul+h_Sa;SsN|D(;%;=mEpjB3=6Ht#Qv6+mmeZT;*er7$pYZA>k6Rl@P;f4@SP1rltZ7(5lQEN*{LGDXuq zBe@$8JowdJuj=yX!TLgp39iX{5#YGdc$xgo7M%pR6umaJ(4Hcrl}vgq^)C zuhb7GbGOj$g8z7<$LXSZl>UsYs!S{+#k!U1FiOa0xuHHs;c?^s+9&fa`tAQ!ewoQV z+xYoxh!$pTVma4UkwkhlIOj}?Ze1<;eM1*LlvN_6H7)>)iDdF#e+TnRWA1JcXFY!N zMvTK@fa{=#T29`HzhV@f;Zl=AbwA-tL7%?e8+6eBhVSI;qSn?`I`L_XhdjIolt0LF zZ(M`9mM-;<@{Ylgf*~xona@{wQn|tm^?#*cHHtUxT$6QiJ-zpbp)&7QHl0hB{s`pe z*4$=Jrp(`gWT5%;;K%U3xWM^KYR!;?!ttKrQh(rWLD0vcy?2ya=P*| z+}tHQf-S71Nw9<2{k%2pc-E4~cvtt;=M1SZ&H(Dm;j@vQWH52FfxTAiUkOmF2I@PG z?sYm-$a&DsrzhQW>hOsIcL!k(>C63pr7f{%J!C*EYA6%>KKIj`tIuZ~6(0kC1RHqDaGCpMI@k6je#dGW*CXPqH@!U7{y9=HcbK8Wda>9iZ$CGZL;OoX6 z-0|hDl}~{X8&!^WCl~O&Qn%&QlT*)F1*@0^*G`RpM=tJp*s|n}Y{=vm(K+HrmJCUW zKZT(qDCN!J4%q)1(t#r=E;E_NXYOnARgcQIO{F+}oV~O_P zcVkx&9j`PBdw<=%18>pFC#n|B$e(e0`?OUBto{t_kTrOL{A8`%Cb3Dy#Z6wbqb(Hs z@8Iob#61BU&QTu&ZmqGdrq54+#ZH?PPDu*F89&QHbsz2KKY2)aQ70JDnfyn8Cxy$EuNX|y!Rj$q*hQp z=l?kDPOsc(df!Pw)ucipyR+y${W}*v%6Bn^4ofM^3S|%QDM`|%&g}c-#$#5vT)xO2jR}Y$p*BEdwS~&{(IMljT;TT zeJ$zC_)~8Xkn?a$$s>T0wzz$3%1C?9DH+_nknUeP)iw6TR()4;ylEGrUta-qtO0zz zrzu4s0}x({GPK7}gelYR;7nqf*MEK*ZokV*g8!jOZEblcL@$h&!;#2L4Hf34sS2%t zPcyiTOiRCgYc$k*Q~fEfnTD3OjDqK`J`IuWCr@w5ms%d<`#949R1?3XC7tn;r;So+ z!ZO_aF}-Ku1%%LurCa_bDE9Y?`nDEVs^c72EFgetbpN<{37m=IVdiHEu&{#OjDDLU z!z^FX=w^N=%A5RdJ$qw?pV;>fWlN%;yejVT7|e3X8gL@)eYUtVkQ7APOWWtE)nZ%@ z+G*9J{L8Y`R-?Q)k=Y%-M|1GwLyA}tic!C0V#sXtS@Ytv$6RTZK)9E=_fp?4FW8R< zF=g^DQ_ZjY*aQi4=kHapgL+Bp$R(pWB&Gto+_`CFw9m(cA{!hnEXC<`F$d>uc2*ER zr|%-qn<)`g6XTCJwJcZyML1`#;i~%&M*rzb3haM=0DOn6uwqvCiRMLxPnyT=1iwq?1UQj9ut${D4_zgiYM_@lLkQ7{H^@0| zYqA4e?(z5YKIg*q{m!XC6Q=M0VZ$)J<~hgUrdo&It`y+bZ;7J8^Pu|?aXAdUYi{LC z(pJQYRTg!xH9lFBG%MF(YfwO*9kms7W0Ovgx~a}eteUbv?{KLqFx))cmRIVNShcZV zU%t0nzni`DVXNho>$qd!2@5Gk$IUfdFLu$R<)> z-C0(1eiMk8bAauqoANR{`0|9^uy@@{_Cyt$zwU5yynRf+%ZOpVgEiO$WsL~y~)4*UWX#V=>I~Xo% z!9}=gg+Rkmi(Mg+#Y->phWX{{KODP?ssQFnX|_J&eouCfm@nC8|Jlf<_AWG~@7*05 zn@{kz!z@V(<^6eHZC;;obV6@RQS;H3YU@kQ;v7_UI z<))IptGe9jIA55jMw!TDWi7JdJfWX~?S_l{#AQM;{@vn8a`LGmIi#bZtNT5`)xo%q z-g~iJ-^ti(qp9OYW?M{GGGBY;H54K(4ludg)bl@)VP4$8sZK9pJH14qUrC1}*u90W zYF|dxl%V8MtXIm`HwoQ>@)B#Sh^5^DNW=Ub->8UX-MmY}4kIRa*OHL2o_etdftj@S ze9TzwSf4Fehm6#QNSYi?Hv8rq+Z(`h`711fdS!0MLL{xsQQ~e3W$7eRUljXY*sv1J zui#0<(cqeaV!x{7zNHwKQE!m1#^u`M!Aha20|-wBVcWU~T@<3AxbYR1d`J-fYHfAv z^!)VVqX;*?XL?@<8@)4&Xf|RIf1G?wS8UfWT1M(o2F2b;Lb}To@FqEgL@i#gHq&fJxPJu2$>%Ysn#PiW7T4`i04%W~ z&}ugP)EQa@D#Iov<(xK31KCwlXX`~?;hcvJu!_mw4tfZyT=6FwN^MI1Q_$0!yxP-M zxGG`ueumQ@v-0M%cV51H!yTsb#xRfIZA<`TVauzf?q`M<*L}j|m!8 zwR}9?eWB~$?meyLhNh%uZEzJ*DIDNg!K(TZQ(MuY?kdi;Sz^8x79$nKB2#L6!ca&} ze{;3OQSV9d#Ri${L6G}?Loe)O%PlUGciL$Q-`KU#kY3P|f;AUEcvz6gc)pjh)Wn}f zkcQr{)VD;;%yraCyOVy~JS!c*UaDV2Z3iW#(V3Szl&(|&D3J4Cl>tF z$mvByRsS^K)@)5heC@`z%i_d?N#sA(2be{jeQ54E?YUK_BZt>Vrz(>GJ)L6rVb={v zioe2Azl?Y9kb;qpV9J4ra_Ve!#OH=OJY-K0fFlXf|1QtWjPlb~-1F64tl3RQn_nuTD zXh(ETH?3d`4(ice>VZRhFEe$Z%LdS9>pI?u^|$^B%}OW+vA&;eWr};7+r=9u)C?$% z^DdtSpZOBosjserXforz^!b2n)1P`R6Ss9i>tW;6D%xJy#U(z?F*HrTqUApF{79XN zv~^maK+U5r-MjR`8x{Y>F8h&vjxOJ{S*C8$SkvB{Qp9e}oK)Tj-o6)oMF zLC(eX30*&hr|V!tdBLQNRVP9v_;h%C|9iYwID6H4K;{w3XuDthMaeN!o~1kv-~yx_|26rK^rt)xL=^C1 zzUndV^Sb<0ez+(|O4_Y>oQKl#gLH~$?$CK_x@B(a?E<4OOEMmWI)g0scyl+z{EbnO z423D?fkxAP=7gIe!mxq;5@NU7rkG!?)A#%ntDg_kY#M6J# zgvm8C`9p0T{)7VccEYQwvI-G(HJdLM7fN^T%v?v zN1*HJT>rOhIeiO=sBUnu-AKb5;F2$yKRrVG(jl{eJw+m7M8^t~oBy17T;ldKB zF&1P~z3l0jLDRT3CIXe!?+UdmcJ+c<`G0KFEZ=b zaT*2Z;js>(iK7IMr|?gMg!=W;op4Ra{sNRe;GQ?FyZeD8r0pcmV7UvRz4rAu1^RSq zU8j#<$CFp1s$S{Ytn*cZdqxw``0(4$O}sJx-|=ddj2xCU!stsnZfQ==p^%U#9=cUJu|8J*O9#of;&e2|%I z60|Z&z^Bw{Xb(K2ehjN$ozBBn^KX0@x3X$vdl;`5~h)zsfOleD8t0i5=x)TH;kSSWw)o!@t9^>GNY_`BHmQh(rNQ=)?U zzIpq&;&^R)L2l4A)-ZiWo|13b!G}~K< z;;d8dAfXbQJr2S$c~=(h3gw4>2k4_*ONTOS5enOs+LzQrK*34>-)cJ#`nu3wjLj|) zh#OPq7R}+{hEVQuD(~CyalG=>tW6C@h6&dU?tSOtd%p2CAIpEdgm42Y{OJJ(&yKbS zkHqeM?^5vps;?`}G%lnFI0Y7Bh8{Di#AzKjnhb;rmQz@1)^zUedKOmqMQD2tyx)I4 zx;3dh0q$sIiI!QOTm@2s1y8;H7_U~kJc?>sz&r;w=B}p@nI(5%^;9NZEqmGpupG}M z{?Yzhqfq&Y z{OSE~!iDt_ge`uOMRGbT(~C#_1xK&j18-%T=IIYS!xa76tF`<`D3;prWYzYpHNhe* zg|Nbf*~Ne@(pjWai@56@A+x}IvG(Kyo?RL~$lnmIAB4SHa6QLKx9CejTG&i>K^A|$ z8ZUo4GJ6APW< z|8%7)+ud1>uC^hB-b=<|(bByu8>v?z%LD@7`%MFfwNMx2daqsDGX{pVzSsX)?q+pc zu51E>R)-zIE?40sCL_5Zka^K5wx~i^Gi82GI5$jgl!P-=ix`uTv!e@l`V&$ZnKh8G zCT3v6kL7il2lAtGDg;pNc2*>mid67#CCT8}%BX0d8SbB)xpqkNp1Fl4694Jjc!g%a zdY!w>U-sjnk-5O`jX`|N2}R?Hk&A`%`?Lx}6?K-VhO}6#Ou)YV-dXU8~(tNJ7fm5Xl;_Ek0s zA9b8(3*7VRNB_b5YiLzI4hbZA+5Cz6zQCG{sqInZ)jYaf;-u2l@iYM!UT&OZ-T-_j z-90lq$y}Hh@_D4{OdZ~0n>Iwn1WIy7TRD-{)3b@a9x_t0zQf=}rZ6wfJr;|L7AX^4 zAjL+2GhseUo$NT-Nw69kKJA`GtB7`l_prE+!=(nQ9OPf{pGe9Ly-#TB=Dc>Mt-MIy zyHZ11pBA?rww`afiv_THB+u4rN&bbA`XapI6gf2%le-57lyo(L(OK7C1bXjeQZt5 z7BL*zH-yqFmlc`_kl#6Kc5Sf+u5C#H17ko9T|&)xlK|~>8@rOi?XvgxrkyX-5E*#i znhFUKsF*^ zD=gInv(3O#Xw=U&hJD)%E!Cc7VKShkw=!Ym*>f>^ymm zDLUtotBQ;3#*q+gaaBqcNSeLpS8IAC=9XRk6FWidd6zBIS({sN$8aPbb(U->W>cO> z&bYF6%~f|)i>~?Qm<*|4WO_T2$@&`QkY!IGWgG4ua?oPa)}8< zkX?t7NB>?v`nm~BP&tTEdAQtHs0lV2WEDiK#Fp!t68|KP{ir7s^eYxJuus$a(t!&# z2tBRDzPh(@A2iz|rO`_rZmmZ>U$TSpf2ucYYQGiayV~%hgh_i}J7Obj9dSyTe3FSj zs*O7@lw3PIw2fZkZP~WN?ovK&ejX|oZ3Q>Vx|Zd_&yVeRricYe3%e+0)4U!SNrB|;d|e=mc7(~CQ@po=Oz)C}$6SNDcQpR5h#U8{ z1iHsSA3kF_DmbsdN74CX9#GYEb3E{kg5# zq9(u|I*jaCk9_3}7pV4Rq{3`cN|=*tSmv=jf#ByS*NVR)1I$J~k8+`j*`bP%^o^(r z@tB0>rHf%@tx73CSP?nxHe9)<>p1$EjORS61%51Bbc9yUUW?j32KAIV5DsSQtZ?>AbY1ToN+|!y< zj{v|R8;#&}mut1qrZ937v2jHUh(6w`W*wQtt(4n81C1~hA;SN8pf-VV`l005&`NwH z1sL&uy$f=30&#lR9?xb!CF5sIs0e&t+bUl893Zsq14!F$7;&G1C?c~WPU^=g@*n4x zOb?rfA`F;MY}7hM`sLp?w4nL4v_sFM1JfAVfP}Mz0+PP=9=Dx*#CD%CQOT5!`K9?QR)-#yNIWX+yA+ALE-xq$#B zbw0S*f8>tNI>o}|Gc9tX4o5c9D-OO_$F;SR(z}`6+H2*_D#A}XMlsK z+Y)rERvbpgu>vJJ-F|jmJC~CnXT=&r{*$Fnnau?c@-uR8g`l;9^>0sHGhPqYk3!3= zL`wVLJFa)t5~a5DAvfn)Cf0LcSJwFeGN<1~wcj0>>5vGM$CG@r66>`AM%I0um8zZJ zeW92a=SPAya5`rxF{lE(;0!oGS7*z)_T{V2nq7%=!c)0mu4Yo6O5iAUdv9bFVw|b= zRJfp?t-MWEn1X{Ny`GlyZ|TkB1y>+$+mP&%nyFh=Dp_)|ilwey#Hs#rLFI=_GH>1G zW#7*Fij+bNZ?yK3nQnizUO=A*;W|w;;^O&b@4WXU>?k03^#iyy*;UrUWAwc1|RLJycqeN^6Q3>6Y-@L6Cc>Z)zJ(iMw8%$;C zPYDq%n_T+9lf~dLL$X^T>^v78ZFZ{dXZ&jH6$JO&jI_@X)pSHw2U#%~M6L)HvRW(- zJ3j*TxpmeZrHv}IQ|V&JzukCJ z-J2Ai@|ZG0>94z*f6H|r3^B2e<$2u`!PpI0T#Z1mRMP?uw*bJ{wM9(U62hA@G{Agj8f~v1tkBe=(XsBZG9AvbZbkP~zz#kEdo{UTR!PM!=AMq`$P8d&GK+ zEgHkVk1J9k`1qp`T>7?fotIUX54=Lx!+-AdNyio?oW$n!1F68;rb%9AVu%BZnP5CG zyI_~C*t`W9VZ|kJY+9g-jIjUpciD=QT{{^Dqmw1H@n)P#k2@iQj$CPNvl7hCBYy;+ zrEd*7y^D4ktbaV8dvWTcW-y-P;wE!>&r`^OUSZ>%^JDM59{od|vmwx^x|vE$6qO*4 z(1h61j%$czA9}JB6&R&CU5_<lpE(XsD_@<9P4+^-t#XN(vp*Eat!v} zyzpeFqqH$GlTNs_ADut0IIq{%3$j+N;RpYPjP{_qDz~jqS5-+6{VK9iG^$-=nCP1n zl4w9r5{sx9nR+xR*Q-mcl}Z`EH3-8~~kN?-}KAisBI9g}Cv zMCH-qE}MI*0KwjaCwiT>^A&L{uGUjcsZ4n<>&pyTMfP;06_w32{8Ql3eh1|n;asWp zwKqOC(-#i@4x|V3>QAZib0>E#-VrJk zW%<+wRDfyh@pMj?*3y4oo8ggau?{-kM@ncVr)a%z$B| zt?YMrn-L@>2Rkq@SXiA=CfP=l;8BGiOWnbxKD9WWBf-j@s@s!+Ei$P#s9?t*t+3s& zQ}S|MUFGWqvVvm1t2OIBxtG?=R5@2I+%NoN88hrewGCIAxJy*xowlU(=F~*D5aVJY z6uZtkdah7|2+1i~V%x;WDJeixvNO3eq!4o^RoycuPh%|0botW0hY6E=kIpt*%~)e~v5cNHbM+Tv9P-l-Dwr^?cvW{udPSMj`=5+3|5 zuDHS+`1(nrV!EyAsqFIi8aGf#7Qch9m(fsbB8b(!cT2J>N)rs+>OYdN@W-6kNv#YP z?zjE3M};KK;sLsYTUD7ncKDZ zm_65(_>kp>dg0s6<$oTz@H7dF2{U%&E4Ar2N3))88#&_As`+ZF7MDaKb7YH9?br7> zo!PP735~oRiIg&}dQwD8O(u=;NM*ak1f~LO(oV;z z8*FzWq9hiSFj~c0P_PwJ^(vN5irx-^ z`TCEOt0IDzG_mis*1YYMy@5=nVyO%0X$htQ#W?_tQhH4+xoR9N{h}*t(Bb*w9f=$L zyfbwkyzm|sSn~GU0g&HZJ`90@X;eHOpa8{Zhoud9Mm zBEr1LyP zhmsRHZWm&bj~KWSBbSNcFj=IXpo=gMn#Zs195wMPYTak5J^8FvViitbUj$!x)F8zEGI5jha0;Ns(yHE$3^cF}J8hOzK5>>*+ZZ`vl2Fe$ zji<>p*n?wxM2$_a_Rf50X)o6>2mP5nZ&xgyorZKkG6vV6!*44M9h6`ww!rf%heBFf z4lqhETwhk_#;mjZ?K$#*t4y&y*=5>C^ie+PF!;2f@`TDytwZWc7ydoygToaH>f4gF zl6j@<653Fs+4Dix7S`$?Mw@4v}zfzr~2@lV~lxfCNsvsF@kEX!} zlDncX;{^&>7Up~i{+6mK=ohw|s$_Hpv^}+ay3-d7(1UoFhRXsI1;DBOuHy>7iSQLz4X1P2c(~X2$ihmuHOCk+VFKe= z!1Pke?0!&!=(>M}FVFp&hApi1C~9je%~Q;!me3|t(?UD$-#;0dVs7Ht_+sT>m+vSH z34|D`hi2!k<^^wj#^ES!ta{P))>n2v1H%ua*POtcqx_ct6aaO>bC=-Jo}r&JU9ue+ z^}6A$nhj^GPHUHT{gM-34bXWry1GKMj|*;muNqN%h-6_;!NLz+n@5qIRuq0+Hc3Gp zHVvH!d2SEeFw>M=)tzt&O^5x=!+dwvz()ivqI@?g#aZnFIdtemQB4&9R9_-6{Q=NR~^2 z1i5-BAfCmvfI)!7OH@@q?1r4Plg>MapO>VY)}A=QoSwuBKb#D?j;ndQ#* zbYTCq#9p9gAh+dj=Uacy%m(j(R_#ztumQWEsy7{+qtNs4u0>~wvgOL$ zjO%v+O)4MyVAN0XnOYGo( z7vBhSA`+;j6uh4LvGyuHnGrg*+%-tG#T#cBE)$MJ$*HbSmD-6pC|5D_a@xHzP$m=o z(tuA8^6I?hT1$b6B~mYeOk5Opja^Y;9ejcFWf&aOXfMLLOcUzs7P1)TD=)0E_ury% zg9cdBBrP4A%SqMqC}^XUko2Oen6QeQq3_U)DRb8QT_gN=z?^jM&eRFz{xs+&J2p1< zl6fhbYQfTl_kHupLS@P&HWHy~zMuPQk=5dQ-etZM-jtrn1zi;P=kNC!I*U9IBR>Cu z5TzPpj@x-Df$Q!Xxl&doPV z6}edf-k&)fc$E1HR9>?;B!{E|1VfYx4g%u( zZndF=uQ1e_6T8VNzIvJ`V$t}ug2(TR*us}}RimQ{!6?AE+K60Tr*B7~@+crAab+7% z?LfbKxp&o76Qj})k}iw7I)$vyZ`f>`dgdOcbr)FphW*ZW!C)5KvPR@i%7s?@g3*BZ zgHU&{%HzU1#LaaVayE=R67v8_?BIgcx-D0$$pkNs;Foo~vZ{#o)^5D{w~clhc>2*9 znQB~oL3n<3o?#}x-Q2-LuOZfInAk_qzv8RL%0X{$vm~Sf^W+O!*F1jgR9>z5;=94y zfJxzHi|bU;;&7uJPefbx(~bfGVY{STBgc4g-+*ZPc@i}K90MgHs;r#(Me7J z%VP_+$vj?51lo9c1aF1y{*D0`wi;TaX+u(+ky1A3V~}NEl=@P9deI_#%1f8>S{?Ol zP&=Cp{rod8uj%k()$m|nzYo3`jhgf;i+l;EX=r%1KZo#uslSLq`(R)gqTtU1=T|jS zEOJj=JxixbPy)*|?Hqsko!jQPn9cG=U-o41iefVpv*gugM-I1na@~CVxzKWpfbWFV z2KTGj+Eu`YSSv}9F?rEEbntowR#y(6FDV>%Q%Mqt4eNnj_!=07I*e8V8YDq=zRYqi zbKT_*sRrrg7yIT{cCG)FjCJQMx+Vdbep`)n=>*6n);;UG4U6C1Y}n6r(Po(UCC=>k zAxsoW35BG35NS3{117u7h&5g<&`W~4CY*A1R+RnYfNI=c3JZFL#mzb{S9ehq z9r*akicH?I_g=?JU2`0KLawCV2A72juMLkts~Y~A8*$G;Y{f7zGd0s`very*+j-WU zbW<8MyOe!JaTVreo!~22nwTJO+Bk;|CQ(%QGz>e%Ov{HBnO9XDLUy|sbGbN2sUYPF zsm@nMou~|K=k6awfusP+O*;{=JxK!Hxw#i-VYMg-{bxMO!`;=I5HIMvImXbhDzxLiU2ZCtgXH9hIKRm9QYOC07sZX$IC;kfKyu$Og zvoD?y;x>m_bPBE43B#7!Lt(HJgJ|x9gxWx9CeyAXqlK7|-f%TzZIMdW@MN(|0~gP| z3&tspcpq-i{1-}omUe-N?WXN^;{&?n=S{1fbsaByf~ti4AJ+1X09G(y*cB8gK$^`h&$HubRyOoQ z$48S%A>xgefFSD@ z7N*e_tlxFxB*b(B3xF)w@eV{?XIn=IkCDLhmr`V{3#E@&4o$hOVgXd~kP z1s1Eu?=PGGIEJ+);-D%k|1Uf4ZT^UTml@uI;Ho=5%!COg7|9EtFH|%4JE|smMur#E zoZ*l`$zH)}J?~^xhKg{JmxhmKBY!C6eeMpo%2Kpy0Q^q&Uef4fM09-GT|eZdWtGY@ z=Ux;z@e%EOF()08hv|?iALP>TmaXrRGr0{jF$y8JLY_~KcFQ>R*#(j`eCT?O06u;; zt_7%3fmyJ(Gb0F`rGY#T|J}g8kqewlic-yo0oz!>jf8JdNK=ha#|z=56QyA}_Zzr( z^VUkvd@&RW5&kwB00YMaxqf?jITv`cx~A}-)OlD2A?ZmQW65S%-6=iP%xgKy7sl_E z##d0o?CHdi@&RZ8D^s|N2es;BuwT-L_o|o~coXnY;f0zk%jQlCK(R}lPh|MmPHLB$ zNm;_|-f6>(uc?J|bL+*da#Y35xZJ&a&`G5*=*L2^&zIdEvHB^q^8p8mbj_j6Z`JfQ zN{)Cxnlcp&g$uj3&dzYs`}hj?;=3Mk0*m<`=**7Z_MniRr_v?cwHV@H6(Y0W5}oP) z(`_`ZKIAFg1j}fy!*8{6-x##wO6qMnkKDB_x=NPfdH=W=H&-qT*mgmTOK|_x{i<)5 zj$3q6t6f2elJ{0-O~RphI*naQ=qSZ2aniH8(J(lt&IlkkWU|8yHOPRI% zM!Pd6vER-8m3yYJG1@&#ggDvlf1~nX5Z4a8G!^cimn6^#S0C-9C`{F0@^_PaK>ozK zwBwg19aQ{_ajv=$uNU2yw0Y#nrrC+KOW*>M|RHcq+j{{oAhX?Qx0|LnR;Qb{s^N#5oXcMtG8 z^PYvt5bLveD|2h!pil=B{O?c=?`;ob1D4r^&Wx%;zVqP3%gV2Yv@K`ObPw}WTa&Sf zv3z*ST8_e=`{C@|$D#sHDIM-SO0%Yd%nRqb_4@TPe@lWQNXm*U=zNlQ-{EAMYl?JK z=H9Pba@v!sW^6sH80<+#GSW`gDoliwfLi9-GOPh~5DR^P!E#PbqT8LrzG$pqXy7h90IaKHT7sn-S!$|sS<9pZrw$~orCB3d^(X|og_Ri(ZMMkVm+QUjM zWkyJN(%-!^)f!6I?AY=w9myIPTlcNm#_nCZWvF2nb^}wwnyRbX%^}Pvy=mznB_sn4 zx2xSUojq1i!!}gv{FBvd$!F?JJ!C_EcHz*>(`v}{c$Cs z1{ZwSy6<|F!NdoeDzfgXzSK}u>G6mf-*VSzZf*_*Mb}JgAI4Y1k}SZz+pWWMRu<3+ zzDe8Z@#Vma!S;loEovMY_s0*LDyL; zVxl|?2Nq-ADBqsrnE@v`ZIi=yzbZ7)SoW=`c5SR)-3J>sg+<(A8LZ#V)g2{sz;y%? zEw+pL+%mS||8tv2f(XjGYA!OaHs}{-i^k2dNrJ-hedjqhH~-X-C-$w*8?cf0*VnDM zi%B^`>e~DE;<7jbJ8fEKQ?%pC?N#Pra-hk3fL1Ai*dBnGZ`9c@10Rvl-X1A@<8bN$Ja5JrV7 z{g}|VUNrzBS;x&i1~m=DptaIUX+DmZbYZ&}JN9jc*&JAP#|GCBN-f{(ET!ZCBMGu6 zEp6`ie5W}|b-JCmVRJ%-5VjYja>MhT#)Ju!rc4Te)a^tI@8n+V9(C;He@i52jD$Q$ z6t!(Fn$bLogJ?3$hp-n1>JpF!`FSC=ZtQ1GN4g|tgqPBM&dESWPLP^AcAxZ6ihflI zzgDx&RJg!))2MMR!{zI0`Q5xKY;XB91#sX2FB=6eJmHWQJw($TEOJFWOfx7!F-uKR zTLHVFoEK+dWxp{EN?~R6TB$WX@RVadkB@6CcZP62T)|++MGQ! zS8R|x&7cHLhzPSyau`{YFzZ4VF4uRlppcxInkF?u4&FMc^ETzTb=`uO=X*a)M8N+C zfIxr0xjj{17yoi=?+v_p$NbCOw`bN}+v8a8W>FD*C_K1s!lueSvGN+qNn-5go5)y8CU_hA+3DL(Yatd$Sw+GZQY zlbK_NXPtU=>PiAD-0huc1a+J##?0Wlq#2L*eX75*wNB@;w5?6@)?H_He@euky9sJDE3j35GV#DQkHdE)@BI2ue#u#AZ_*T}wLcQ?rTyr5wb$Kb*M;F+!}4J_j58AV zC{8dwIN&#uPL%1_TV)r#*uzvJBgW1X~Fo0=Mh{C)hX^KScxU3Rk#mf6wIt)b1B za%5EJ?D>=Sp3;r(Ts!N`T^24!qY_q^ciicXdh~1S^6AUxyZ06xN|qYa zlM-pClsPIg=6XZo=y8`{T|X3ir}pan$ajmz4(oW`Am+`hF1>yG(Ti1M`g86DBu`ae zIX0CWS$v~%Htpn7FS$1tJZTNtb5HFU{kmCZ30r-j$0`#`S!DK09+O4PY! zr_amh%C0ON6%eb_JkR&y^@Wk!?jANwR1+(se_lFrngm1F-iCK$>Kvi5ooCPTCwX~_U=>hFCD4^^BT??NR2ZNn0iZb+Sva2&PS87y4dS|J&)-*jJGdap`VcM z>VZm&OcDy_$8MRq z@M8YteMMduPyYNo)@tH{GYO}S4$TVM`TCKgJe?s=No1;D(`RiE6KdikrwoJwE zWzNR(7np5;4i>#�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‎b+1!5g~HLl)NbpD)SgZGFh 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

    oICnQbio)R9%AC&O;z`uv< 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

    %`XXJD5)ga<}ST zD3CMf{$Dd}~FH9C|#FzCq;3 zSBU(0r^ume+q+LlxfPg={Kl{Jmx`RZk}IDrwsqela^vq{d_?IX z6}u#`sSCZWu$_y1ppy#y+b7{lj{MysNB$1RSCC%GlQ%fdDM=p@dGZFag}bhp({~ng z`h6m24*7A&u|Fa5+DdM`g5|&=?+!U{O#B8>c0dt;8#YhN$oMnuc*o?o-sY>K|rR1H6ZBOgCiY7;KiX=^t(x|#MJezT1cPm=m zA4$4J;(nI~Y8b>nya!(}oUIVKShTHL%YQZJk%twUG5FzY9V#p8-G`@Y8Edh<;*)hS z=u3(w;!e~-i94|N>z{;O5ad@aG|;B#a5iSi)D)vKj?Ge z_<`(?gl4Dt5TUmVIBXTAX^-BSZg=`2Q*pf38OnlAFk9R(`I|0eB|$ebxI z9nE-sG@7$%(;s^<8z`<+ck%w%9`%j!M7bl;f%J&8BPYX4?X7O4_@KE#X0|c5M|~r0 z+HuCjih7cZJR=p+2eTE<9lFY4DDpyxB>hN3RZ-84|%F>?+p`=$qG+B$}@4Ce^l2kEq4JnZAOOovzX zhdWAnjvDo?HDom>tX1q$iBteGEM}iZjJ`M5YuH|o2`^rf%56WxdZfL&2Km=YJ0dhE z6kV9&g`*E-jj_zFKeIg0gTsjfSxqz7Jn8Lu`rw4T%{<}^XCH~N&FxoJKMWm3AG{=e zfVt_?I8AfzKNdMSsPr6!lY{AL&aPv$R*suAFT58x39mjy>fLC-kX>#XgWNCrq@Ma> zj@1`08GR9yzIavWxk#_=sX*_crt2x$+Bj@SJN!M*U7m zor?6ko6s%iXqu)4YdU5i;orB5q8>dEYv4Uf&R5xrE(?-hRA35wk@m4?Oz2l{r1&dq&t4p!)OMe`}F?VI%v(*A;exG@8nR<)lurz)5G-d0v; z@CFn&{DQ16rLuv}>8UFH%>;jPW{k?6J=Z^v!p1yWjD1$*cd=K~{5w7+#X41RcHq70 zU*F()g8g}p%p>(KJ7Ve^_Udu5oST5lML!Gb`yI&#?_{tQ=Iy_0)V^?x<{4lvo8Q}> zmNfrNYxj?0@ige*e?LBcFXY1m&DYEzJn#4vsq5D$E@XRiI*gmyUfc-gD19%Ye4fPn zS?qw4kA5%p)f2E`P3_h)@mj6}w7aP2zzsFbAUr4)2)ocPgFYY1=4k!259@txHw`k~xOZ?$mOa0*k25kQQK4|PU28?^pJ1zK+EZC;8 zLl+6D_!r!pM zA)5o6wwh)Qov%+A@D)LRW76<7L8bwv1YT{?$p6HGA)ABsq1*F`Ak&64yf?@^uqOrn zQXU#S){l-4y4F?$hU^V^U=IpB!=kBQWWk`pW4*1D_}7M**Hw?-S}T4>h-pSTJfG|h z-UKRSKBuhkFI({6gm^taE%9Hp!jD++e}#B`FOu@7=aIcRSSmWQDEu-j{CyVuehY50 zV7xhOXm8ta+xp%u9U$!HULbPJ^Bt|;w<4h~U9uir`0TLPrk?ICN%&m5@`lw3)~LFr z=k_*(_g{noS|Bu`#0=I$Y+Gf0UE}&CO>1+sYZfQy?rQ7o=xS3DHn(1Gh?y+!>QHh9 zy?s5MZCyry*zq#5R5W)}D#eR@O-C2ifwKACO=N9u>D$omY;N1E-&YDsKjcxrTTt#I zmd86+0FPO6OHZ=9*VN;S2!5%Z0-cYDKH-O4)<(TOPqW^hFB+c%RQb6vwMBy_WUXRO z%-i$M<+r}Ov(+3_yu57eY}wSCKT{Pwf5u*r65hG>j)@&}MziiY(HfmxN|-2mVvDGF zW1T;OZ&Z6aZpR~wPlN^Coabq5Lwid{*Lq_G-&$C1#}IYOsr^+WXr`W^FPKZ%l3d@@ zw$b0mt8f--VGB;#n!iA7 zwp}PT+b&{#QFv2%#C%gB|L5gxWmMi)fY+Tjn`KRE-py|V~9Ufv=znQd)yDP+R4vW98!P7mF_v##a0m|4qNS1)9Ul+4KoSm!Ndch-u#Q{0gf9=9jNeOlau;zGs) z`KY%R$h5${fzI;?*XIfGKP2%$KtX*@NxZnobo7;S`+#?&J=;pTefdEtfLfKpW~H+8l*pnfAS$0_^Q}_*NQIyxPouB;%G;;14-hf)?<&^E7 zRoCwPg$pM81|)o9xi_FPs(p?RG4AIx)bs=OMv?%(Un;}=jCccJ8kZfvh~Iz8bljj7 z7xeiY&!X|o^za)M-!JHipk+n%8;r>hDDN*?uP1CyLAPfpNApX7W?82BotdVwI#VdG zlc3=qOl1OQeT9B!>SXouYEpdT8vb1=8#Z0T{Cg|z@j3?R@VF;jV1%oCkp(Za;F~SD z%Yy$@@_y3sc$X9gu&U{Xm5Og=eQVpsmMxux@U--`3#zu_h8sRW*8?TnYQzTHNN0EV zZ5pGHXiDPM^iU{VYe$blgf?`xwe*;%5a$IIKlG37MPiGIMO{T;fE#sn4~)?{z@Je`^|rMPk)Ox@qByuB4hd#+QXr*18psF&b)^w zt*KjR4=)ri=J;oKp?Gn}zc(Y^WcKD%fcYyU(w0JdbAQlpAE?mY+#fP+K-qh9+s4Vh z&(B>jchYM|zp1$5Z zewQZOHd9>S{7aUuY+B#Ae5GT;*DPPL^#75bEHgIZR~Rt9tF8D^D}Jp3Pj~$G!VurJ z;?FVQa~!`OF2s*n@fR5|)?||vKW@ceVZbQ6)rz06;;%Dc^Y8aT8-9m;Yg*aW@~vU$ z*>Vl|BM$e6hVPg5YIsE2qv0GWN=(k@HK~-l+~$rOxjo~Ym=K8By1u2St))Ov;Y8k< zl`qlywyxXHkWdAy`3E5-%rV?oFn3_X1aks92r$;r6@W2>jQk(+J)zkMFLcJ>zKiib z+dW~ARbHVprcgZlo`85gR{iqb6N;@(rqDg1c#>yPgLt41YMjDn5qL07S%5-k5o~c5 zyeAOiGugAK{tEe+#96eAg(`Aq(G;4s^>lVb^?Rek{9(Z7@eWxVAI^s39>3C`P8&#T zd>`RXyto{DZp&>Qt?uTIUM7_~&aEx2Zf{#>+lIbwHB^AnE*Hy1@%F;6w{1ba+uF9F zyS2?_Om9O=SD&j0R&HxMG=Ne(EHk&IlU8->9d1XLQU~hxLFXgvdv@l6TH88lsj%Pe zZ9N?u?{Goyg$fs^qcv$(&*|v89U3iNbG>LkTJNgfcNpZ2x{9is^HX|#5Qh~^ z=CX(0QZ&&l5Ppi>*z5TMmF?)-OEExpjK z$vvvm<|2D=7t^t6VR`CXePe6*w{;o*z3tswsB?ST`c#)2wn#ySb(Qj;w!XHWUJSk> zSDJBXLsr%8b-nx%&iYaHSa81V2hykh77DvdUO7KCuF3~~xh|)9CW=SN|06xr=PqAr zbOG$g0Ioc_69;pBOGjrb(^{Qd@8Cm2-G4A&6@8W|?{uf>>!C;(d7&0}Q=?SBw6=7$ zt?!tp>V-2&r&q5rD9a#N=iUewuU^aZ3N=;Hde`^f=3KwDY1NX&iKXjTB|flx{jCe? z*Yi*z?!+)2E{Eq>?hy4!gE(4uDhmnF$#rIV1m_Il!n=P)(S(WNo^-&(Fi-wNhfO5K zdj#{=j*S}~1HNu))3O^Hz4t5l<+L$5ww=5D?aUW$w$pn$Sno}E2~&4oHNv#c3{1uI zxBYZ9-}M^%h-!r6_xHccsp0FwQdH*tO z7c~70^jqw%b~Z+X)Z2Twp)|M<=d27S`Pc=PJd~+dasX0%JEj?xRG-3HaJ72<;PUK zNcjmBuecm`6rKJ^c@^BDNO`>yzKxWt_w$iRId~eJRHS@JA-|FG$5p)I@bL6 zofze-(iNA(-n#Qbad|DSRink_(C_8MBjwN`@0_6es_Rxs`G9iE%AsrC87VCvfg3F; zhdhjPkn)A=mXt#t#)(DB!$8VoCFON+<0a*awuXPkFSY9C9*Fg8EsdkCZC}|3pc7RJo<) zb%y)~9pmM2caW>(SIKqBrK?mq@Kc)7<)Giu5-!J`=@aB)t!*b4{1N@+rpUdQT-+@` zOfGoEXik-bH}_-YzDDky zy@y=byx)m($n78(Z;_uw{^Z_;{K@?|@((K7X*qa&K1A*!a&IFSc0_4emqWH@3%Ssr zOn5{&Z06j7e1nRQzZ|-1yHWq3x}cPwAoru>hK-BETTPqB)Lm2{G?a%9;}0*k!i#fq z@6hoVAoyJQZV=d?{=ZANh*)rGj)tS_?h0|6#9b$DtGJ!w_KCYq+yQZSiMvPK{o+0%?&IP%^TSZoRmR z#BCIJg}6=Pt`oOa+)i=(#N8(DfVjKF-6QUPaUT))adDpz_i1qtiTk{`FNyoAxJSf& zP27{>o)#DTnQFhdRpQo)TPN=CN+UxY<$;f$?o07<)cMwt&hpaE8jbKmv@_^UbRG4e zc}CjVR;#>1w6j9-Y}y&(U1`aIB7BKeE_f+&_T7wjTxGRm1nmfMJBp2Vta1D&$&vE> z_1oz9X>o`*8CqOb1@jXj+o?`rT3p=}b^cDs&!)w_5LI}`gm_Gg+d65zck?cv{JT&i zkAv^cUds{v_g$9x107Gkf2MVzfdJeu|ioJZiu*2_7+E=v{cyfN_oYqhCdf% z9T6J-KS6${*7bqx@+l)6=OLz@hI9NBpe2F!Lwk&&DC73|c|3h_4fP#HWK*e!6$>X{=brh+_HpN^hOlp*+`cP8w8k6ul9VKn9ga z%1^^OmRXny=IhpcotLkR3ig4o!FJQ~mY!?Wga)l&xpd79A6VL$ucCI4S>Zfkxr*lr z^EbeX;P@`8_sp8Xc-5>b zzW2;JNBDZG`JRJ(S|y)O$)`{9*(Ui6NIpZ7?h(oFamnuq$>(Xw=aA&{yyWweC%E&1RcgM9vw_=F}-5Oi>xEI5FT16p3NszC|=z0zyR%O0{;cEd zh7JSp_)Lc!YgoxWuY!Jr>0yc!cJ5a=l=*#P3F`x`3k+vN&8abWF7pX}@W(8R9)O&1 z?160OT$Ue(&VVaazBgv(iySfae_2KUU^?Q{m$8P@jVig^hp$p{ry=N9fK6raTt@l_ zv!QtZXk=(G9j3I<@i9f@*>xVJBCiS?<&a-~*5d=MaooLG$hJc7v*X@v=*MV+&c~ni#POVj)^VU<6&oy;x+vb{1P&U!y2eRNBt{UA>Y5qj@&(wpd zG1RSA(ndvZ2;A`KCE3X6kcta_=IYVUsc)1UJ_!GHN;d-X2#+2(QmgpsQkn3H14ry~ zk*2mlxsH^ZFOB;@NKe1z>*O~b_S4;gbeQ?aGdldQ zD6Gtgzl`FeZBI=rdA*VHhHK8}XDDrv%-PRHc@FD&TOn&oudRru?CSRUb&-ET z)`OY1&i6-HqqtlA*Z4Sa4XwBOU_aZ3K5^$Wkw*8P^zc>pQyELryQkeXhPM3Y_;^hq zwj_;tdl9vX;vJ4Oe>!W*?Q7i`IoUt8XtcSVJl*2&!@AY=)R+X<5YHu z(XSO026vgbF15cz+7ErYKaur`zJ*?HEnkg&sFCj4!&eQB1>EiF`_Jzm!(NYe@Y+kg zNb~7g%pcVU{nUTWLu&pgogfLXU25&dp>JW;*mOe|NKsB7!D?<-Vh7swREe>_Vg5v% zeM(Prw12>-gWA-|4BbghhF)~&X2zHuhkQo-NPoZ>?=19H1ekta z6cGMUYEMA+zo58!ZG~lT)2`A#GJaV=rwJ~6T*0HLPn$GvQ%b8VmMo2 ztj&nAHmMJx&wi%v6D?a&OKmHPu?~8)Yp>C+lTh>!@7wKUay**t|d9& zB4)c`+nLhLb_R~|8N=)BlyR>8I;Z#Oe^y3N=@&|6&Q=D__2=GT8j_Z2fi9R3>kd-m z9yRnwMMeKVbc0tze@ZGfRwcSjoWxKTctaF&DbR;PXHJFaP?^p9-I&((6^;*PBXO1; zQF_ExSr77D9rEy*AmKz|=jQ2z?*y?9a`xS+51{Ag1@}AZJUZb%2N@WJ3q>TKoQ`rs zuMYI9VK2~efcyLsx^$eIp*KeL=`zAQ;uaoMVd!T8E)^B~0Ffc+;r8;U{h;ROg7av5 zxpBScK^Isv>s8nDuvqlMhwZv1K*!^`i8W`g#ox+W!CZfo#^6!ue~gQg2}%xS_w63e zvhJyyGNnT_HikZ%gxJPFeV|uH?;Y3^%dlsP+)^X-y!0p17dwv90pp0xddM6tcAQtR zUqPQznNJ6KBK=;4*B!`WZz`hwYXQYAitZc@%YGV84Q0!W_=GRj=sFysen(#6X2>ah zM(xip(Rdt+71ozCTWm{cn++=8e#16|vsTR&^mD{$p9+Jm5AvTWXK10m2uNQ%JwE=J zX>$Vh8#Mfq@vYnUoXmxP5H}@rVwbqM3-6~f+)rbu))lo;t)G6Zhe0)lffVcbsxWkX zC24%sxHd~&lKZl($9_#Ejcth5StZ7v<~m-olpZ>|FV>)~*x$-TeoN2WcN?}%e0|`e z&j!(0o|pZipX3jcbw;R{8{!~pgCNp@ZAII-hs22@byp43LoyeN zjJbgG(LU=j4(&8Y)}CR_PLVmH*R4HH8Ds2nI7GA3f8^6a)G2N=Zj++N32QqyZqQK| z<#Dse40C-B^&RFe!hqXg-=;g^z2;pEV|tf?qp z^l_2i7qMR%^*AAYSSx+kAp0TY7PUS!-9OMN>+@4*kG3y^@SD<%Tcs*F-Sp47@Re`wsAm zV$p~BT`4;#I1O+K+->R(GM=RIt8KQ!oE&%Qaf_Cd6XP0?FBfSJsT3V-;ChSNO8@*r_|i1M}W^H)joESIv08WQ1~$6 z!f(fV5kH(ePKxp8fOAXcTvgUk&PGY8wLTnW$0G&3tTku@+M?@!1o{bMuzBLO!HI5H zd6kVQ+en#z6&<3Evq$a0A>%&gjEsUaph&l?TyRb3cGK-gK0f~hm8bIBkz0%OU8LqN zI&=%Tqkgy z){`o*)|Wa*;5iys7dR$xy}W2j*8i{Jrvl73rFDsYH^BCdHH=Fcbl7NnH{$`;{ifj>%Il;7 zUq%>ScVs z7UumbA>l6)e5lMzj|&TQp&XLT{?;rM~mnFXktn`ms@G}YtMgJ;WKD#U!K8l&B~rsu|IrCG3)Qw z>2I;ZVN?605k6>ze~IAJM)==Z;Xj0Z@=7lpJops`9Hg{ z7-w6(0skq%iwyX)1UDM+|0!YHs~Y|x!A%DI9|W&6;3=4U1`J;QP6K`~ee@ad8v58~ zz+Ln)V8D0N$1Vf@Jbmmj;J>Ah{RaFBeLP~;k3JqZ;OR8ipD^G#1V3%SOJM)mfNvrA zc?15ipx1wD-~8Q~9E;a`Ehb0Zu)Jqmeap(dr$&4fEzx31sN)t3`{-^y&|-92sK zj<_`0($jZoERW*73A*`A1#BH%t!Gtf_q%C8G|elSsupt<^&s!d5G|85A8qOE?B0-1 z$DP(1lS`~My==HmCvIA{a_Qp6^&jbh4oekaMBLJqC#raw1$Y8c;?di_rLVR7BVA|K zN@J@5^t?s6pePo!0%>G6we=X1YhF-GXGe=)V-7^&Sj^Y6tfa#>oh3}oAX{k5Z+R=P zA+G%5SkjA#)wY!j^mi<})f2z-r7vu~iz{Y_C%cmqrq!8BwevGI6Hbtovu%-?bD0yG ztW{=KnA0XKiIjY)lH<$ki)40)Va3XpTNGFcrcTH-=Q|-&>>>7qlpx4YceLH!HX+<6 zE_jwu+Nuh~FbsC}<`qL^?J;>ndOCaaSCs|fbG>eWx!v#V!jfl@iOezGnaWz#zRPl* zN_e)fe3G(=RC5RG%yoF2*LpoDofSs;Tn3*oOPx zf>bR#+giG|BqvmGqIa)iF!db~rk9n06?0Smn6hicraL3 zR_#Kjks%y1SV;e#hL z))uoKRmd)*yg6`J(jBCj+XY%3`U$ekHFF(jZ84XVD&}&Y6#cRG5B5kYx923>INcY@xLr-6FSfOe z=MiX&=#S1ao<~W62M|t|<`mo(>KC}wUsdH?ZhbkA``U8upKaw_-p+Dv*S>PD&*SAh zE~Dj~-)rI@so;F8D>z@bg7a;x;P57Ko5gLf;C@I-{H+ySf6zWr-~I}&|A4?d0aN^4 z0w1j4`o2)X^{AM_<=0N({2;T1@|&h`d972pT|1|6yY@`sad||-pPa(&Iwa|zpThGn zGL^%lQ+fPq1+JUQ;jyV)Z_s2>uZF2y??nQ~0aLn0f%~TNKCo{pkKZA|H)}Q4-QY`cE1F7Bjr1d>w97vw<{cFw>rxG zF(=C515uvO`=UIbj|hBH;OKNNXAay>YR`1;?`?25)3{9Md{fi8{E_KAzYkC6cD_2D z+j9zT7u911x!sh{3{KxVgXh`S8C;)TGdRD;#s8qdFOfUz!!-W@_foq6Z=rnQLJ-<6MNjp;Mg%{$nfAn2!_cY?1na94m%3od~9{WP^Y*|mlJI{z$ zNKe|_0{Pi`(hzSly!g#ktV9tjEhN_`MeV0SmrK@`_7-o25Paz35X8zr|>JC}dWT7~#)4{I;UQ|IJ|; zNge(hhvlep^xOdB+BzJz@v4pR>*(7x!eNVPjuC!mfZslJINs0VM)-FEY|mSV|9gPt zT6OrWAnTFP;Y$f_H^Mgu`3|7N?+Y^S@6m%j{iY_-E~kgfPZ`y>nZ6#F-tf~&C(vEZ ztn&NfRsdX*v|2c@r?W?(wps^I0pT>^n<<{rAIx=hkxyK zmc6c>!TTL#s~;Cw$w|)OeGdN5i~EO+vke$qZ9)C%B`jllHk|EA>l`VBvlWVG<7|j$ z>Jlh~v%Od#KO1L5yvg8f&_~lOx^2&-PXqQZzBUQ@$>x$X;B4KuerVGAG#VB(OcrNr z;sZK|vsDn*20p@F;B}4Nn64(gs=jVGTN@k9#_Gj}s^js$4haA2mmn7=Ji|X$vTMRi z9!ph5V}mmZC_A}*SjqN4CMcXrjn&5n)39sIakVYsTgLv*aUO$Tnd1F>z~kFAcvshJ z9=wr6dL5L*X}-ePr?XDtfovFjuf?ksFFRd}o_m|KG6T814+_sE_)lZhwmI~hO}{$& z-43327j}NZj~qM@9}N#}9}N`WoY_ZZN2M%!qW+Q5`?Ij=dM@fsc<{QBRAy@u{Gx&MPIoXJntRu1 zYphc9ahC;RKRW8VOQ`NEGt=DAT}$S@e2+8o?Hz&Sw|4|%_`Q5jAolG&A^HwC-+fmg zdS^O7?K-aPw^F@Q_ohoBR}lMlb~ofgN5EhF>1={=a8=L!kOiamgq)kldT1PL(YE;Z z(Zl2)a%ZN8l?=wjzNahUE1`osMgs>|qytpP!03i_V2H*d`e&m7=U2>k84b+X=?u;Y zOz)4j4h3cgopq4ateG7>KzSU<(m0IGiNd{qYzJh^jB(yMKK>l|W%mlNGVCXZs4b9V zy`1{yu)!-o7xS0$3oDt?R+*HM?C6>YD)6~*sY#BoPEC|vQ@Mu?xynk40)oc zb0BB?*tuXhd&1(a9x!;T=L^5=F5wZ?bLIk7x8dw#m=|;>2`Sl{+*-*vsY6Auhx{nz z{V}P_zhVsH2eM=2!hZADM0QW}5u0`V0pe2oYU7aSxH{c#+<&;AAWwMc==X33k~~p2 z$P8X?)#>lW1}Uz2{GEr-QxA;vp_yB>5{@5oMv`CLFV&UZ(_mbZbzuD`*}y(uBD z*xY&Z>n~rhu;GfuS1)N?dd*wQ^O^q+Q)|Feec@u0And{BAMO}_pJHdad`)-W3f+G8 zoug3|z}lG<5AHeg=5j9E+JzpEQ$Y@Igbup%#1>!;+x~+s!(-yFZ2Z+SA0+&j z!JWlx=DfGlj!SOM`D}+(y)A?pA6_Tn%>uWI+b%9_>V`sW6Y#CTpBeo9;?aG^@-2i% zS4H^UtkgL(9{s3)D=3IZ7m8=&(THd64K^OFQCPfftW44f!a@!T?v-IAlfifWo8B7h0-L^ET*LfINzk z59H2vBzBC}k$=(3wo&|cRC;Nq`pQY9%d9+$wY(*8c-L=3FKEgJiJpNf;60GfCVZvV z!dE_K;49DvqI9k0!51)OZvjd3e?-%a1w7dN`+d?VHIMQvBOHBW+oQkSf^8Xtn=JUl z7JQEd@3-K`E%+N2{7)AAFBWXe7!*9OHBHde53f9Ic0D?*eRiA??(^`etIyBD)LYPZ zq7m!OV24`Y()uA^rhdc6y`G(8ZN8iRJ2rJ0oS=Rzfb9fVux>BB^^F?USTQ6^dt{rHD5fyR3Ijk|uT6BxzFMpGgI{56YdwXC!O`{8yg*Cg^w# zpMfUT)Ar%Xo{^VdULTw68M&X|hkf>zAWs|-`D<{353keA2#R&Jed%I>POM(O16^jnqbf<+=?SbwV3FnQZSJc7W1-5VFHjZc87fHPb_y)7QY_y1MLF!dYRl2 zhxb0v5|mAK`XLYC9*RDr#{K~7f&d1laT z(I4NY=Iv-s=3U84(>TvC=ca+fO;dV0hO$u__X;KZJe2LEyr(D~D)&?0+^=-}II2(g zD*X!~rDNqURe2fFm$Hc3F#8PpIk45~`G7o*$U4UR#zFb6H~I>1@YQ+r8Ed(CghdY$ zu9}l`=Qvu{eX{Y;-$7afkB<&#A3r#pojQ8oDClGr2S2BD*rcdk&cXczQy&}@Iv(iL zae{$|yAK?NUY}mAf6%Ki=L~uk^j;A) z`tT9yH~m%)T}zGad|xof80Lr`z<4FnP_ z`|b!HUzZrjCYAmqmc!OMw3Xh%SikxPuJPwm-@N!a9TyuU{CCIFy}!@|EPHY|`w0U^ z_$e#g4f@0DEjjC?72aos_gmouR`?z(e4j1H9rA}C3i-oNg#F6V!@@(h4PI)#~o(A5e|GMZo$`EaH|FXsRiF>!4F&T*DUz& zEchR!Zc(Y z(*u}IU zAzQ5FrUwXaE#iA5;Qdy5$T{yc(tpni|54cE$3*yVt#HUa!#p9PoBR22w&}iM& z(Yn5~qqk23!fIh!Ov_!R4Yr@n%=U8z!6PKde|4X5BiB(U_~mZofb!hReUt+;wQVpj zFCoCRqUX!W2=xLc+R_8{Ab}hpUX=s5ni}e8^>UydZtLpp=e&79wJvrmzM-Psi*H9P&`xQGWdKu294h({Si6)S_TNqX zH)ptV)%p&jGej%ASzp^r#q%tLN-V#JKaSN?dHpA*FvcRUzfNIvoti#gFF_~$1QxrA zsS1?ex3w>C`F&fh$#Z*K5AyMo@9YIuU>{)lBwEcaeOr3``-ADPvc7tG)B063E3UuR zYL1fj%SB!fgSIk>R$P0-N}BJg3(-9YWK4C*!wxA3G_o(zidGaS+I8f$y{S zU2J0xG7I3tM7o^;mce*J;Dc~4V;L;Cz>ASzow&2bog;2cT;Ru{nT!X+-6JmSWzDJ= zS*-=E?`2j4>ve&w7VKI=b_r)TWGi6TvX(CPuwz;KUTQDg_wku~-c^J<;6fj#^a4K_ z;&Psb|6-N>?GkVV;5#Ija<;NDE-!u?}DGlRg_-by=O zZ?O@teDv5yj5EIwJ=O(*F?e-$lI1(0A)89_*fiwmZuyOy5085#D3;O(FX3>`G#=A~jZ~VAhQYnr2dT6`OCMr-V+W zX(yoJIYO_{^u4gtpAI;QG0>_sjnAZ~);inMCOkU_4`orefU{~0I&~G@7c^)?zb#K^ z(Kj65k#(FVMT248&77ZWv`6?`5FwG zP|TnS)vIr&3Bk6Qjsu#LGdeUg40_Wj#|=j*ZuG91s9(gTZ+H7l(43%$pLV#J)ZdB@ z)kam-US*1f!+M#55 zVl>v^Nu+$x&QtF^G#0&JINKZ>$hOiiBD#}7@5`sNHSEGh$DpP~W~!7v%L9u3IbG>l zKag#W4k$V?-5qOmJj~0a*k8b$NVo&(Wzm5&>Zx^0cEY;@=QpyiSL22>9iAZV#{=kB0B1?}Py#2rx}b)06(v%KsY{{Eq?FovhQthHIYz z|281_rQ^|?_8H-j8^XJ#?oa5|8x45$gvCL&2fIw*n=SeeY_@_vruD3S!U})Dg8#P# zf6aowZNcBS;EVbOPi5eOk4MO3o;squG#XU_`?{+?!B-J80^&Duj~edocO4IkHwus>CumlLO;9l_OEoW z*Myy!il3n$_+D8h?9PO6?+D3#$h1)wR(516p*Nh4-%6E!wk7X=P~Mjz2VWtw@qg!f zyqB)SXEJPA1o~5BhvK`lz~y0sA|`jEu;@t#E`2OHoQ1ym=ui-M=t{op z7Tob3(>7}q?+V@1LP}SBV(eGy?sIeI1mV3w*nznRcRDZso&A)5lJajZR{5)YWAdJK zj`!5LX^hf7<{0@ypL|H+_B#JHD*u4wA92o6_`Tv0qJEDUeI8bQevW#x?~m zLXsBxZ}HY%?S3=!m-6mj?cSC73ne!r{39;-P_}0wXU6&gXbK3giPkT#^}I*$z7P9m zIoW^w!ETuIR0+?4=fGRg@AQu%?{I8;x<9a8&7W%Wk3{2X=2;Q`p`302UORzUJPjV1 zoz8G}XOwN3;htAQ_lQ7r=x818Xwlu-+UTI-52kkRruFvOW~07Qb!Qq((_JdZr(6yj zB2;(qj>N2cPpxt9!Q8DAUIo~j`<2{P`enU<_Nn$cHIHvp{9l-F(+!>%@WB)bpMt(G zf=&P%Td;+P{wWqdpd#VZ!a747gcm73>0t#&XsoUUuMw4rv4pKDl&S10V4cLhGI7VJ z`#$ctW!k4lAq%k-^YdMfEj@~&IjqS+RL%uAz zBO46t_-PfvnEQ$cd;2rT)=tOTA5v?7kF0&zPieeyID0R_+TK>Rp}*r(wC@zfQh|A5 z{|a?Oy;Oh4JR5khcL$z!CpnNkl^j;}DoN6ubFrpBoej9bbd=(we#OmfCoa-IkgX!W zaPvU+La~9O^Lv5T-V@a4j~jA$;HTSR@SyE8_Oh?LY_C)Ev4JQ430Lz>d42F{`Ytu> zgqiu}_|++1-0|E0bDVo%Uo7w_^~Xz8H}I_?FWBUPEuoa$vv5xVeIEBn$l)~n=lHm? zLmwZewMKh#{Jv4eMk%}B0r?9>c9A}_8Zs^{eZ0YN`wX@A} zR;^m$F6r!O>+0k0Rc$?lfYbM7*AcewwrvKMM}Ie{yMnul5K#m>H!N9|aC_Q%ligjd z@DTXJTL^JP{H9fl-CoJdJXi5oYkZq$JMOi2$+vk%W9{Fm&rZWX?l9h{4pYM)=lOQ| zHc79|4&#;hGbQAJglPFPlS`$aemms*t>sk;=cS$C4>S$fC@6QdtwDzU>5F`{548Sm z7VJ8_Z?2W}i0|>}GjGrn27HOYu-~KOKOgYNH*Hib6u3h2zna`v4OrFB;$;dOFv90p z;V~=R=B3(Zg;!a;P<0`H_#P{KzZHJU3O{Xyzh?1LMT-65G0SE~gB9-mg>yJ2zd6{n zu3{^Gu^KloezCJrz7IP86M4~zyjQk-$AAS7S@5t0k618xm5&*;ofjP*Z>>`wbS{kV z3V}iA!gxL^@Dz)dG|Pf7wcthzUK8N)-|BceZ7|-=jz{bAwKu@yT@e%=`d0Y=vf!^; z@ZVYR_X0dWVp86i75?i0&ks%ODgz&v0fX0SnE``#1Wt<0Nk{3PUC*0#j)g{DH9Omw z90TRUtf7|U&h74KH9lLq?$Gg_*0zl;TRQu4(djnY)tcj3@Y58`m;L{a7_7>wY31rw zO`@S!6=`}aO*0;oNX|26M}qp7)(Lvse>cVqZls~nH&@ksEIu6A(%Ne28S*nD*-Eo5 zHR482S~Y7vb>3Fv4PD4y4;dsqFD5re(N-?zwL6P0?BE{;tpodhsD#%nT+mQJ`v5H& zv=611^-|X11pjvN?-c(>0-P`Q^AI6l)j~T_Z~Xzb-7@PE-mek9Rs5evy!rB$U(fsX zd6!dqAp8rM_JVk>_%A}ddCZ3gy@%Mpk>7~;9}`{x?AvGu?0&$%F3j;EOAI}V>g}G| z{dAal29Ah-eG&5xtSjPtpr;k_2E=__{11tqR_y;M=Y+T?#XVKT^*${ywqv+1^sL7D zTnNef50=ZB80LK-DExQ9X^igxUL=dS>L6>d4k+2XQZ76yM9)iBDljb2*;q1{oYN#chK(w`Vnq%{5<;6rTzGH`VsDN zyp(=L^rPaAzd>n#OTS;!@0axZIsJZ0Kf2@{rwi8cf1}^O(C-KI`zQMS1O2{3zi-j+ z8}xgIet%8Bzog&S==Wv%eTjZwpx^(Z-vRo4hJFvyZ#Vr0>Gw(c(Ou&>-E)qAoPKxE zZwvhhPdmPuejW6q>)P=a`hAdoH`8x5{cfb+b@W?KzoqnBOh4M^qS(D;7R_#QF7A2q&H#`iwsd&v0SX?*t^-&>9Er19Nud^a24YmM)O@x9FW zjvL<%#&^v4o^5=)#&@;x9W}lq#pE`4imhfD7V19yjw`3=n5|dm>vNeYp@FdzSPG9jGOKcu+2Z%+a3@e1MnRHe}(=U z=pqGfyCT5%M$p~xZn&lEwyy4vbh)s{(6O;&LrY(qt88g>u!#y+++`ev? z@NJi0*Uf}Df&6jITw#sI3~y~y#5pt0yLw%Z8#!NG%HvFNx?9_HwUS$I?d;g#;>F!D zYmrOq;?l%hg>~$!yD>J;krPpoSn(XLlYgPH>iw}P@-p1~p z%`JW0!)`~H)f2gwdv{|;TPHF&Ykc*ppNl>rKKEW{OK;!mWGgByG4sdkB8=D{uX{_M z+r81<+_t&92lh65+d4O@j%#c6O@jjdR+Zfi?JXT$6X%RVuJg_=FCJTaar@d)H@d(3 z<4q`pl5|tPU7P%2FvM~jjYCJ58X|YeVt+o#mcDkkv#o0rIUBmW=qf;YQ9o_n(AL&U z>9%zBZb>G)d-|+)YTAM6hsmIBsFcoM2hfz;)OZOqif;Snj$XED)Uu_oy}PHQ?+&-6 ztJT%9XllD1t!-V%%8cz;@g+CrnQ^-m(~K7zxoqie+0^E?cDM1I?daOjxrJ7ey=*$r zfBv!JnVx?iXs5zE(KA$&4h(6IIRxQnC|55>DQ`kP2>0cs$FHlq>k|JMy0fv~$ZvB? z&ux@nuVRl&_H=K(!#&&Rp0SRt9h*A(s4nJKB>5V}a~e=bKGN1Y$4z#&we(V7QpaKD zwj?P*l12q9tg~fPt{rL>qDmb!UAp|csM+wz#AgFN{{HIf#uh~W9eol`XF|uujcq+h zt-60hce2e{ZOrdn2JXf--QbniEOF;ATzCcEK)Sc|Y~Wc=^AjzkjUw5C3~4LT;dD7l zw)Jf8=tGVaudk<#PMePIPPT?bU60Pq7vzBAV+>O-&ET$%&099BDZaU7tNP~RrOLDp zl4v9!W4<%Z+v0s|*eb(S98Xpp)z>oJtmwUWboI4tJxhEY3B9alpbGOHwN3*5E4sUJ zpn^n zwUy>huMx03G12q-ZdZNK0(To1U%%9CTCr*|O}Y);J*~KpFJ0>EJ#+~Ugpahf+~!#O zg&pSg0P}-+9pOcDmP>aP#Gz=CDEbopEuXMkx@_7?Mt_MB(A3k?)vFc}tzf(>0&AG# z-b%+XEnzNm)sm)O>h&#Mt$P2_=Y#4|V+G0kq_1LKdaKJ5hDBn8nTR5A8Q*!An`(mI z@ZN%{r7XU+5n9r#)y1CFaR^c8o*{}X!*vke%!Oy?LdRPQ7V7;jTa1BT4WegnSH zF>Qz!Iq!0qUr)nIk4nIU!9G`Oz*q;13>fs#CIiOXVy6LvcW|2lLodMN1`PV?s|I|F zW4=8WsW+iH28_40MFtFdYm)(EUm7rA>~oJBF!T<*WWb=$o-kn8V-JTs81(sC0|sCD zA_GR>t~21zJA9whFy72|8Sqyelh3Y5(SKhu;HTh(J@_E~j~Os@r>-?%=m*BbfT!Y4 zXTXqcOd4O7MsQ_v0RIz<1-G zZNRukd&*LZocqz=1`MA4Mg#sF`rCkUCm%B4$8dKy;1S&I4fy{A*tVO7alfxK;BVsX z)PP|Ru+xCQ=iBi3E#8}t{@w8Rp^NdKBum)o^Vu&^x`pZAJp7xFfA#oxIsPrczlHeM zfPYu0$ecu_nX3}bRjKBxWOG%zxhmmYm2$31I#;Egr_#>j@>JS+D(yU#cAiQ*Po7RN;_Yrov+f)S83;~wDVQk`6_L_N?Whe)~mGj+)|acUZt&9 zY3o(mdX=_brM+CGyy&m3DzjyFjH~pwcc>X&0)r3su^MD(ym*cA-kUkOydCPT{1svI={l>Y&C;u)3#V zu0c;CWPwB1vn+7vI)`O}SFkK_=mV_lBlLcj2@Wl1T_2%ktkX7ht>aX~y@vPB&{D^7 z;Wo1DaA*mg*>JCByOW{CtVcE!=XcG}RjeB%^gfm`4!xIkfrRKVZG`(rtP3P`CGW+d zE9i`e+dz96+=X-oz+J%aqoKkJ9acbs;(V&^hMhj>$9emF{nUDPdF=(0*IB)v1@V1vty-=cJmt{M=_C^ADrH;ct~B&>e)EQJ3Xg8n z82Fm04Nq0zjWIruE+M~C;p5f1Ad91Z`z|NP$CXYz=*x8Ctn(1xT1Optv4aUuKPPzR zi$u?~mJts)qe=!s*YBJ&)Q^Vfsj2Gya}9X^YYNpfEHb|*l|7L)XRc?0^K2w{o+**j6vq1q&XE(;mHJS%#?wLzQodVTB>wRKB#0!l#F_ON0HWZs(HhYppU4` zt&mf2*;b?0d)jXBRhQ^|6VP|LTFF7!x>I%8u<3XTHdrW6qE2hMsG!Go>k`in$Y!MIEQ?q~=+^kJ8E zgN8)5Bd1@~-JJ!0H)J9r(cL~>jZr;b%{*RO2YiUe%T;}_H;ZwbW$BUG33nca(4-%nVw7?1-A8G3-Hif-AGnAlFz^-Qpi2830Jp84E7GW;Ne zv)ic0W~xW0)FUi=&Ciw2UB_Ww|DlkFnByH*IuH^*oelZ;@@dQdomJKcEXbz$ zlvpsrpRo9=pEh8GC&T{mzOX<1v=#1Mm45$sE>o9fJ{z4e3G0;bv+29Xc?!Bty~N*Q zh2QBgA7!6}gGK{7LR?_zJZ(1MXRP%9=&)=%=w1l_krn<+3l0VR@VOTJ4hz1*f|mz) zJR2QP_TLQev*2xlcxl}NxZ}?@;1MhSw+T*we?Z!kvBLi&!1HC7q_4!>D)Pqbk|5uM zOdFxN|7iYkk)2o9Xk5Q;pRbj;3FENEkZi>fy=QsZl)+OyF*=lv+~8vr%f32}s713B#3>1TBTYwFF9^+k9%5=`XJQQPAw_TqpcyPK_Z z8a9X|DK#He$&G&r**O-pkc)N%ssUhEA$ZBSrnDy8!Rb9~bI;^$P-d4Zd} zmgEUhmCECvYly>l2*{FSyM^5Z@Mu8R95_1cBq-f#=P+OGEG>&JGU$j0-5Y>GtDaTK zHf;fe7X>ouhXq!05t!IPwt1v@USRVm$hzIuiflUgW}w?GCH{|!KlW+d|DF_A*}jvE3q`(s}j{)>(>h*i=Hf%9axN ziNf4|*aWJb%`{(>ds5<^mUw^2_tAReC@FLwJz3`IK{?ZXwB3I!6}*oYif7+P5pObf z=I<{^tXZKJqZBkq2}ZxIH0`A~G9()Yj1 z8P4v4odg5--e=(6(_^Vjb8Lr7S0OU5`c4S@0I)sT=yJZm#esA@wFx2LK4RFUBOy6ws~0*$}ApF%bt z_j5^`>q|;I&A}4|hZh;?mF&IbiL%TzH2)t%e;=dv{{nYz>Q7yMu~Gggg%=rq>W&*5 z8pC)-uj(FqcwTDk$LcP0Qzj7G9>SgLMM|UV5S`XNmJ0Hmi;5mP+<9Jgl^xx&3g?0B zOCnnz5&dRSC#vp`rdRTJzlhyjlisgbI|A?Q9)cjg-|`Dyr5m;4;-yrFV9J9Kg6_4nE_E`uEDn>s0S_36&eYvh?-R6wOx}>+0lSwrXfFTQLM% zZ~L`1Iakd*YR95|Z_};@m$M*o00eH5dE0Ff;r)I+jvh&BvPA~d1IP?<6Yz%!` zks8dFQoE`!=V&dI&VAr$U2;&Buk>6}|IJAarqQ2Rt7R_6^}N?XLn$}bAI6}X@_GvN z6^c_mGMIf*>Vr6DonE1KHPISvIyju$&mV&A_^tH-Rx4mvkj~gCQg7_xpvP&wHG#xZ>_VHCpZ{wb9lvH`sc zI0T*7juX@g`zh$8*%@P9FG^oVl=WNew$A(_bv6%VbziQd`LvGG;T%W0-S+yXz8|K% zapo_QypIY!`aMP;v^rE*cOa+76Z%*BX-;)fgLW3P{3Fi%R{F;N4%&48(2&BPFpsd; zqr4TL2hMVHrYuEm0N$bI;b*c%w8zm}q%(Bw*fn%cM|j-nY)NF^qi6u3KwiJ;OxM2> zI)|c|n`~P%8r!SRg{tTSO2>-U_j5w@52<$m=QFweh4!geYNvY4*Xu3Lm1Fw7-@qQ?DQQ!veQTR}t=KHD)&4+t|Q-pJS+3L)?_}$qCHQwmQ zz1d zv>s)RD4Kqm!g~a#X1zKx7YGlAejdGtX&azH=O(qUK|d34>Rm3MDZ%Jx70&pNYAx)| zdUV^=*f-D@VhbH~L)mbW=U$!jprYr#U3Brxi9N{sLVrq~O^-~6KBUxGO3o2|PR@xz zAMo1DoS_}xq&?_aD&n~dl^(3MV;9KVQk~%Yvyms>N9Y{KJoVjEsBG%9S7Krd4CiOP z`=G+_%s90)FB^s1gsUKRMyR&+}W1bsnb2hF} ze?6Y)uYZy`q~U+V9%PI;;q1pj-=s;QbUWdkdLDYO2v-c!{(8ve@q<1lg*W0oi13pN z!5e2Odu;o&5$MChTN-pf(LNk^W@M%qZ;&W!8|Bq^2A#h+PpU*06!agR>#T&{tg>oy zU&GzO$m?fR?}$VAqUc7B0B@qa&^|NXn4ITN3;qn6HQE($p+k;!yW!jn(4IM)=Im^l zuXxu)d(rN5F|V8b?VhgcwTj<+DDE`+o+j^e$Ecm7XK3eTV&6>L_(l3Dl>Qf(KMB|m z+e`cQi%0d|aa`V^fae9AC28nu`!($s5qB@0D|d|sVym*ZQ2HMuJ+%oqQNTFYQP(1Q z1A=a6+Lv+>9LM*ZW?zsa~(wuTg>L$9PLfd2Kpo4$Np0yWpG^FU)N-iKjt&xof&@n=6kTd z51aJ>o;OA6p>Rk^k2m$H=K0>t=exaDkst7p5~<&J1n=G&{5Uph}5=ph4M5Ev5u z;dBP^J)g!P1-gpXvv$+iY-%UcYWhbV;lE{a7U68c8@KL5w6EX&Y&J2nFYD4;4jbne z@Bs3kd9L9nbf#A@KLTn$Uo$pM_VaRUEYx03dHfpoCF$(T(Qc@Yga zp9h-bN(WuB+AkS*H1Xeh%pu-K#_BNMM*oGx9_rPao^lUx4#9;NR;IhQ@K>(unheUO_B~ZX>m9lzOAIQ8-x?!@1q z$~*_U3i4k-X>@sNT>AOmjPf8OPxB|E^#+>n)boLJ$-G9J+WQIDruzu;)a{1t*+BD} zG~p@lH_JfY>fM^sqdfFOptLdrys01iL|J8q_Uf`I?H3ozn|iKY)Xw)>?figYHx=a) zp7>l*lKW4$rv&RWS$LoLmYmnDLvuB7{ORi`?n26Y=2`X_@C%@vCk%V8sMkBJdOdE` z>lL+6tj3s3UzZ}B-{?<;zfylT4|#pLlj=w7;W+O3I*(t;9#E$HwV!!8biTAsrc>Kh z{=m_Tr5(>mJ(0(6B@ewGpEdH-eFy(vFs{I7B=+~@$~5-(&S?KkMQ@{VQ+vI&$Gds< zc)`WWu2K8>O_>GQJ1n}J*}s<>dpO|w0(<&9$^Q~sV@c?HrFzr~?RU1=wcvgGrVI^q z*|Y+5zDldC^9qy&Tx5n())6&-Zc=N#Lg}K#nQ>D_^U)|eWnnrW_OLRx7n@Rc2@v0` z2d$U9^~jPx&E3admAxM{KWBu-miGU8!cRuuHJptTUi9k1;q1`C0}2mmpnK^eMSH&g z=pMrRL4RoydMM~I`;;H;hjflaFOIwpnw#V9&${FS$Eu}YA~u+gP~4#_Bd@p8{d)z$ z(X#5-L3^tcU8kVIL5FP%^%KJMJZ;jtI+K;@)u0Qw4-qbPe>PAygUUaU)wtEyDXf|L zb_M8N`py?O_5y29`U>@Z6U{@H(p;rzNYD#8m|Z4!2he!={o+88x@#@k9-{D~_vh}d zDr{yvw!M=4XCmKq%@1XoCkXJ6CU_S}7&{dGPOW1Q)JQ-d6@5>8xZ`fmHV_P&uIdwlt3b1Ml?{>%V5F&UMtV&n zcu1XR4&4{Ae`AfdlHW4AWA)>li)|kZlrfDpdT;gXiMHWv(U5nRqaW&|AGBXH=1G$I zt$>596Pm(I;C(rq*0=|n=R`u;Bd)mx`d{U)`1RlB#zxWdwf~pJKN@gqa{7#AY-pc; zs$SNE6T2%5e0X2oaJFR1s?5%M#_Pd%h_wXXL*VyK27Z4o<+;wFWyfi39;WiwQrjvh z-(c*;v1@4@AAtU7nXfC=*~tBgKEoce6SxiGI1xQZ2tV3R>%K(w*F(9rQE1+T^Ua$$ z)-%r=tQ(xS?Lu4br!^QQT<<=-Taf>)>?xJvN*o9|)KJPvqG5Yv}zAJw{%Fg~pZ^Cn|L;`wd_cABPw89T%P@bORbyeoD?q;?+?VzR3VNEc!0w21l#uEUBhCsr43p zaqLN|FWr^LDx@#^Wet3Z>KBu`cTzr|QT5%c>bnyGnus`;r2KT^BCwBkLpgK41Qs)rs zq~4zW(VMV~#plY$sl3B-|9(Lj8H2)~0$d&icULRHlJ@wi7Ozq+cKXZh?+{!W*jksXl~9G|)Hji8*qwsuG$}ouX5C_o?^d zoi=(P8d6Q=cTfhTuN> zj`a`nz6RVz%@x9TE(VTMt=^%+RB!BKsKc;~AKpK)C&kS>U^CxQu-=ef(R_hRIe62m zejWKl=nVfe%H!bNVRgUcsAC(*?uX#<_m_)FR{jx>oL5jdX?uI;8OTiGFsWSIKcbx+3PM;?Hj8HyOPb)M;LRh0E}pfyQB6 zBVMVz(dd0p-6LY)#c5J|Bi%!v!gw>!OH(EgZv_8GnaO*>xJ_d^M}KkpGXEiWm!RCW zJw6ekgZDpktRXg>ZE$!W>2#S7yvukOGv5}gC~lR+y^Z36-v>NiUo2t1)~}X$`_qxj zhO-de_L=-Ri74Mmo3+0F=2WJE z>OT7_|JtcH*3Msr`R=>OSUbRFfJ^H)PHiI(et0Vmxs`aYt*M}W*L4SH(p_riRH08^ zqxKM?85iA!q4-yxmUXG{p47}V-DRf}ei1;~f#~ww`)^e7a;#Bw z&#ME@bBPh~N(>TSL~R0(1MX%0e7*p$NQ|((!JOb*Ph^f#5@PnnyFhePW76q3pJl1>7C!AmLi~ z1@Qhwe$=LFmuVE>;V1_^jP8buaj&Cu9PjIzjxkU6%zbLVU7I=JIL~37{T-FNmG1Td zr-$hxuxH`kH42`U@__SHKZSvwO#VfI*!CdRsVGoBgT~w`+a~FPiiWvztc>F09fA2< zZdUK47COxKO-^)C|6b8L^|_tI~oKM7!t5DrML%w;tn zPCrHK;Yz~EhL&M}%ao7Mo|qa{b4KB+&7jw>9=izd=&G!>V=o4H&(7_I&0as$ChyN; zF5+!e^RV$=eN(0=nR+;oyf$4j_o1U$lgE@DAbHcE_FYE%!IHDkJ!{pOy?X3lDJ|L< zqqDp=`G6|(hbWW!4f{h-&L79Hfm5Prqc>sfv$?ul@W9cST8}HozOCxKCWE>}sVuub_4pQF@wh&qWpg%KbEt){gy1#+B;yV!%y38;Sb)h>Fx(Kmxo! zWk?5l6z1j%HK)D#|1!m^aiw2esgKL`(d*!cZ;mUTm#dT<ejsI5*QATM1rV?;S<>-AV&IZX2g1GJyczVjfa=^NXm>dVRT8y)icLW|_Y+ z)Ejk;Dw#mqV`S}?Nm%O$kFl=*m6Y!n-o#rXieC~Iu+O|$;NH12a38u{hR>q5%w z{x_MX9Ee_%=DCTx>bq!dt^xi+=S=APC}%&dMOX2#eJcaF13W~upCfGt<#C?uM`oYv zGIe=$*Q&Xl;=XedaXV?8u+JRBor?B_r0h|F*b>!lthpc47~1=BRNcE1w8so)r_nqE z&9YXVyDMp}E=gAk|0?d_$mg$vID_w3bUB=XbavEKxIE7v3Uc3iXHi1o@t+7%zH*kY z4mt`yQ+?RHG~FF!`~^JNAES8YTTzw5g;Ha%%ZqgaTu$8=3|cetxs=-8soH&0W(&2O z^IN0v?_$Ek33pZb0w*Q}F~@^bOKo2B=J;n7ZJWj^oGO~}Rkc^H&g>!g>*gG%dAN$^ z;fLOY&9wU!FG|>zvz_X({?OUWDW6hXD+tVv>M-$vsV-$@oRry z(;jl~e^MQ6!;e5ZldF;243y58l z23{0uUVf0yr58iZcaLIDuV@~~YCf;3BydxYEphu`bhVYH9hb%^G?!ub;h zo=J0j!yCAZ`rmi|g#2*+fnO45Iq>s!4)aI?&;Np=moUDzCctvYI_(XV*6Y7S`V)@x z{$)d{jPGrq)|#TD^y977DFf~2AAnnzSE{`^M}tZjG{}_kK0Fc!tx?^Nz~7|(v4556 zZ?z})r_qn+0VgtO>G%a{??!sl4}6nau9$I;M7o>Wm!p*?ytq^NeP#M;Im`5VFyF(# zGhx4bmYDrR>!O7Ara!|wc_N72dB>0FsoK3|F-$Q58x^9p;o4bqN!2G4_qIHE<5`1s@WJ4AUw4_e}l@$tW=@6SX3_UZ9)z5eTC=d1PKKrY7NP2e@jo6>Z~T>Qv5 z>UVy2Mf7|hzpuSNU?0GI&E5S^^LZ3#W_%{*@;QLHv_j5dw0jPfb%UF#r2Tm2mrsw^ z4AEGZ#R%`Bvt~7wk(+lTi8OeS{+Q}hX5yvbJEJ}0cJM5x?oE61_B8 zafXd%qO_M*yZ10Y(SJ|&s|t@>k`Boop}%>Ldb?5KWs%o&?-Tur^d-0_rc#-;yU)); zexoGC?;CnOkHlV3xC!WVxZ4MlFDP99ZJ<{~hqC&;5%c97$}1{)W6npDcV`Ev|ElFK zTOGSQTamas*H;?vp*zYedOqqftS#U=r|`BwVeg~<$NNb+;q`Qe)RfU#gm_<)y?fek z#((z3H~oAK-#zVzv6s;rKaO)HWW3ux@&=tPvAtBEN_F>SzRXpb&%HtCS#+t|bJj(9 zj2Fv2Ah&J|-Y&}LIL2=08{;)E{@3{NN9UcNeRKxTvsR%^XuK^TIN2<@pH))6;Kc-; z3uWp4IEVbf_hsUHLBaR#dV}YV`96$&`C-C`&HeVzsb8=*(NCLC(we6;;3xmZ^_f9= zJ%=?>No!*M`8oMCwDZ9{XXg7Pou&oVzX5#}z+qQqmcPO8CnD!JhPOJ*J=6;{&@Ap0 zawg)PU)T9*gWj2-zWtn{U+&4B{hxK%h9+#{u8{Sp?z)Nl)w_0?!*}bIxx4P_OcR~` zMTykIN>+&GQqkN8)SY*^0~L^K6m7zLr(?UZ=${|_XAakUMf20DHsB2~fwi_b8y@1i zRnuIqr5|`zwG0PzZD8J4(C@Rq;x)Die68+Ys?Urxmf@;;4y z-aKE+6b<8M#k(~~^ zyDQ{uy&CgB>OUjRd&U5Ed~*}PQKvCO(Byt@AlrTLM{hx4tX zvwX%>za3SqhfhSkH31vFSQovN?iiiN$Akucwb08yVXUF0M*K|_{~*O*I?cpfO7j$GoRz72EMt2+O=+o_Y_Tc0*$Z}@0Qg550ML6|6-|22h{`f zNXxw_9B7E|_*Y=B2(g_s@clyHC~Rwi*01RMl<)HwQ6CWAdU!tLC(Q?k6yH%TX#CE) z4Cwq-Lgya>zfbe-EOa_yUG5@yoxC4nP99J^AiLH4{H)$5k_Hcu@pgfG&S!=HL(lp7 zzvTIVd=^o?wErdIpF;l82MKrg_M0!O{e)#)N~3<+Ps-<&Qi}&^ANeuNhJp!+Jt{e-;d=y*AKtY*ULE*Q?%TkoD# z2F~0le2cZhdt4V7&eqNKWWn#U%0CXi0E!ba;@oM)f&9U1v?sh~$dq?kajl(JQ5O@3o76r?^|i4NJNTaalLQN~K>+-Ldfw znNTu~N{3l-JQXly*Dsa52l7wAf7Saf;Tbi?T^R9V>b-7XHmYpeugpZ7w--g!o6C*3 zx7T1F!@d$S(p)5Isw7PynF>`UwnNiJ8GS4Mket4^#E`;y#+0m@znp)UGf?L{L+bnz z`UQI7L%%H1PVwUse=EfY-*G+peQIidTF)&rA6*XKxkuD^I?flV;j~vd zPe(+@I(S`O@HnVC9>@6(-uy=7r}cZQshOY4X0C?^5~SqhB@US)IEx-#1!CbHR1{vx{OpCl1ph>!bzJD7pnOR$H4{ zw?J|8eNYoprp{FC6_6PaUN*d?>9@J4@SwtGdy||gI9u_ygY&nE&f8FQuj298<_@Ig zJjx^zjMr`x`CfcOj!Y{wVfltcI@DN!c0s3u2T%5772eXZoXRVU`Sv6J{>z{e0;zMB`J@E0kG1#wX_$q~?QeO(Z zSj8R4x&kjDcs9Xx1kWLNw)lS@v{=O#@?Z`(s8{?T4Bt<11HttI->d32kZln7V+6|_&C<(vZuz!TmNBvyyBJd@lhIJ*qD3uugAxqe0qHR z6e08@G)Yqb!g298M{w+QrF>>Kkh|yL_;?>p0_c^GGEdfV)75)vSkAqOcIDiIe$d%2 z*H!C|4Z05;4J26ZNz(r;A^VUkb*Yu~wUS@8z|{h~;?{{fTiiL~#>A}`w?W)R;>N{o z6t_v-wc@T2H)`m+<@v;WBKG2_=zNVz+?;M)H7}WeEh_D5pu0eW+)p&ULdiymT>ZKv zXi%&-W|!PmkiNZ{d5f_xHOrcW+%jZBz~>Da0KCU&*{RsS^E`WxDPux+`u7Q6PqD0r z3fFt3p$tx9l=-X4*$<_Q-u7w5tH!3vkX&h1^^8p0uXBq0jyTGJBLK)?%?y zwypa{%j`m?PRrM6o=aUn!o6!QC?L-RJxsut2ORW8G)>E^=Ar^||B}KEQQSEN!l63? z{rDBek-*);%;uzOIV&C{Ham#j|vS}zwvI1 z4=P@69lr{6)PBM-h7RQTqcJ8qnsdJ=`w9P?JcachqI6dY?nv9!alrp2W6~vVOzs0w z+T;3VFUYIUb>khE!Y;Emd=FbXz-hoPdC=jz!tZM2CtyKZw;kLd!87E0NMP3&zsR}B zf`O~9U z{`5%${u6=wCH>*4Y(wRUQU9k){pnva;J*(t3|sX&eq)6{{t5$53Vh7Sf6i2Y{CWdM zcyyXSymp#Dyfx|%PZ}_6OujZD6H^)9Y4m^VMgI8P3>e`9b^h=@2KE7_`|D~_`~b1@I_YmVJrLvEBurJKj*M6TmCEJ_1b9F z_gW9WkNV$T&Nh03lK+uw{qdJA^Ov7k<_~XP;ScXLVDk_9lwF@svC>bBzbX~m*0*e@ z|L^K=F0tT<^KS->Y5%$fziGi!0)BXw1<$eIt1UQT!Rst|qXmD|f_GZ*ehdB!3x3Xm z|J8#3-GWPle)xO~j#=>47QEVmTP(QUfV2P$Aa&*;Jp_7xCMX1g0mL$hv6t}q zJ15NWDl7g53rXvEbiW@YIMOzQ}_A$bwf`@U0fyXTf(x7{Ac- z`G6G;dwP!=;or5we?;(z5&j$cc+!BQgnJ$|;L8X;Y{1tO{DJ{@5&W_N{~5uf2K*?& z$Ku|8_RUg%eSqtpGV6oz#U6Za8GRe@0)nFkd_BR{2E2)2cQM-$0DO0uzq|t${8bA+ zRL1z3uJ4Zte#C%JLH{Jd%>tK~`_rFS&iI?QPw+?OjIZqxc%>D;-GaAS@B`(H&&4GD z7ZFbQ+7Vd~e`Ce}Cky_W1&1sA@VORzsRb{$;0+eMt%C7E-Tx0-;a?;8H6#2vEBq$} zpS;$yxA3MFejez*&OZ z4S1a3qyf*Ovwo`qzn9>C18yOBrvYyxc*uZ1L-0NW{!4;W2K)npA2r}#6Fg$TRfG#X zX~35ee9(YzAo#EW_YnMo0Y5|LaDcS9bJQ_~oK*YRNw z0~R3GZ)oZ4ycKHlTefW4+}72%o|WcnTN!LnSy?IUUEh0~v!SPRea?RQdbSze+S1j= zpO<#@_Ox6Yb2hZMZMbc{@@JFw;?vRUZ0zW2)t)e*z@NyvqpPp2r>mt?EF(%16xi3X zxy@l~3!R9mO((Xubab&%%*`ELw{}ns`&w@8Y|CS&!}CaP>20@-eb_cGOnV#Jeg%pb zbI|Ky4w`+hUwqxtoB@#a)KNWc8~NMLDA}E4KflS1d`3CeU%TRls~4|Wf5SD`tXkT% zzG?B*E0(V3T$ij|VH+8-{R>*Jm}pWXS8+)mQ)WqED`Y)S1L|Vxl9rB6pU0*xEj_KQ zjlZ+wwzkeYco18iB`a@EG~K|>+}ySq*>}uqU5_H1rE3!F8LE+y=|&rbmb5iy61RrS(J9 zq1t-=*xh6KE#640iPOUHdPmo$^uqg! znD$7$dT!X#(}VrMD=s%nI$L`CRwrpv*#TWN|I7emfqQF4&sEt3jNWg#t)tb8rMVs!a{(6DO?=kNmi@4JI$@ z7?U+L*cwh!SGuldR*dBYK0ZWr<6`}5EiksIfXuHW=RGxwy%q(kdyXi=J+e zwH`AqUDUyyFNg=ERH$A_z%hkY9jvc?IyIkkSaNY>N*oqBMl^ZXfmTeZ?4$`8vi#obamILSDhKhrhF;bUG68@5+?Jd|6+A&0_m11osPfDE*oaAEL zvZZ~A+uqdR)YCRenP(E{q$eT}@Nh%|*gma+j`M^VrH<20Gwjn145~<}E-@{rmeADR zVBSPbU2g=}RvkX{$tvr{qIZTU)yr_%BtoenAh`r-mY(T&-dJS}G3#8$3}C9R{!lIT zV*L@_1g%}XQ^`JJ4zfv~SSo$u1EfS;{Fjb9i!_&mHzuBpdT$n?{5VSB`vIOf);zTcLv_6B&?qxj1yeA)Y-^~meX_3JE@mx_Wkt5KOoxON15;DR&x_a|n;om7qtYVF zM`N2rbGWtXlranBz!Ib3&STzmB(jdq!&SBW_dMNDv#WO30n_wIrRx;de#&SxOZ4Od zV+pl1vW3HkdZTPOcUp>O?hwaT)1xr$h$`i$1!s=KpwK?wQq#y%QX9`2>3F#5L^Mq# z6VEmblE;}PaZ)V%3`sXfY3<~p;W%uT=jvUeoko@8M3-$sZ#Bo9)|a%*RLu0zoH+I) znQb}a<1wG})AF(QH#fzSd)v|+oEMpfTO>HSpK)yK(;$tgC?VDUrVhtmV7P{yOnzSkJ%JBb@;7NlF)ZnEf02pHeCeJfhTq zgpWx$?g?yon*AN-Q@kk``>($ycnfmRDCNyy3<_2QKc(0Y;!&kqBz!<{MEIk^p8`Hh z<-xm~=ftmJ1K%@1{&>fd-$?lijoCebw|_mbO{qAro$8a#{JfQ1{(URCyxoGh=YVu31SbXG6ZxGv9KQzmY05W;!>{IW zKi8~c{?S!DF1m&82R^UV`Bf|*U&Z}CCH(hRar@x6PK4)qIsG6otW>#|)5pCE)PJ9s z$4A)9=?!|ho#S3kZyI=#+Vu{?Q%V&AJBa=W^L>Jag1CQydJcmA48;d_Qn`RpD%VE| zV>IqQ%Kd};7$^sR+e82430*{3K$GSQHQXXDy zMXKLg?vKz~4zFJ;`3X(~e?_Tz;IAtEvfuw%s(&u?t8+P>0ih>yS^ios%ik3F$U3Hb z)-io!9m^N3XAB8e6BbatdqKZU>8|H?m;nA7r7Jii;bk9Vx>B(EV;p{2=t;q=5+2Lr zdJg1Cd4Xq%5A3CM^LTwL-@x>~4J_Zdf#t)%KBa~vd`!Z}H}E{Tyn*9SiQM}l7sw}k zh1wVRIZ99Hk$mQl=W{yO@;Todg7f*DUhqzqn-aWnr_852Ip14%vRt;G^9}o1uGi0U zgMLnLT*4D<`J z`7~}~>;nEe^@Cu16SvFECY~?2cZB(Ia}&>(d7--koZhVfx8M7Mg?Dj3UcQUhmHE3k zd_l1OZt3^CnI00X-Yk5<#?3NcH_Ln#3~%Og#DFB%CE-1U1vKx^Bi;bbugx+ZHuHFo zOZe5zoPTzq%-cfd=K)`(_7`jfKE?N>uucsKP8M?fX^H8l0nWer0ZCWz<^wXWOO)O} zE|)Mp1*CQEMhTa{@4+NRt0;K&k9OU#y zgED^v7bHBdl-sScl-son_+^qWWx30xG7n05zRs8OczqxE6dGb zuh8{Ew*Y^G#v|}IsojCYG#-IpqjmxQ7L7~b1!~tVT#lJ7T#g0c2=(752)|D4@d+;f zDDbzbKY+hO@qnWwSH|&h9}nYxpp56kP#KTU^AbKR^hLojU?t59!Ep(X3r+}5O8l!r zUlY6`I4`(R#_e6cmCIYbmD~I1Rt}E<=^QZ({9Vd#E0=q6tBm`tT)$f)pSz9ufo;sM z6gmu~eX4sK%MA;Ed>fC4HzZs=#PUAD!iQL{Ug#mA$ArEjcujB~Nb4b9ilV==%elV_ z%b8zY&hIgjH@Ksr}nDd+JzUC#4op`7Pe(RR+aaXXiDY&*BpmF+w(2Oeg6 z>|v&FKFs-rcCdWo4$gN_FfKT~gXP{6^zUTEJxug(<4*3U7QwJ!MDQBqvA+q<@8tTI zJ)-nJPy@V3@dYEm-=}d6`~xb_BOGrS_)RK5koKK7fMYbCfY|SWe@N{C{3Gh0N0ojq z2mt?>@&jI?{sw-F%Ks?0SL9KyPcQIKXj}vTl-e6OPVE5vGnz+_a(mna(thB5jOj+f zUcrlklY;Ytxgo}#z(1#c4srkDzN$X=hj@N9hUDBY7#4m^=x)JY!2!V`!C}Es!UB4z z8w0*c;~)54+V6K!zHd{0!0%E0fs-UJ_`dM7cQe0uHZe_ zry!4q$qJ5#d$l?q=pT|ikoN6y;8kj8;NSE8S=4v3f}aDX5&i=zKViW!zCVj}imE7_ z9#VG#|B=Q|73UMKV)^hM<_`gXMEUJuxe4GuQF?p0o(p@pp4oc|r)j+G<#@Pvi*oet zm3qSX^Ev>}z zmve!zfXbP>pWCB;KabP%`+2^P1OJEQ_w#(|f1Jl}T=4QI3IBoS9q{KgZ-L*S=ZtDj z$5$=$Sm+|5%Yd|xR#tQS)e9c2=5dO9!zgb{HILIap;bxrg_E(SV}!9z#x~xDc7T9%X-Szus*+P3vDT8k2cLX^e~&HMRePYMDHiK&KXy9S8dbOb z!F6?9n>DVs;dhT%b&79XNu=Ym_GKRuy6_}FyRDS?ki)%-j|**Fy}W2jOR3bY>H9_g zgXDtpeTzx1&?M&;{$qm1mDkB?zISb}$6jT0~25wqNb4szx^@$nr{`mzbG zW*v}x^<6ebLu9uz_PdMbOUuvI;oGj^hFn~`mkop8o|F@NuX)NEi zbsKXVb3R*Dy~pKpnbIt>w8;ePFnCy%ph(@JyeivwA#s=J{u=th2=m**pbx*Mqht0P z$Jp*hR(xc&EB;ERw%1VuyMQCJ89lHMfF`Qd_H&Z<9H5u1*k0uh=&U!6XxoSEJFjle z6{uK~85nUXv452rO6eM9XrCP>`C#Ss%)qsGbsiy#1DocJD92M`Jw^z&fL*Psg<`x3B` zR8MgbX81ELjPz-rULl8Y!;duEdhbj8n9_s(u-RS)d=KOI$Zq-@rGNqYQ`)aF?47)x zQ0%Xj`iH`NU)0~>BQ_|y;9l*M^EEu`=5z%wBm!zE5v#e7FnkDvb-9ML9=%bvVzGzS z1{*0guZoRJ=%{@y(Jpp6%Z2{5(D)9U?n%9S(X%vk@VjOFHDVn7d~p%wJ52KRR2Hqj zPx&LykfF~TV!ziaI5WpKetHwP?z%E-*6Tl_SkD@|{b;ufHobmKfB3pZUfNc&=(R&< zvkdyJTIX&sVfY6{9d_{7FVuAreiYv|M(y1K|0b}BN0XXm_Plfckn zg?_EpM-_VmCVybM4u;JD@B>sg*!M?RoZ3R$y6CCVV+HnJE>XK-J_pqK)Q6Z;tF>*)^WyVbuLDW{gW4~15W0kiwT&M5;QqJ8 z#W%gd^Qg;QkFICGwiToOwAFC`Xge2@hCz?Z7&H5B{X6)q5gImk{+8$>L(kc!f6VRp zg!UnTFsx(6tjnwYpqJBJDG#K46&KT-XP*MBFO0Sz%+4b@pII){r}^0LzV4Fy-$q`I zBeUyez09C|;ScjjfbBM*Fa9&gdcsC~!e1_Yg$LD_IsK&F+>yEYiocpgK1q86lpcKG z!hgii#O??9HHyyy?%)sRP!8m)?cdWFf)6bpm1VVFtDn%i#BIxz2X?8e(Jl_!mv1x;Z!e@&(?S^Tqo}rZ164>#pC| zm+%JqNUkq&T>C_V%-LCl1$;vZvmP})4&~f9b|C>AWycX9}DrRxH!sQ#2n|IP`a+soq(V?swp8#@7G&rgp#{ z^&M?%{_xz3RF}Whb$(8dU)XFz9Wjm-Y@$#e=-bbW{43g5&6+uk?|vOtH#Y#=d#3R{ zAUZdHH}+lF zbAwF)I!9bL{JfcMyXiUyV6$h$W%!vf)~{dy^DMnBF1$YV%f777GP3^TJ+EPN!mvf5 zZ1x@+YuI*>k}-LQ`TXaDPhj}QW50t=dnkvU!yfw6JY}+7g8h*CVP5vuqg0j_5(>cf zhq~~t*s{)njSAkodSIL4rbo^Je*IjbW#0CSy%3doc1E8Oi1zqG*Vz^zpZlN>j%Teg zYO66h>!7_xamJ%ILmlhIZ%(zexuW^z4ZX^??A5E<2NddQ*qcKA*HYbE;ujJvG?u+_ zJ{yLx_RAU1unDz~@-4IVQMIX$DDC`u=_4g)<)6v9cSQPY7=Qo1xQMpG`3h}QNNE;^ z=uhmBxI?eaWca@HuHl!G>%l&m^AzjFcx)0LwbDYnl%d9xt5SO7J`;bXGXJ*VQtN~^%NN?@ zi%j%Jq5EAa`4N{@{*sA)NN6MdU5f9Acuo1WWLV?JOf>k{Z2V~(zc|y%FSFU1m~dP9 zQ*JB2&12=qJXZd^jlW>y4`x~U=d-MQe~y)3oMYur*!Wj%{G+R^{FYT#{(y}?XyaeD z@vqqUU0z#!o861JjZS^s>Y3y>v(*v)z^;H*K1&^!Zz0z&VpkyBMz2&c6OD1+W23)d zqtDssVH^ET8~tq?{kDz%k&U+7Ey%Fz^}F~!)Tm-Tu7Hid%|`Ea@qB0#`DbnX=WX;0 zE}kDpME;zM=YyeF_AR1o#Lf!n?-SjYnbIqx2OAYJ(W@w}uFRC4;Qd7Rn)v%|diu{0 zJz(OWvGMze9y0M^M_|}Q!*0N+iGBxm?K64((0ZUmLzg}z^m+^tH{WH`da5?P`Moy! zP$tiRL$CCCqGwF>=ZKznr}FQZeBk| zgkSCE^z^JiH$5REFN8C^i;0 zsc5W|Z&~9D)O?_V*r1O@`U<7Kf(ezesA3xGMOwp9D@g6GtJ+mtv9F=xKuyD*gCfET zh;ot=%B&V=s5Znpp*cYiZat-S<=Q$;yi;KQ;!2B2T|+1BLP|cVCbjeuy0h*fs}Amd zysDz!bT56$9dzqWdaI(N z$|u#Lmfk5(-JfUmh4iA!-GOg99cya{x1MPYr>WL$U2C_nw6c!*Zt~L3(cJddsDnc7 z?#ENAKT9K~^epXJrsWxPZMnYt06tZx6&l2j)jip9rgb^KE;s9g$}px`xl9$?oj8Wd zg+-6AHNuOFb3*CUZ)l1(tEMgO^pzU!c2shmTP@wVI&mB0DwuA9>d~`A+ZxKIas>DYoqYshx5xh|+fRc$o^g==s9>D1;4#{_ z1Q#TpzSB+^$dG&lLxM*#xcqqL*|3Z4Fy-&2^Fs!wI|#W-x!YdF&prruXL5Ylck^%K zXC8#ZK3f6q$^j3t4L#KJy-Y4Y&L1dGxtr-4H{VI9ck>;JHaE+6xw$a|6iShxqQu;X@5B5fIXCKdAx}I+yvr<`23WS!RKUPk6>I7 zM=6aT_D%=M=m31_>?omwLsR(w?}~@x9gj(qt&((iJn+C%l*xA)mb>Et-2(F`-5n3e znV)Wr@Af+$kUM0P1HZzSFYKp;Tvj=J9<$Y>XcPb5k|S+k)Z?(JtoRImL|Hxt@3^ww z1%ce{+y#k$g!NbOQ^8i1{w|1GZ?%n;?k-3T)#HpqzBsKn(}sDwcR{vn-M-~^?}7~S z&jrc5AURZe!-hO;z-#|HV!s~0&BJ!NVL#dJOT9Da-@N&KvSH&Kzstf;EqpF`)mi;c z+n{|mI#+16M?a$NMZ@ns(x*21X`cLU90>I!;OE|;*=BD2?0>+gB;{MA_&sZ->bZX5 zvB6na;OzC+H}%ZoeanCAvei*JM}A1M-!spwa}glZ2$i(w4NewT85tO8&Lhq$VLLu_V9US)M>PgpkA}v?1>Q zeD67T=0104V3TNT^C=(p^US&DzCHK3FXx_DMZeV}V~tFCu%Bs`-^P;cH=1sXT3%F; zgL3SGn2GzEhW^2#yxhUaZ*|)=c41w1A!0w)8a)EE2|m8vf5d;8z;c@YH`)fm{wu(~Kn?zaAk%{8`@F;9U%j^ozY8>B*z}j+ zJ%NIsr)Qk}dxA_en)bdz@S#9~zxEv`KlmI)eNXuOrr=WQOBm#`Vvu(V?S3PFnJwn4 zLNiiyqThu~N!JzY)~xkiom_a3FDI};@}+Gj2*#B~(U-la+3)b;H}Gd;>Mc>AWPaPO zI$wF-jn=xM_}|ETjqztLIFo@pl|immjQp>GwQMd>@Zixoh z&;CJfJbiUol+7EiihziMigc`$DBTUKqM(4FbT1(yEnUm5fOL0vNl6JvgQRq)uyn&N zu&@hTXTRS$*ZJ#xCa!&FX0Mrdo_p?jZvVB8){)kw2273TO7L)PwWPiG8gs;|Fkz$0 ztBMaD`RdOmxzEZj=a-=FNxswdCWI;Q5NeL_4;V zPp_RT2Nb_j43}@D4rL!Z9+kG8+`Y)0?r?sfYaCT_{QMPVi?{&TGB7R?p~Y>xWS1GI zU2MCgb(Js#Jxd-+)q*TNx(az^JV(BmBF6#OifEK)y2OwQzA-NOQQ#i$SsP%{!_4dG zbIf%ma(k)Hv*6+dEZ}Jo%11E* z?V6;gY@1wAzc$U8gl7GLk_b<&yfD&I@Za(3kGTIxogT^hmG+gx`}bB9(aawdjGRX& zoI=>B(p>ExHl*Y1%MZnJxqZcoJhBbE=4LG1VNsB6+Ljnr*%bEq$+&7OsaY41gD?N` zFH+8HUx)_7WczJh>uvh2qkdNB3r$_BGYT9VwXz_$U)fP0YjgY3-n`D`2Bk)EpRv;k zyZgN8(NA+p^adI2%If6~&o3)qElciRKNz_93=#TA|8oxJ5-L?F*9N+7imvoe-*YEHvNsLEf}~XtLA%u+RqXim`$=H<$j@az*srZaenNb zC98b5T|Z$p0RDy4gf|lw`vAp+(dZ4Q_q}`V+rqzW^kb$(bW7^+Ms3BB%X6CLyFwt1 z0ji&UOain0nhhFj$Nq$uCcj@G?vONUq!?CdKC_Oj_Cn?{IY zKPAj?AW=#Em+~Beatac%P~E8ER^XSqdd`6q$dfa4+E1gk@9BM_K4pTVJxD?40t$Ru zCykb8YXxy96Cg@eCQE!^%Af3w`%s&8i#!eg{t3BI(I6PDNZy6~`!J0npLwo%R@Z}m zaCC*BkW=4rTkNVw)y2=SV`=5?o*j2@{|y=#3Wi%ad_#L_!fboQ z>c-B&BbN;4DnqvND{iKLo1Kn?Blhmu)!mT|Lc44-=k@KJy*E*4xJyn3DJv|U{nFjl zdKyS&EWFdms!4j)B<-ueEWm(%$^-cjNxDo}y`vZl_&FiYhi6nA*`_l)#0}Q#BF)^1~8!!uj6X`czZK`JxA(mO}qB_w38 zTA){6aF3*SpgxA{K#mJd5qCJfZB3Trj{bzmdU3q*F4c4IVSjZIPHIHH{wpKONcLttSn#M+b#jmlH;szr|M_v(RkY{pre^z zyvEF6x9d+$%tGPySkk%ITb-}|fA9Wuwk4e}_22!daQuyDT3YYxRXeQ-UOESf)7X3` z6)r|w3C;6AGS%GXG=chkzjz1HZ$@YCZ<>b|lbJ^7dxML#B>q^*F;^q3J5@d2nOT&9 ze`1-h1Z&Tl6{$u#6S)ss-(UGe3`#xDu?#e0Yx)}1&7hl6ddEL}xazx>{2STVvvnND zf!<~q=D`Xc?nI@X=YrexXMZd1Yd$=6FSN^~f9HU7SQMRNcNR=Bb1Wt}$44@?BQ0Gg znJ7W>k~ed zf7x~n&{`M@aHFNeIJe(I9+seb%u{^UTod0$bXCR{DLP{8+32fIbO%l(s?4=XQymKZQ z(%rI--@_Ft2B^|r2tYD+_K!_Kw6%@{e;HOSB-TfKdb|BTM4{J7I&X=4$HVAct=n&8 zd4^|7#)xKI#j<96|YY z)e`fkWpz3IRoncV$|}YJL;c?ee)!i-V1Sn1>l9AAPfN{`(z$In=5M(4FAhXTQKKmh zlHPc0_{vnG0UHt$WOuJl#jLu!nhYg_FFU1z=s(TS(?pDy5A(}k7TA;8xW9GsK!kZj zyL~D^*ePzU0XMpgSr!_6FqGtw)c}zD8eAF?%gSI(-!H|EtMKp^aB?uq)OR^N3JI?N zTw)_+oCbmat7omkN1+~%dynTud6`P-TArj;wA9i)D5U@1JcZ7@)21)+-mGCnKWLn3 zaNLxkave0oU{kY7SQC`2o-_TP@V(Om>3n(ciXiSU5_p_F8G=!$0`jP?8k}r z|4mps>S=!{tMePCk>qC^)D;%QQoVj58WUFY)OT*m-`4sYTwfr8=;XCxNdApQp2-XD z8v$}oKo6Tc?Q&0apoLwGJ{)7hOFh?0?Z+>joZaGWH#qJQl1@-Wypz>#$lo42Fys*u3uC{nG&rrOqslz3c`I zKPo@8N4EKKO_sp@#F2M#RF6*Vr*>4Ygkg7T@%zmFxqYRAgDk7wOkERPncM1>^#}uc zed(`50j545>Pesz0V`slF#~^TjX#d{jJyv_T8+xfU6LcmQ+h@h>wrN`} zDSLk*x2M#TRYKG`=?%dB%1N*q<^9GF>{=Sy>MY&JP5=Ig--vBHk3_PtbhtvZ(P#RH zAi9oeQVIV0LRX1g$^XoNE5KK_&D~YaNyAd@XU2he3yVwHbldd3gXt&^|3D|L_N9${ z=i&oVoLI-_0v$~qFefR^a*S;zO9lJ~aIwegg8)tv7YrOd6({>bc`;DNTE)ld7?tW< z;#u#^V+txj+?xXb98ys!CBO5xt3gvt=z79fJTTq$9wp6iuVxZQzeCfy+0kfUobn% znDWGlU6{Bh=__t4ui$Zw zJJ+W_;Y;_aI+nXT_ts2djVi3z+XGKI_7~KPSh0`DJ6F4#WYCpCu+24)JEB?|txbjh z6;w@uISsD?-LgEvynspb*r$zI%`FNiu-G&GH7n9j>-Ri zW|RsN8wA^31GykVS=$a<4P+!mDF#A52L^#Q)-qYZtwFGjHIytGM}?0I0&c90OQXLA z1Awcwr|ElZsj}!~DoA}0vH2JltQ!^ncMx>6TVDl>_U?_%-CawSMqf~MjCRA_5acY_ zhvdY4vA+KGX}Bs-aeHm|e}%-pdm{W;|111o3m0m6|5qr3rXzZ!rr?Z?` zv@dzCCxY?6aRay#$64O8bs3!z55bC)6Gw}f-`x%OK#a@$-@j%&?CSJF1lTeA{|A}c zxCRH0PO^~Vt(B=qe7Sr?p0m;AMhvq(ldRR}(U?D<(t!A>&V1I(c5VLv%SKM{ir81$ z9Ny=nV*B;kIB$*bAT5Z{K$1$DW+vWI-+wzzv63ELcVjxJ1gL!re|B-Rg#0~}jyw%7 zc3kax27F+CJ-!uc!idZ{ZEsllOIW-y?MFr+B`0X!EJ5AF!&u~_&?p1%d40Ouoz(_t zgSdS;k|0Sk1sn4P^|9Ku%s};OMzrKW$-~AEa{OSoxP38_YD!Us&ugJU>QF}XyV4HR zmnEI|@Z&-1&Wvb4pgL%KfO8O;6c8(Wfs-d1-TD?3$@FWKN_eb(Xc|ORGUGWpa6ACE z+r{@v9h80zd~E4E)CKT318#RA3Oiv#1aR2?bCONtK!|7!y`}G8B7aGATgYoo*Pn4l@xO^iv0}gEa z6I)$n?mI9el^?jzoJeT7TVit$(xDAWCIOmsBcq>pPItqr&9D1sP|j3%KPs$RAj>ENbUt96LWKu)nSh!vn*=5r_mI*dB74_M>pj9*d zyV>5^+fIJH@v0O)oe)JXt>^5ZM^)%Z4sOs~tg@)qS@EPq>+=AAZ;=1fn=(`I>;{DI zr(FOtVlYKh;FE~N<2*14!)dEn@MNm0LzYl=wHUD)9e-B3S?fX2uyH1A8T%ySil%7w zWwZJu`H7uj!yn%C(6YQOk&*{HZi6;wRv!&osfDW+Y1J}}_qbLRtsQ|JZW~?GDV5+W z%>HgtZzE$m#PjSr*!?`ABdg=~b1CoMBk|{=-dW&utf?sOvhlfLUN*iWy)2O!ZQ6YwM2PRZ zK0oZ~S~_G&;!RnB1-#TvenC4$nd@?L&^n(NHYuB1dHRNVR#~ql;-&UTx_J=K!3eq0 zyg~Yz?8z1V>3gvK9A-Wy`q`8c!8@09w++fB?(BTRkb(JdWxvai)+ZfoI9wB)tdt_vfdgfS94^7#2cpKX3G*gI6KcdB9#>4Om z#a>hOCqf24L)?4S%5Z8iE}_0LZ+$htEeFRM%#fJfoEjjPVRiWs*Qq--&xxxJUrolY zBtSk)tpg~I+2WCGMFfHE`z{J`iR`Q{S_re>_VYM};eoi-2OE$^4qPNZ{WP3=KkS`8 ziaRBg-M^^_@cJ59$8v99NbQ=UjF42+NwHgu@D!?W8t#nmqk&zb^c_XkXYit$P3ek=*d zp6!45eOE}VSVBxI=hQls?26symF!8_4=BYH0Wh2zw`$Oh3mlh0QbKG$z&>|7$Gajl zwNtH{A5FRNyzY$AL-?_!B5B?NRnI9kV~NfY1TdKRZD6JPGEK{k!^A14s5Bl=a!DKF zTBYUGzUC5Ifn&em{DXt>?Eh}fZ{IEYw|)n89=7IJ)Na6?8uafisxVK_**{xi zJu-M|ESTcFUj}@$>l0&`aOKE`GQa`V6p9b6+hRzNjNv56rbqD5DeAYf*adUb_NV)C zlOP)TyJjhhz5Bz*JBI2<%ENzV%807B)g}0-Z~k9j z9Cx{?zc}tb2Qx|ig}yxlDC1pb@Nb>S&j2fm3id*Ge%Q%x-jwj*O3i$VW_;TjM+|Sv z_|Ex_ENkFl@xPpS?5ZDWr!yA{N_cxh$R{R4Nui&(P=wyMCqM@7Q?*;Xbenk1NEgUp zKpc>E6K`YrPotu!I@X0C)%zPDS8Sq(XtnxBP|`Z7DFTGM5d4b%8UGbL`BM2!s?u;? z@MQ4yk`?D}V`K$mF_;kb<4;2*U$Rgc_EFt`5R5Z@C|IdBGE?blN!bllSN_ZNUu5c% zDC(8}Pt$+tW!%Rb$N|Q>-1IUj6Db|cZgNa+dc583Wtwm^Bk}zv9@)&QcvJ(R=vt|k@d;eeDoDC^ zSPW7c4fsrxIf#BCg12&z7vo@Nq8n+q{n+DB+#8#3DfA&2`k5lFIS^G@+ff!N1;6^d zcd+QP)*u)Wq$!KGrGn%HIe$g)v4Z=%ojnoR+`i1bk5H?zLS6_`!;9OdnUS&RJeY~( zNlLZrnuQO>iF5hAkvDkLOZIC}GW78*vs6U5HcQ2pziBwml*p*$+!S5ANLUqJ!$3~ zRrpGxMqlHjU(dwru57aK^U=|WF;OZw%56n@mArrUOi{-#09{_Oc@c{&OQ32e@10HO z*|&Y?{OY=|Z8e^_dL6&*Oxk@g)%4a^uNMAc@Zma%E~dmM6J64rg)8ckdXClYY2w)k z>Q;HKlOOmo2hh#ub8@R6!T^@5K3Ka0iCuHvw1OYi3c!%&seNBcnCwlH3erEA=Wd+J zFpr9l%h0J#su!OzX4uvx3ps5Q@4lu?3xd7Wzn-n`1FDicH4Wk5X&>N`Us9;-!8?)IY0Xa@HSi1e<|@(Ik^Pq8M4@!V4SOlxkx&P zz;}!_jr*K8Xx2lROYSYAM$xC&@ufKBTC$K*OU;PfNuCD4%U;7D?;OsD63SRCU&pvu z7Tm5}hz`?zsKZimOOi6L@YgZTq$Y-pjO^*7Y^mDJ<5dgzn4*XIdCdg_mQnq^!^r!n zeS?gkBSfs(+%{{w9`EGAvbZk&F*xPCIlXmD?fQv zq=nm^hS4CK%mzMu#OY7MLbk-rzMt`(ng*pk*X~WVG<~5vsy+TCLsVeA_{>3E_@Xo% z5^A{|yAPgG`4y=+#z-j_UA1({m;G;Rw6IeKIsXExW|`S0mP&AQroox1LZgxNrFHA} zI0(300H`>9%Y7wCVTtCEQt*`CQ%QEgcZw$(;*;@mChsr0R03mQ3CG!q;F3y#W|7y9 zsrgYQel#rS;#EID$kqF`Hf>*}EHC8$mCj5lB5O0u7s#h*3A$05L{)Ax4a8SIK=gJ+ z?N)!s6Wq<1aKc|=S3vK$q&i;Mg2Gjo?%49t7Pfr3r*qE=*a_DUD^wc4v$i$g&K>4O zgSlSlnu#ua+^+eA@!(~Ks>i`^$t%Ba+pQCA)4@bll}gy9+rd9rqhkbNc|%UI*vift z(>O_krz5e;j^R_YI+|oatz#^s;88CBo=DvH$W@h3iBanw-~vecGH zZ+$#~Eo;%1K=PNH1*p)6&gIU5w3A*llB=__Dj|4)sc%)X_0f~oK4w81Zi<%ZtP57s4C=!J`{_coBU*G{8Yhm z9Ol2z5?|dQ@yPG`R`{XbVj3%{$ef4lQ>3R*4cz6juwr#y%_ry0lCan~&5W0cD*QRbXT98|C=4}(g#oCEIMY<>Vy?s z?J=+$_9_!0a0im;NVo!u(21Nz2VWH8ScjXEve0_f4oOO($YkJ z@qKMsF+ZUgB|~XEM=^7$9zKe?V%SLnO6Hc?1KykMjR)bEUm%s#FU4kRf}q803Whsc;$({fu3i5iQD}L2 zO4&*U7wCM8HMcr(b44H@uJe8~AyzG9;ZhX8uHH(Y?U}>{p?+pu?IEyPWh{}Puu!tTnoRCQooE0MQl`74vyY+iGw>jY3bdz z1c5Do?MHsD{2fIrx44crB=9p5^+oQ36Dc&9PBcSMHn8d)nX5lTP?b4*o#&u7uSEYc zZNN^%dIQtgvy<$Qb8M2li_I8jx@oo0Jkj(8qcM{RtJs zq0W`RozsCllqRNTgPn`Djw?3ZQg`lkMm>cVS`9B+5>He9hlZ5~!H(C6NCNSg_kVyF z$PHn`0v@Tos4PQn^2nmgs30*!*Z}>MoCu6HNu!VdAAm+Ji%w+)j~hA|1_*`SCmWV~ z)R80#ilXXzX0iO|0OjJK?In$-8<1@9NCQBJx{3H4#ek$o8z}|@Ize`ttZ;OusTlm6 zkBEOsN_I6EE4|Hmpm-&rv>W?_dKfNn%UQQglcBA%gSWJU_l+00-FcQ6CF`J?7XFUx zGL`UtTj?V?o8sUl!k7xnNe)>Fs2~BS)PR4eE;B88>vz<31!S@;L|3D0NPzoYxo&H_ ztY}(tJd_GN6i~sk+;Yz1T}J{t>WZeya#`~v$J3DG-N^CeG zl7<421U1Fr=8!02nUEYQUZs=(ax>r4v?PdMWIyqS(oXYV4tqhLrEjXS){iXhWc23 zk2MtY^-fQTH%NAJyn-y2&w2?NyU>4XdOzwFv?N8Rjed70ajEX}Xgl>M={@pd0cc*a zZYga3UyJMeM5Bh58%D}%{Dcc*iN){|DX0Ez>PjzF_P&^waJ+G#?rXOEB5N;Tjc=dx zXT8si#-k=P!n=*6ZpNi=?4GYx^aJQ+;pLa-+%d^F*Eo=_vD z#ygVj(o3Ud=%~tVXIrd^3^K=Jx#HwIa1_3JhdPwRE~#i+?j zIcoxGUvz&K+0yo*HM&Xa11dBW%FJ{4=JQXW-j-&adWTwfYi9!Pq{4Hi8*5kfpye~W z>|X~}d*o}UjAipimBdda*SrJh@CJqyplt3mJ8*f!(uadMGt4exNgifvVa`XW@_8=tmFF^N_lvm7}L018PsBo+%E== zNiyQCiK$oHUOF`?*P$Cn)q^Ozgf_2k7E^6DtJ}(wY9I5L%S`N`}MIGAwktQo2L10az8d zTs?5^6eAOc98JjSJ7+uaB8^3T3`oqo87`b1vdhavEl>D%{4mbh_B)1Rm2}BD;ww=9 z?mfTnEp$7kH@xUq93NW=bv| zui^mvc8z*_ZT1eHa!svz9hm@meE=tV<_VCJ1Q?~heo+tAt^IJ`9A^aOLugt*{kX~( z#R;+3y$%vbRBVM{ZzoH(n>#AXIfg~hs2z+j@e=9HA@-+gD|9;)-+L$R5weq`5ohg+ z27=gM#MiP-Z+>eSo1+1jlHN1zH|IG_yGaHWM{}M3yCJ2DkLp@;Bx@3leJFgHxF)%( zas&R@ySi&4_U@F|2_5=L&aW$$N9dYgUM6caTExocH=lIQiDU}G&jpFlW&)h_<>~fK zaQoWsB97jgV{KWz-MJ_xW3RBC7CBd9#2=^SYoAVO%Ql@B^}%M|*m&QjbgNn9=c*Br z*RjfUhRaT_PFgjx^bRO>>G%B%C-!?F8AMHW6;eR%Wp^8Px_-+s zhPR45wV=DTzXg;s6|}4lKMm(N{s}7KR`7ZM+imHEiImS^t*2%isl$`iJBEnnPc4uH z$NUV(Pc}hcNtZ*gvoZKnfk`2$`q%1L(0-oZj88Y7_d+tluaYy)ax2`8ldf@$Y z-$ot9)SNdRUUE1-@bQtOo`f*&tgM2!-*d1j~ zO}R3@kZ3eMzqduIdr5k23HyG+a@P5F8}!(8Oj`(-$=!3{6Mxiz7222X3Hq3?E zkD7d}Lp%<6MKzI8vyy)%225w9G9W55+AZU5KXPQECr15-DEo3`^j4p>L=xRaj%9Mw z&aZ`gd&DDU=4VyHY>SF}{}@-gm2Cmy3E@5NaR)KwRMZ!O$=7+~^zY$kQHrqO6esQ# zbXypy%47Fy_b6MYTu;qnwhMLjk`(vUCSGOSJK&^t%E;xb0cub2i85?GxNYY+r`E&i zCDA2AR@#UA>b)eV@lTS)t0P6<%Op~Jg5MvEPA1Q`{EG!$6y~V4w&gsRSLvTd_a*H) zPh+Eov0Js8yE>9`C2qc3T%*Gxx{iwova>4()1T&xACfn=hN7T z?x$O)Cel~G2PB2k6)RsBv!O~3QAJm2*WXo)Pt<;qVMwAQ-D>*ols5dd1AOdL8EwOD zcfD}**_q7www3u~7ZKVPMyrAq-wyZZcNI;z`cB?%oI#0u=&N_|TS3Zi`B)Ai;>c40u{rAcOlZM%1?;BQ*317rDqJyu|W7oXZJifa#d>u=^h2FwE^-zrmY zMBSDJW{CMyeOzSQYbmMo&+QURJjSR66qZrJZLg!@f!=$${;a;&wtMG_u~!m~pHs=@ zei>L6hSp|zyXZqeQ3u|~tZc{kbyXgLj^D6K)W(=3ojLhsqY_?3Sk?NcEqO=J4cQOv zJ*dZ@XsAJ@GM2Wh3c;bzUIiQLB?W6y(!@pM08DL4CJUIQ5QLO zR)yjH$)jf_W367kB*$vDef@K^i>gf;{qC1^X%`pmfD4rnwCt- z3Vvm>4;lNofG5s5#_=tGt-W@YWpko#9~>3EZz54e$Yk;3StxzI)OaXky&PZ?D^cuN zbGPw%O<#mIOt(d4nO#rHb8919#Z?+0Z1a&#q@u7tM6mC)kV8vF=V;=QZ>OK~a<+Mn zT_y*wJ_)9b3$-qjp`FJ!VN26O` zriX8)C^b(1>09A_sfjp;*-3JQS*3l0(-cgWbg!0!Z%#7*3SG+zfIhm;R)x${wh3Su zkEX-+4K$@Q{H`6e_IcXxWOF_=;$vw2aQ4?qIC?sP`gJK8;& z7LxYz1kGv2#u}+-r_;iXxwwYL=Ldxf0;oUL=RWrKNirlp8qNSxOzE!XVb7NSSA&p%RN^v2PK#8M{gq)`ISj}6d&xW z-9LsMi&xi57Sxc69g>K1cTaw5ZBSksezjEkzR{~Q>cVN^pbu+hze*3|W?BAmjC~NP z#VzTRE9DtdM;0@U%{`9{xQX_0C<6A5M@{6*K8DI)SANXafwnY7*lO3})OY`6Xe(m64P;6B%eM;MlO(=C zRg<2&Ib=2}Y``;*r(t?IK}SJIUEko0v=5}DG}gE|re~B++3zB;dV3S-ul3H32EtyF zTeg%3&Zg1~UMgmu7F)EEo#x?8SfS0X1;2UYuOf@D+k@SPH9wFle!sH8;ANJ9nS4%1 zpB)&(eCVqHRd|f`6WUw$c!0Yl?fJJqw>HgUYWE)+%+n+0rB(x->7<>ub3d;iL;F54 zRIo5`Yw2Ch-fA+w0PZ4p^GKU0z`aAAJ>m0d9vc!TUjI>Z_^9wTLBRDjQ2iPYX~;?Z z{`kj%kY^Tsxyziw8%JqaH5O)CIY?%Tj0~EU3Q`>ega1G35s^FpJ*dg?)3+u^GrZZF z-l2brtRydU7TR{16{|rBdAE25$6>3F+@> zT>2yhtVl>1Y1f*_y`klF=#_xLdv-PVN1S=xnE9<*b!e|i(q~?&^t-2$RHrsre=;GS zSh_@e5JKf^=~OpPG~ zc6%@SdMn{K)-bxVL;Bg>zSTEwTh@rVn)`HrT9p}vaf50D#;Gu|D?v_5DP;+J zQR?eQM)Ul}Gl&ygyU`X8fnmL0ifQ>}zw3PZ+OsBFN~}jiGv;M|Pf!xDroeI$A?Wy$ z1b}H~zCMj}kMg^w{)*INI^JrVJa(177kYuP*Ml*4&b~Qf)(1`tgu3kYq?#&?-fLT& zI6BZ+uKyBvNpt^|NOF+)eGbjDHb^)Bq|gs~T;O6Q=mAt}deuX!-s}aK>M-#I%4`7Q zO`S}xaB5c4?QwPEkXH$%=-xh6`y`I4!+u3T-H#-M3Ud7ZXr5a3!tv%@t;)SSdo%Eq zUAF|nw=KAGdrx}Pjcm;Pt;!w%RUH)}>&fB2(oVzyNg4Z;~}!OzJrb{NB7*yzBRRL5R)k91)+nK`}wis&gp)MQh+d zere#qwnPBuLrCX$`BG(XY~FCJPY46aDXpcKY^n!RL~x>Sz)OMm>LK5a{V>{ZsBany zs}=bPFmkWQNN8EH6*YtGWm;(s^DHiz$NlIq%Q6!+y=q0%b$ z>a3uslYf7rb^t8HyFb8fdbp(2To5tc@Kd3W9bs{vuw8hO}cQ=d#v^|om@kk-&?7#rx~_g>>`HH zuYF8J1D$Dq6BXKinNE4VzEZd>?7&D%a;}nykZ!n78U*g*R0r<90D4mGUR`I&$KF*G z34}?>1fGv3@|1=IEiwKH)673HA;M6Jm zjE6RuBDZ~sGH|6mUSGAkOh-|_n&L{)a+|Msenzsci@F}xGizT?m)%ON#mewkF=FMK zY+u53q^*mXA>i>iUu|E49rgj4(MVLQBt4W^DN;R@V3h%tSzJIoLr*mYG~j5g6DcR-=?!REqdoEG~^66ZyLw9GrVQ3t#H*g!ED6m zD>m5KKNQ;r12F7`R@aAC$unok!)8UjR#SyLH?LcZF~S<`tkvH(=@`sp!Dq^!qRwoi zHSG+afHjn7rV9uNLmWSSv#y@IOdv<1m~{_F;uSo_o@wP781tpP(4jKLiXIH@zd%7$ zp@)3Gge34M9%n0~Qq^^z-j~Pw`zueyo}#%7Q*T|1D?@EbE^L2ZC@8*ySrLUM&+0BX ztPjhMs>Ygr4dc^Ro#oxxvt(*l(`gp&ef_z={ZMU}>W3iwMWBCA-zSs6@G&bRsmRYrWQ6Lp$m1_UQs?bWul8d=8XxzZ2*Txi(A|8`gEkTg-d^6zm zv$*NJjKJKQAIlo8^5-o3T8g{>r2VF%v4cW%+N1=TTq9NE9l<=H=@-$Uf4`1%hIsryKO%lPdN?R38B`T0i2Y$952e~ zU4RqtOZ|U3{S1y-nD_U&(Cf9lb255|_j=hFD{#$EpE+A@8W2~~tMI>LwH6JI4j*9! zuYOL^W-{76YW46LRa`r!kpese2Mh|Bfx zPiMF5i7J3)&@VUWNdB3tn(>%f8P@eO^7fqMMFO-s5!%T~51s7p+*pg1Mbl7qe9gB2 z_r|-0DnY>fTLS=3!~iQcib$=#i#aW21^*t2g=y~$z`YOx+Yqd;$_eC8H<3r|$BRYd zXd&3|VF2LdX|d1Df0>QM@L0e6K?kR92o?SXJb7~?u-L?uy;JpXE$)i*s+AI>2j1MC zI_GDwzn~&_@?0a{CM=|C8x5$Zy2qTz8W6}*x7-aK4Tv3b=G?`mZ(MvD3T0`&`0=e* zlMdTQh0nNA%@d?4<_oD?0n7nPy3cF$=ctNi~l) zjL5%!NRHopQeAyN0a_YFcojFVuZ$4-kI4=eA3UrwJ0nDxqiw?}(9Zqghf{+8n*f!J^7&+Z(sgj=zVJMG^oZC4in z`3c#WBnH%I_V|3v${J8=WKV%CakReF-QrG7X#T!mLvdg>RBZ@JGNN?8eDQg4X{XiS z`|WNB2ijw3A$}|S%Q+?8n7Da|1+NIj9nw!`+<69L9#477%Y}4l*>&1Yb!)Q@)irb^ z{Pa#Uo|*C-sN{EWEY2G)WrrsgkF{@`r)acl<4f++?f@g*PFAkXP#QOg{bF>2bSRH&UGwY$$W2 zp3TPWx?k$+Nd@hfyGTu$QG^W18Rpgq_8}infl`R1M_8}>=$r{7ajB!b2bBZ3;q77iWjU>TcZN>z7{nrf8*# z>|wm$4M4K15uI;6`c2_wX-G5MgjJXG4J;KCN_e>D!2lcX6^q}mbECB7oBkobNo3;K?W3Q|LVJZn-p+y8y81)K!>23kg&_p-t)N_Sbfi+7 z=P&Y>kK?6xn!H}EedTT<@3e_?!)6+g9kq!U|+{ClJ-LB;yTGi8;af(q$HGi=uI1hj0tGlpCz_(TiX$DD2hsK z`Gpd}clG-&a31L^#CUR9wqkDQY4az$xbR&Z_Q#ZHLq^6cf1AXz?B}N)Kh^JFg>YzR z-hqv5^J{08s}9ek{HYF3{-9bSSyhsx?&x@q>wG!V|6Kbgb6s`0g8WmTbR+d>F8NO& ze$HFt->v=+QD+_2vy zjBXgP!GP7-_jk^9oj-QH&wK6NU)!Ge#C@Y>Uy2W|5-QuYY1UC#y8fV*LC^T^wIcww z7)(Ap<9yQcshnl-p!csa`UY#(tEPhFmcHE<2+w8DdORagHCxm>p`Kvvt}W^KLZVAu ziSb;AW%Kzq9@{AU9$KYdb!GV(*wQLgTj(nC8?OWX)lml zeQ7)TV!!-1&VqQdu#AC_7nqx6{e73}REL6=d&%IB%AMO=xi_O~t}>C)ky0KXF(#sf zSN@*=c3VPu*zK1i{nahDw?6)bzLSNH_EMQRt_8DU)%7cX+*nyynWqDV?xdZv8gK_p z;u&w|SYAm|-OSM?&-sdd0CoU|?r6TTn$7jupd%vk=d94es>+%u#MyYyP|@6)ylX<6Vkg8n-` zYK3#PsZVsIm9<2pjej?&OZ%Ab|69W`?#VpEg-*fw_f{9Db!_m@_7%AHGv+suwNs~VBpcOX%4SA*$GUlA~{9NDUyy87DdM!_>oq0)V5Y+bulD|}CRd%iM z4R_bFSOZ=~JMrbxuAgDCrN-BL^kw|>O|M1d^DK3lFdTI>F7@QGCjQ1eDB60oL)QUH+vVW6pIxUD=$5vl z6L_3xZ;;cn6_|W161?mG;vzX-9Wr})5e}oMkeQlnu&4`WT@{I40HhxSj!+Hh`1&zR zR-Eqy-0yl(5oCP5s0|9z1bF?cC&iPBA@c(UG9ktV^sf!+Tk+8gE9# z-#IJQ!WeCa)EsvbIrq@Qz;(G=-EW&o901#PZe=>u{Y3|z?mzLv@l|8SlR?BiB$v(6 z^HsX?C;oUPXH_!c`LW!8Bk^z8j^9?Vs?i1IY}C?GUT6Q;()M}WXa@bF?#Ag?%MSx; zZ3a%@Ek5bELRTUSn~ihmfbBz@E$H`#34F9UE^ZzI91^^HlyO4;p(rHuw|H7 z+No-V?J-{oI8`cyItKwqFLgi0!;~wOrfz3X_k2q-_l>>=V);UHzx+BW=qkr3AT|4D z^FYadxtH?!K^Du4Gg~378^l84iHs-o6&Tc?8Ok1`!W_E~@op;MeIM)AJo3(poOPy-ijFmU6L7~Fc>~E5g7xKbvz@Z2K>(ul7uhmVFf>0fS z@FZ>wTgzk^sjg2q*s;n~94|czd0O3P2B3YVh|may-PHui%l{%|MP+sb-rn5i;js7X zwfYv&+`>$;k#O8O-L|{=OK9XETo@lu11hrsLjOee!cBg=nE#Q;fu7%_>di1dX#e+b zQilQ`PD&t_>&%6LcH0FAPUWP!9;?Jj5kL_M?K^)!_&eR9aIbP>-QX`biA@yWl{tPY zmn;Cs+&Ywdy>kPs5J9MqK$EoMsTGcYcO_s{LT7VdDy$}Rsx%Z+VcP_dAgz7Gsqz+Xc@az{Z#? zLX%>?GO1WW-+a@hv`aUhtaj>&7&73qtg24ei|%fjiiH|CENf^F^WEp)?zGwNY|7kTHE)kw_!ovKl7n2VFiVyoM7OB%f4@0Oy(=951)T<-Oz82X| zuadTS);6izEAk99ROrhM{uQM3+)#q$Z{i;I|`Sph| zJ4?8@37$U@jxg(*X*%P*%pY6$EHE21E3J9{_q86Ajd{jzyD@gQ`}Z#VZQlsi1G*)t z9W9gT`Jzh4QbUx80w4uc{`V^X1Jyg=c98zS`00;^Ay&?=aL z#*C|;KN9q-L7u5SUqP`!9TKW-bFA;dGn9ov9pKk(aX0@lp*AI(t5*5SvPbbzd93yS zRa^j{V8;IhGfgJEh4bz$zs()+c`{*{jCykOLUZiNp4u~ZX`4TsK3o%py$F@W15Uv` zhNo>7K7F;c^CRU-kr&lJ-5WL|pft7quP0iF4}bPW*h$~rldC7hna&|p*|8+A)nae| zXG*Y=g@RHae?fVaf znv|ICwmtHQ&hbTjg0lJ|7=X|bay)uuiZXPp%|9gwpEbsKZS9XHCSC(j%_#@D~ zOmj12<@wpICu+Af$~!fQA67ZzctHq$s%zxu+S65Ob60x-NlUvk)T07^&>zw!&20L5$llVAmwCqj1 zLtYJ8VCVGTTG_|q;pm4A@`gEgIfBIlJI@~XF=b`2HIL4uu3euhuvnht?W3p#IkDnh zL$~Necs$vyOhJ`ZlT@GEy)CFMSM*P~Z?GLcmr4i?iQ8}8yv)ShEqJBz8_K>)8$5ee$IQ%u& zMegAGqHbCQZn`+fE$+1ZwvbLHS-Mk)-MWGHDpmRD3gT^Kr4Xkp+Py$A6Bci@IE*h# zBLK~6)YJ3H3!K1k4322IoB6&*sdlZ93AUA+^f z;I9~p@Giy@(;?clu`TS!rVP&%09@-pJF63c5;7@OnpN(sGnnK_uJnmFE%nONW4+!7 z^!}|y`QiL)uxxCKVh)%bq?ou;ueDcTLD)@ zM|+%;Kii@57ObTy&^Nwmu`(t49n+?@&i0V5;@^(JV$+|Y?IGUx5B4|p`aYUG%(3G& z$w&k94?dYn|IN0;^D6OBU?KLIrN}{a(CfP1+K)^63VrI^cFSbz++qU24+l?a4^-|& zi3xZb{Wt9S|HGpHH@wWTK#PyJw3$^wDt4KA8HN!zEry=`yMNWSnC?z0JHvLVu>f=Y ziOV9Y%_RB*E=9;ddtZo?(S-D%g#1%L$=w|=ohSa{E;8H0Ek9RAsT_7$KW`{hK=?JlJ6_FAwO(|;GHR;5dhCtncOsE4+l?qg{+1f00J^}#M*kD3E# zZ)=D5ypJ}Lv+LQ_ZQ=v~xV70bI%qgp%*#-}Ntg+46gO`vl-+^ZP5QoYnY1Z#ZMKLx zTar%Go-MtHI9Tu@#&j=9S1H0vzVZ51@QiSVx)PEMI(}lq)_Po`Iaj@De;9gpE@X-}@S7dKVIwb9A zPxJ7gU*%_J>kC}ZuCAEd**P#>dk-@ksJo&pW*rDqEeIiLcBrH|D|G41@* zd?DIWvNqnWV@~~#37f`odtcDMn~z`fRkT{ZZtl}b$kG}ME*NEZU6kwQjH_p~3Ga1? z{WEIV_4s1Xu(8kW0?+2DqF~@%Ej_vBE6*uqJUPFXax`?&%PP}4*XOLGD$>$wv>~8t zS2`lPUS0}(c1k7TNC0HYb{~{~hzZ`&Gyi69@-igmsU$F|$=!X*-K*tiko~rW5?}%Q>tvLPZEK%eu zRgdHq3J)WIo|jf{Uu)EEFz7Yl84Ir3zH~)%GZXaf0>k^ z!)gwBFmkZ&PJvg({O&}{sZ<_Qg}@U&$WN(lylc@yeyI)t_t#|fLW;f9nA|^E8aw_a zMMU}6z$v9&MN~VOm_oXEopq`eULC9LAiq&%jK)<3ib$v0M4{`#!VHCS6A3X)#@;*? z=jVSdI#_gMi!ic^+fUb3RsX;VpW=FMu2nt!b#@CMKY{14eGMM~FvK!lyY*6gYJz{d zFQjhzccGOzklG`25>Ih(PpXuV%;c|dz2pd}2+BR`#A(=AJSXSgT5tzeW z8c!KNYFk&wt!QTO1W)?|enQqxb$Y-7^cWQTstxKv45D82BHI<>_ZTZLIj>(3#l5di zzRuaL69&S=;G7p5x?gdZ;aS~VkFmwFUpr)J6wKBNnLTE#`o}27Xci(zI*4`Z2=CQk zw#%+)IH%W}DRdt#UYGqcloYzzPFQQ_X1g?`HPio6V$~&}yB0(NnGbXKU;RvhuOw}e zjg+H3+?U0bkrLEGcPKVf9x5#DsYKlRh^}Tt+not~^XRxGYt+>(@cu(mw}HY7AP%h` zi>bfw_^Zb1VmOF4w<=cP23q=_7$6ZhUZ@lNxea@c!p{A1uJ)$tlZeu85A$W2Y+tHt6f)&boC9 zegSEJpERBI_~6HNQ9CML5;|i+y-CNL2dAyd*m<+TDupqH-Qj#t)Yf;`I{_hT9I&bi ztFm9To*3810#P!;X9vGH>b%r|nY)js&f4g%>fjlgD(KKN1QpVp6T{QHCKWWJv-*%T zlz1F}#kau)dtzPhs7!q~cF|K^;j!*6->O`GVW{VrkfOj2j`_SU*;9r&3^dzvPE3)w zv43uI`kQ{`n4`-uhJh{R4st{@3T;5;_0^~yhWHFhb6Ae(R4&&Wu6k8}M z;tAna8BWeMVSnk%t*wF+|9(L%IoW-DeVprqqfvq+jS_`d%jzSznvP}=OS2|@1+#&z zkJgf7qLQiq`1etB6iHJ|=M0a{))VPA_%2th1Wv=x$ym&#>iMqB5`Z5OL2&60QSv*q z-e(;d|I6TNzVh*amRZFAZ9$D-B{s23LgS;{{xhGp26WHQd7y9CaxThTF9{w*GpsfpTom zd|sHJ5Mp*@vV(t`_krFSpFE4100)I*M>orq9_K77RA}as{Kq*Vze9wKjO3}s#misk zh42{#lxrJq`zDdX_uI8;TP^lgUF@cy^Bw-uC*16pV79png5bIEwj|h#M^75W*XQY*Q#nAQmEb@@sx*EjImlt5I*N*8#`yDNz2e%QhV>2+3!5I zA4rv!(cJQe>1=vmL3&0<^@uPn;tlIP1&RKu?N{Vo`g=7DXQRhl=S}w94&V>=_CtSL zBFhnCj{8%a?_i@3LBrU2oyW_SV3@8bXwk z!2bmBv&dcuE;7B#T$dZ(gxkSLd#)jKLqEfb@llYPXSwQ=Oaieun44T)p&iXPDL+aY z(v=d|%=q~k{|Kjxn{mC(2x4^_z2q5%7R-Kl|TQ&6x8@ zNtnXu1yZ5>-QJ33#V?~!!3ocZ`e1s^$Z1mcnN3=L!)tkzR4HdagXS&o=diCY(&iM= zB2&<=m`Bg)0oLy0aX-iG_uVytStoKKd@+~5;w!>s z1ESs?GW2P!PFyWG*^GQzHO?MuBHu|l)wZ-PYE9mwR!8p(J>5Q=cM~6S zOHbF?@%`7GX8bI{u2lnYp&uPL_v(~^!!DCu*3+*qs)n8H_<_kziG~kwmsU^EpKh3S z$2QpVbRW!=9d&in@)E9F2(vyRF9Y%8sc$p$tgxwj+d7LBoP4P!EvS&B% zzKrE61&&pP__s;B_Si#`Dfhm+a!UedihqLZQ5c&y{1LeQDR-=+()Ud#57>4L6kuhQ zSJ$G685Z)I`ogbPbmsXZn@i^b>VCmMJlm&cNu@j0AMxf|sVx4VN+~r{odB_~Q~w@` zwU-Qi3XX7{@?$x3OC_Z31^C7UsX4Tf>U1$PMVgbJ7rx;?#HGvUO1FJG)?L%W{+PSz z05OfW+r4D}?i01;Uus}a^5RuvJJY`;$v zig#Ozb5SIS3RSj#lkPy8yp_XUiMrH)7pY z_X#o-bOi2iaR&UCApaB5)AP%E%A@TCra7fQzK_@?kK5HLfcBx#gmE;*QBTAE#G8N! zf0daV=ga1k`Ul5ynO`zq)^yz4DtJ0=B_(3H=PPrZ!tpeJd0dg&QJ|$&Jz9EcYn{X| zt&*=Juzs=4J@6npCF8h^wM)3Dx#|r%TTq_FY0i*&bJi$n!zul^tS$6N=|+SCX_Ifl zW)zNpvumN`P^I;O68(27Zy^{%Kez+_RrV@RHl@Ff{@~8pzE9%Tv~-SUUAJdb?5x24 zA}=iDY)KGS$VHH6-^9Q8;cgh!nCotr)4*<+e_w7vgBADE*DVLFK`_w&$-$oj+X{RF ze;2TFL|i}uSBOZ{X2u{&<{wN6?~C?kPPh079tYlgGsSNmb0jVzANK|2{$#6;Lw-l1 zy3`uN$*IMv6O^ah9z{w1TRV2O%hoLT=HX>) z`E*9d@rVPnq)(5^Gp8^#H-x~S4x!5ARo4f(M#~oB1*?Sw!FxJqR8w=}eb(=gYo$9i% z4N9A-(s>Ia8LtXi3z>iKWGR04H{6Gb@=MbE`!H)%OA^q3!o6Q{*8*wvPM>UXBIvk| zVmg@X<&l4#LZZ`am(@M%%!EikTe%P(^ItNN7uc7_Pp0~2cw1Ui_lkU18p!A4S%MJO zel@-!3{7%X&s@L@VE|2VjBS*#|h{E#gJfDSiiK1Od|pB+Q1Tx zf|!?6AQ!7ujVZci)azH8G2+%B>&Wq0L5e6^m=k#iP62ct!4!GJBWYV9iyyLQ zmi6In;a;;E{s8gR<#S^ew7vTc7^6_s^iLru^`C_Rmlmd2g(apwe8KTNm|L43qI*}k5i!HJ~I7!*_z_%Nz1ko(9OG57Yo zHW(h}Bo@-A(3zoiboO9P-T8SITV`3Y5N+|07aiG0eQgpr9lBAa_A|%n%p~TZ3Zd-c zyg;lzC)uCQo~;4;y98|w99_YGB)y0c(^o%`oVgk|)4KTkwq%ShE4Gu_yhN-O#b%PQSe$}OZaxQ>F0XLq&L(PstmWx_e_|CeD|b2W={&HL-kIw zTO?a$Ji^hReEOsFB~dAxzEtmh&eVY!LTp#_!gfYxWj;9NME36|t%BN;4U1SQBxx7& z^|BBk;eBP-pXlR4Wb{Rkb)XF0$-v}XpA(c0FL@qlgb!f}3Z@{>>G_o@T2A$~=04iL zj23m;H&why_=VyG3KT@UKE|K=X6T8=i|v_TReh{tH{t#}0o66IULxu0MJ=n~OeeH< zrjTOXRjEU)xx3##_fD|<${s=}`D{-rL%m}U2MRyw{_964^J05Muhh5( zvPIMEx3~uQ11(2VB=tl4IuVHziFyZ%kZG7m0P?k)C`-;*G&jNzulp2+tDhNwsp<)4x-ufbb!qtI26>x zkAV)=z}AqrOP63m#@XjdDFDoC-$K?XF#cg)8!|mC4Bag+e!LmR zXr3d_Akwf?PfM+ZJ}RWFZ@OJN9s<3g@HaEiD?{8H5@voJwW>Vl4ZAF5TfwsC&<$5EuKX#jR zm2;;Zo5!82ZW#Sa(0ru|^B=@^U8GiXYmOrlO^E?B8oREtLI^a>Y5&K{Sb9;YE9#)Uay?tRuljuvnZMXELL( zuX0u2nf~*wKUUA!_F_o+3FHeRr~hyegrr2ZeHSe~V(@W4qI}Bc1Ikx#3ofd#O>ILNvet$WweOGe8D~3=1RVV(p6V8(H>to(z%>@rG%phB8PO|9*<{YYUlSk;j8#0e?Xhj zosE(J$KuposFKGBQ%n$vgdNJ|J14T{!9^w%khf<#wP%a6x+8_Ps|{33pSBu8Y@8vX zJ(IV9qbDRTgkX_?EJW?9&2|3Hx-mYV6f_*BaNPb)kzAe&dox@uV2nuXTqQD@&iIxC zP#Yw&Is9GG7-b@#3E^uc(L2XNv2xf>#5b0(Ukb7{E{0ipu4a+u`m96R?YgC&Kt68Q+Ltc8Z0S>*tZ?j%{7}LV@^>%ERN- zEp$0=r<6t8U&phI7JO*T+W>Tj>{`t$aAt;ShAEDPsz`DlD8aMyE*n`>8fDdQcUaBB zi1c5)kNn<|M$XBK;XeFcd6Caraa1+Rg!hTN{9D1;Qck68DE)7WJj{3LO!`#cE9|7S zFFIkqsudpJlIZKdM!F>?TZ)^!>DHilSbq_{Jae;ZQTm0S=>o@r&GuC|Tu$wafWGoz;NVKN7?Z-A_T24wy38j{9B7Con(tK4UiMoAtmhLuaztq;DROk6(~`m&>-# zfanwy?v{?e7bH@HIZXraCiAq-bVB!|7k)Cu4ETm|gC1c#o50PU#ribFoe>u07DlHI zDGZp{jHcf3)TZ6Q&upkOO`QkaYpb+=n6@Yv3wMUf=Ceki&fiDUsWw5u{m8e;JA0)s z&=MLu!hi*>WP7wpV6p5#?1@l4IyCwr62BD1%MO#FG3G1M%TN1?=OZnpEsSw@$WFE zxdS}z#E;b}&0w-*11Ee3^7m0SN{W_RjWt)M(`S+0NjdfD3~lTw1@=L#%lY*aa)9^l z#$1@M9$c8xZ$qm>p!mJrW!RUm~2(IHZI zV{zVJ_g5_ZVvqgv=em;du)fTTHVLJ{{fV zU0ARV7}||kIxTcOd69F$vnvpdmluXMQTXdmvB%3D?Lj^)bEZ_=H6Koz)q7?djvM6! zf4W+hBNQMCG;s=$@zxs-k;!_W{&;M1^;s1ig8QV}}60X{K}rRP9C2 znFN60VoPbmAtb5x1I*d@%QidTKC2+sJ@(X@$v+S*rv*YI1Kz0W{|GFg5H7hr@~jIZ zpb11iM2^%uOX;PX%u&Hx6iP6_T(xW29goTmXbh4{MRyL`Z#oJHF8EU-K91P#1 zWTbd0I84H?4FxVr-~X6bQCYFOzScgOD+-AX6dUT^}aZRARj8Aug=Zp%t>zbxf;6!7q_T@Lp?nSlIPoZ}&)q>PM z;1bcU-%l+zSo=_iW{+?w5PH;+ysmTIGlPIMtYje~UAswR4Vh7Gw|}u>^;a&9^pD3pP`@i%(|$71V`Sg0=9nw3T~3P6G&U3cJh#})eW`u2vj5&vrRV9B^o6ZiLn&yH$R8G zyte!dQx3@jwV(q_4|Y~lnbm{)u-#jN2vt5ToZBrI|h2^A6eMio6zI7WWbIfs` z_ug<8MOc>L`HtnZeJO$$g2KN?S>xfHZ!6BpKayfKXUUt>wpYHF`RqU8aES#`uE(kHM0^M1-0fua4u5&#+zERfk z?3{slM)xU5VR~BEc0u=(p~BI7+-lim`XZ5{DXXtgfxy$F)YQBbvPp5u6X!}fH|y`k zc`5`Hu7W2xzSfj0bRQPuLsK(7+2;yhsUiyG*IQ(jVXwJZ!xOecqtW>AoF_Jxv)H}3 zFf}V~pzK>`a$uDNxe-)Q`2@DA@Ga-$$rrJ6+oMd!i25i}PKGN7&M?{PTu4&to)ry{ z7Q+rm>mX;Vwp&mu9+VeOxR7j1do6FmDn*(8F`XEvk3m$I64_?c^YLse48h(-&t%%M zjcl@d3w^7EZ<7zsEuM%oy=8?D*y|Vp;_|UFRCZ;t*(#C|;uCEs8Vmk}D&6OL|1?Iy zzmv;;iqKTY@stU|IUWYgBvXmX7aw3AAC>)rpZm;=c#Jj&ITOCPIOS}CXo5R*H)F4= zj1gYF?dU#2uTdG)bulA8{GX?dvzu}O?s-L-miJcfvEXis)?J)sd>IvMpIV#rkE?Zn z2>iuADEnF6HqXy|GZmgJy;4T1f`N*lY{RFPc}U{fT=eSprzmhZp6aRFD9sr9_H!tZ zPwxEdiq+XrU#-@o>13rQ4<}o9(erN?D&UEPz6iD93=p#o*7(pjdpb*&v+)WujtfcY{N&dEO#j8MmAg zC&X>sE3c_jZjl`Q_<|pi|LGC>t_{gMg~4T@nTG3A$T)_#MDTi1)a^p2%U~dIC$YOo z3{DYsWL^oI_M0z2iOQArAz|a<{u$JC7v^lWnT@k?g zkfHF2^%1}9b7_Hc1%iSS2Qs*x^EY&9%aiT93nJ^r;@e8#?5M3ILNI4g+R~cckjZ(g zthTrz{#mn~R^^Y~jCvRN3AIBRIH+UOFn@cAF3{GRU0|=s`*Qy6@XVD}Cb_F%$ph4z zgxlygFMA-DUx)V;B4iZEQGbEde5M*P-ZEjQx3~0GuA$-JT*APG@Q&MtH!pk5K*qYt zxY9Q>=y2{%sSBJQ4&d7@XWVx{uX3Jo|4jWUatok7rrAgbRK51gpKhQ+wRvY2cd0o_ zVZ|qROlo%GXHcu_@~L(L zZW6%L;20j;l=NE+*J_%e*X&LwSMp(5uP+ZzD@RbwTn(Y}K4lKsp`K_7eIACDdW$Z& z-+?;%E3E8J{!?OWrU!_{-5vMI<&spky~~w>OcI@SgdRa-@Vl{j6%Kg>yJp)|WB<5U z`)TgiZutj0W9UV_rC{ zS@AjW+p$H#79rCCkSNbL;3JQ60$dv|S9TdMNEo8+9dqL+A0t8eIT1@cWw3Y4g@M34 z;Kk65j5yr0NAS?(o>sEsb&*z%?I9dEW5%v&%w)MxBVoi_)X-lhV9bxK+1(S-Wr7&8 zv@g@!o)X)KfWi8)iFEp#1? zjg}Y8In6`lZHymy+h$T^?EHu^JP95HHqr#2qcW1R>%_RDp51}3lP0fdV@w;CY?rT^ zew~a3pT|*6Qz%p|W}_IBXO4g8;|;ivXtQkNT3Y97>O^OqbZxRcIU|Q}f)W+mb(g-} z#r%jkBoR8vOP~qd_4Y!$QdZ)wV{a@*uX40n9(N=rt(=?h(ddEuP1v<}lLKj8W9M%Y@E~#p<@R2H5fP2)tQ)X-K)nsXQZJLFY`CcAcGP5TB1A5Y$C#@y@j~SAJ3bhPHS?Z{ z`FC2LE-y|V3MS(H^$58#ngqd`Yw%TgMpW%X|ECr9yZb2<0c}nt1nPq)`LH$nj)=dB zbrDr;+_r(Ye5!i%gg4F*GmUH$0966>k;oyPHiF#?VlztSU`gvYDR*983M!a7 z7#VrcRD0h)MM&Rdho&A)GbadDM;%gSlsuNVpQ8oq!FpZ=4BJ@#IiJ}>Q?}8K{dxxy zAGq#o!;5*Ni@hthKNQRspEQd93YKVNf=tR@>0Hw8yf=k~l~~0UZZp_`-D-Ou$c|GV z{qR|FvJ{Oo6$7`LEyoMtNRAM>JSRbGLgLHlC=-d%tna04$NOrIh}br6FX5HmR`X$I z?=-p^H$8-3#bh0CuA2|B-(UjM<(n*T=xEk!z9%c6Zf5l5p}{}7j6k?Ep*5H`IhpL}i@mtWwWsAcKuXq(clWPTX za*IwtCp*1&xiJ!4-DtSy%gT_?IC{J0^AzMtYH3^%xvYDnRPyTA%PDpH{xZe^_BMVS z?%2SGOv{s)Zb&0V(D1h0x6c-Zv$&t~DM!pm{!Vj6+p%9u@2M@}%Gs1jjdw|S@^ZH9}jStN@XB+yR^!Drv zM^>Uk+aOy$l0o!Ab%}Wa;=8HXGEMqz2jr}ZKmoe{OP$(thlHn+dB?k7sjXsjO8gx% z$hH~=q*(-zYrR|>rcUwl8i;F}Z94P<{DJ6W%$-CzVUVE1n4=6iIjZgT!G{e2?pn02 z7|7}S_B>4;9q4NSiG$z(ge37|XNrHpdjIn1W}v<;NqHKqb0i?GdqHOs z0Ziv)_QI&_AK(w8$)(|aI6f}#pkL=l&&Q>lM}sifke1&Vto-|Hy9K-loJ`EITV zI)@}TF<5HVVt-HApZiFa#Z_dL?#Pzzs2OqkNK`@9=C0YxSV~~ubn-ViN&=fQk3&;t zIGnZ`Jqnp>qN16uc=utk)pBVS12M?V;g{ITy8 zg{aLwirWqST6tPfo3SIweyVOWIY)Si^pMzI@7Hv!l6k^i=bp&744+7BavOeJuWQZO zRO7D`3iD4Y-aD@k2Mgs>IinSnL^~laoSU=ORrE1`*e$|GqmyR)1C{rt*X_xDHbwWH z7q7^mW5p0;sr=aHG-@Quxc0#jnLbT2bj=IIoCURva(`t$OAGr)h0T6g$6A6DYfQlS z{yH--K_~8R$<8QhWZqt!R7HW!XN8{JY)f{r+)4S7q#>ue0opYRJY21MZJW75_oy)I zk$9|7#w0@>xJI<-=!{lx(Qq7aMSYIRXkFg!gw&yy0@(p555ANmR10r!ebQ(5>3UVl zg&Im|&dC961Hn8aPABt4ZF)`2yu=RI>~g4+L!e1+SaTij6Glizo$X=TY!qbk=L{;q zLMdnLO*2sOG!tiU-_Rr7Oc$25){c$Vl$G--y@;I~4hf}1N|Ulnda?NNnFYsCNLl=; z*{@>hGk=@@fii2&q@eO0_x70IHEvFKkhsOZ;>o$TPdzVIw9Z{1T9>}4wqhPU?c`5( zDG^x4t||C88dDzYyHRJ)c@We&o4_wWGvG{-aBkneqjAQ559zz{bnFp((2AdqMD>Ob zd^S&0$>+j(1Lutv@2_KB4F3BmYgkfi8hz2zmYuPK)NTX$@8W8^l-hb`C;&H(entD5 zd!H$Q>7fduxu+1xmJ!Ul3*ElRmz_q4p1Ays-pc%n-W;dnKRryQFNUbhnk`IxHrZtE zn5|It6o%HlON$Y7S$6@+fpqLs7cHlaD$!icO&6E@kf+E?4YSrh9zxu+w3G5qg;dHk58O@MCEUbNjd$(qm!y(oeAsbPG= zyA$5jBZd!_Xn)w_1a$-sC6<$5@~SPA&|sfaq5Uk`fLO1oQSeHSZ-a(R`=afqS@`Qf z7LBP~o(}}gkA&_4*+>R~H81bG7$>i_mf5V0?9sb8&(9xE#S?T{mbP>CGQmSKZL)hy zh}yKXL;m-Kc&>8=mGdX^^JwttY>Ycp2Z9?^FM`Ug|6E&yv5H+T9!R}~gSrm5GLueV z!4R2OuX2EWyMkPx65iwmP0+!fyAIj?#N-TBVl9LIz0<}bvr=kY#cya4N+JO-aMyZY z`8p)>cGm_9&4sHQhr>TyfydzYuH_12H!>g1Py##>sJ+b?FIfQDc-nDz!>+po&h-HY zfEOqC1{BvBL*T-NJnja%j^2`bE{umT*?`nf(rV=Th4f9s+0=?HY+^eSB-fj<_aWX9 zE-fBh?VefIJC&xF*V#Agqa3I!71Xevhu5Nqo_(*Q$b&xg9RH+WYimO5{>IgQ(-kfl z2=2(723qaK(-`}_Mj~(PT=`O*)!IIB&`%|My}fwUFOipf-eY+mwb0HWWdA7;q8lUD zrpH|^yfxSlICBc&F2CcC0(5{SJuIBz3 zhO-HFqN@27&|regzv9E!IX1=&M30wkfB9t9(e>diZFJ+}y4s^tJ-ob7BnOEkPqu2Hc+^!EWoq-RinpMzulH z(OU;5;C6&5hl8V7;@6g5ZRj&O_ccb8OMun+{&p&_&w`D5%igxndg^r~uNI`tBo#ir zG_jOylm$7zW6tew{TLl~rmai#;Z_G8{WV}o-_vh~?`I5U_~c!dC394E)EBAF1HXQ_ zewG5X;D(hN<_D>ZK)L@}04+sm=9;$|E6QfdbN->}T}j2wpYf#pw0M`E{t>DD?WivP zfOP5qcNf(yQM_-8UA3<;t8r5Cehh7=Gjnw^K$&VtfLmOT!nev0juwXff-eV0(<~MN z=)dP=MqZmkw*m0K)BBLE1AVWF&6j%Mw&QC@2{Tth4g~v;kw_(mwK^K$4*d&(i|e(H zl}#1(Bh`e*gbOZ54Cz<+_PqI1_$ilTNldLN16!7$fMYLTz{0A{4pzeIib9xhvhwF% zkQ^Ju55ii1INXF3=Hb!8Z%nbErvIH- z)Z`U<>zaif9>@Ox(9|yUl1^rm=79@=it+noO@A2Ty-S81g;* zU&ma8;4NMo!0(9!BI@as7MMYV99*hWKqoqgIuIgp`+p`q94$VSjF8k`N!b$+5|fiH zNmGG1HJP%+YfV{#T!>OQk)?#(-PIE5ODS6Uqg4@FVB3E(y9S~Lgcg`fmV3W(AVcuS z4?K4I0kup(J4XL8s3D1z#-{+Ft9IXA4zZn$!Fhyc34Q&jHnw{rHu(Yl@5 zw>`r09$TRPju#YIys0fMI#Rj!m1XTsc=*J#AGdz8{B*v@=T{qdkCE~7*dHV7CfE61 zwnc|N@ok5+3Lo24-`XNxZ$LA4%|N5nj&*TPUJD32kcBm&TVZW!zd=!mc;*u&jv41t>s?_5$Mz!*y|mXkCMBaRC)||s_U&@Fn8r!&os=vhSAF8rE(CSD z+STkEPsK!*E>nk=E{};X4@Z{BwzXRAI9Wzud>E*w>x8)wppiKF7TO&;4S+tUA<*|~ zI;D^3`57pY6ZLJJzt_NlS|Q-TLD~ZVDZY3~N)&4z%S4J-;HdX4T6@2yBz(}&{{l`Y zOO0myeb9hW^~c4-H~+!0nah`dZf1gt+t~0!{zPK-{nD{3ov<{h{hh;jg^p;2$G$5s z`~n(U=ZD}mWW6D7kedptTXL3NV zAUXz1r21f6K~9%CP#GwO?{ec79b-`!XaPn7lZGAvSBS8i25hT>(1Ak?uVXv1gk6Y;Et-p6eSQZmxG<&P^{>Dpy$h`9OJcM)e(?^{NOc z>FxOE{rIO6|9n8zi0h>)HdobbuBzHxRkyjfsee_(xhm#d6?LwPTdv}kbEBxZ@>X|7k5+JVwlsEj-7=SY*Z?E0H<$X>?&kIN-Q5|Zq^tQ6mp8w!P#P}Z)W~pq zv!^SGRCXCTrOlkGvRp#nkR6i}Ti?7Mqmmq`4gwT^Y&`mQ?p(Z-#s#(eTK?S7-8H82 zqNY?cr{03n#fJ5HtNv8H*7By@Q3z>AfdZ#=06PhN*+s<2CK%ktu8-k{nGD~Dz_yoYHCtiyUb9$W@UKu z0%m8E>D$^>zoxOh15e7X(!#v2tyIjdtLhSp!nATOoeAggNq3lz&hBgqw%k@L@Hnh? z6H)2a)!m7bkz;tO?8&Pu-tII~=eNcwS5;cHgeol?6U(N{3_5~nqj5J1w|tk&vundc zcD-C{b9M}kuZ|6!Ygl2KV6%)LS2wq2&K>nk&303I`7Si`Nw!^9`jprneVXmLg|>)| zvS(Po9{g8B_tvxghKP93r&IbIosk7Dg03w(Kv#-;9PT!{^NU;QXa62?6MptTDDY7~ zr;j&1q<7rU>7O7M0tirg;G0V6!JWdiKi@RIZzKF!@rUj%-()&F3VZ=BDxJsa`12SJ z_|F%A=<7uK4dUMf|5xb_P{8SL68|3Y z@0avoZ6EBjOJo zV5rX`@vjm8wn?0RT>QHy@wne2>2DSPKJniz>F*K$z2aXqnfpIHna4%dWF8kiaDSV^ z!TlW?A8_dcdlc?w8pm*dm+A@k^VIKCc>LfEQO%zzJbr=#*8u-Il^5>sQ9FsdGRXPI zgPi|HxHMV!26-M8P33u2A#Qvshu<=l!zZS4xKo0kJ&oN(;?_;$@S6ng6L-J3N5u_J z=XB2J9l;k} z!ReMMLa)i9eLzAiwiha=O8*sQ(|NVIuCzt2lhyRUE$eD$Z{=+&`ms z75quTUl6w@%_H4ZWA=T%4POt8Ij^8W(`>yBkgV%F9$HcvGJ(sKGeKIfM?xT9W zkIRvGAICfKKF$XZYk-U39-#3eaQ|$^9}?KPf#I?n7;X@Fhrq`KJ}+>^jSR26QQAk~ z#EslvcvDCJ?75NqYoEaTZ{%@!0B$3-BiuOEQ`|%19){aW<59vNgZmKmGu##$C*pq+ z?mAjG;jX3rhuci^Nbu*8KkZu=B!4J}^sS-w@+QtFa1*CjbQ6a!5f^$wk#6`VPPgnP zPItE8BjQ$wJ0ETnt)BuflK3?eKPK^43VyY?E#kIG__&1YmUx>4-y`^5@!ulhwhF#a z@cn|{A^4qw-y`l`3Aaze?U!%|1b9ToV@9A1y=Zsu@%#XWR0rvuuFccReq=^PUl7&Lb8n@np2 zjQ2k;otG8~jC+Ci>r7h&+yM7ge!B*YONI9czuN+atwrw(OiKj3Q}BBQ-X}0f=iXoE z_iOLNqQAXX_!+wSof6?!3*08~MuE2o+%NEMGyQI+O#=Q|b-FUhVHE-&ogEn-x3qQO z`um>hfIW9}8n4L7Fa zfYqo)5iW~ek9(~0#UrL3|8%&!t@>`Evm507QQrwheOFuQmCbcry6ts(E3NcOYT1tC zB=OhjbvSOSaqOmu(N# z$+ev&8mK`jviC@OAzg!N(sNNFIlN(D?I1utTJHnZs_`sRtbAEv?fiEIgw4L5GSN zr^wlsG~>~l_*QYxwh_fA8tzZ_K;NyF`w<%_bd@P=m^+YcxLWIzdVXl-RM?%_rg*rE zwA@TjT5jQXr8|dtQ^9LGhvLr>UTl=ZrE`bE->7uH^}EVObL*`O7B8>pGJQ+yOY%4g zALLYzQ~Mt`b$?pzGYxU9^l4taW$RL5eNKsQQ@USOHgqJPT}lp`zdtKHyK(C8N=wGR z-;fQ0tn&`hLp4F<;9%eG54D~snKZD|aKyvB)+^-~6CR1M@QxgnU&7%zt!3q5r@OC= z>fM(Z-cMs{O9hvwMCq>Gu4LoDM~yMISH>806Esn{rs8ya6^IUkpdqUzayf5BDO`c{ znW?AXZwLoFlkjtbY5mM}G<_>rJfQr|cKvho=RD36whGV9OUvM)T#&m{b}L2Kb4c`1 z{J?Qu$M@ld(A&FtMA^?HI27BKgiS^relPSo#du8mMdq>?ddgyM{R*#Qu0Sub=-b)| zSsIxmx*c>_I(EFZ`V#o3Fn_YNK@a)Qo}SqTpOo+@v(`1H(xf__6WyziP?)ojU5SfK z1hp0Cq0_&Ac>l$Vufq@gu?C@D-taRcsMlAdULQ2(Z8dDp$@kw}yjU27{K~Y<@u<_LGu2z? zzBrH!Cw3`WP3Su)qq%?AT zotOvtP(yw&Ol1#G&D|ETVd?)IjY(>cAhkhZg67jFOWslK5=%f=oam9^I!utcP$;@& z%(VW2(lYg~!ta~pH_`GtLVnOk<4?8Kis2-5$i#OkyJ=-{wy6jifWMOUtw%OM*rvb_ z@8;lnJ5QVYSs&Z<1+Pn-es%PN{`*s2w&C3CW!o76`9Ut_xQFFZjtD$Gk`4&F!h|F}XSLyEZX9=U_s{IlskR?n&!UQDDPm%6@pyQ%XJzen}CR5=1hIRaKWf?4tiN*=*qV0-+m^(e|x#v&>B2 zqO(n9Lb%Kv?kMCqjIVC*?tCnKU8G55hq9IFj#NGrOS!Smj}#Y^s5 zYT~<_*LN8(%Ir9qjexQ>#azi!8I~-#7X;)5E9w?4UB0+(70P9mWpPucA9ZzxABKlh z?Phl!_n&#!)N)KZDJ9<^e%rHz+wP>_Lk?PTalb#3C9WL~?iumZ?2U+f!Q(C;-r9=1 zZa>9yY5C!Ak>!UxR$G3^BX0O1E%i=k;yr0xgOh#>Kw0(~xSdAX!2@u@z~RG;SMf`b zYXUx!BX%~qqX`y_a{CQ^auz6Y zT8_=5-t7R_8FDf)L+%Q4Q(KJqkef;vaD&5q&>CLrnD#95RNkiC1x;b+gaL2lY*O&2 z>0eW>0PnUH1`Klc}j#v^>ffR&t)g+FNFk6QTS7XFNd zKWE_smYi15lGBP<_(}_3W8v#8e2ayTTlhW;zr(`sweb5b{NXX=(9T-;^A^6ylH)42 zRi*SZ*WV z0jaUUVObBrkXyqV*d*{7Nmk?C=>=TSPcFw{$1k+tiH_F6s`>P18d+dXo^8R9+tP9- zQz`vr9{$$x8x0tE+g|D?E%+%5-fqFWEcg#Cc+i5sWx+`cK4-ysUS1z``AaRili4mg zmVh(g%j<;>zsw3>@8$JE}0lAt=L(XK-%ky8u$Gkk>b$BS! z++pA$N3-96JtF6#!(S#tM%U*?AJ1PM9&$3phFl5m_z?sCxR2*!Kwwxxsx$C^V9CAw z1>Opb@FPCv5!2!GEIE~#e&!X^;XmYOUa{2p@-vT^#y{&9JCXwbk(J)p2tHwi2cO=_ z0v^MFudw7qZq4KMM2BBaa9LqG{S5?14g5<4FEa3doX2)6HU2w!Y~Nbv_wRYU9%+0i zU+h^5ywH-Xc!1!;M)=PVeAEcPo8U7B{%;9BXXN*emEOgCUhj1HYiTT2OiYKrdjhX_ z8s9;1_r!F5uUK*yUv$ZBrtDt26ig;Ly(=sCGaY~{cvQ*ALC(3SH*rQ%3MaX z2Fny(P)(IJ6T2o^vluIoU1#+emNg|ikh!4dE~NFYHb%->!>Dw!yvJotD@!<4&={3d zZ8T+sC&yRBZ*mBaI|g{l zH+ot2WRv(qR}ExLdc}W>D&$XAv#}+{~2+M{Ol@w z_7rcvz`cHsr(|moPyEk_i-$k7LrEUP-Qpe=7y5A!|7;$|haNt(KbX+a{w4X`?mhX^ zzWE&Ph`>Ynoc=k1q3;jrLG~E!jdvl{&l9-4p%W18G#~DCI@iOcK~gn=+iQ{FYs8I- zTL*Ur^%vaBsK4M|PW>hEnj{=#nte3b+ThM~luR@5-GYaHLNLNW4-opdX9D+cFWgzw zUlMMsl)F#hez>J{?}K{<^^>H#kK9>J{Jw?o2T;#uY9IKo<999i9~1vm@Neb!Ecl-l ze}91I1>UjXk25*^=L9(Yi1L@joQ#92Wm0;(tccIV=9>;NQu57?2L$q2M1ZV1MXeKsvL}N zD}|RePk3kv-y^W%p=}nP);!^%y^r620q+(3L4gMy=EXfKu+q)+S=O@y7{osBYfS40 zd;#uG)(;LCx&aVAEHKtn?=F511`M4+NMF_OE?&1)IDsn!jtU$z>v>nYo2EP@V$b!2{u*&v2Cyqd>Z^&5oO0Q zHVPgoPhZ04GjyX8Jl#uv;8{x9a!=`cU7_@Imx``e@TmC{Y=czWGyq?n1O7Q@`6&Eg zBLx0RKR5Hml?H^@Ezit1Lit7#KIlCDXTx5|31usm`Dy3DZcG&Xe|4ibiyasW=PPJP zcwi?Hwj7kL_!!FXn+98yR1aleYk{)2%w<%0_N4Xnr}^)o!n?!R5lA8ya zU-7YUV)PSQU-O{w+llQo9d{Fj|D@=AhTiavMjV81Q2fRHikH)C={LI)-Vi zz&4!1EmgSF%fX`;9vV&QunopW!qn%A*SP^U>Q^cKyq+-B^)4SxP#>R(3?!eIdi*+- z=?HlE;)&7Ujh??94U#dZ^$hDWP#q2`KB#niza?WU_>m69pZA6G z#4zk`v`}8JhFRw|^82LOu5r#6c3Lpb{BiVAVi@Dh58fPsVZQ}wMWG`~ez4=1@UT8j z@ET)}J?x0iy~uWDV-R!lz#{`{yn>I?LvsW3(5v`~VAt)%G(T!XY-9!aNIi<@as+lb zOda>MXB5INXJr`n)(v}~LCl|JYW@_dF|X}1!LH)A86Q>l`ItvEBTwpgS~m;Bk|)~% zO7o(^rfz`pycag53_A}+G3He-7CVNgB1-qTvY`sT;)*cOSJq#?D|s8n5Uo2C$^Z9V zwxd=kyL~^rc(HU3_}rXbNnh|4Wi#UYu$>uUI|e%4#a6l>rL<1L7AJ+PmOQlXd^4Yu z6sC^CM9}9>W|;gK*9-QsBNXmKqT?Ml0`_>AX9RairH{{()?)}-IB5Ti9H!^cn%@dK zVq@DU8Xu$c1zoT=fQKflPGf8kYZO0B^M@ShD(-BhGmE7sb^L!nU%re?z6$J-Cf@|M zAya=VR45P_bln2yUISM8#VmM@1$SHUGZy@c1^>PUf5n2oWx?OK;QzGX2^Jl8#u#+x z`4(Jb!M|p~>n->*7W}dWf5C#kWWisv;D4~-kum7Z0gDcNr3K$Au%@HkVc{=H^eSsq zYO&Jq+V@niT3n^Ils%e(5ZyClky)S(M(Nz)g$3`N;;G*5pv5K?gx3@EHE{8gy6U^D zm#bh_0dndZa=i^&ft1=JxBi|i`f*YP!lUCukJT@s>e_04G9TpAl}gjnbotXhnQz8s zZVI?>UVZh-Iz@rfxjwXEZFx)cMrv~%0!8K)Hnsntqs)@kW_{4lPS=x!*L1Y6O})Be z#jmIR!ESrj!-G3g=rZ0(0QP&2}_GH^`gjjg+pB<717-NfB?FK0nJX_{1g$+>LN)x!oi# z>~;9^*?uGb}-Z*Jbtnf2F=4X7HpYc1y-S6jeDSbn? zvfd#0hw|9JN?hzMGwGt;kjLe3%H#Z7^7w7IP2jk=cuOsvOZ5?Wlej(N_KK_CQs&7U zN|DGv!}I6;M)Grd49WHuvc=dQbG?N?1{r&Gp>yfCkb~~6Hs@PNu5k8S2*NEi!sU7k zL4_))!XUU}g1NX59{E_QRDWy%Bp0_BbDtyiDf^;un?!|n4I{wsGBb*`}rly8AY+L=Igh5*xXGFfvzzMTYI1#T%&PHeshJ=+W{I!NYNLbOgeo}rrt_FNPdt> zj|BM*R{19o?*RovHk_7@H*|Vwdcys%6|U$Xg@#?zv>uJRQBUHdBanUH>kK6KTK3RC zX}+1ppc6ynMN{z;qmVZ~`!w5+)89p+AHi$X|0{GBiqUy1MtOWn-r#lmNL$ky@~FIc zH=Pp{`|QE&<$-+uqaL1ru-T9|drp6vevi?whklgSX}Xe~zKwn~PfuTjtbBCa zh(ElY?c9ch-r?o+i{&j_zhl!pE-ZF-C95d^%5t|3fwH-29Rg3%Qh(1sQ`>F+@~P0Y zfn><5?f!C_Iz7BsUx9L=49@nXXHH_&%#Z3&c+$gVLHnGTI*>f!$(|N$7t%U#dXVt` z;C5~ws#kGj+em<59qt>VFXiuWmZkPX*#q$xss78MOQJAH?uV)T6QYVfH(D0?yX2Q5 z$A;P-r+PGO8~LM;{KfY{2keSm{Jz+CS7Wv;#$`br@(ufpmD`~g;pq|BSq9Fu6OVLt z8b}xEAl+=~_?`QQ-=VUfanu-4bd5Ztt#%OZFxBa(ldeC;=ph4!J@W%J4i7ll+wf^h z<4-BfP71R#N0?7jm_MK}zVLUGPUO2u)H^_9(i{A4vMtQ|Yy3gzJ^8L09~kd?T>ZW1 zV%RSWEBL|iPEssC@R1#p-vJN5y+jO}5co6p$**62pc7&JQo3jQl%A4d)z-9bfNl#K z*95T#3OziIVILi9UlB~OG#Kq{*#(6S(H|?@p!X?Tl;|U@39y+-@uvPawMR(kHUUb< z7kpLGUCvQm&)&_nzmy!J(6f~MIBdi#T_`+Xp*w^I=4t5pp|$UHn0~a^pQgR(G_BXC zZ>JyaHK*fK-L}I0!IzTy8*5gP(2{B-+@-?IF~XSsCE^cT#K9b4pbJU+p96h~^1%9& zEuZ7T>}ebl|F4^KF~)PTMCKys=m{hK*C=d1!Ts^HA83KWh9^h96#mZ81hEzWrCa~v z`<-E)OL@+H!~aSB)D(2v^ZpXXaqv4yPvkq4_ji&Qx8A|;B%>4#>$eWKhr;3g4{w9B zg?(Obf=d}Nx_Q*QENXE2E3-ndz$>hr_4Wao8&p=uyjM5)e z=+It_ajo|@O^5$I#5LzeA~r&Iu)?FX))!hd`4_N%OIW|`>!2Ub(Yl9dOvh!fM7a=W zh}wS(rL_gRgoK8?TlT+4#fEYmdVEB;lcuv{?n1xM>hkp7`8yPTw~PLHpQ3-#`j@i9 z3tHqM(8~=SVyp}3$r)d!aM1BH>y|Vp|v1;qZN`F-!?JIq-9Zh=*^3%K!6BUh_=2IWXjb9D>(rDXf zlDd!l&>JPTjki#pVpL{rYZ`TW(Q2ptTGv-BHnN7o&!(^wCb?<3X83Lik9uvRZ_N1% z))~eOzmviY5xy_T{0Y#52HeYXK0=to!OV8mW2?{@f4eE%ZX>Ukth^qVvK1I{{slB_ z+PAumbKfQ-UO>*09|kWB9p1m^VP0aq&Ht;1`IY^m-v|2n@1*a$==UR;`;hO3{WR$J zxzEdYAk6#kdwA~usfX{m(6NU(ABeuFY%$|pdN1{-hvq!`2z)ZAKg#WmrN<`S5eh4y zBhS)hl^;ofExI&3NNY%RTQZN@PwPqYr+He`x)%@~Z{REWSE}c!U}k#Y38J%C;n6I( z0O4qy7aquhn?Sh#L%2j1oS$(2K)5Y7j`rB2XeY(LI3xgB}{WC{+3J%K|0eqfx?p1FQxJS4UG4x?6JzD$Hx_k7w zI^evf*4L43cVJp@@-4z8|&`vihqWA&Y;h4GIYqS29Rr8v7vXHPNWA zW8Cp@{{0WE+tFRgVu~|S&Stzv)D8a`;Se@t=q|%rbJ$ozK11W?*Nt(rPrYyNQso2> zmM{2%I=k8FY^8Lz8tHu6O6LrXKj>eKN78%S0xOKJ*D!^xp>W@~cxYZV!sGtIGzKEk?@8n3i3RH^Y= zhr9k;6%@XL?k^Re<9Ik%W6WagV%%O%~j`i=UvCjF?9kFn$aaMG~v5|kknLy^tSN|`^ zp>u6%i}bzH6&g$Tz2?W#eNXGcDBZj0>@|>_Z@njc*61@`euP~u=S2MQzNF9pw|n^g z4`)KG6WAkEKT(}@AMK?(4BA<(B{8FKqOp;gs&7_|I*G(rscqk!CTGAr=YioVawgMb z2kq=hJUik#1FG}GGId5jOKng^^#L!PzO$tE4>}jlapb+isPCgjef2pXd+r{o-z)N7 zk(E!Yts87w&ioDST^r)@j?V7pCfLI6=x%}4wDryOy|N+_S;C)AbyI7sv9tRz4L7fc zl&#}bEva=@EMM$YwRS4hbxpVE|DEerIu4A~&u?v4Mt+d~;#gQrNjc7PCEwND+1wOf zTxI5chr3j|9G17MOg2uH?+#Ty6ai&au9Q=Kr^|AOS#0MHDtMtAZ&txwZc0hO4$H7;7;p4-28?%d)7JED z4$}ZMj6u|7z<6snbtEC}jRyQt`gfB7;|$be!012IRyFcAZB-XY-F1EO+3iomz#sCv z@l6Kr;6sjE#}f9bK?8;@`r`%+`jcs!`jZaJ)@T@I_j}UtQx4z5HH^BH7%<&aUACN2 zH&Z9mZ#vBXqhY+gnzo!#zcz~(a<-up>2-(SsC0OoIa{pw4VF%&^M=lzKXI7vS%=5_ z)>h0<6PToz~7*M`>g!xEFC|G4O{WRo3`ZfZn49N|CYnN*cu+9f5Vd;rMpFs z|Ab*%{m1n09xMMN2L9g#-eb(4x~cB+bQ>`6Rny#X`tKLddzxot0b0)$7Cgs-ZC=j% zE%;#z?zP~43;sO|9<<+-JeR zX~Dnm=k+Qf{qdI;{!KrxSJ+d5|0fIouNM4Y7Cgz)fmCY2H(2m|3$DrI^=U}*|24s5 ze1ey`*$SWHWiD$}XRLZYw2oJu&wFOWm`xV{bY_Y1uZlbGHpPoAEYozHSDv>k&#ny* zeVops$~Vn9EuQLdKPCfonp0UZX16yrZ`5qgZd5b$&KiEzOX`*@r6|lNZEBWCF>*6o zx3XfIBfespaWi3OQE!q(J&Q_cdG)f)D%*U|RW0pZ_H>rBXWK7#urR9T**eZc3f4pyt=lwe#yeDc5mj=^=!?aiGqVf*Mj!}@MJ#=FUR@5qLLJ&B@TmKT_Nm|Nxh)|66wF2KOF3#TIVc#KcLfjyD_LtLk0JoO) zn859aTgT@dz)D{UUA}9;YfqQ4)o|};-hH_J;=fy9#iLL0{C;+4i`(MoaB;XR`HnVo z6{SON=>yD520b7>;*WhAvH)BBqKCuJdRF?xzu(XLI>6fx!oyBK>sx{T6WI9O4VTX8 zd;F}&Wv`!k_xDMB$O1s`#{sxg*%mRvLvIT7dBDan^qs(#aA+CxbV0vKT^{Q*fo#C6 zdcI4dypRnjZD1aB=tWU_Ro2kHEbtk)bTRge-jvzmM#Zgz+f3s^;JxAwiaR9kdAMuo zd^|zY5j`Z(v*Np!^{hbeNrmV)sTBPuO3%ubqF<#{^sCGg{VLapewAsWUnMO1RjwBO zDp!f#%@)>Q5^5Ej$sczVV_WE9$0>%}#(GLZ>nR_&?P5E*gZeSR<8de4xY$r8_*j6) zF>EA<9ufVQ_p+W4goEwlP$%mL33XAq;L?O@fxCgm1KdZczY91%^izgDLFq|4XA5|~ zK|cuKFx-uzH}YQA4+*!Ykn`ID_c0n@g`i#tc$3UTL)8-@EmDxbhHah1N2U#IpH7kVE;>u6jF94KP9Ox&ortHtdt;_~h& zlJ*xEcW=y#BSk!qV22Fz9p^Y)$lSoCL+%jVo2mbcxLl{;-oiGVRefoWhCWI4 znZ)yDCEQO@d&1qsww1B&9h}7T>&PT7-zm7CcG$M^XJ{P5?GYQxk5fBL=JXoiK0)bC z=J>lOb9#efYx(?St`}@6W1dE*Fx(_=+Y}DhC-4q|@n(Q|yqjF#JX$y4meYDMh3mB+ zHkxUi9u)YHxQE3(HHFI;2y%TF1-V{LL9W-PAlH9ukjuL#$mKsE;f_lBgpUx{BxdDdCBJP-K}ZMIX#lq6g+du^Cw*wi}-k z{Wj$P|B}p8-Ly15r_2+rp-1z_mU-G@q~$odd7*QKvw5Kr?)Qy1lw7>fmBr>eXKr5T z!#D8vSTavT#g6Mw;~E?{?=r%Thj%$RgLNz8X|T%4&AZ$~d)A}Ioi-Qma^S|?GEZHt zP2<+rGPh#x?G@wXUB19f%PyHGw#U+!bYk0+uv6%FSU&@J(QstYcGalrh4JW@PdUs7 zdK!8{U}MHBVLZxqABTBY@eHN;MdLkRF7bz7O2Sqc@c!Tn+9up-&|4KRfZ`S1mh7K8 zknEq!^v4vPHO@Q(OkeF!vfdTeo#G+)gH(nl@a*(4j|}3h6gn+v>oNH*rf+TUO3^`` zRl~GZyq>aw6`^)$VBV{FDs9v;F-pglQh(&-WXc{~A~phj6c_d}FVBUbmJ8c5f^tK* z4d{hVg6aCuH?u077@eqX>mV%$e7b3U8{mrszlgS_sOgOpg&z4a;i>ZUJu?EnTg`hN z65T01LGTiD{yMJ53gZv80Qm@hLj0QwV-=4+n8DD3Mcrsg#y zrqY&PItTRk$ct>hFY%({57Xn8jyZ2&oM;_FVvF-l=pZue#0}FL`g)$l6MLEB4S7-N z{V7#rX*qb4N|Be-NBQ-egVyw?~DC5gu^_8jv2+XL%0dBbw^|UR#pH0WYGWODb(*H%3f)on)9_X z2kubiCcnZLPZb2y`iT&p=E>`YmLDCr-kfOhJJPcKCgyB8_!O)*1y8&un3g5bd=4h> zHfZ-BhHXVXhZUc6YkgXZ84i?{Fd2EG53>YKh~!M?=sUL^-A z+(zT{EotEqORx_p)5e;a%-w|ZDl z%=WkP>!mPB`JC`sVq=oAaQ9330GT={62fAH8DC zyT7M-x0Uwyhp*zjA9Dip4|C#kLCr&$7%jBsv(29dyO(dP`Pr}5&X+~Mo1fZ0`0>Cy z1JFfA<(P1@;$2SZwE~YZ_(VO=_Wa1{>!CXJs=oC*y{Y>B4V4jmQvS#jN$_G0(HMoT zSIlFS5oK1qwFxfUx7E0P%~%^=%eppPslH!RdCZhH{3`4k>+rP457K%DKCw@~%dn>* zF`d@9(+xCN8syy4P5FOO^ir+AHO(V>sbW z46`l7#Hey(tjF-*^q3d92WsKuzl)wZ=81nQ`7hYJFu#=i+~%ZC2X+?`W`UGP*)NCO z1f?@u%2g(NJeR9>^m=1mnw64EjE$74H0~Qc+we8dBpLfoJE!6fW!C$Y= zR`JbgxM8!pYp_k*w2fttDhr)g9{=*G^xDI)I+H~CYz{fKR9nQjEzVKh!Ecw+8K>5q zCzF0TJJA`b(4%zkZBBk&+41FenN1DLcc9qF1*7hccQorvOYQuf(j8&>iu&Vwr7Q1~@f_fIIlm7_m2WLr?*r{PcgAmsEgr=7kX zN$9S0`nKgwQ0LB9Rs8>ybLWpRM*3b=w!Lvz^Y`pf@;Lq;9e#%j|F(qxKM0R76dviBcXrs#9Y zc^K!k#z9VCFWOxCezlSVc_E2${#o2F#D9a~ztiBK#d#6s`>v8%t5bX5-f+K)^FE1# zd8EdOvpp&MfGQuIeG6})an=x)@`cmoE0^-=^!(u$RGQONIxnbnrbs%WOQcgIIx(lI zdHtZ0dB7g$k?{G(Sv3%Ob@%}p6X+wf7t(7;JeSdKUjbix1D}0$e8j`}cH%>$H&FVB zW0nc?!K@p#-Ly8ySgJX;YV-^0uJWMbq1SnXU)RhZ`;9Kc_b43q-7a-j(&KSE(h0t* z`pVmvn7U2PomZ8;*(WaW7}q`vZr{t+ujy@}S|LrC6_8WQsh2)L9D9&hlY;RQS z>JGK%(wv*xt@7TX@?Il(KZv|(KHful`^fcCIXBVS4bxq6b>x1evzVlZx?C>l>2dzhIMQ3C z+MnBG2Bn2`e^1;m%6Vh~^kvFAj&(9@tmQ?2*6_<^{-3*$mLt+@_#|Tu zFO>KbRs8hYJ;7nUx+gEB*KSA28m8YT%ILiCYk$w@)NbQ!mC~tegbnFC3v|BHy=7n7 zKyqIN%Zr!cEd?^;aje5HCPR25qxLC@OFEI*2<{92LuKlz;PXiz!5dTG1@FY=J4oLc zvw`Sq$zbWscg%bIDvDpTo8>(ruaJ^a=uvy_P@v)1+c*pK;GL$J-|UKm>Gvb2Varf{ z#Y0B@8a%MSn^H@z8kmSp^ENp-%ZKJb&@K9Tp_JB|Aq{gW^8 zke_Z7eIMenlNiO=*+h9qE&T+cNLoJuogGS%Mj2$hepO{T-Bk72zjF zxV_B!`sEJxJakSNVY;n0c^dl$l`$~aEqh{yd5XfEq5B{7V|V3fr(==a^*BhlgTdVO zSS$CHJ#rxe{{(m{p!2ZG&-P)C(LJ%24yq5*??x}{Zv|iJ4x6sS?&bYi!$yw#)MJ^jYr zN1xeoS5WUpN*{aQ47#gLrMucx_H6)Q>Vz||w;^5TLRm|FYCkk(#0d`b8mj6{c^tXG_by$3 zzMm@F^w+^mJ-MyzEzO;+-G%H|m_rT<1hO>}?`Co-;DQa^9qk?KJ2rHMYai?CZeAZ| zeY2pfF7MdT-P*n`T-C9@v9&$CxT(3lyLD}=LN2H!eAQxSPCH8&3GzQNu%)AQ4Q(JBZqX^>yRmC=Q`geY z<&Ev@n!8kZ0T51!sBE~UQ^5;r*?)a&yR*J=qjPPhSkAcqO#AUi3_nc|K4yi7-x=du zyXUinEB0jig|d`$PL^<5z61{5WNiEK2MxHv!W}o@Efx;EXFAP&77j9n?}h$D{Pj32 zt6}~@cEU}P&Qr2aCLD6y_TlNw?Xm|C*(X!4IcV}>0|xEg)Kd=GCsS7H4k_C)ktIQS z=A?++9`#nY$O!*2$1N+RWKWFn4+*U6i}a2dc!WP@z}*5DOa4y~e9FgqYjpfGmaNbP z0|q{r@5YzpyYX`@e1(NyWZ`2LzRAM3S@^9MzTd*{vGDsW{2>c}#KI3*_)`Y_y8`$6 z((P4e*)wk|bf?#2;kQ`$OW8vQ{(yxawD89){0R$x!NU6|y748JtX0^;S6KM@7CvU- zS6X28!%+i_Zsk2f)5xl%DdAj&*dJ5Ta5BHl%(;helyds`3G4o_xh9X+~9HBqlf+W z1_Ord)fNM8_3%2XVRw4(y}s0qMv_?5pRr(D_RH4Wzu$swz5U;`;FA`7)`ESO?9>bk zo@K#h7F_NXJBE(PQd#&K3x3G!wl@#iF=h8q;y;!}cC5m{zi8n<=jHV;WuM)`f7#3H z-)zDEoj0vd7Ww16-fzT*zV&?u487||40w_yYc#}@q87X0@XJYvEBX~BUk zvS3(mqEfzF39g#SYX;yu2|hBDdGi26Z$0b-Gzt7^Nl)9gc*;uu#SGc7M)RDkq%6i~ z?_Jqo&p46a5^wD6W)ts*HF)=_9(N68QkiZJU0F``^{rj?YZ}`-+FRE&w&?`31miHD zOy#N4Rq6>KgA#A-ZmDl=?}jRcwx3jpO_}#_71{-*Ov`5}REtKWnpAukO2srZ9HY@q z%kN{@3280wsyE}iglk+0cX!m^xx~41iIVR5L`P>6zRA-SJJb3G1=21=ecbYAO6RoP zp2nqXS}Lw}OlL$@x}_yh7M;?9$)-!XNqR|N^2}CGWtSbzNvC03M69c$v)hDON>8`+ z`o^}ljy0+LtxIXP5uGm0*nxG~EN@qpa}CTa8QDSX+9=IyQ8!X;yN5f;HMWoAPt;7SCGA z=M=c$vxhv@CV`dhm{lSt_JGKTJt*>FtHoB#Dt^OD*~=#Xo#MY&{ErKKPVmZh%qo!= z`xxJAAx9PQvTc(JaVxznR|TD^kk5*WTP65KUY1wD*$Z+eu)_`cF5GJ&cXiaua$Lne zuGegFl}#C{Pt3>l=@EE`z?C9bRwHhMxOf+ZTm$6BLiH>c2D{n&1%KGj>A-d?(m5xt zvI)~j^%eQ8sK|4*h#VJeyCB}yJPv1!h}vq_TE1V;Tu1eT5~>!K z1Do}6zE{IPBK}qIZ{zzk{A*{?-l<+N&k@e z9~S=;lKznRpA>%>bAa4cXad(8@@1&^B5`5&TD7ORC&WD~F6>yNAHo6dhl&9AgOU?l z$@Zz0{c^ZnY=;{1T?Yc(?n41?-!l^KT!8zfQslps92ntu7I67s8wKS$F7TW}##a?` zez@18Usj6SAa0Aen+mx-alc19^cJ$+=N*OIo;$@qA^wmPLwg<&|AXRxw2<5LnD`$T z|MQam1@VXAL+PhjUTl`wqlSM-{38=N{R;7~6#truoPJFF>%@OA&c&akexAtkQnMlN z)k*yY_hFF>qjqc)SILLb<$GrlxBr16jt@Do(32w1MeW!t?x48m;66p`$z+DNOy+h^ zOlE&2hlT!_%Kl3J2ug9Lvwu|FEz`Mw@IHX{KMMFW)E;yR4$*-(0{0oJpSa!PZV`7c z+-GS#!+nm*E$*peUa$N!xZlGwSgvXQ3|UWRaJd@bZl!d^?SuO`_2UdK*EzV`sK4O$ zP!JiQP8Nr_u_q>F=AmN;u9IlDn&~_Sk z;ttK^eq6m(P+RXG^;^6YEAG%jDa9%7X-k10UW&UHcL@-rIECU?N`X?mxCRNq-J!Sy z2yO`;G$;S}Jr`%rMfRR&PxfRc7s-CscdgGV=S_d)u?^u!yy8lhH-2@sP!cQao3~&M zdvmoA#Fp2jb(9v@nR0FG?{xW1_NsZX{HB-Xj>jr-mGf%Bc*y55fdc0 zzEp)i#l1sP!C728HuCG&eg^Dn9@M|IinP5_pfA_jmr2)=LfPOcnfaP}te`S31`o1B z`FNRb-}QXEH5izqgyh;PIq0*H-a6S_V8@0VC#}>7HS9+;ly*}5LK3vkD3gXandOL_ z2sGapQ9tG33W5(SUhD|uO$)i>rHIe;Qn$twr3Et43F5iEa7+MvL%UGa+oWqUZM_8wfTw@kw|W z?YCs?=S@S__+8Z?Ic?XDM9O3qWgC4Ljj5An%m%r{x6_2&c?D`It0m|x$M&qq;iKz4 zNR$1x8O!?nr2$fv^g1)R2Q5p4w!{2Yo>e;Ft}{CdZ=%7}axX-L<2$DR(2tQb{O%WM zK#uS=PlZYI`NiP@+`8KMB(jekFVcg+#xHS4J4pyURx-UoET3td$ANe?jf( z+Zi|NKgcsPyJSKAUOEl`Jo94-6!ywg8dBwmR$3p8$(rcOvaFLOP8^tSI#mDE7@jZ6 z4exSSlV1o3S^u-*vpZcRI!lyc6ExAoK%CtZdjce>4w{rDmes1sT6uR;A!K&`#cUwp zpK5jHHFQx1y^cv|OMu2(ljkK+oV_7{`(5M`KISKkj6v2}Je-WMDJj2=rYWt!-Objt zzNYN^B{x;_b$82|h5>(5(Qa+LPp82MCLT}wflQmwWM#X(!u;vg{%^N3HRSNzpXdST zwOBV%#fdWbeCeP)cey89;I5arMgQBeQUqGJnR85xbiL(7IU1YEqXIPTcrJ+8f^u#@ zvxB8RKY7n?QU#jEsxBjwx|ZEfT9{xu%++BNagdh1%k_SL$AoRb*q zB#9IEdq47|@je@ItA{8|u z4g!8ceNn}?O>CWjuw7^Af4-()AUY?5rX0l;`qXbRo=yufnM7G7vpW4jigu|j9({4x zO*8Qg+m`f}S&64H0v3FMyW+MvF0fOO=7Ett%T#mQ71u9CB?N+4|2mq^FRd=nRJoYW zuPwCa*VCUzKd1@VDJy~e{JjF@wVev4jThIZT*Jm?#}XsUm**S$jekndi-W_D$(!0u z&2$^QmqTlQEuSXMh52}EJSS5bz|Dyq%lZAi%HhaWZ#x6BUfCHr;`I5^DPz7=Ge^nK z0mS&7PK1c}xAyE&zqn}IskaQFfU}b>hXFhPy<>i4%5eq{_-YPB3p37XlCITp@YU^e za4CtbhUX^&^JRFJ#Jxplus&QEh!vgvmUh+GXJk@V_4f$4*w#q&NGtX{jlOJCoGZmw zTKmK-mwGYM8dZ5XYRn6U0d# z8|lUEJ>;fnti$FaxVK{1BIm>Gqi_MT~0>dR}k9w8)a{30>3r?AT+C9NrrfW!!R4+mmWQv<21m8X6jA7SVn5F$_j&r z^=1_nu(4Tvvni%cd4oBmf;HPC^B0U34v=yzeha}(f$itiU`=2$h4z%j`_xo3mQjbIR z*?!G)n>T@MbMm&I9S1C2tL)v81PCG0uZ{P0cWg^KW{vhk1#np)QT$L7-&o-QTaHSQIjxSW;i;V0BPCmt(2`TEV`s?fx`{yUGup8HP7HB4>|w4LCT zmfEVX@kdWbC^1R5zy0&cYpgi}z((sy4-A8!ZdniaQD(Kx zfOA4ySsSeR&r-U?H&|}4;B)Zg{?jbgoIJpO>iqHr)+uatZduE|tH4Mvy4PKmVG=l&>a$|qnkKSI7g{TDFGD#<@1)X`y9$+g zvn_>%c>MrEZpz8+_dw6eycZ0NWt6KqL@zZ}_{Z8lyMP2rd#0Fa0))eiie1+y(oL_Ai$yB~UWLBX|*ByWn_x4*Gz2+yZ|*MUc+&io&Z>Dcvh8oyH5 zgyM=PvM-A1UWQwI7&A~rzYr$zKMEmhwf)=vAzQ>J@PmfYvO{F$_Pw7KiZvhj{UAGK zfD^EAcoLaARbwo!4|b4FJ~%JlySxqETIC49PW#?`;ZL1tnHg^|m~PFB;;IHvwj37- zX(88));EN)uLLULlpyZC|9(G&?ZkO#al+YN!?9hyixEYbYkZi`EjT$JF|`iwM+$oH zq?ZG;T-eKiS#q{BXC$&_Duw3Bich5G-jLpAIQ0Gl2ep^I6*lztc=PadBXnz>;}#L5 z^8LMcp=+@RtNQno7?M}l@6e`4H!r{E;3-PW@f|;H6}^~wa9wa2As2C7;8Y07{Ai|! zP@A+4YlL%TWowuRxP~>ww-U?K^0Y{i_RDMrDco8oUkv>2IeV?e=|UScKxDv~kvLlH zkC`3uyi8r~@xnWwqSZVcUnuqddzL-Pr!xLfX`|zfELw99Fc=v1)E*#C_xT2gL^dDr zagUxW?b|1d=!6vH{Z8#xUha(uL{a~_rVJ2${ojhjoZi%BtjvSPlAge(lk`tV_1}TX znft660ct7NwcRyoB`?KqZN)_v@!=iXcFPWh4YAM7zW!t@RuNN(fDTPd zJuCH8QQMr;sB4u;)01bljB#}bjkY`&_#<6d*F=zVrgYZ+x1=l*B}z7YQyukM!Pb1S zu8ds_dzn70aC)i8xcrGv{mGd-lfQt5h_P2$6OOX15#tI?=kd4p(pBVHM3lBUpWG5A z_Z3lpelOI!EM!0^9TX+WnDOtKzzp{Wo^O@TUlE8)a4isO?{(85ws; z{zrQuSQ$4ffB0f<$ii}+dFycAw+3EIZlqcaU!l-yg*c{HnL)aGikkpGg5e2tPO=w7 zMVCUbk_(|-g4L$P#7Kf@byHyuN0LC%eLS+~$3%{+G2b-0S$-*@OwINwR0@4O_8TeO z?%rrS!j_^N&)?&*k0%P41wNGSG4cwzuo>6*)4YAqx*i_faX|bPaa58&tK4Umj>b=s zCLl;jjm~9VntEbZ4GUcUHk%+3E}dHAM0R&{znn4Z<`?`lpz5{4^uynatDzs!tUIj& zsI%n?yYD_Ye1BvcrkCU&`dNN*wgN zvs4O}x~Ph&yb=1#*>BWQku$M7nTMBs&L|^OqS74{3m6?AaNB}6p6AP)p%c#EnRR%4 z3q;9!W=*q(U*M$huTzu=NSf~Nslwyx*1I(IDUQgQFJ+bIMCE~;+^SEU=*G&2x^h`- z##dMrqCe0fpG7GEgUKr?UH)m-h{M=|wsCLn6?S9EwKeIcwXHL!wF4ZOk>s9er8n`5 z$twYQrR^#`-PWZjSfS?Wm(z|a8O1hy_G~5ZW&B6+j7zk6u4;yzx}B3aE)t1vpxbUT zb&S0FmgsyTcWGbq+68R9e3xrYcb`i+2en2&)E#{h9#ioK(V=*l#bL0<1nWKP^@~kK z)FL)QbXxWB=v2ac$7f2Z^T0s?Ki?1~Zljy`1jcnCK0vtEkGfWR+*~Ek6B4LI&*6qblwF|Fqk@ z24o3u;IfT)Fx*D`F)Wfy%>xc*sJY3EN9fG_4I`a7&=>?qt)7N_HZsM;_!`=neU z%sLTP@q4T&Q$^s*!dq7pjT!51K~R2b(2|5U;*A2*KF&^ZPyi6wzZ}dp>Ys= zj&r&7b@;x2ZhvQtlNTt{ite*wpn7(*I`i-ek3?L0H$|xKJ7GT{r-U8V%G~tA?50ZZ z)lR2M8zE;cw56@6V2GGg&T5zz-~D~d$=-ouxp~3h!Cy~)qBiVvYCQAdR@U1Tbu?JgqoV(o|bcluJPJF!glrn@+l7n5OY{ zMv{Ep6Q8I#s8i|BCbhfY;PV36tC(O?+q{;n8}wAL3ShvfcHTr2j(aQln-!0r*>`xo zDfW>xR-E-@zz{%SQIu#+a#$1&diLmKP>p{i(nBh|)Z)l?C$EWr%M6sC&82LCVc26& zeoVVipgKQRO(cI4l8Y(vlC`3hZLAQieuGI_HZFq+kSvrZc0&HdU#E*;zLS1(3v zbfE$bs-iTW-oGRaF|b&{OV(VpNzf;}wmd)#F_8h^M})JG_8!vsbwmJ&!Rkls0vl~F zk>gaPh`et1St`;Fij#o9Fw9vO8AJg!x48FL-DUx(`;C3*cEW#MaQzi{(4N+{_?9Dn zqK~IBfTo(rdhu5F{~860JTXSWqUR^pip?gF&t74HRp3L*bCsLw4fm!>n9;xk+b7CV zGpJ}nHkfduzh@cOcp;o}VGg16YXzcyzF$Ydg6U2a`WwA?{Mt<$QsA=EinlXfPPJ29 z>z}PLs-2+v<%`|TJBkKzTi|6&b_j0v3*#ZT!`-YoViuAGoP91kD7!-2kuq_Q*;BtS z7C5+WUH`^PPMg|(c30*WUlPdVJ5LnvS#&*8sXIK1G;KVC{F&*?V^Kb*UI9RqkL(Iv zv_r3Yn}mI(YqgNi9%x#6XazEIm*tXp9T(DNqM#kpB`lt5?JdhQM9-Mw_-`(EA>WzQVL7OtlgSUhvQoAm?rH*VIo5<|Lw{i9!xeBe;3J!0_WdGZ1 zV3S}zdPwb74por;nQOrr^+3Am;1pDIJkT!ok9nBb?e9MXQ6kkT3Y_;fsBm$Dz)zH- z>Xe>ppGkKt|1v^9EXo8)+Imqqa0r<3dM{23oiXM6;s8Py35IF7F129H9EjfnIODzw zc41Y7v!q=v!z{$z&?mvz?7^#%AnKK3qP(6@VB9()glTLxoK(lc%WbNeOz z;%=`=Ufoe%95Vjbok83$=&ju;M&aip7$p+4@J|5?u=o21|LvPj1U86q*i}94=HG?H zJPfa^0kA~loBsYTob?vdLBL9#4cRRlH*zJsA3z%)(?RF`(cJULnOWjl z)Xm2G2|$=31;`xf_Hko7H-=9afXSUh>wGO(9eWi{!qu_xsD_CICC>~e z=AOI_IdyF*8cir^tIY4dtj z;*|NjusIU1Zw4Rz>?dPno%zwPp)JtK-4SKHUEW=W}io{0ycVoFC$p{s_gTQc1uBT8{@g1aE1e5djY9vVRFWOA!mZV^zUaQ zLu`BnOqOPjA)orSS=V(P^LkheY%u~av6JPK>*zijl8B)JfA4DH5&-&vS z@WgmQatRHzD4^Y4?X8!W?!u7@pFt(pncwK0Y5bI*D8Ia^+-kpQPlvW&Fc-L)i)^$_ zE2AvzsG7z|M+eu?dHE7lt{0#v*=@T~-vUoHj+IvoN;T(;>icRGP#58x%ZTicc7flE zs#T2~Ns3W82s53-Crj48@=N3Xxt5@&v2~wt*$-Y7?oyB_3$>{DAc4n|zUkGpN8BM$ zb-MV`M*@=N14`LnO=fPppH^HAOMrS9j7f?IGU6?1la9^2Llj6C@?+q=vBHjoU&6GR z^5ewtN{p_}c)Y#B$y+hwY5w}@Ik=uVPfdT(?^Uol{cLv&QXifZ6fh00cLu+NRB zmkYm6Yp5)NhFcKF4R->`SFe>p))@`x%MkutIc^7d45=O|b!e0=r%6GiJd5^}iH^^)~?Caw%n5mI%$?HC*T}*g^;&~ZO4YG=uP!cGy zd6EvLK6ja;6ta?V$t zR8I5qYioaU;9v3nWuy59-{`}i;=YF;lSA%$j+yi9$pn@s7mA6MUwE7)`g}qTJZ&y~ z80cS?lG>l7*AJcPfAc(w`@s44u*WJ>iQ%)a_o3!mK>Z?Lr>qZ0EBs8Xi^v^ybeLmyb-(OBs+TIQL;|_^Qs)H-O0bTZ-6`<{YU4H8cEMYgpGKp8f{($xA+%HP?!z)z9~)MyW!O!6539%?MaWkE zj=t)@jJ7%evYQf5s{Y3L4F6)>9FEWbWkbW@xJe6Xlo*o$oQ&0Hd$M#{C{3t4fF_YL z!@(z}1QnaA2Tr`TD6OgzvWSV#hICGLwN8H}2X}qV5BEvMk|%IV#FEE%j>dxRb9W4s zWmF5}yPdnbF@)q59MhG+^WGxM@$_!FUIpiM#l<6~N$4=02pIPiz;z><_g?aU?kR!? z*T8SE_&nbSpL0;*L1dzCZ=QD*22NBMtk6#~-;OBg!v?@6LBzwKY-i+?G=>e&7f@bz5B_|kBD(Y}b4PornYTx{(Y zF_JmAf1h_&X}L!bLyy$WZOBm}t8Cb2S(9c9BS1)9C0T{)`}+m%p~yf6nX&zCBeXTc z*YyV*!~fbY8zuv~{ zSB3|$?>eS|bcN+Ltkd!>Gzs7C(ggN~b+~k)ZMy9E7P5Dv>ksh^&#yb&JXWjX%*= zE`ljc!;L70qozObw&02W1ZWA|I-5=lH?2;o$vzr6zOd!gA@VAWaHA1u9!n}BUu2F{ zcKX@y%BpUN(I%CBs75dLtkJvpRrsh$THq(gi{|fVW*?p?E#YehWTnVub#s@|Xl~d- zK7n;~&+j$fewNaWbFvmC(wpU)N>g*f{}RmQ{r7F-L>NbT8iTrz8&L&hU_JaMZXjnv0%EAa$qcx ztMyPpS-a!#3S%jF$y1;UGi_gJC`$|~{=QF_64@EtwR zB81FO#GYsOj4G9`Bz5f--fzIh+(?iEj3N8@QUaZ5%KQE&=<`kUj}8+7-zT(@0$25D zXFv(2B#V8M_1x>$XR~8^;-=|F@}U_8j6>5Xhm@nPzoF%n?K)7bU(t3brU>tZSoGUd z0LT6>a&-MBVgl~D>GI@J!T$)rnK+Xe6M}c|Ajm%qzi{dWyna;o{}mjE0)GohEW|h( zjC(O>jP30`h$|?nKf3ULuCo|)QenvFd(CfvD#sdiLgP9*GHQ3Q7NF;Wr0J63mTK8M zGJdfBM#d~{Oz)@8`GzZPc4eNG;q%_Zyw3gOtj-NG6;y%yeq9Lu3H)C$mIj#aB+i>S zd|5!u#QaZi)Sb}CMwD5;@7Lw`4^M#vt1atLZZXp#S(f#SmnG%S5;X2|`PdpEq$?%8 zO*}<;Z`LoaY2E$FFfUCicmIjlLjfE8p@dH$g^yo8E(QnvaOsGWQ+?1Iyj*j(@;GT` zD89*?O_j`J2_<_b^GeX>-THW5_ah1RK#6+`1vVn5`yt@ty4Pj~?qWQiWH7iBgeS-C zZo%E-RV=jf%AM(Cx3r@#q@&pmm-Sb_dwR1UTnEmKb=GrhG4Kna$WE*(H&9jo8oTVdrS#7Utx9!sKN11qDzC;$4ef+ z8Y`R=x{A)So|uXtHu(PpDBt5ypnvzWrlF$oLOvpu@Ius(19%}HR$dYUKX>P(xZB}{ zXsn!Xnmwl_J9mGWkqoww8W8w7ei|MofSFUT%ZHH>LDi7;cmO3Nd6)iE=x5|P9xHWV zdUM<>llX`@ykM`Yww;3x?V>ifS!h4ODOl>#lv;s9DQ0#Yzav{C5 z(S3_Zjf?fqs|aoTenu_hOmdf@4RwFXjt`!zGrU;-N19++zX$wS-Jo<~2Vifqll>!p z2UeKCEY%R?%pxpu1pbU40ZmF5*66Ceo~szSBeOXHQiJ`jVABgTkO4l`e$I2hr~Jom zc6j~SRkS*~w^!4TCOhOo3+_%IHLrpkvQ=!c4{tx>C1h=PXkG-uI4{cYrneEae&F+R z>w>bEmAG$80u5$JAOrD5s%^0UMm#)6qA^~Zk?5Dd%7DuVn@lokfBNNxnnTNx=&oGa zJzVQWdv1P@Wx2`yzKVGDBN!7Q^v>!C9vjo8!(S_BU!<#j zes!=!Lkw3Tzr=pdc{hp18)sgv_@A>;!_&l7M|Bfa*N8o}HEg7v8L2;%_{cdoNC~{o zH$F1u&`+m}tP(&55p-vFS(CQ=^-MYuJ$fY~yMG;98wwzsca$huis}WgVmIMROG%AV z&vJq^jCL!glet>0u@DIQ|L_Cekl6JhXS-E%Hjm>W-2qsXLCS-6F7b65x*=3Dg5&$Y zEr}d;7Gn=IX&9QmpyiJQB*%G_6Y~r1Bv4ffO2@!G<6VL^*EBfV1cHi+9)7&W7vR1$ zIRmAw!BSxK4#q129@i1U8hh3_A$L&J*J<6Sa=?Xucx1=kMcUFu(SCHt+H5j_o8rgA z!MAhsjCgKU+D~sPWM+M<3zsww57kb`wFB14J}{q!8OiZ8lLoA=xglRb)sAbl{rb=T zFtG^P{RCsX)mtx;D?W&6b-4U#bv=MTYWoz`y^A7xNbtb&um7;2e$BH6+{x2v9V5wV z#)cb56MlSCiPRpRs}X)`I_kqcN>=6;`;oI@#|wMrnP1EA#LGbU7yf2jOWi8yn=5cS z3s9amm(rhWKfW%9p9w4B6N(+bgXWC2@D|Y)p#gEqHx@>vY}zU=M@O)iHSw;i=UaE?h1ioKh-?dW7_$S2Y}O`{~0Sxnc7BAljK2THeny;&Xh zL|F?cON`cs3V-_8U!Nk05lC78)CSnIK2Shh-=BP%1(345%P1hBvre&PJJE*o^PWZB zL{&sjJ28G~kT%30L8A`r~zc~H3)Kn-&a(-F8nfrDxQ^t!Dv`od&wdyfiqMj_9# zl#({%%u#I1#dAWj1ch#0clu=mfPQax&~*9S58DDL{vN1U)NH!>m~CYJk9ldw+Osj{ z|DuXL0n2r*SnMl!d(C8D$Y@2nbAG#XdPeX(yXJWeFj9I(W`#lZpa{}O-%By?jcaWE zH!$UU?x(JM#o(Ruc(>Un?$hG%y}fM2Hvt;A#Rym5y(GPvD1q!SdI12x%;H|y;Xg+F z{uTNb{0>f_*&9r6CsD39h-k0bPmraR%6kYBc$KJaZaMx}_Q&uuda8>?qiUASAQ1+} zmiVPF0Gl#(Io*Z#-V`%auJ5Nn<3b;htY;6IDU`un9Ku9dVuMfns&~9&KlT#`{H+}? z=~gh;_2WGKKKP-6u6=y+;roq-{sQ=O>+kTX`3n#c><8}ahPDC>4^g{L$1|23#Z9V4J`H!Zc>PpLw zW3;3s6`*%G7poUHPQu*Q-yS#NzmCT$HQ0EK|yBpZTST zYV;?6>K{2%N@@o6f73#BN-w9~#z+k=Lv(L?-_@PrWoY-&^)C_Aj~hzBsWIYpnB2_N z!dhC&Ok(_-8*w;fey7m(sWD!OPi)DANBfW>2VCACFF@n{>ym{eT?HtR5}LT_&@$Hv zmE+JSY=7d`wPRl+&^VDVx4sY6+*P;RIDC0X(8m0&L{a5xNMmL=TMJ)ob~QD)ri0GUG#t%yv7ylh*jnBFhm0J@{DH@%p*GR zHdFgD=7W-8NXeGD1LV(xpiCyeX1epGA>yrBN!RP;~0zm;T}JqVM}7JUX( z^U2d{3~}=fu?3V4E(%Qthd!v-k``{K=@VxTu1+)%{+^2$%me>BPA9tue>8*qm@J!J zf)j(G?bVZ%E>H((!2L685#CC5BhN%}4b1|4dR;O`(MHd6OG8c;p%V#lu( zkHS+S=5q$$#_qb}G1Pz2b@j+d&$*F}((}_1F3@M~^dmQ4S(b)`_RyN8CSiF>1A$BzCNs@n-d5~L$>ufZ&*XG$yKmQxX2$Qk1pUBOsDw44r;F}hj zA6q0m8w@?qYX*bgtaRgvDQ?h!#T$LQ{rEXb?{(If5-!IqbBv|p@ortFRbr84R#inR zVb)j*ErL zRZeAMQm6;yo#Eud-p>AQ2baS zinYn*Qp_2(F-GOq?$M*!(Jn?G?C04>dHw!WcqCv&7+Pl^eCDEPD580z$C=}r{7WEL zbklKlv3#Xs6#i&@J)@uxHM~Rz0uMe#Dvc9T=1ck$dq*# z76{wqHxlam$y!3$pY}L=_tj@D$42Y|7g%eIA}%E} zMJ(`kQ)m}aEVN_Y!}IY0a%_6&QLyJqA9&!e0yDWy+)h*TYwhJbbq|b4EKWIn6OhC; zkE=7K62A2r;LDtaBN?lr^1OeD*%u?OOW*(=OtUDY_}9eO z9uX~O*0a!T%rhdxMu_Hms$xJiv1Bs9ciIVS6WwAxU3vDa!+XV3I0W9jZSy!z-gb!{ zvTV70`Ncnh{^p^evt99S?5(rSi+dFueCSv=&=|!>1D)tbh*`*d&Y;^44>}bphk)Xd z01Amc^-N&2tv38O77j*H;6~j&&Im)hqwYK5xHXH0T(CO}Z1C$gI%w;ES}(N>Y6}@- zxMv*mX>Hhf?^3Z~o63meuT$c(_<_*_eBzOf4X}gi0!9CmGeHx}i}|-BU%ml;M2!Ik zbzA1I1MR*=&!5MWjz~88{g^A+&OJFt2IdLWaJO;tac*k>EQ&P2bX?date?aHHHh@5 zM+t73*PNNzx7zH4B0c8DNDm@8>n`+sSR4d1NCLGEg#oO(czFFP@!T5LcX|Cf!!mbH zA4|7Yf??g7qm*VA(c599MyQTji{<{oQap4to@UKf)E6V932XIyYv3#mbdED%p(wEC z<+Mu#x{x|JE^*#19_Fxtap2(4+^Z9@G+H2!t=i}IHP6m(E4C}rm~<=|s%@HxQLj~9 z_cVyA8rOH;Zsm5u;x5H>%>gr}-k^t<-Hc)i(1YDe-5%KGdB9&wU}xgEbDmnTA?r0B63+7< znWjB}x=oA(r+vPP#FYEKEh+W`J^YOROrc$)^Sk<1CBTvSWC{}>+;1u^r2W^pL{Vm? zRfa)unSvX9eZn_BtOIy&jGuAfgt>Nbjh@7mX(2KLk z|MEt$om(mBFN+cHp;nBE7AZVsLddr6IeC^XG`KLLaK(+83ybSewjfxYD6ob4V7^JT z>Tvk!baUwOCqbKGXB*mZWx|Z_9QGWZf+|no<9$9+uT&r8jRbp;(F@AHS$~PiDHs#DA6%{D&s-8^mPw_8rrBf{}`m2tm9PplU zFD8C+-4|@@_!z+jeDz=CVzLlN_w=kFZl2B4!QPKNtwwBoH3gjq96lrZwIw;`Do;oJ zoV_xoGHudV!BrGB_St;y+oZ|EQX`<>qIL$ouHP+G0>{r(x0APnghmR}fA!PS-PoRr zalXb+{Aqnj=HuO9OF8CQN!Os#H@lg3(ppU7f%7?exlNi?yHD*VyzTkObICN2zN#>v zPeZOlu{_Hs;A)GvgZgC{$E9Jhh<{7JrV|ju>J{J^V5S8LCk_TL4av0U^Cu4-d|BI^ z#Lqa7Y=4T>e^PBzFV}^5EzIRf-BOfzDvNU`d?AIJs}060OuT!yms)X0bhDnrb#Yk1 z$l0KLG#(fW(@J9ThP`M{8r0Z$aGtY6jX%Mpqf|f!FghD$G5;3Gu*Mi=1G;B*6k-1c zVVS)8xz14gqZrR1!Y&3wMYAdUkhFd35jdz8nY(rUe2eJjak^wL5Dvf4=qifqr|*(4 zes*OzPRMfhI7l!MJgX5rUeYzO&vWxovM5JlF}ORigqf1MpK648TL8zzQ_rrw+Hxa| zhlM^tc?|GXW;qiN%g6nwI=B;;ZGkMiaX^ZQaNZOIcZR};+obyINUmVHXyaF|CM!6b z8Q;D?UlV^$=Ib12`%eJ7CpR7;mGK^%DH0@`j&pKn@S5}F;%lzXg^);^mX|n z+k;@Y7g%WI3er28bKVA$qcClsxc3;sk^Fjx(tWxbrOiq&NsOAdL?^t8!j(0hvlH%S z`*l}d_3N@*L9OWtmuu8>+zMJCyabMx|>KrHxa?q>F`gfvmJNs_~vZcPx= z_ucl*6C6u#=@F@PXs55&dPw2dfjg~lAr)T_eX71L??O=KZ`S>v^KM;rNyq&JL>`fW zr0nxzQ2H_G;SXhY`Gf0Jb;MJ@2NjvA7t1K!&5M1h=mH3}CL}3aOlj1&u1vVO(n0w|GmaLlslkfPSZ$U0OJUKF$JRj>|=>YKr(|Eo-q zCm@+d(1a}9ggQ^_DR*ez-}U`pDG(zrzEnonpjgR67O=Krcd>yHV80YR}a>%eQRwr&d6 z1VS_+pe+nda|+{r3|0obaQ(DmJtk>^1ELosyJ6Lx2BWJIVErid!N)D zT{hZAJ$IE6lDEy&Hhq0|96>Son%yvu46?iqzgiI{ zrvE-@V9RK1Z}eiIiugI>l*>rc;0RjRSjiuw?L5*?(7B;$94xk7Nl_Sm5` ziwh}Cp$OMJX{OBL>(^g-j2#4z9}p~E}hd$pfQH0dABH`QBLI<4DVN5T$y4Q6(4#w6Ee95>XZSDmDmKPL`w*{aW~ z>a7^J2fjD;q3(Dt)r4r>ajhKsCT0uqvH8X`L6;J!$e?0MeCV1zY?o*_gW?r;lDf{+ z^!xR~j>(<)#yRmLxc*`VQndx7q|F^4J3SA&Lw>|O)xb}u!J9{r1&jdC%e92yT( z&(&5F;XlxPa^TS~cM61|Ol=ZrE*E4qJFYMx8FL{{k-G4Fo|K zhj9JpZ}2@U&7%Ex+hy5UYfZPZyTDB1HU|V1pz5u^J9V(ap=ZYKAx$6~l!K^kQ z^|q|8-qOu@e1ZCz%nOOPQim72gsuk5Y=P0^O42XemXb0uPuQEywq7pv$>I8q&f|rM zLC*p=!lRk^Z1^)6g}T=T%jtYi1f>}H zl9PzuijSvlDJ9*7qQyR8bTw!M{x{k@kX0`^{nqH=GFD;#vndx@?@$Fij zb%pA;`}U4uVWurXw&B#;%73P1U^Ehi3$Y9qT6*>bTrt;HGv z<5kshx+z$M2)BQhdERnEVZtE8$L6UgTf3+e=y*jSF3?ppYi;(y(mASobneVG)5CHFHVM?qNo3)O#Wq~W}wb^B9AtL4_}`Vgqc7{ft!GZXB9 zb}*jKMT>P@8F84f=e>A=EBIIz#pV9+2v@c6UX2+n%>3+g8bo6UyFjXQ)3N+wg&0d>N1w zxG{N73mLC>rM^9L&KFQJAobCctcD@0pN9Rcl6(jQev)E+13-IOwF%ImcN`uJ^^96nnnZ~prYJ=>m>qg$L?h5 z)tqHZWG+!+-(}?F!TKA1Gx=v8tT9z>cz+J7URGKYoE`m5u##pGI+{dBUNe{EXD(G` zUS?}CB&pAPUYJxZY1It>4t)F%M$CD(Nt{M`D>bRNRdR8}Wqg{=&%XD)*7{94_0YM0jeQb)P5J_gIE}H4-`uAL2&OOcya*OlYmrB;IG_)2 z{bM(!lrTO_!2wX5P^QEgrN?DyzQ@6H3h$uQSy)*8Q`gxetrMU-U{K;{_*~bhu!x0% zY4Zz{xSPEo^Y}JAg4wn9KHh%?I*Fy@rqh2?6r3I3odoK%*=&LikAT{2QkbHP=&-*c z05+R5@thY2@~)sENV+kOsDo3ec+&+pMnG*h0|hbUBzZOeK|gV3l>gQLht0&b>KUgj z{HJRZ@e|F6s{}gZIk;KTbTsBb*-Q8w$br1MbuLn zfSH2kzfeMhd2YmbTqa2Th6EQFkckX05L`OTxeOci1_5wc#~V9M*S`T#ki~w`l!u{I z;Oa%6;?Cw(;kF9-Y25vVKtDwXG$lo12pM2=6Io@A_+|q-!UV4b7>1eIR#Reu3Lo)a zZG@n{kv;wjMI!tvY0!oGfJOWsze*~M-F*8}8eLrTgO1T2Zca$Z5CrPT$naRT3N+UqVK#k*lf-^=NQORI}V4R>FB55J1IR67QW z0k?g-r&luPWh%Z;=;5}^J5{PKH4HNNLjd6^H9P2k{eB7zpl|;f8b}#}2phX5%1hY* zn_?(5uA}Q8TraPW4FyHT#UaGw1}VrRkG ztw8z**+giRiq@{V^gvVLCqes_xAi}21j~%I{Nr;YMp@UdhyW{toB4V61}vk55jFQG zWm!#lFBA=jZrn3F*r4P4pl<2XK=@erp*=Hnx@gdrt`sYcxT+;eKahQm{fsxJzz6Xm^w%39CB$faGQvT!Tq`0 z-$91z3hL|t9tD`^RT+NR$mp)Xcbz%JKI(|_Wl|q8gKx$}t;&&Oc`uRT`+;_Xn0SdQW!M`sNBg6?u!V7`YxU-uA zGs>X{6ikt~zY|Omg_#J8RIj>3c+PlsMKw#V{|F#w%L0Hb>})&WoBFHVxMwwHWB!ea zH90?o7tf@f5O(0r$G?+!l*LH!owd~WDR}d$zzGtqS9wPZcdYT;2)EQ!t`<82fiw>L zE+Z&9E6?O(Tmuh2VBxX!x|aMP%}F+L#o}~Q#b*a6ee#KAmkYaGHSb?&fb|DHay^T* z_zXT6eS@UbGmhzQ-mN(aC)zcpXLzkf+Eo_C3+5i45C(=0iP^QYS0n zMcEn$kv@LF@d3i{1k-KAPs|KPkX7#WJU9`Xii9!z_v>eEnC)%*WocaVPH9~F(HoaP z;5_)vBHC}V`N<1}sUT=MIB`%DI&XTG(q(Vk1#Ypv=`3RJ$4eC5!uqFuHN4aysjAtK zdj_+J216jtMs7GA!$7EIDqM-;v=g)r$&x>DEb6^BtInKTkZPJ*hyMXufVr_5{Z`*4 zw|~Y+1drAlydLwNT93dFlK|PSi`NK8g+lB_(k4y{98r5GyQd~)87nWu!~7SO(`!5N z!h2s3nU9NmcgKJs{1{pbnW!-uY&fsScxopt_J1#cRLq?L5>9;=w1o>QqKN(9dxQNa zFP$@Si%^%@u;j(^_k-VHwzuv)0r1W!5HihpT;-yb+~b0be00j}pV`7ELH^cpZ(KRJ zFB{G;0oj#Z_zM2y#=PGqbA{pl7Lc*uma%PdN4LA7<>0ouU!%IcqqTUj-Ks_X8N z*Q<#_2QBJMC`Pf=kLT>k{0VXyL|;+yDZX%3^jUoG<*ev4g%6PSWhYkj$p$PRFgM(J z;tjv_GxdPZ;b|=LFUqC+7l%cB_3h>-TSIOPyHe#ID?)l%6SnvhDM9KRIa}x^NJ{I_{MOct8KN!O4Dl9x{Jt?ykv z`h0|mJV+WmWcX@M6~`6Fu&D4{i6Nj#Z^^z~gxT$5??TxPL`!4Uu<30k31m6a)cIi( z<%7r*%0=3qjF9MoP{#?J7tg9RS~>A)0iQJb7`j*Z066LMSdjsTKdF7$&)*a~mDW7<@;(uJ!`^}&9_ z=2}Pe0%k_$S@DTsl;`?$jU?&933GxLn)K=CV{3veZyN4={%8!7XYnBTcuuSPLvIJ# zkW9IGI?XrqIe9{?*t)eM1ZzC3-{jYhV812!1OrTB(YW(xu4Mh{olX)DZF&C&7hN>B z^!I3h-hT}(L)Qg?2-hJ*NT9ae(hF(EyFf7n<=4QDLlhgT6iE~^@I@gzj)ZNo3I61YH(UU5}_BTf~fVw--VM#NU8byClZeS+Jd@O91Qcy_|Qw?Sg(_{JU2CyGs1K zF#JR#1lS0nN|Nh3^#^L-lSbFk7s@2?MBJ~RA5KKJ41ptB{Hd_rz1x;EJ78Qf*Az7f z)`2%f5V8gT?bfYzn5`GUAxSC((5xg?%^N->*kAa-Qxs@w_ll+G=sGkZ?5YqsDQNbo zd%r`T?cF&Ni58|q2gH5~ZlAnqbRJg+X{Ssf)(p@$q7JZ<WbmhO2UovLvo~xnyQcXd8D9b;%&7g4FBuZb+1O}D zIWasUmBibSQm`kkrD9ujX-~`@qgtO$uqr^7KWHiX*ody~S6;+3YtTD`N#d31%z_IOe1*D=?$SDvdr@kHefl6>&sV~GV~~BX*n`22H29YSqL~~O&np*vpLwMa z@-gabPyCrY4yc!z)j_X7aqMtX)NY;*?wCSMxb$+em$tL*-N78Kay9l|6Wxt_N}YTd~80;xG`-lNuDkg zWE!+et{KAJ2vuxhqf_`vSr9TotB{ctM%7FYGf$X-@1qI(LMA6pBE%?0d>1E9JYQ`T z1-qu(Y$;oEA6)P}1U$uRT7G(aW=93;nK%irr-EFaW>DONt2>k?p`-f2oQW&H-(mJ+ zLj)3U3Un}rF(K3!-vY&UA&>|B*S>$JRqM39k_4z^ak&SKnFAMZH^3?w-xzSfL{8cd zCVpz8(wmBxQi)8zV$9$bgrzlt#Ui^ytG0>R@QPN5-B7Y#%V3;a>$+iqp?5VcdC0}h zXQVjnruJY@9aVqCQhT_!HskjpJe4z4SU5SLF>~H+vkj8{e(-ub6&b z6M-#Y>}O~~41VrBBq4u-B9PCmN&zbB?#na}H%BUY#xWr}4KRtuFA^5>uO)mMC;EpV zS8{Lg&E6RX`JQ{QdI?riH2UB^+JKT@rCC2$+L*{x*Zv7OscrSXO_$@yrLTWftiWUH zB~v}Q%Uyct!Gu45){+Mb3P~ig(Ya_`>G-;D(jFKgWV~S(aFPpex$~%8YlZ0BYD~-u z7mw1{ju7T-rEQh&MkhfZ3@QM0_J({jw=GmCUFDsgkAFw>Rj3xE1d{c)I|cqYJC-2+w-bKwh2(sm8$&cm5KKXJ*eXzmhS>MqVPsoAZU z`|p+s>bB)GG3l}NT0OerPDzJW*SGDe>)^&Wev3~M-nLX!`l9Hzb5{S71W^4HvgjbP z;P^8b1;HCuQ6_n*@Hrg&j%G~~(sXR8Z9q)M{dnz1$AJ6#qLRDMLm6@h6V+7V7O!@j9t`mnFV67cr+`;#Ek zgL{O<6CAG|1>#*#uwm32rOiOV6S^)D+nG* zDWNxnXvv+XIUSqdda*Nvy4E~D!t)Hj`1kx;2T0}eRRM{Ph^6<=V$SbUw2J%9$?!hk z(pvtC>?i9o-*b?c5TjwZqf$PHi1{DO;l5D9L`XWF`b|noXboA;ld3Pr{O2p2BT}E^ zMZh8h`9GoABq^k~R^RRt;i*|XBY_{bTYYBnZe6G7?a_tAi9E}4 zWM_>kS`^m9a%ASVKXVUsmujlsw+)vaS7)8uF8eq?Ic4$M;HPQt!z=QS?n5v08Qm09q`{zQ;$~I*y71P- zPWxc|*RvhX8OL&tJ<=N65U1N%OxUSO0Xv!C~YxtE-!X zZS_mO4cdxR8TYq49TX2a@GT6gK++Mywm%nrprLnq37OxF{JI=YBqRwKp&>A$Tsd7Fl`nCNp&@FYOVZOubjd^iv}e2-l+-#A< z(q3j{uZmA9Zg*jEetFT3GBlpuY56D9ijvP?W@lG1x5l*J#X4zbg{3TGB9`!$z}DnO(pe>?X=-ZUbXvtvQ>IIWQrriHZK&;jBda8 z>-&UCrZGVt5-iTH&adX=kh(g7KWq`a(6uIE{qpkK?bJq7Yoa3n{B4VwXq(LZ?d5JUw*Ty4q)&#ZO=jX-tXS1V zZ`~3R+hh8s^@5`JQ{~nBL9A`EN?8PChEpZL$IEB&+9?;f>emq+#vSif5cqzoPeGZQ z(1oF{%VM4-#`B?9IG<@eWr^SfXUIYA%d1dDANL<>ts{N?!KBMF zo1ln-m56yvuqL6njqFA*OkyOlUScFV=+SPTpc@GyDgM$k$hw(v7y54>750PqntdkD zp4dg|jAL*qWALOyEYWdAz3*(I0P z%SI_beqDB{;f~y|q-v+1Q|2$DTlQ$2{816XR&N56m{Zm&`w=*0uFTYBdTcE~75F69 zpSee0d^_=x;5F-%u?d&>x4;O=&R4b*=`qLt+O|#U#PJ_nhsft*NPTlA>kNqmZxm~@ z+`&UW)ilQE`kWQmFO$-&G(Rst?+t&^4yf*}+trNhv~XZ2<4l=-F1f}^#dyV7kStZ} ziZ@Hq!My&d_Poi!c6}j6V8&$cB+NzBuAMXjBQre?b&?(B-DXUW9j|kgomi^1O;VO| z=#k99-~78$q5%+b+#!-;Wq3Md%hmwdoG3|_8nX&|+(bIvnQ+9npFOIVm?+~K@VC`{ zx?%KrN*CydQd_F)>}|@Sj*YmsP{7~lQD*y~AE4S|D>;U0!T1q>ww9odvkVx2+V*#) z&_5*W%krCY>g@C|2D2Ai3^56Qy$UM18|^sev9gDfSI@TzW66$gW9bLi!`h{;;>;`Z zRIYxYr!WlqW>$P+-0NdeB0&fy==1nQK=$(HmjL@eEI!!6#22Ot1D;YDu<_&nrZz%2Mo*l{6o7F+l_etr?;_1jFee2s44Edt+;g+d` z9^mZEnL%Tex01P}a{v4bhYjr`xF!MoxqoSO!ww4&O>C$B^t_Ap*iifrF)se%^7nY~ zwb1vooP*ey1nO_#As}paAjy$9MawDR@OOBfgyC0!<_DTWO+u{1SZ9l2k32?zXsVqO6umMKsK@DELTBzu4`d|fq=r137kys;`2C`q^ zeWuGJtH_~_A295k*?d6QQ6I=jqD0R;PS5hoOI(^v=9#QbPRaJ9BVVuMvYak(p-nyP z&(WOjDffXu9+oHHiL}ttm%Ct4{CHNU6jQQoi;4;l3>~-vl5yJnabl;x534 zTdCSlv)}lYdoE+>->jJJJ~c@W@;%PRbZYG2)2$rC(+lhr^vj>rdgWp5-Dfb?kwgJj zoEx8%4Nvj4&HU`z7XKT&gbLW3Pr{q9fgnC{npnh>h&n z)HUT^sXy>Rk#gJ^_%0uzz4S_~eN@YO=1x3E+2)36^FJ9knbYx(V{jorq4OqiR;$mQ#~G=QqO@esx#Tnx}UAD-H&JbM$J z8W4!+^hizEEbcO*Xw*7?8{E70c}=D5)6H6_qT@I6udL#FJEBuUU)h_c*8t0^L%at?VFVIlbk)GwXijS^-&ww=;4ya_tvY+PxS{#O| zZI^QyWn-j2M6En`srICwyBRL{%)plMzL}GCZdYe=$q0ye?l81y!)+B`fDGn`C}MZ1 z>=s@hti}8|m?fJr!L(iAxlFlRlr-+EPTl=&cm-$_zNS7QQGnb(AT9~me7;kBrM&+I z0@urwAId^8_9(uF?&Rk3Uw>1ef$_ zm%2?8bmZ~$@P>by+v@}GtDxcPw}*fqIA`TLA57S!y5+lmaj=Z<`AgKQSu?0ymilQ% zuFFW7tU@hi8zT*C2_ni4Ec}lHBp_0Lah==uA}3at-io5g=gUbo1UL{~jM zSbe5^N+5X7>Mf+qcFu2XfG~ZjQ}-g9HFk7P?=0$W;Oe z=AYHF-E0SGyx(I)I^I6)^rqQ{p5;{z&@61q+BJI{*0C#}=hxkA>&7`OY46$l%U&hU zLRA&oX)L?+?mx_?OrjV#E<>neB72xl zJCgIrOKm^$3T)`P>%P947}})8ociT0^4mXHRlS^SS>)P&U{h=~K;W?ZgkDutNW2ny z(^{Z+K(v4%AkG9t{a|He1qHiFKfXG6jDC3V2+O@E?iPj@)*F0#OCQ!tP0hx_=VgGy z0b)L}FH&5flywystUp4`9JfPyk1P0-EIuFs&zJ0TgfCj*omsNIKR%K>G4G+l zQ>?{)t<&TJ#qMM#1Y-Yz>V2EdD!JsR-8aJCvffw9utcrKnqp zr_&}*M1sH}F*NSdPJDpKvV7*c@E7uji78cYGW)*)q`Zz9Qj6mjjH`Of&g_)0sVe zzG2eUYby4`<&?T;XHn*>iYa_Ai!6=&~$^4!ewm2Rpm*dWK*ML&=W^q(}A&^Bh&VF>j%rf*IPW{DS+6VECdz#C;V^+4Ps91RI3Tj?CP(ksh_`VKf3d|zBkkO(%Xdtx(aKMPfCw-&+_`BK-Z_t7IitUEI35dJNT-EgM zrcT6mW}6NZj(=&45E4!t41@UEoQ`!k_Ndq(hPCU^vfW|fNK#|HUm^QX>JBKbG9ruY z7H|>+UAv(wDcQ>S=Emv*g|VH+n!j(S0kr6SNsOl))*pcV-3MTE`zia+bW~2v;ZJiJ z+|OPe^J7S`(mW@=*{ux&HzRZxz1Y2wI6q=)UugI;&|Xz3gI^(CF7B!CzGv{<`Y;aN z6h)WZEQu8Oewv@A^1xyTYK|HlNvvgRYZq;m&CjeUrhd12;-LCv=Vgs-afE!)!_2y^ zbc0{j+l`+LxURuOG{(K*~#I$-Wy8U9<*s?l-{+!7q%Y^NX z(xaj045vh8-1vcN3k3bhLW0}^SSJo(MJ<1yY5C1FL`)vuliun8jWv|MqYjPl#{5Oe zUi{Bnu0Xwi^g`|;$Uq#+Z23r!YG21&&j@4F&xsvB_ zb}w|_-aGlqO1>bBR@us>3>nJDi+uB@+uhyW;QlS+S@7b;G367Kg`$aXQ+G}`O1~?* z1AFBFIEm6aLXfvKU{Av3qQ_I*NYH<`vPO=wfQ&iB+OmqRa^bGfd)IOwlcP%aa zrd|GpS^d+j@FJ8pkuTVW606dALM=&-MoB>2a#$-1*ntf;Gzd zF4I`X?q=J`WKcN$8cPvp$rHRJu-mfpj<&NvSj0>!kezth;m>#I0Y)K6V=}T_^QEre z=E)$~w|ZK?+CeRUK@RiD*zE8}MQ`9At_H{;hm`8!?azZ)ke~ocqsL`9J&bw(?+xMi zuN$M<69jK~F?EGS+iy-6ag0A3xb7$$FDs#cJsSx6(&O@G4IbT}c6iyxK>_Ab#CE!H zsiI02)erGWWdS2{@6#^z_16=7Sf3YP)hO{Vs@erF<*i&tuU&jCx2bDU;${44!$D9`{yUz!skLKn zeT1EDZ}rnld5I*9>^dY&_$i*kbEk>7Z--|U6{*~YbY}wIHgzo$tp$?k0SEm>7)aro zdmLuaPoa4X(~bl==HhhET_4wtWJOtV&C=hDHajk7lfO016z3dbx)WMYEsCDnFLQY$ z;IBBjh}TTwfW3VT-2DG83K8J<$^WrWIvE%(*?PHyKC%T3lCAjSzps#x-jYOwnlpwmki7*J_BOt{#`}A zQ=I&WM_$R)vmf~uHj6GmF68ItTWae(G?$C4QeZ3%UXr zrZ#`y#PqS~`XLfeR-4g6WSO8%yZKlMN@auZ_^1M1An(|^|I*-AvhR$z*3e;?>lLh>Ud!+u?%Os-<@Hc(LQ}K3+y9DC zQ|%{8?g+E?!r`Uej-_PcRl;M#{d3h35}QZcTZzB!L<}BrWxKLpJUpMH9tI>!zEUK4 zYQ~Q8KrM=wa}|d)$mdA@^A*P|WVCBu!tPI{J5TEa+$gV5mpYJ;v4f(TxdesX)|;~8 z?3*%2lTU0BO1_=1RyCtdlb^w6V^@VTv806WL@_nyKcC3_Cp9GTNUN%D&une_PA{E+xVpq%(T>#o+0w)b( z_M%W3cOby6&qkFX7`OO(sfv21)yfc+gkc8|w5P0{t%L_gC)#Z(qwp*hgZ+SgIn0;n zIaC<7MFz!0Wx^7i#9{OlFnPj0NZR(j4o@kl{d)Q9&Od+00C;wT&V%CZLhXaj+@R>q z?~6^g--0)voLc`a@v}AwoGK~Eb%`0g?*&?9rubF~Yi%e|o{+fsN45Mu_lC_vU6BJr z?-+oI*-V3k6x`BSKDl^i&v3f(sw*>26l%yj52NX<>C)4l;Flqbx1@9DufeJI{A2_-om=3XPxX#jM` z8NtAbIpbfuNn{|9_t+SF@=CVORo*2kdPm46ZwD-qVLWh>dZC7bI;0}&4VF#AvDLfp zH~@HSA)3O~hC)D(uVD&(hMmq)QtV(Rd2Ra0c);Slfg3A`wFzVJLICI62}y z&gGr5UQw?6{w)`R4)uJ>Mv12Cm3zNsc;rIv2~|&?zaRuQ_)6;-o-&q&5;}#7sdt`0 zH~W~{P4flPcl()3i~Wlr)h;e7G^HuQeuJ$lu*!wCMcSh0n~cwzo>*iApgXya8=7z{ zfF}mKys0&OOK$P0sSjE)M@b&&HdEJQWwc?i=JlO0#Px?p)ZpouGadm8^jH0Mr*qf+ z?Q)!cK7V`R$%_LtHw%k6W{!YVHaO*eSOWB8w`-6M>p3h8oQCh1bMl6OC7^3VyhD4V zx62Ey6gJ*5B61GgkvUIr)RLc2in?TYamIfvzM;;XJHa^6K5-vU%^#97eHEm+k70f!1$?)Nd%q!sKzB$R}Y5*kqA| zga7oS|DAy0H5&{6EnV?C#nByYNG{?FfLj41RZScEbgRxiS%NLlP(lH}Jp8*%oCu`* zXF|{{*CU7|hNTuU)V@>cAh;5R8BABlRac2&;__7bMX^b?~%Vu#6gRZG8R zmG^pSy3SYAX%+wI4TP(0Bz9YOFK2!pk)05?78R+A&Q3G^#pAWxH;HwK@n(-@^dc=@ zHRkoGDb(d{b&lHY?y)42L7Z+?A3zqOv(e`QMfi6LtUr9`QCliod}}iGSI5NW|0Wg4 zS^mWcfvBOp9QHxoqlclnv0kx3q}z&*{2CK?Ypid`WP99!ssZXj+3WZ%%`qKPEcf^} zrayHqPi)dCWI#^BOmpX-k^8&u;NA8OmGUKSzIkv7smw=3<8*X`IZRh+yY=8gu9OfJ z#nKgSm_ZL4mHg~t`SqI(^eNAaE>leNw>uqo3Sw;NM(_P$VfHKhjNllter?&fhKEM` z{-$3RK2GKoP!oFDPp&EX8hD}a9i_sP{W|ZLfPu*f(`TT(aCkamBNI;{*#65QJo?_u zt}kIm0$62hW(J429v)*5q)P(?2C&`!uw0%roFMfm7C-$3z(CYuW@A&&e zy3p$VU`-6YhN47hB5{`vB4%7SYc6T>c7pbWx!i{#ue%uK3(F5fSfuh^-~lDQS5RCY zb%cB{%`H}C)*|Aseg47aAW#ap6JCSvATBNjs0Ah->>arF$khl?z8;>EvzfhWJ9L@1hxE#->&}&)t*ATLA zrs5jcp5M>*pOUjtb!<`O$135ies0>VbbkEp4yN%pq*fO*5fd_H>*LOSK|6!~qLrTX zcy-hPL}W%l-(<8oU2IFf%AmYpDR51%se1R|Tj3qWl6-tzm@8o4V}1?rqw8yl`}B!t zME&6f(Ipv|nNV9$f9kQ9PnIiKtWW-l&62!}CqK9LCL^;{4$J0%iak_glPY*#>h0c6 zN&Q5r2%3xQqmb+k_N`7N3$1`VwG?Gx!q(Px;(~I<@~yACcQ%igiOu z_>F9)XP>Dggpu?O45A~{wg!L93s`)!zvs_WuF)xf=*v|tchf>6f)74M{HD;aygn@@!09eNU98;zyhwcO{zk{F=Fdv7a|mJNuI zO}7vyjFccIsrOdF6ai=4RmB?my~_Ks0oX{sN$$Q3?4wKVPBZ~>=^7)n=;8>&>a^Af zBGOmQ2pib`y0^{~HdD19r#l!bf6V7s=X`6l|0s7@AEs=-O{1GGvcl*$oaNYN3oxGb zYW1)fcgb}al{ebsk7#>d(cDqtr|^?uPfY1>4wR7PEm*DcpXp|`!rpxFhyr7?mDs#v z4Bz%9#JX&O^{3&~AqVw*onIX(UWZhpaSB+K?^tUj4n&HZHER$JXt8VyQQw$g3L}y z>-xxcT4Zs!qsY_iT6%SnbChjH)AA7TPd7i^X?@jyTahR4V?feTHffH!bhUvjp2h*s zM;7&!l%0fbi)b#{8s!>Gs`X-r;pK#A=u+D3b;1Y^2}!5GJXyIh*G5fMn{3iX-sr;i zx=BGXDg0Q$p?PB$0S}bycS=sL>#R{mnuPaXJn&oVPT2%nb&-sTQ6?esp%5HWkxW}0 zqm=gsNg^$Bkn#M@lEv$%YRF1&b{c}UrdJ8EF8Cok^)YG~pynMQL`8SR^>f&6WL1nI zBzlUTRQ5(AW<|^TGpR*GqN%lsfGl_~u!5vcr`+hN9DD_;x11Ip%XlSRGacCxl7d*? z(0;JAKoGUhHvjopWi*RzX>xgqxoZ7MX{?fyuMvzg*7&OI%Vwalu%l3$#Au_hhlwn& z#<2UVWZvH2sX}k|e{h9rXtZ>S~8wpJ|1Zrb7}p?75dZc(aydj!`bJ zbABzz(wmy>i@+L9B4WZz3cNMp4+SS>#F2cX~eMq49(m)NqL zwVLd#IB3%Q5V}WM`SYg`&hE_3T05oQD*GD6N100;#N;?^$2Z6q@;|XB#ndz#$aDfMVBA%h}IFY z`X-Q;#(k{5HdsYsV&K}@XdmMh(mFoi0LHm77lGX=ET`dvn4G;O1}Fz!uHBq~fF)th z*^+1C*Sy^f`H)N?sv8$(V*g)kFB_r-sg%fM`Kfyk=fH%Ypw35SZ-gYQ;*tpkT5v%i z+e257&H589x^+eVyG+ngy@*5OU_6?q5^?v>Ezk+ny**&Lw|Oz|XR12|Iy!DY{%&u1 z6kd@4$vMyTl#y+_NQ7; zOD)dDI6wI>PY|V?{LP-T(w-j?tdo9BUc6|RNg`A9#@_6+9qGHbo$x?yl$hKpIt_zxrAHWQOgmuw$iT) z{acc(ll{-d8xg45^@6CKJTlvYR94$Ymye08MmTpmgc+tgK-$^l=b2|U1j@S3*8R`W zi@ysn^*Y@;F&`Z~k=WU;Nj;`E=1LenVp?|zF}YpRw2ufl?J`_?t=7h)D3!mnmsSa5 z?H%xtm#ovN6{!sGI<2;VNdwgiQl2!0O zN%j>mKm}C~XI(o*904Sops%W=IPhPQBS9)+9Cwyy{zmA8p`3o-L`HNkz(+sD|MClwwJv||S2oHUiv>m7WI$$+*|M*4z zur(0U6`^qQCoXD@9A2=73KQgBWvlBt#qA=W*=50&IVWC{5Msg{ofH-zI^(?w)5EZzY~Txh%ib6h6_5 zf^$hzGm9cFZt>75blA8EUHgxDcc*dfR9Mt3s|&<>Fen7Fj+35C$-0_+4Fb37TXh^B zUjLGc{(BSOE`Rk|cfhi?_cDuc*A(izx(U6x8RsJSvr{UtxF=Hw7qhI}s5U|P3OpeJ z4~uzCL3A%#{PW$o6>P%u?AI*mj1Zb6u5}x(Bu%^h&aB1IrVU)C&F+mHMYYIvutcgm z*dv+xos&j_Q@YZtq1xkF6#lRF71TtbUy3EnbhO<(R77&AC-|Bz@$<0O6q}cS-CD*U z_DY7dp^g~iwij|)*0ps@&*tnZCYV!LlQ}k1-&pb1FV!zKHMEqXUFknEn5>>J_vxcl z4MklZ__ixWIZ_*u^xXYIXH;#rGp}+)ov7#17$HT>2W6kR`FidoM7A?y3OpYis)d)- zzH@jYxaEp^L1tU3F+)`B$Y`Cc1zBQ5Jx<$#{E*s7l#IblD-!!^Z=7y@dzL^92DM zqYaF2j>%Q3UbM{S^qu+bAvK3AE!n=~Th)KV1Z{CFu@P3aKde z*iY|yLiKg8wPoO|zH>{|SF57)=HA62Adnd{TE1!ES8+@8@@fOkowOrwa4ud;MPxQ^ zT>-Hx!;YD+{sF_7x8aF18$hsl>jD*nT-P^56$SjOl1(0Gde0ZcwXusL_^gf>*8W{} z%3Q;QoaA@z#1^^!gJgFIEkBdHcTM9vBE>?wZJqZtI#BEGnO&0SEcC@a&yPiUR}Zgf z?1O(u=ckoPQ!OwsGLn(@=sfDt^QKo=+d#Kk;Re8mE`C_dDUeN9@{P0J z9%X~QmsP0JlT|Ppq*yZ~p}@a_{g`L+%d2~XcPo1cI*RJugF7K!1AA?JUt7`XW%7+HcJjb_9miNt_iz2#(XNL z4OrxObtBXk>8)!J?UJ28pYqT=MQ3?0t;yvV`p&|NRep8i3f3v8%G0Z$*w039x8`}j z*4U7o$!W5g>LH+DH8Z*EwEMZUL&Qg-=8s;i&bGwDYcBLamPm5B#s1KfVg+#m1l@xh z?*IYm!e-(ozO>u2);~v6^dSAUMk&2)p75s972$k8`gCh@nt!OOj-LaktE(3Yu+TT0 zagM7id3(tCYZ`SL=-QBcYTCb3Zd=e0#UTVvbEbR24o)jd`9ZWMYu~F~}SX24R+RAr^%O}=Kg?jTbt2HHzkP4P4!i_Xgfwm_Twhrxk7E(KO3Dj&% zd|@T3BzfTbC4*;dCE7jI_t%JYqi{bV_;H$VyAL&y&X%Bv0pK! z!GgP^y2m0{I5!ubU#$DMQ{A7JB}HB>Xv(W~5tdhIGm)s2quj#i8$sa&n5=b@!lj<% ztc}$aJ`A|T{0p@4u2B3VG@I{jx!^tME!`9i<2cVbUyGKsJWw^6Sz2C;Ny$%X!eug| z+o~XWc=*dFQi?z8HEeuV2vE}L!|8RDLO{`WGWm5O?G?aAwK(*x+l81n)mO0S=k9Jn z(T&3d(**4tT{5i3a+oT28TjCkt z>C6iR8G$qQ7UTWk&1+=T7KM5h9m%>w&jzJZ;D7=q67@3zcawIv8tSetNNxV@8xVYm z843-WXBxsuiF5E*fU-@3IVL2*L!s>gXaVdGp&+WXr=kZ7i|jz<7l2o8x?#JXdCyi{|r1?PQ3t{QV_XXYCXD z`SzKKa$BCtST*^w_di$KP2`r_NvS3Oo+K4-vBYpE6%=x74b3)LNxp7V8l$VSWYXp5 zh$;O*CQ~@G$CunlGP*LOFChF#@&2E+$+SW=2?%1jL5qC=?r64n2I5&5$$FRB!J8bC z6?J>_Fp|ZySWbOa1!sER;2;iBvih?-Bz#Y*8`I#oO&k+ai{|fIj;6D8xqtK#pADFm z4sDZ+smig~99`oAzN<+N_1GYL1T<)|+LHl#MQY9OM0jKA%B)3VK*nr$~&n{2YVkISutV^M3DNkbW`M!HQEZuF= zo+O7ueREw%($t~;Y+p+O)w=%7jo+U_xpi;9UMRsq@o2q_>Q41)H9_LRAj+6f-CVJG zU$dv04%ECDx`cCI<~z~L8sM&8YA;chPKmV&djUtwD)c!S=}>o}^LcMb=iDCXXoPk= z_(-|MKHlxR@`x+pCbh;Vbo+Xyr8wb65Jz}B{n23a;B>E$OsGGdWJO$Spg?Y0KK|-j z;A%^{y+BOSb&bJuKMiUjGv54v0AxU$zogxBm6Li)nv9n*yoY=UIYj7 z*eyX=zmyx{>~}AZl<#18AbC)DH6m12+>`LWpzguO+seI^PO;J-o#p{?g4(u0OugF` zi@oN6g@DGze$Q3>07OK8Vw7d^Rjrh8~DJ=Sp?zhWFbQ$r+ z*%R5W^7E^)xji`;{FW-mpfgRShdzY6MfH8H!hIS%DdC~f^c^}j;#cCl+{bvw#CVJ< z-WbECvlAP^c;YbJ*N~~5DDPmH4|vn?vRk@{?(Rv0?_9~y${VScr^WkT(CJtA`wuH>ljt^APUL>+$L%hg9->Z4NWqav9Lc``Sqp${m{gpcPQ@-)H)K9@=cbunjmdgti}}9k!fhJz~{Z`w$p3LIjimd zC`a8c)pk9D+tvHDH(WH5FJ&oICR0P|O>vH~UIM4e_Ym9mp<=79xw9fgQfw$4dG$snsuSyQw=~4YsAmNxNC5AlT2#`zj`%UoF z^>`Z8JDTI!toAnOb1Dc%mA-L@Z9}LxG?ka^^N?S`n-3i>uOeBei)f9;TCMHAfUfgr)DE!O0A9Oivb2LvvuXQ4a*t7$ zP>Ro9TmbxCZJ&kWgWk~O z45V~Ua9y5GQagIyq&4E7DF0y!^Si>!i1yWXTAm4}(|rN$?{NEn2edLTzfZ09Fs}#B zI*{3KqD=v7M<_Z#b8R5$j|#ns$|`es)Sr1LLskp`B`o-N!b8oBW z+od^Otm$nQjZN1Nv^A{Fs|N>?t0C_;$Yt^4V?p13l?HEcwzHl`&U%>N zh^OCOduXG=n=bBDIL~`NxOucE(xb|~DabC?bA_8YZPXj_$zD{e*0x^q@8R`&`unIq zct5CB`T1n6Mi|-WriUerT05i5MlM5I4WI||IK_FkPuV?NruJgpChx-@Av8sVN$d0I zPpx$!(07GiMr*_Ld`0VK89udsz&?s93(NndXeKEgB6W&pLTmFIXK}`hf+k!y`lE{% zQ~5cTzK`z{?kdUSN^w`;K>hXsu|sqB%|Foe)tYApl4hSnpUDv_&l^)Fyn}k7&*xCw z@CS1D<43$ssis9@{W~msO-}>OueQrZ-te&Al6swo>%b z6umh%a+K0UxFC(IAF6K-@2D~%?IGVLDz`_*&?d^mmHr1Myrf-A^)}n+E2@sn6F_0U z-5crs4vnX`0#qlD^7~ej{AkVm)(mgO=Be~Oqeq>!IbD@bAFnq^|I?EGmOjBz9sX46 z;P2a(jQ-9*GWrU&nZ!?zP0%5isd4%1qT7b=W{Q6KW)HcvroZ*L6CIg{djHGwedK?- zm41!%TSdQR^t+RO^XOMjKf3atroH_1tpeW+Iw}2bqTh}5yMcbQ=|^+!^!4<+j(%nI zyOw^}(C=#c(HZFURrI@(ezgCdrnz^T_J-3m;ZDQe5NNNL(~su&X&Mx#X{|g>m(tU8 z-#86^49G~(`R_EHtxwZg_B5SAPt#fDG|hq2%H|UNCctiyq*+M!+6%D1MD7-h-T9k+ zTd03==cTbS0^WGo(ZSiB#^Na9k|yqToZlitqZ`S;Ny0_5gi8o6oCWu);QU!|F9 z1^1lbyjgI)g7aj-eTs0A{~vc>10B_IrTO|#YJ{*`NPxg1ZeyGf2b?y>HYRM`0trKm zyzvs$NySs(MwoZryvo+ljvmJ$KgG zdoY*oG3M&^+FT{R=w++n)t9@aEl<+kotcmF{7U2bKKeF-stitl$VlH!>8CY(9q@m- z?wOJI8&&J=DSWPd_FhZ% zVLEevFW(K#^%wTa-t9*pHLy)dad&q>oqq(^rwMoY6b3=lw_Qwop!019BfeB|WCz7R ztNJ(%yh&Qe)jAMM`rcI7h|kJCv}%ybeF1d#E}ZwAB}!Jq`IJ2n<)!WSdwO45nQ~%7 z$EaM4qqDX}N^|$ye(IMUkQ3!eKS1egrQa^C>sNLprOGBJJD$$EQb6BVz}X)bWcLQ= zT&0h-;w82D>etpR50yiE<12oB&4tWkA-@auyyIbvH;+y9UunMuMjGw=->3KfA2j!i zso(nL`{>T`eY{A|%$m_}UmQh$CqX;_>QH;;JuG8ETXu)|8|QtlC)4Nm1J_dH3yS{_ z$D=>I(jQBt46nS)%R*=NAgUMZ%*XIfXs?w}dt|rlk$WNMk@$OQE^Qyqo$u~E&sXHh zAITFxBTsy1p7^`+)SK^pF6N0(=ZOafC**AA`apa2_t1~yC4IG#{x8GBFBAyIVVz(b z!QVZ617C1NdSO`uJ{(c~AI+eGKh>|wkgLcrrRrC+KPjI#lefR%pW!I>uWF5oW1)IbR-jVl?6;>9T&THHuLPi&r z-##M^x?BzN+izJejVLI{Il5T^BMo{@4%Qp4WcB+)=rlMuZ^reivb{Mjznwc_=MT1N zr{(uJ%WZ)NCBZB5`!DM!#-G=2QrW2xJzjo?g#KxO`+A$ubpBC#9nT4kYkTo+xaHEn=uu<;<6!+OQJxf3#69VaS<;w|y6#&vBitQb17 z0m9kls=#4C-D&L!z2WXO;!L?_u%Gq9e=>7j-_%HXHKkcA;h0m@Q*R}1$+XnkLa)%? z4AtA})iL(vwXTs$Z*5j4-|Ji3n^6mOG4)Zxg({hOplfUV&s6zUy0}ohUbnTrW?7Xh zZdA2hT?bj^i>+E2yT7)Yb9HQPAA!$^HPCuEqi^DHy@Mp4klY+9FSY3yv;P%qE7K>5C7%T@*GYQ{ML=37)WAFlxQ+VN8=a{#MkhK>dGqC zMX#rk+Ni#Z#D>OOIe&BO!!1tlC^yb6o!MS-rae^bU2T8{{-Vm-Ws65Tk{%1(%%Wv( zY~Aczv%4IApQ>fGb@j3Psc6;5&GGD8It}AbNcA`1-qm8++P=QSc>B%m>dl=;BQ!_X zZEV>(((!{fNV1OUQB93pRa@)EmbR_j_pX7}W2A{Z$prs5laO%}Xyk3CnK?2olIa(u zwKi)BjhnkRbkNvt9W99`NzNpmX4zIaQ#c#pn%RtZdpq%k9@a_U?O~ntq|i#|e?Hs9 zARl!5fyarn<w&;BW%xcKY44--D)bzl1m=Lg)UF zgvYRtSMpg7c1FOJoxYgQZazAb&n)El`M{4|#J=o+Z&`;hD)_SZQ2T=1PMqn`u27KM zSr+8y%OyU7d`lQ-wqlv!$zIFYeuxJiEb0M{YsI~c(~1x{r)Ymn!uU9TUg4P%6MtwN z=Q}3hO!&gMkNEo{&evST&vi-IBjIxhYb@)yR5&^%}w^Di>ihV+&Q6x6FICv_&3yF z6S@8KCUW~@k{*|^Pr`wTT;IuwT;Kdsrk9s8-5_*VDVNuca3i&&l%GE);e}HEJ}#B= z_*^dKc8ALNdkG_4#rT&MZPXrwo2Weq>AO8m;dBZwf&MsscatdWp!CW7{q;$hoXq14 zSpmj#|70HL1418^@F?QrRIbp)Q+WQDPvQB0dZ88DWCP zlfvl_Q9D3?it3Z_f`qfDau}1aN5aDrUXXC+G!B)2m{O6CO%UH^69lJG&fTCR5-R^N zlLY5$g80~hLM3mTEOMv`BA=Qdwz7pRg9LpZOBT zg1C}D0!#O{xnuP$^E}H^Gxk6e=xsdk!x?(Y4vHQ$bbz6k3xB8wMdvV|P9tMjvYVk4HnPYxV#VUNuk?*`i6e{wCEJz9`&U1LG$$42IxFP7nI&{t^SVmKJy&B zuvjep33ZoSH?&CUTesk8x+4qA@uU$)Ep2$S-%|1e;2kel&5->JU@^*a8f4yF%SE8!+OzY@qPr^B<6pv zyG!-q5&yKIF`?W6thD~LzrU9MPyP!Sl7IG1aYD;+9BwLeke7>753>-Jrj0&{y-_U}F9b8T5IB z)|OyJ7a8;JIw`UKil2F>En!{TKJu_!nY`bMeEMHHIu7KMdK$Ws7|X zjm8hpb}w6b`Qd!|Onw;oZqV}O^T`rS?pNzO?ESLAteKN)@_x`XePx~4yH=m1q zW8rfTiyj^L>k=zJpNqStd$oq-<8$W)bw5FFK6gudOV?Qa{oj7)U1R2RmzJr(?e8Cc z?!PeSSC;jZ@=pfeWXjG>d|e$0_1o|j1|IOx!G8Ntvfp<6jluVrC(@mU-}NN$0)+o? z{D1-13Up{e@Lkru{R-=xM%j11C3YYxbLkO7ET1m7S1PHwZhTUsK;=+~Cs;b};PQ zY$a{`D@tL*2)}0@*tbMriyx!8@wl>e!F;+=>6ms6dAeEu6m{bK6Qwf)@Di*(*swe} z^gif|>(~ZV&K=#0GjFSG*A1IW*b;i3XQ@3X|LpMa8>gv%o|Wf&=y~wX z$0_bh61S1!-WEO?cn(3e#C*jbr`i1#_9aWK`1VP^Aikb{1Lt56Hg10}cmr%p&o+RS zGG85^mYMfacBint+HO1Tb?SU)c(@328t0n=tzKKjf7LG3;!+fcd2V&@v= z4WQq!0SwByPOVAuP0W(-0&P^^2K1t-e|+89K2o~ATA#ya92gSYG`r8w8g`#q-R`ZSe==Qk%>^rMja zb9Bo5TG(J_vUDuyY(B@A^p`BlIOUD)4|}w;{yRK}&eI@mmXU@t`~MGkg8F$Hz6$4Q zv>R{2lflclY-N2tp3&0FCC#CcUhHwFmwDXjvyAkJkzQq_FE`Q~jPwmg`Yt2A&q#mX zNIzht4;bmkjP%n+`WYkrn9rU5nxWTP<#(qq)##gq)_rSUH1c0Dd{1U|tvJ0W;7-3_ zq+c}Bx8?O!ITDZ2;kzAtl7;fyq22jSdB`6tZ!w=AiC1#3Mk~EYgMQecO<$OwH)s>D z@f!yH4;IgNN6+ksM*0bZeoOK?_#nTw_+sfOX1KlEfphJ#Sz{$Gu%z^SEqBMD6q&G~L zGf6`U4KSR}4m=j{O{b*-xjFJt0Bt&%Mq;`+DY;yAW||^>qgNqOnIiZJ{>i#ynb#ko zLB+WM1f%703kDNiozQu4J(_6AC$2L!;9M0nCYrKBOQ*ImAnUja+1%FA{;<0_nZ9%) zM)m-zc13V84*%tTyfxR`LDs0&u(`~0Roo^RrStgy8+tx_B_8K>=mFuL8~O#vq;kQRW>GKU?CXh>tSQfcT{n z4}U4s7csAZ_-2Xkk@9y*{F8{UV*MqQ|GdQSm-r)6{(!_EmH5}B{L>Pjl6d9QbRO$D zK_3U}6W)I>!lm>)!h2Z;3cVlT;6QHz`tZ>AS?l9;;G#e;qz&Oc)Nav#+KaG;^`I*5 zXI=ySc3i>}5}uUsBJ!=EJ%{>!`p1aZ^K<#&L68m~rf4txA4C5IdgW*@Y-uW1GVg)+ zy95KeTH5=BE(@@}$;<$^TlqAla_a)DpRz5$^?^4*J<7K!wPSyP`{A(UJ0s6skbDpj zqTEmcmpik7^Uo{b{P3fU{2PP@?}F!Cn6wXxOxPoMJ_`lU$0K+^4~d?KNAx^C&hy$^ z#r%QydeQF)imdqqS?t=GLObRCd%y?k9*fUZ>e!_B>i9s1#^^IOUp`Y7jeJ|ReEDS2 zkwV_%&G#^6(MwBttgnU-bhvPA_&|`Fg{`aO16|DH)78WWx;RFku3@TVAi(%QbFESE zfhfa2j=`tvKxtllpsu#W`Z4Qp+;;o?`D2xBA7Fd6k+SW0abKAR7sh~bqj9I4aSCONx9jP!vMc%ye5a4Dsq^hR$rN5a1>_Z zRu4`W433Pv5zoMvlalS$4f&&e0lm#X86H-;8-at#A=EoaG;|~&GXn0bBg^@(nhfbo zg!(bOPs!hbKjRBMt$ehc2>08EM)9*ZQ`z??2WJZYMhVsV!{Oocd+C`+!z?pJeP1%_ z>sENM&j=puGa5eaGxi3-r)|!GPYXSY7^OEEIuUx=KNDGRvCQX79^QBH?~=@^V;1x3 zSjX4OKj`*3a>L#6etUO!zw##szg8aci|B8Fj!LgxEdCME2KGa+&sKOL><4NPvOdV& zO5lrCd@bm5PSZ!%JIZg$KHHHiLLOWY&f*Zir*M>#gTJE?cN|ZuZ#m;fPF=_QLoAzh z=v=Zi^^rDN#IrVbZBEp8wtQ+cZk7$2&KUIkXc)J3%WLbGR9DumT3PKnjqB&?TsX2w zzv44v$M*OCLMEomz#tRT@k$^Qi)yrz(P%VuI&^%I8LskKhJ(K~-mDBq#~+y?X%2n6 z(BPe&{8xwmdqI@R*a!Ju{L4~~%OJSe{^RQ^9##xqi;W8#-= zGw7WL{Y8WBH|T=~Jz&s3HRzNt%EV57|GaD5$t-e?Q)K;gp-x-8jcbp03+P=B1 zO)1Yb(!xY1bg$T1;!>ot6jEH1C! zdn08idTK5?3hxp1>sx3)=xiBr>W91#x;+gR>-J#(1RkwA&)>lM>cGZ;d=pp|IL`yC zLdimJ5}77F7xr*|C8K;lpSyukaYShB*}&+4z8+|WGy6eaAE(~TINHL{_`Ww!I!j#` z-Wj=L-Wbo17o#e0;md;0FOSkW=Er5}c}iWic91^Pn?0fK)J&Y`2H*?apZ3Kn>DX}k z49-==mj`bFf1gU0dQz}h{G{^ZTWqaTe%y;2DpS9qvzCv}Y`##^6IOn=Rw!M0Pb!-B z#1=s})5q~D$+I1kR5mVNH8qO;iQQU`-%hVnnYL}7dA z`uf05g)V%x@;TjW0~51S^lQGQo!@{lN@vhgoV}wZjDw0gKW6wN$C>xua8}nIamzJd z+m8QnoRhEg_1g)}i3{{Bj0(66L-o6z55uMR?=)UKRbYJNQ!i>R* zyf`1ypzeOoPOV8>6o^PUBwi}E8!FG>7>D__t!8+PxoQNN}WH0^3-S&j~# z>ffFAx2bH%se!*WU(UWs^$qmduz$hWXMFx!Cv5SFIW4TN*M6($>8!Jru?vB}JS%ca z*+ER!eF{dSX@7KkDnRF1Y`GJZ9@?rw*0nlmJ7xHxn-Z-`qb%TY1fbhz{b-U?rbo(D z{s&_`fAAafo|(!=f9+5?&7Gz2!wC5g{SMI?yn_0L>fHlhr?*g^4XWM;hSpPfa$>(- z2K>|RSnmn%hG`=(Q#9N&64_fc0}e9DZr2GQ21~)mjPvrh6jc#p!YMoA!Y{ zD7S{nn?qqt>AXz>JqwlV40CqoAFSRdZ?NfSiO9y&wRmc6`Kern`Hyf6u z3L198U%b+@@`OL1F0i;=PfEL9SKsiep+IzJ3U*bV$m?nz6f3;ry1_YWZQYsP?}5x^ zwemSRiP9j;f*cQHRU()l_$JF0_K@-kzvnqxFDs~RetqquI*&k4FVd4nc~jK5*Fj!W z3jgY=omQCUE54=Gz)z;}{Mngso>58(bhoZ)w5{p5RLh(6o-2v$bztTL&x{f4gEIoB@T*B6!av6^u1R& zvQcU`@QR~-`;_nYGLe%OtF@VZ%@>L9&N783tn9r8aji3%>V?hKLe)2`1{ceEzLe^W z8a7yEnvC!svB9d4bByJk3xKTu|6 z>nT%njpvS|^9{e&QMK0pOnu|t)*U`?WO90`(^lghYAL?!bN%)N!6-mqOs0M~CjGAV z2HAT&)?}rp+_#`yVHf}(BusHrv4&FrC_U3XvFV7L*PVw`Nc&zQSjJSgohr1_>7C2yHhJQOe+(!(=7W#LP7Q5w_ z8Ji?b^Y>_~-#$9fZ(o!8QmQ=6GIsP0>`37k_y913Aa@`5&u2rCUS+ph4E(r(y>qa_)ex1LWNbGo$-y`tH{#-CKnP5@p^R z7+htMrlx_gJr= z@pa0bQ+NsWO)O3Iz3>|DBEmhX8_Qq=ee|p+@76UFQ z7;u|1ygSqF8M}~uc1Jj?Uu}xx^n!dZx;~%VFH*(DS&y-$0_aW;Vj-==KLW#^L56vo>2DQ2lqJ73Ljr6 zeEc`Gv(H!1PVEj7a|-(a^!_2AfLs7)SFHQa`E{b4UrTU)jTz_HhU!tzuj3Vew3g4W zp=x!0MOpZUJlb9sy0&95>2)yg`5ft^wcR80RN<-(RHjc5A5xrpOxXuuUja{2JkYJ| z&aqBf;S=^Xat4S3XKi3P+hc^^v^}XB73UXwbF}H7(H?phI2!)G%9JH%NAxSsL0C)2 zU&-3G;JICF2+N>4L3XVQaj0r!uVvCiZ7 zXVn^sHN$!%qU}LJ9YaX7KhXCnoY{a)chI<|s}=53rjJy8`$Ps&r(_Tz zYJYGPjEK9$uS7^}F>ts4p4dXQP`|X9SZz_wJ}LzJsD1XEqC;TTuk4tx)-QaV_Ld&| zf6H?{REI5fG*cZmc$3)nwE5n!XIe{nU(v>Dlh&qy*q{J|V!qg(=wsTZjp-4o+tI^^ zP9SWQx5+v=TfP~=4*=wL5z9K*W=fY~`Q^UdnQOpGs}LDkF8i3c;HAMA);`BENFxP!**;mm~h8XkYx4{UGt@^Xd0i3K192k@q%=Uz;Ny=iH?^ z;vs{amm?l~UyYmxJR*Yu57KVLg=D_h@HwCMhEw+a!14r!X!w-EoGDb#spr*m9rSx5 z(xnWX%h_%VYxoYxy3RZ0zohn^Hob<+QZ<{icxXRwLaWaq>NniK8XoLB?d zrtld40XolyciXV*0G6;+xHQ%V61!8OEj1lG58z|YV><`;(k9{N&J-)!j{ z$EFlzU7~q$NpzvWPk?vcq|N!$ly*Ao?mzgrft*3d+A0>@u*2ZPl@Fv|C4bk;0{-$| zN{9Xy)@4Uu3^*B1p0vUTf<76y>2;#F(C;myJabCc{k8JUaap?$%ViVpS7DRV2b%uv zq~8}jtpEL(hwWd#AnW{M3O9PP`dhX1`_mq_#lT(KCp>I-scbIseX6r{ukD;Az=vV1 z^t*|G+A~j`Rs69PgB;ZdSsr9~4$qA8=kTiP7Uqh>YNv5 zyyF6A-w6R%mw95RUaykp^4Ui5))B@ayq@9@#?^W-h;z%%yNMSGW#=L8Er1Ppz1$b5 zxoO3fyc6*m8)I6BsHxN7i>gq!`4kLxE4tnOC!F(*bd2dj6Q?u^?f32Wt2Ebx4Yam) z@3v3LI2Pc&8@AgRSNNm)Aida4^zZoD;b8|S=Q5RtGw&tA0}d(wQ+;eh9C}L0{HzdT zmJ{Elu+PBvd*wXj7aU;bQ@E}BFs5OaGeZU&(J_|7vGjY8r$iMWNBu?njbUp%*|J`6 z+u<@EBJi$(--SAQ19y)&rmoo+HXTX zi9N&{+Kq8hZSYCkWPcj;QJPb?WAB^Qi_7psL$9T1?G~91_#((|lnkHR^LNU=f3ICE zvYTL3+C}pkGPhtnJ6;9ac$LU_IdOO&-N(UqXq+H#^y53CaVenh?^kLpzii_ySR!Ld z=jv%0%+-c{>b+-=GNxX2PG(Un9q>&KpsCyoe5Ve zehc@&0of0-dcLuta>&Uezf@-d+J`;i6xC0CQy_g-D7wN{toPhD+AlJ?qE;5ai1DbR zkI`A@3HU)U>v&uF7D!<(pV!;`fgEj~EN#ASlr~Q_+KjRE)0ld4wVUE=uF`JWf1dbN zj&`5Z+6|sw`LRgNu?q*1$_Is&!Z*}=?hY!ySa5-nALr!5#6uhw9s=)-H5y|)OKjyA z$eICOX{O-^514@6w?ghuYf_B;)m6y;hG!P2Z@1Sjr?n}p;go|ns)!O_5{FE_OYux0 z;;C_;8kV~ynhUQ?;X8f48>jOX=1C#V^HQOSXZarbFd%b2)cvqJO9f>t{MHWP6L+Qj z;d@g4(DsxiaUo01+w&IJ`v%so!!*wi81sClHqU1OdtAoH`OcmZzK!0u^j+m^-~pTi zr(J8kc#i5K-g<|Px`N>yHgF|vHAZzbMs+1h_ndwMgLo|Ofp@2}d?#tVe=O_fLD~0b z(xg6UW&IoW(Yy4?yM#0MTPuci{eNJg7h~;@YI`5;3y_&Pa}{$4_nif@&tRTD|GVLL za;@_w#(Y@TKaYkn@7R}MoljC3$w*e#P%iC+9=WrJ={&l#XO}uM2a%Du{Dz262khBZ zk^H=IQ10EzrH#0&`?b1*m9>-l@K@62=cLWhH(N;G%R=XSSv=?M4~K9jdHU>Z`CdkR zC)7sF-{Q~p+m*Ml{6DDk8r0sK)%|F-YL~)rjvS?ZtS9ByWOP{j)je%!JFWRU)qKDm z9?F88+S#whMOLRg$`8$h89y}RG=G)gx5kBcr1X0@@GeD4c1GU=_$_+(W+jhK4mM=DgCkj(E^)nl?+ftVoajZ-nVf_>hz(GF|M%3seQi`pG+ZH zGc4_QF8c#0c{PD=Z=W*<{c}_EEzW<|oX8f7{zoY$RS z*Y&?s=wd0qlER=bi(^(}xosDK(+68n=%G60A2V>xPH6Nxq4oCcHgL)gYjmg3C%oDG z8#J7;Zu`H=hhLmG|r6Gr+; zBfYuAoxY*OoqpL!zha~xFLkG%D|M$=m1XHSEw^n8Elx@n=FqU$)@@yWWmy`QTfq8_ zrTg3oSiiG$ecONqL!mB>MthcP^`ku%1}OBmIbxzH6#GJvG&x9-Zb+uhQs% zhv`|`_ptUlcm6hwhCMpyGA(~sg*$(@Myv5Q(qA*uW7FN~Yp1)@tKR2MkG;>Ge&7b9 ze{OK6ubtse-!Q|S9=g$;UU8#4ye}8#@x`k?Ecm8tO*32{Lj~MhagMPrE zA2#Sm4EhO!USPTDJx2OB4f;C<{R4ykxk0~f(562WU5Q)S_Zakz20hoH7aR0ygKjhE zZiD`+K_4>cqXrE>2h9_*{#4#J(tl&nKA)Q|_3@7?D~lUgrO_WDdRK_=Q9(nO>d8>n z{|)>nVEqjXz1_#_?QWsJYSi~_gZ`fe{gOfd#K-HiQ{J0K`mcSwK11e-bg$o?KF**^ z4f_2CeXBu##Lw${v(z8+^ZI&O>RWH*-|FY}b&KTxqMz4O^egD3k^hjN*HZ@v@%u*l z%dpLvmbEoIWu)5%4L=fCAJ<5JzkvfdIl${+2!O|1#13>)D?P=wA?h4MBmXjkt~cmM3V1zlkoN5?;Pv~Ql$SK}!?dsEQ=1#x>f1YDh?lViOmua! zA(=qrCn^(5*zEq?KK6h@%lu(v57c(WkATqJ=)sV+yr~LW`Q@v1Aw0S-lmpK zO&dB5v%=Bg@v;T9H@lJWGM11!S|94{Pza5{GL)_o_e3beF`3jIJ5e{Rs#`K&wT_bh zGWdsZ0d+O=%6qD7s#j9MK%%Q14c{$`SEq_^@9NyD#Z}kTtyG@V+5Xa%?(w~8Lu2P$&6={QqkTO$X;Dr6x_0=J&%OZlXH$Hun{Lcu zh&jSsb2F;R`C&-Xh$q%{bhPD;t)~~rIPa>iS#=-vWnH!S(#p~p-bFQd3rzWStz8NH z*50hSrD>!SFqhRQmoUOB6^d+m6af`6cU?PvQC2fgYoh+sEu9_sNu7v%qgk;kWWns4 z=diCGkx_e^zT-qoeOqgyE2p(CCR4qvJ?$CS`|7LZ3?}epE^tb!_*VJS^$D!a-<8!X z^jvzJ4rQvn2)-6qwvp4{Ku63r1*oYmIMd2ibxZGGxvcI%hE=Rwytulyb_8nSh7JeJ zk!h{10#O*Z8fnN|<3RMpnoTR$;`?is=$(uugFXZb5-nZsht<-H0+z)ptEyH~6Y{_= zz&$dvE6gr_A`ScMI2dkic(_+$VUow6CO7n|WG%i1sLCnR3^@@f{`0>9uE_yxDX zFSrGM_W6$xw)@jth=+i0uJx9}x(1eAm(UIHiA9I#g#{c!p5r4P`nZHWz$|Q~`U|;z z@EL^mR|T1FK=?P5FUawI2sg5vXDTt`&q08^$+Gqe4a`Ec>v;(eNEjT)?Fu7or}iN1 zpngFZr~C*%Wm#t=Jy^u_e1x6EMgAB*u6)!tK;=CEPy9i_ng4&^u|KmT>-q zl7B$*Uy%HlC4U5%grBFg0KzBeJrRC^o}0+e1qG9^4B?Z+8kTZ;q?FT_OBhGkOYbN7 z2P8ZuScZ^CVf;>%^0=N9`v1~)wXsoM>G}90_Sm>E6q6WdU@r$JOH^+%PMp+5NIM3z zOSW_~2~JBY+VSAIwkHfTlbJCKTiSTR;Ul%RUgCgB*!3jwCT!%kZp9CjMG;A@Hf!Lf_xA_%z7z7<06t3T z13pIO25h8!zE_uzANV+}bMDpeZ2~q?djLK`{S5H8ss19|lUjEo!XIO~i|l@h2#gHPp7M~Mw<^Yjxc^yqvH+z<&aZ45nrKlS*Pur7-Nh|H{yQ1FNS>XTc`7V zV4aTt5Zj+;^9Y+Suz8FTx)p0_p*YUwj~K5qUSqtqPSi+XiE%&?k6P^)D_YBPSz#*z%Y>)jo6`%Wce{%VL-JeX|ulo~hh@)Ss z_UV2p>C@@Qz8unZ$fwhPgw2CK-Tz$h>3(Y5r}J}?-LFFaHHs(f&r>{s->3A^I@0$8 zst@b6e1H-AcktJ`Ui;ev9H#fI*Y6oyuj4nqUdQh$@C^!&)|tLHDVz;DzS!qOI6F7! z`0iqJ-v*tJlN)qC&$Bu3C52j*4zQOl5RG%Nv_Wxh5j$bDSN8|y=eK`&ef0LdY zc*GZTa16P^5@LA{j^*SQb8tY;&EaHoaC}wd?;uw{SU9|=L_Ws_48y2LeKb3cUB=W3-NOC@%h2j*BN6_|IK%T-;u(AWzR_qZqbuKgIrj zTg3bYxHIJo=yP87H}#iS^-k-vINDtVDkZ*M?h8kZXwslbbjqZvs?4nQUhIn-dg*8A ztql5;Nf_aDyuqMFnb~Qk*nxhLXbOv&oJEAz56J|H8R1yU3`Y(dnD&!>cQO{G)wF3q zqk|E}ex>qb(31S1naV`todzp=V0-eeY;$WXk#md2-L~eTB*oi|7-55#35Hr=Y!ET^FgIQ1H=?sLGI>TT(3rb7s!Xg0f0|eJ;OsF}l-{>`o;U zX(vz8;f|==-ClR*%&N%9J)Q2Rc0lbU5dhUdD!;Ky{s38`mT*!<+@w00NOgwe(Qk)U zwG79dl#+V_mA@m}X<(Sxhdoc&anS8(6dkI~2)BL2%+Qja{TE8QvRBDQ5VlB+s!(^h z(b~PQk)itJYC?sJ8lfNyj396B1A-8Cnok1G5ct0KQ zYEL8&^QMyiud@rg_&gK{{KsA~IREYcaa45p?}SL&+0)7OD+RVZo$br!$63Ya(k)Hp z_h6{t?${Ey-`(-tlPw}<9yDWW|0qt47xg}awA)Qx#8YMmIjOp#D;8x+dl%&#h9ooJ z4trgW92&B+VskD`s=Mvf(}t4m(U9Fgq^K;>L`hctCpXu2tdr&?TeLzfqXAXqsZ&j3 z3z7!DkQ$KnrsEQ=1oSJY3SqIm)*7%>wSW+Xvd#o)xTHr18?C$2T@6h&nA1cIB zJF2+aZv+|c89c3SUYS)w7pUZb~+5cX1Fi2Uj zTiMyW#0YdT&E`SuEXQdmZx7|oi=55(2G#Hiex=KkTVhAq1pCEiibgb?UL>d5w#%Gr z->%*7T-|ru*&RvY?*gL0j=!)q={^&DYWEHu!$o#_iYYL+g?xO*R5%`q zJy|hH?@{)Z@(wWIkyYS(4|=2zI8Z4!1GANKD{!n*27!K$Yy-Ac$RyD4$X?}MAqRkg zGC2giQYJ?ge|)*TqWqQ1Yrv@rISZUDm$#LEnQ?OiC11BrwX5f5<^aBSf zWDC$=AzOh)g^U5c6|xtYt&l^?f2F*j7N(Ssz$+d(37o2wQ!1P?c^lYMCab`anJtq( zV2?*ODx~)Sr#x~GaNZ+h%1r42PEx)B2Rw3AnLYA~i)Q$s(3H*8aZ9WRsX|E{$)d&} zap&VzLY#@q65^b~ON3Z_9wWrLgaJaFTkIjkxqwrI(6RhILY!ly#v*Y(u>;{3DK19| z{UuaMCG?b>C4_#GAwuX_ev=UQQr{xP9aWkd$%}+hgiH8CLY&upn-Ka%-axpt(0!f| ze5v~h!HY`mM}qJ40AWP)N6ABk{e&k8sVT}K5T_t~!sCQEuXBzNI+#x*UueNQg78Ze zpQFUN((e-DTg9Id21?XgUbdDf-en0rB`1+DB}z|&93p&?5NA^V9O0KJau#{LMCme@ zrL+kDQ$pw?8ASX`l@4T>s-dK<4p0^Wn4=N@R8@cmo3p*Z>x?# zS%aT@9sl5jK&(rz1?Ii>M+ z6kmR0iEt$3xY2;1?cs|*3G+CSlV&k|zgI(oinVW*KIi50>OCi5Kxn>Rq_Vh#&rvhC zE6~SS$5_wU$heiUg>e^SE8||q2xE*f!+3lqsvw=%Xc?qY0Z+{+kYj4@^yk1+N!_Aw4Jo@X3k z9A&)7c$smWaf0zG<8{Va#?KgUG2UUsw_KHe#%e|%V?ARd<5tEN#$Al9jC&a)j4{Rx z;}OPQ#y-YD#`BCLjH8Sf880)AGfpsGWxUQf%lH}NEyg>H__nOl%~;LoW2|GWXKZBL z%GkoVi?Nk)FJpu;#+YF|!r05$$2iD%o^gb6l<^|tWyW#F3C638*BNISKV!Vbc!v?= zi%LIZHKUKQjbdj&j!Y!k`O z+ut2nPVv_ngO=CDF|7G^dT}JU(ru6Xd*B`_5zgIq#hd8wweX%H4%O&AB*J>VL(W7xOova|2^4 z#$PqIQ$1*<$`UKpW4Go;czPNnU~-6IP zkj*DWc6NSOgC~G_WNN6Kg#W0LUtOfD#DvbirSdy1z|%lWS{Kw#~ze z=M{IXZ`+&_BwGqD1#kbHT~3^FlvsOSDcD%-eBC6YGHWCHhXB&FLHXJ1WPX?2WBY3|pv=7n##Z@iK^~ z*PsrL^1W;OUT^i=!z%vvJ!ssEN14Zrek(~&hMq(8K*wDbb2ZLBKzaRmewmND?lpH7 ziQ0=UzF5W0(8hInAUF+v>ygLNSDaIQ#m^PTxl`B6z=h*~X)1Q5f7&b7SXJINmd99Q zmHO9MtHkJBPBxiUy7FvuD}|X&_FDs##vbsxQ(T4!kLYK5D=1AdfGcBYrGm~fEL zARHmT7wBoCr-h#L^fb~_PmiCTK9W06ajKu{pY}}kTPFqd&Am=>d)=z?zfSr1x@83W zt)j8)4B|=ur;C4w+5q_j@6JkUt93mT{uqTza;4&2ZfyC15bt?tjC-5%qS}AXDy6(Z zUMoHu@Q@o$Jm!l(zEQ2X$GNU1gVS%@_x-7F_LBV|R$I?0YpnYpZfIVH;EkPQ{cpZO>D1g3=WtJa<{noc_Z4!Rlpljz z;|A`TqmM#(;K3|n9-99^o{Twn%0tvg?IpQAhUQ?VsmQzF!u5(&$!^6R_6IXX$rIDT zg@u2#;|I>HUkGRH{mH_@yT+zr%XnoV~G1ygx@PCaSZ2O5S7iQN8W6 zDoNI$xNcZUqWQA~FVKI$9-ym-Q% zEuN2P>{?Z%_Yi9M!5Fp$<5rQ@bq($n?7ct_w%V(qC& z6fI|JW2}cla|b!??sGLix5t>^^rtR!?aBbnbptfV4$jaV?mM#!3olPlzfm-homnT= zOij6Dy(IImO5e%UOjWjjy3~Jy=3OVKuhLv>Hh=!Wnqg}t**!R>IiZV1|J3ENkCodY zatkn5bRF}cf?s2WP+T~l^j$5pqX7i^Yj4TQ>nXCc(v~ z=JUo0t2%JPf?k=kRL4fi?T;VH&Rp1rGCgw>eAZd;P70bok{fuLbNw&ZZ+p&c&$(U% zFaK{2TbW1m=Uk@E3rXqLIvwGxa&Rd(|cOTZGy}_Pop29d?vX={JxB!c@2@9D7lC|Pfihj9bF7k!kE`d0LV#ojYFvBt02=pF<6IbZd->K+H(^ZGfOQ_s2P z)X&pzL)tzhAQcsldEG!hIXQ3t`U=YhCOyCh0-nhdvPQ z*muRAEB0Ft7wP?3^m{KB={;aP_=W~unwR-O{|7!w+zZ0Kc8nj~8`@W-_s+48j(wo^ zBCQwH=BWa=TI5dd{AI1pmuIiL|BL#Y+f=qFyHe(U1b6Tp+u3}OEm$M1)_g_oy$I^w zE5{4igKKxp4p+RY%A>hJeFIl>G*=Ip&yV@_j}!=3?UfX;S9>J|?6Fr82y0Hmn&9#2 zy0+uf6rb7Wf2rN;6))7Y&}&@lE8zb=*BmO>*SX^OSsVPkYPr|jFgJOR>+`H?58x%G z3(KxQ@3{VBEFBHz=TkNGX;9sJc!Sek-1&sM*t5tFc$c6TAn7lhx9)e|bo~b(7rsg0 zJH@1{zI{@x>sx>pt5<2kHAIMSF0(Ya&GH%&{HOSi^pj%ErDD^=)G!*1Z;?OjLxFSR z;fEhF%@@eHaJLlsf8W`!o_MBNon>sY>NPr~-H(O(8$D#Ymg+$bodHflJ8r z<_;Y7jV8Oyl;NB;+@g}GlEHDv594dvFQ3>m51R2zM@0ErDjQAU<732r9|bp{F1kyp}5s#TQb2aIUEBO!3S%odEQ=+GP3%r!HTQ0GH)Ro$@b)UxjB!|`^O zI$$0!)b;&bY^bbsdc!)Wme{MTwr$x?pBh{$>r{7kVtZuke)F*7LB&Sh$hXsKnY7^gi9nh91}B1LVjkG` zKkWSrSWMCTHxBQarkZN1DX9>q1CkIz&Qqc!giwSKLKGDtObJN{A?FZ}? zikw3ck`Vr%HM84m>-+t^&-1?TbG_H~{;z+Rk2Px_*Wq6GVeNa}OGEWhBuImil2oCg z`koV~!MO6i>yY_bVvmsJGs;*njOv@E(#cv)>2ssBS!OV*^|0i74f*Cs3g@q;+6Psk zru1o1eoG|$kbFCw&Zxc>*8TmT-9uijUS9NHeLTpYa-U!1_1WKbc~S<#x5*xjBu~DN zxeDn0WY71W5?rsYI!}Bz1JqTq*+_mAqx#kz#y*eS>d(7BfX#<6^!bIn^8)t%x;#1W zQ7`VCCojK%%}4W|S5WWzVqQL)&DYiU-}X}Hz6YxQ)QL~kKl!Z;m1iVR_H5_JwIlu! z{5WF=7}dA$Xdx}173VsmisP2RyFQyy`ApR1uQDp%iMsrCMzvnNE>GrgZF%|oj7lf0 z%RgaMdk56z$5+d2#KYGb)*1cl}%ZpU2CSIBJEwJc*-5KR~MDsM+%JB#xRF zFHho@#q#pxQ9XHa@<@5ocyZ-)1gco<{Og?!{=Ghc7blMYM|_h0?a%soHKkro{!Ep3 zt110V$ZrXo|Mz+k(@s$LOEsEE+%6F>|L@Z^>)o%(&BZ$?uh(Qo2vooQ+5Erv*RPLO zy}b5+!~vUGFCS4azpGyUWWD^Ydil5Y^6K^CUYY+d;&e@{m!DoQzo=e*X}x?*z5Kd* z`Gk7;{q^$o<9yw&moKfCudbIDtNokTua|FKFW;kHzHhy}cfEW-y?kW7{K|Uy`fK|+-eUu?be zo9pHG)yto%m(Q-3FR7QWu9r8c7x&7lUcP<3e4l!G;!{X<8usVB3Dck(j-NzUzl8>| z2ca#Ght^Cy5Lu;2sZ8_lBO#|$8umX|{QDf$!Qi@ECI^Si37pU7rr=@0{;F_R%1~4^ z=+u8v&i>zyH%0!pSN^ZOuY>Z&4$3<_C~xi1jTi>w{U^-w3z+ z?N$|x@X?UpG46Nq=`e|s3|pZ|7(_SSzpTi0p-^^r4!NQkVk@xf$y6Yq+?o?gnO z25g%t-MND22Ty`_ukFFrjRbL7xIhb z$XjNS6;EuJOq)1o!kqd4pGV&c4)Md2>eprREM)*@rHkypXQs_%SOc3;TI{N7ht8Qp zl>h006M09;Xd{mJDSma)e^e)fCWHo0R^CrcT_#9X;qj!FvjWExr#GcR z@gIdDQRI{^yY=1?T=#ywXv(BnApu?N>)-sJG(e7$NJ#(r7M3dONAe=Z45KpD>fc0` zNU?XJETnT`!ZIp*ROND9$9-_?V3z@d`>95S>Zg*}{P;N~#jttRV4&w%UZNU>%4Xs{ zg;;F=?SzWu{MS=;oQgd}R>Tm}BO_xDj}-qQX`+WUbyV34W=<-?$r$Mld&mTpI|l+ zHV~U9{<7q~(QF#erbN#$b7z<=HYGNQno#8jt9%bhyQ}@diaUYwKbp_yG5m!Q@N=UmHt)~o zjWks4O1=S_d8y2AGjFB&t<014MP{RMJxRx)pV^pD+ci}&G?cNT#-UzfRX>=CRk31h zN$ShSj2ftl0Yy@>?uWF~E3qn8OC=jyimdk`$H{sha$Fe`3h#F(=_Ir(n-;Oxm9p27 z^*iLbzS^qe{@SYJ+qG5ge1kn+$R00euQw$zqWn~G97qfw2OU*^2I;7-57SXypQxki zpClbsf2FZ0vB^k3Wa+5-DTmGHu_=i`rPd7P)lv0Rv5u;}OG#>h`m_0RlA5C(*nAa9 zn=(udnZYhGfAg`uGrLHS2Z3KB%RL2&LXj1NE(3lAn8mt zRuBG+3QU8RoA)dtB!lqQpE@+^iU*KwSO^5tw9%3RX>!mX*qkoilnVovAW3dS~eAtSX(xr z2LqNK3|Kl~^QLUxjLpkPY70KY=55*3j=j#Fq-`+n*yD2cxUYdKMol6~gHS(`&PID0 zsM-rg0Mm31=#a$r3I?5!6v9AG(onP?N#~+pNjeYoLDD&@m|#uA@H~>Ak9K0yLPJ&i zma?g71J!%n+0?Uv>bNhPlDP#^&LB1oX`mW+VI*x2zRezwYM`oDd;=A~Co#nczfWY( zC$Z;K*nAqBSFZPI$gcNk$gcMx@xIv9icM|Vv?rT7v#C3qda|hxoBFY70Go!eX*iok zv1u%u#MAZ2`G%c)c|PU*GXC1IDrnC?KUyb$u80Y_o25%GQKgTY=7InYP znSW{Cf%*5ZuB9OI{uz`1({;AYzped0UDuQOm*&ZJ|2s5K=5G~@YMj-Lwf{!*B!0OW zFaJN$Jn`Rf|1a15KY#!C`kOFkX4{FAW_Ijq=g^+~o)a{!t$n+0?K-s$m^LdktnJiU zp@iA^Pf?z&e=K-@@SMQFkoKX$bJ_)G^1YEjgZB3NdnsqF{fWMbP22T2p)OX& zM^Ii&zF`YQjg~nx3WaZ$h!IFcB&Gsmx?B`3WhP{i%haRP7=@LuL4PfYE2E~s<6}fC z)f}V5j7Z7|Cv+35*f%4QhAcZSaVEl3T}z%N7cY}2L>^MPOu)#kC2}3H=Sm@Qu#=Op z#lvm4FM}m4E)xz`7DY}fnMgeNbk&(D%)~UIkV!)cMIDt}Rh6Qc0!vY}h!G3P`-HN7 zkxfJb6Dy&f{CKI3NF)>XD-<%TmJwH}EMJ)lq8Td))?Uua{;3!^-zeT9iv*xC88HHVlpt@G~$FQSGG}#}THjV}jhF=Cj*W&fF9eLp4m~4Nw2%nxw8aXk z!oXFrj+krmXqm9FP+-+Eav0+$W0v+iAYW<@@|DS%NNa<=a*do=?HpfQ18JNe`Xx%N zP>9K*c%d|o(RR`mTw?kOB-Yt`!~^BSG=vJZv;m5B(f0+;5mpL;@dI?9LZm@#8>xs9 z$`UOZwa7#dv1hOh%BWaN7%9|lClL6`4CP{xNG^ZD2>M5OiQ~{%LSK*&%GYAINJYD3 z!zOjPOfE2R7CR}lv$ZReM<4^$_osr3`6>5WI(V9Ymhjl~AGHX*%laP@{+DWWQ zLxFH5x~@{8q>qypadm+D9hYSU>sTs}k*TRkMFOECV&h565_MNBW?U3P zCL+#NCa@Aq9it-F*jNd&qSE4q`HCbQUvl2gh3QB%tTSNB3oppjyGz!ffW6kILRBz z2W`pO?JQ-MD&n#mCdP}!>xE2|c!HKT6UnGssfA?~NIPa7_HcG)q~ho_Ml5HR>Pxi+ zgyRYD8ikysaxo*g6fY|9l|{>$ep1Fu=xgLe`f9hfflLgp^VHv2rgXuO#)$ZY0mq^js#p)WB6Ex9v!0aiT_%V;m#TAv_dAm@zkEWN~X5p+cJIXk(bAz_7?C z%ta`)P?*a^@8 zx%9ZgTI?u~V5Hh<2d#xIPIr_t(hw07Cys6%A(X_(g<4Evghq>E#uq}x-T=c&gAq>@ zI*4(d(q?R6!D`s%j4PT@A#xCZWlZH@2-;v)U~5A0fGjykM=IBXNH9;sFE5#T6u6^U z2A<;7n8^`ExGqc7)%I5u$mJf!Oh;j4v}()O9S?1D4 z?o0$Y2}O4>>8UY9n6%1Qz~TQ z2$`^@$V;eU$}Gb(#PT?ip1{UXTBeMgqs6q073{WV9Az3(&^EJ*P=#+pMiZ9`taVXr zrr$(Hpk^2;lSl9_hG|5UTu$cb zG#H_BB3~hM6*a^#)MSKqA`78ZELLQNMyqaeVopR6!`x1;!T1WzEGz`#AaoZ~ppbxn zc{z%Wq)}k5WRrp%sr;mKhpZQ}0gS*vArx7malz!IhH_V2E45tdki|G@xsEmviq#pZ zk(OB9`hZ7krO#4B@eP56VhYnR{xYKwG#H_^VZAU#A>W#0#mEvw9*n2aI}zB4DA6fP z9M#cT&_?PgxbM}0QCN!{W1^WLf!Gi=a*Y-;a)qxOqmV^gN0C*ZNp3PSI^>4pa$!q3 z3D~O;h#7scKrECJ4Nk*%tlj54%9-U!AafIF3iBVC))D>1tmMk5l)o^R3 zmt7;ffkKf~O(=?tlnWiY5LX<>f(j#t;T}1z7M4fU#p=@Np-o8t`FfDC%82cTX}N-z z3m{UH3<;U4lX-^*5n-aV<{CdqoE;BAoJ{PKs_B^B2@+$REXY^=k_IXvM&FY4Lm)CE zxrf--XQ;DG)?~SbsRhGykjtF!&XkkUE{`zO&fFVqVZcbG=IZ_IAcZ5+mCp5qo*9`m zE>i+m#F!(?V6p%GLFG9a%= zpEGhlU#Jf1q^)Gq!Z?LZl#`Uv5(vanIb*QF5Nd-$*<;cu4_Wg@3P@cU6D@OebSz~i zG9i~3Ny|hd3owKmD-|uRAtGgiHu`B$e2H%B~mkxN?S=BH81s$ zicK^nR?aILEF)Y+uGAB?)Y_VfJs9v&Fqetzm?#M&b(M4yiX&xx!BVb@WOnjMu{lwe z4HRinPB~FbV(Z2NVM{NJo+Nu_w@^<8zCv93vV<)Z8#XphOfwhB8=|2g^?aG7qQnV8 zXBney*htn==&aR{X(0pGa-1`*fs>U`m?cn<(F-hYCXq1qO z+5$zEmLfvoY9Nks5)kPti(;Th$<>1t3{>8ZVrHvIs6DZ%ojgmy8XyYB5KPTY+)liq ztsvUmX0^3!PneMhu2smT(Ndw@)U+dG2(5yVffO9UZY)Ip>CApc<{?#vK_pU04y{^> zp&cr2EVMLe0Z}TN<13SEcQSSpG1kvb6BWa?eWQJ4>S}GJLMsSfaf~+fEGI^Q&R16_ ztlmy0wGbLKwvJ_@vlK=$#@(<5qd_bHQ$1qYC}ffPN_ilNU_`!xzRKo{jxrQVi3cw# z3UU?4Ju=8*e63|3jOj|R5xXPF1bUT6&6esD8Z_4lpso~{OZFfB@*;pCj%zVS}--yL9*0hH-@A6 zGS_kgg~*AK8N{hE9zvnOkx>_`>7hMj%o>rvQ0#2o0)#4$+Zxx}lTrA_$=X_jt7A+E z6*5D~QGuzcAaXe|uqb2?#66Db`OJpfmn)NK<48nQI>yj7&3p;S6hOCYlM z)rgc^GBT$qBghiDT%qug<*CaRO~o>?C`YKIE3x_ji8Qv!Gzs{k6D$hGVv0C=Jp*G* zAV6uHowQ8-6;e~Qf*tx!u7Db?==eObrAR34i2*AYD+Kb1WUNPs*GnN8A{mK>4zZAR z)%8kcGCx`3;3Nejl*pyhlMSQ_g=+^!sFS!9t!H5>m-!kvISGm7W^5QQH;jXfZyK2; zuw3D)NR%o{)%r4V(gHHmsLlvmwlqL#MGgwWf)onv31TPLWj3X5%D%G&E z6X78;`+-Up$5=_3M(Q@oPMT4SR8 z5hd0`ZX|SD57we(YGj)DDoZR4vL+jew5$37BR~Bk0iDuiHE8DBd;R+ zeG|d4fl*W<-rXk{RV=(y z0NJ}coxSEPql&R{fz4k6E;Fh%%H;0~KzzLO0OH$y6SxiB1&A*o&7#0&fCt3;Ubwx4ccDTEo7FJx12S?F04;RO{hV1j=9& z%Jp_+o%=C?%0D|D$Njq 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�Rf~aknngDc>wF)V+p&$D@x-nplylTS;E|z;R}* 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

    jfK!nQ33NsFW4qz=m?S96>!zjh)$& zhBcAi6}{OR)jN1vCrc9juDPtEoWdlDECKH5}uJk5F*?Sb2I4O$T* z0ozYS!Bu-qf66TR!wNg??rA+5(nEKm+*`u0Z>%9~KCYPo)S+%3B0{7Qy6BPQ-tHEO z0A5$3@D+_;+l1isbu+7F+^Yi4%1NH3#FRml-q~-tJ2h^ZCEi=HlOaUI7ip|0B6Slx zasqnlkhUf%B18t2{z;h;y;8)swmmKZTSg9GgrQCQNdU4}2p`##i2GX%1+Nf~Zk~Dz zoxoVPSe9B|;2?^=z1xvR&5WqoUU-kUVDt=G8LwW5mStTp+G9VvLj_o zX---dZjFBjWI2{?eN@d)EdPGTk4vd`VPHyTTtrsl%T!1wi=KN$BIo=0@=;;;s}?%%NkeGJqJhSQ z1RKmW?~m^px4C2&n9oBBPfS}wc%;;s(=wW`+=17s)E*7}Y9Kbslf>kNAZN`>Amy1F z)2|mjzh7cPhIAfJQ_Z~4F^IWy{8{dx$j)H}nqTX9erWX4qbIpiV!JPzb2Ze|`(Icy zE@TV=OfRQ-<=lhUjS<^O$xnuq6ZP-MTYFZ*J{`S#!B#_E_cDC*LScgT-GYSAo34fI z9dFLc*3StVU`63~2lCsIl7OtX8ybv~7h_&E64M&;l;E=kK zWMrZe=ndNy>A*oa&filUr_yK$(ahT#bDE?wL=w~!`O9Fie2LqXnQ1kPfaSPzm;BN` z`Xgl}wHeWz-zYN+G(N${xR$|-$fk)+8BREm;>IU#?v&iJHe zO3b;wzfvF2f@O6v{?;l++;L+LnhtQH8&(`XHWKPbzI0NGP3!&;c2UI-eE`s%(<5B1 z;Q9_G-FT+y0h#8$iE2jOkE)V6!M_ky{y}g3{6nNv-*=^=z_gD~ALcP`M9pTyUO%~Y zB#C|$dyli@m+1^F`rc;)oiH^X=Wh;1_C9j5mzIbOe{d ze-j|8rzq`2PVWz{K$f#d`CIvI+p`~Rt@;5w>w-rs!28I8>gp?rUSF!qFE|t70T$`^jo3HKzvfNI zE|j)zhxCtMSB|7<{@uj*Inrerg@!7TxBxlNnSI@FwDH%y=$^K<*TwaLzi>Rocd@zCAD zDf2V}zB-&6om(c;#h_skTN5Fj(v9Tk)dwGnN|e_hf+;om29;>0fggzXj7-2#1~7xC zp~S2r8R>=~LfqgP#p;E+_fcnP%Y!+7#r&vt)!*i0FVTu-8Rp2~%Q?#)}?`q%+$ z>CXUl!_W|FqX8SV38uh!$D9}Dlm+Q*nSX<$ZJXtPeNVGJ~vpT)?&d zc1jZeAxUzGSnZ{>rhJtD9B8l@zW(mqChLi312(Yw)n6q|Hwe*jU34#B59lkUL6y>U zqQsVWoOwFMe^7^>(Dci~mi2n&^xZ)gXNwel1v=D=Wz3`DcSwkT%9f6_ckGj4KeEFg z{;*Qc$1+)EPv`32f85>q#-b8S;n$=Y*jwNc>=}L(YaZ zkvvXvle?v~*VxoGTsAYdB)H+jvdtq&vlM$yGmM)1fUC!r3e@w+G4=8Cx?h0hvC!62>R|i<=+Bfu(#QyOkt_SU?->~YK;(FFW zim7d~3riDlU@9+7s(}WN!i*n9H=jUOObWUIE*T%PA&Z@7h$<&aW)_NG|a6**h z`B%yCC?NrLy)6uhp&L*M5Rogtvw`c|lK0-g!2E zSQqJ¥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�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�JQSf8Qv){~7D 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%0AfJ$|4MV)fT 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

    #^v~sWQ$)sXs44FN2fB#vjjyJgwFeX1Q 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%0DaAca8Ayre1#l1mY>P0r%dl1H=q&e%=Us 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≺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 X{6m7 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 z%0A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