From 1de86458a6791c78eabace9632f3249e2f69ae4f Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 7 Jun 2024 14:46:51 -0400 Subject: [PATCH] fix DVRS unit deregistration issue (units weren't registered properly during GRP_AFF because DVRS doesn't do the typical U_REG); implement 12-hour timeout for unit registrations (this is only applied if the SU in question has *NOT* had *ANY* activity within 12 hours); --- configs/config.example.yml | 6 ++ src/common/lookups/AffiliationLookup.cpp | 94 ++++++++++++++++++++++- src/common/lookups/AffiliationLookup.h | 18 +++++ src/fne/network/FNENetwork.cpp | 1 + src/fne/network/fne/TagP25Data.cpp | 2 +- src/host/dmr/Control.cpp | 4 + src/host/dmr/Slot.cpp | 6 ++ src/host/dmr/packet/ControlSignaling.cpp | 6 +- src/host/nxdn/Control.cpp | 13 ++++ src/host/nxdn/packet/ControlSignaling.cpp | 1 + src/host/p25/Control.cpp | 15 +++- src/host/p25/packet/ControlSignaling.cpp | 25 +++++- 12 files changed, 181 insertions(+), 10 deletions(-) diff --git a/configs/config.example.yml b/configs/config.example.yml index 365696df..000b2ddf 100644 --- a/configs/config.example.yml +++ b/configs/config.example.yml @@ -137,6 +137,8 @@ protocols: dumpCsbkData: false # Flag indicating unit registration will be verified after some operations. verifyReg: false + # Flag indicating automated 12-hour idle unit registration timeout is disabled. + disableUnitRegTimeout: false # Specifies the random wait delay for a subscriber. # (This should not be altered.) nRandWait: 8 @@ -218,6 +220,8 @@ protocols: verifyAff: false # Flag indicating the host should verify unit registration. verifyReg: false + # Flag indicating automated 12-hour idle unit registration timeout is disabled. + disableUnitRegTimeout: false # Flag indicating the host requires LLA verification before allowing unit registration. requireLLAForReg: false # Flag indicating whether verbose dumping of P25 data packets is enabled. @@ -277,6 +281,8 @@ protocols: verifyAff: false # Flag indicating the host should verify unit registration. verifyReg: false + # Flag indicating automated 12-hour idle unit registration timeout is disabled. + disableUnitRegTimeout: false # Flag indicating whether verbose dumping of NXDN RCCH data is enabled. dumpRcchData: false # Amount of time to hang after a voice call. diff --git a/src/common/lookups/AffiliationLookup.cpp b/src/common/lookups/AffiliationLookup.cpp index 65ec4fa3..fc7a9acd 100644 --- a/src/common/lookups/AffiliationLookup.cpp +++ b/src/common/lookups/AffiliationLookup.cpp @@ -17,6 +17,12 @@ using namespace lookups; #include +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +const uint32_t UNIT_REG_TIMEOUT = 43200U; // 12 hours + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -30,6 +36,7 @@ using namespace lookups; AffiliationLookup::AffiliationLookup(const std::string name, ChannelLookup* channelLookup, bool verbose) : m_rfGrantChCnt(0U), m_unitRegTable(), + m_unitRegTimers(), m_grpAffTable(), m_grantChTable(), m_grantSrcIdTable(), @@ -39,6 +46,7 @@ AffiliationLookup::AffiliationLookup(const std::string name, ChannelLookup* chan m_releaseGrant(nullptr), m_name(), m_chLookup(channelLookup), + m_disableUnitRegTimeout(false), m_verbose(verbose) { assert(channelLookup != nullptr); @@ -46,6 +54,7 @@ AffiliationLookup::AffiliationLookup(const std::string name, ChannelLookup* chan m_name = name; m_unitRegTable.clear(); + m_unitRegTimers.clear(); m_grpAffTable.clear(); m_grantChTable.clear(); @@ -70,6 +79,9 @@ void AffiliationLookup::unitReg(uint32_t srcId) m_unitRegTable.push_back(srcId); + m_unitRegTimers[srcId] = Timer(1000U, UNIT_REG_TIMEOUT); + m_unitRegTimers[srcId].start(); + if (m_verbose) { LogMessage(LOG_HOST, "%s, unit registration, srcId = %u", m_name.c_str(), srcId); @@ -95,6 +107,8 @@ bool AffiliationLookup::unitDereg(uint32_t srcId) groupUnaff(srcId); + m_unitRegTimers[srcId].stop(); + // remove dynamic unit registration table entry if (std::find(m_unitRegTable.begin(), m_unitRegTable.end(), srcId) != m_unitRegTable.end()) { auto it = std::find(m_unitRegTable.begin(), m_unitRegTable.end(), srcId); @@ -102,9 +116,67 @@ bool AffiliationLookup::unitDereg(uint32_t srcId) ret = true; } + if (ret) { + if (m_unitDereg != nullptr) { + m_unitDereg(srcId); + } + } + return ret; } +/// +/// Helper to start the source ID registration timer. +/// +/// +/// +void AffiliationLookup::touchUnitReg(uint32_t srcId) +{ + if (srcId == 0U) { + return; + } + + if (isUnitReg(srcId)) { + m_unitRegTimers[srcId].start(); + } +} + +/// +/// Gets the current timer timeout for this unit registration. +/// +/// +/// +uint32_t AffiliationLookup::unitRegTimeout(uint32_t srcId) +{ + if (srcId == 0U) { + return 0U; + } + + if (isUnitReg(srcId)) { + return m_unitRegTimers[srcId].getTimeout(); + } + + return 0U; +} + +/// +/// Gets the current timer value for this unit registration. +/// +/// +/// +uint32_t AffiliationLookup::unitRegTimer(uint32_t srcId) +{ + if (srcId == 0U) { + return 0U; + } + + if (isUnitReg(srcId)) { + return m_unitRegTimers[srcId].getTimer(); + } + + return 0U; +} + /// /// Helper to determine if the source ID has unit registered. /// @@ -156,8 +228,7 @@ void AffiliationLookup::groupAff(uint32_t srcId, uint32_t dstId) bool AffiliationLookup::groupUnaff(uint32_t srcId) { // lookup dynamic affiliation table entry - auto entry = m_grpAffTable.find(srcId); - if (entry != m_grpAffTable.end()) { + if (m_grpAffTable.find(srcId) != m_grpAffTable.end()) { uint32_t tblDstId = m_grpAffTable.at(srcId); if (m_verbose) { LogMessage(LOG_HOST, "%s, group unaffiliation, srcId = %u, dstId = %u", @@ -204,8 +275,7 @@ bool AffiliationLookup::hasGroupAff(uint32_t dstId) const bool AffiliationLookup::isGroupAff(uint32_t srcId, uint32_t dstId) const { // lookup dynamic affiliation table entry - auto entry = m_grpAffTable.find(srcId); - if (entry != m_grpAffTable.end()) { + if (m_grpAffTable.find(srcId) != m_grpAffTable.end()) { uint32_t tblDstId = m_grpAffTable.at(srcId); if (tblDstId == dstId) { return true; @@ -536,4 +606,20 @@ void AffiliationLookup::clock(uint32_t ms) for (uint32_t dstId : gntsToRel) { releaseGrant(dstId, false); } + + if (!m_disableUnitRegTimeout) { + // clock all the unit registration timers + std::vector unitsToDereg = std::vector(); + for (uint32_t srcId : m_unitRegTable) { + m_unitRegTimers[srcId].clock(ms); + if (m_unitRegTimers[srcId].isRunning() && m_unitRegTimers[srcId].hasExpired()) { + unitsToDereg.push_back(srcId); + } + } + + // release units registrations that have timed out + for (uint32_t srcId : unitsToDereg) { + unitDereg(srcId); + } + } } diff --git a/src/common/lookups/AffiliationLookup.h b/src/common/lookups/AffiliationLookup.h index 9e214ec2..f2c8e107 100644 --- a/src/common/lookups/AffiliationLookup.h +++ b/src/common/lookups/AffiliationLookup.h @@ -47,6 +47,12 @@ namespace lookups virtual void unitReg(uint32_t srcId); /// Helper to deregister a source ID. virtual bool unitDereg(uint32_t srcId); + /// Helper to start the source ID registration timer. + virtual void touchUnitReg(uint32_t srcId); + /// Gets the current timer timeout for this unit registration. + virtual uint32_t unitRegTimeout(uint32_t srcId); + /// Gets the current timer value for this unit registration. + virtual uint32_t unitRegTimer(uint32_t srcId); /// Helper to determine if the source ID has unit registered. virtual bool isUnitReg(uint32_t srcId) const; /// Helper to release unit registrations. @@ -102,13 +108,21 @@ namespace lookups /// Updates the processor by the passed number of milliseconds. void clock(uint32_t ms); + /// Helper to determine if the unit registration timeout is enabled or not. + virtual bool isDisableUnitRegTimeout() const { return m_disableUnitRegTimeout; } + /// Disables the unit registration timeout. + void setDisableUnitRegTimeout(bool disabled) { m_disableUnitRegTimeout = disabled; } + /// Helper to set the release grant callback. void setReleaseGrantCallback(std::function&& callback) { m_releaseGrant = callback; } + /// Helper to set the unit deregistration callback. + void setUnitDeregCallback(std::function&& callback) { m_unitDereg = callback; } protected: uint8_t m_rfGrantChCnt; std::vector m_unitRegTable; + std::unordered_map m_unitRegTimers; std::unordered_map m_grpAffTable; std::unordered_map m_grantChTable; @@ -119,10 +133,14 @@ namespace lookups // chNo dstId slot std::function m_releaseGrant; + // srcId + std::function m_unitDereg; std::string m_name; ChannelLookup* m_chLookup; + bool m_disableUnitRegTimeout; + bool m_verbose; }; } // namespace lookups diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 4a8b3626..0732c51a 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1153,6 +1153,7 @@ void FNENetwork::createPeerAffiliations(uint32_t peerId, std::string peerName) std::lock_guard lock(m_peerMutex); lookups::ChannelLookup* chLookup = new lookups::ChannelLookup(); m_peerAffiliations[peerId] = new lookups::AffiliationLookup(peerName, chLookup, m_verbose); + m_peerAffiliations[peerId]->setDisableUnitRegTimeout(true); // FNE doesn't allow unit registration timeouts (notification must come from the peers) } /// diff --git a/src/fne/network/fne/TagP25Data.cpp b/src/fne/network/fne/TagP25Data.cpp index a66a0a45..e30c8167 100644 --- a/src/fne/network/fne/TagP25Data.cpp +++ b/src/fne/network/fne/TagP25Data.cpp @@ -992,7 +992,7 @@ bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, uint8_t duid, } /// -/// Helper to validate the DMR call stream. +/// Helper to validate the P25 call stream. /// /// Peer ID /// diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index ffb0d015..7ec12074 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -174,6 +174,10 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa m_slot1->setNotifyCC(notifyCC); m_slot2->setNotifyCC(notifyCC); + bool disableUnitRegTimeout = dmrProtocol["disableUnitRegTimeout"].as(false); + m_slot1->m_affiliations->setDisableUnitRegTimeout(disableUnitRegTimeout); + m_slot2->m_affiliations->setDisableUnitRegTimeout(disableUnitRegTimeout); + /* ** Voice Silence and Frame Loss Thresholds */ diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index 7f742571..a874dfb4 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -994,6 +994,12 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s } }); + // set the unit deregistration callback + m_affiliations->setUnitDeregCallback([=](uint32_t srcId) { + if (m_network != nullptr) + m_network->announceUnitDeregistration(srcId); + }); + m_hangCount = callHang * 17U; m_rssiMapper = rssiMapper; diff --git a/src/host/dmr/packet/ControlSignaling.cpp b/src/host/dmr/packet/ControlSignaling.cpp index 9f58b5df..27aca4bb 100644 --- a/src/host/dmr/packet/ControlSignaling.cpp +++ b/src/host/dmr/packet/ControlSignaling.cpp @@ -156,6 +156,8 @@ 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); + if (srcId != 0U || dstId != 0U) { CHECK_TRAFFIC_COLLISION(dstId); @@ -1357,8 +1359,8 @@ 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); - if (m_slot->m_network != nullptr) - m_slot->m_network->announceUnitDeregistration(srcId); +// if (m_slot->m_network != nullptr) +// m_slot->m_network->announceUnitDeregistration(srcId); csbk->setReason(TS_ACK_RSN_REG); } diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index 4145331a..9c70ca5d 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -278,6 +278,9 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw m_controlChData = controlChData; + bool disableUnitRegTimeout = nxdnProtocol["disableUnitRegTimeout"].as(false); + m_affiliations.setDisableUnitRegTimeout(disableUnitRegTimeout); + // set the grant release callback m_affiliations.setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { // callback REST API to clear TG permit for the granted TG on the specified voice channel @@ -300,6 +303,12 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw } }); + // set the unit deregistration callback + m_affiliations.setUnitDeregCallback([=](uint32_t srcId) { + if (m_network != nullptr) + m_network->announceUnitDeregistration(srcId); + }); + lc::RCCH::setSiteData(m_siteData); lc::RCCH::setCallsign(cwCallsign); @@ -324,6 +333,10 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw LogInfo(" Notify Control: %s", m_notifyCC ? "yes" : "no"); LogInfo(" Verify Affiliation: %s", m_control->m_verifyAff ? "yes" : "no"); LogInfo(" Verify Registration: %s", m_control->m_verifyReg ? "yes" : "no"); + + if (disableUnitRegTimeout) { + LogInfo(" Disable Unit Registration Timeout: yes"); + } } if (m_voice != nullptr) { diff --git a/src/host/nxdn/packet/ControlSignaling.cpp b/src/host/nxdn/packet/ControlSignaling.cpp index 3e128445..07550467 100644 --- a/src/host/nxdn/packet/ControlSignaling.cpp +++ b/src/host/nxdn/packet/ControlSignaling.cpp @@ -163,6 +163,7 @@ bool ControlSignaling::process(uint8_t fct, uint8_t option, uint8_t* data, uint3 uint16_t srcId = rcch->getSrcId(); uint16_t dstId = rcch->getDstId(); + m_nxdn->m_affiliations.touchUnitReg(srcId); switch (rcch->getMessageType()) { case RTCH_MESSAGE_TYPE_VCALL: diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index e5804090..d702cd68 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -415,6 +415,9 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw m_controlChData = controlChData; + bool disableUnitRegTimeout = p25Protocol["disableUnitRegTimeout"].as(false); + m_affiliations.setDisableUnitRegTimeout(disableUnitRegTimeout); + // set the grant release callback m_affiliations.setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { // callback REST API to clear TG permit for the granted TG on the specified voice channel @@ -437,6 +440,12 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw } }); + // set the unit deregistration callback + m_affiliations.setUnitDeregCallback([=](uint32_t srcId) { + if (m_network != nullptr) + m_network->announceUnitDeregistration(srcId); + }); + if (printOptions) { LogInfo(" Silence Threshold: %u (%.1f%%)", m_voice->m_silenceThreshold, float(m_voice->m_silenceThreshold) / 12.33F); LogInfo(" Frame Loss Threshold: %u", m_frameLossThreshold); @@ -486,6 +495,10 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw LogInfo(" Explicit Source ID Support: %s", m_allowExplicitSourceId ? "yes" : "no"); LogInfo(" Conventional Network Grant Demand: %s", m_convNetGrantDemand ? "yes" : "no"); + if (disableUnitRegTimeout) { + LogInfo(" Disable Unit Registration Timeout: yes"); + } + LogInfo(" Redundant Immediate: %s", m_control->m_redundantImmediate ? "yes" : "no"); if (m_control->m_redundantGrant) { LogInfo(" Redundant Grant Transmit: yes"); @@ -494,7 +507,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 != 0xF7EU && txNAC != m_nac) { + if (txNAC != P25_NAC_DIGITAL_SQ && txNAC != m_nac) { LogMessage(LOG_P25, "Split NAC operations, setting Tx NAC to $%03X", txNAC); m_txNAC = txNAC; m_nid.setTxNAC(m_txNAC); diff --git a/src/host/p25/packet/ControlSignaling.cpp b/src/host/p25/packet/ControlSignaling.cpp index d2053ddc..861dbc62 100644 --- a/src/host/p25/packet/ControlSignaling.cpp +++ b/src/host/p25/packet/ControlSignaling.cpp @@ -214,6 +214,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptrgetSrcId(); uint32_t dstId = tsbk->getDstId(); + m_p25->m_affiliations.touchUnitReg(srcId); m_lastMFID = tsbk->getMFId(); // handle standard P25 reference opcodes @@ -2619,6 +2620,26 @@ bool ControlSignaling::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId) noNet = true; } + // register the RID if the MFID is $90 (this is typically DVRS, and DVRS won't unit register so we'll do it for them) + if (!m_p25->m_affiliations.isUnitReg(srcId) && m_lastMFID == P25_MFG_MOT) { + // validate the source RID + if (!acl::AccessControl::validateSrcId(srcId)) { + LogWarning(LOG_RF, P25_TSDU_STR ", %s denial, RID rejection, srcId = %u", iosp->toString().c_str(), srcId); + ::ActivityLog("P25", true, "unit registration request from %u denied", srcId); + iosp->setResponse(P25_RSP_REFUSED); + noNet = true; + } + else { + // update dynamic unit registration table + if (!m_p25->m_affiliations.isUnitReg(srcId)) { + m_p25->m_affiliations.unitReg(srcId); + } + + if (m_p25->m_network != nullptr) + m_p25->m_network->announceUnitRegistration(srcId); + } + } + // validate the source RID is registered if (!m_p25->m_affiliations.isUnitReg(srcId) && m_verifyReg) { LogWarning(LOG_RF, P25_TSDU_STR ", %s denial, RID not registered, srcId = %u", iosp->toString().c_str(), srcId); @@ -2744,8 +2765,8 @@ void ControlSignaling::writeRF_TSDU_U_Dereg_Ack(uint32_t srcId) writeRF_TSDU_SBF_Imm(osp.get(), false); - if (m_p25->m_network != nullptr) - m_p25->m_network->announceUnitDeregistration(srcId); +// if (m_p25->m_network != nullptr) +// m_p25->m_network->announceUnitDeregistration(srcId); } }