diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index ede5aa14..55631616 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -88,7 +88,11 @@ 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. + # (This will enforce RID ACLs network wide, regardless of local peer RID ACL setting.) enableInCallCtrl: true + # 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 # Flag indicating that traffic headers will be filtered by destination ID (i.e. valid RID or valid TGID). filterHeaders: true diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 0395e1d8..a3fa197f 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -83,6 +83,8 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_disallowExtAdjStsBcast(true), m_allowConvSiteAffOverride(false), m_restrictGrantToAffOnly(false), + m_enableInCallCtrl(true), + m_rejectUnknownRID(false), m_filterHeaders(true), m_filterTerminators(true), m_dropU2UPeerTable(), @@ -125,6 +127,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) m_disallowExtAdjStsBcast = conf["disallowExtAdjStsBcast"].as(true); m_allowConvSiteAffOverride = conf["allowConvSiteAffOverride"].as(true); m_enableInCallCtrl = conf["enableInCallCtrl"].as(true); + m_rejectUnknownRID = conf["rejectUnknownRID"].as(false); m_softConnLimit = conf["connectionLimit"].as(MAX_HARD_CONN_CAP); if (m_softConnLimit > MAX_HARD_CONN_CAP) { @@ -181,6 +184,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogInfo(" Disable P25 ADJ_STS_BCAST to external peers: %s", m_disallowExtAdjStsBcast ? "yes" : "no"); LogInfo(" Allow conventional sites to override affiliation and receive all traffic: %s", m_allowConvSiteAffOverride ? "yes" : "no"); LogInfo(" Enable In-Call Control: %s", m_enableInCallCtrl ? "yes" : "no"); + LogInfo(" Reject Unknown RIDs: %s", m_rejectUnknownRID ? "yes" : "no"); LogInfo(" Restrict grant response by affiliation: %s", m_restrictGrantToAffOnly ? "yes" : "no"); LogInfo(" Traffic Headers Filtered by Destination ID: %s", m_filterHeaders ? "yes" : "no"); LogInfo(" Traffic Terminators Filtered by Destination ID: %s", m_filterTerminators ? "yes" : "no"); diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index c1e31b17..e5947132 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -459,6 +459,7 @@ namespace network bool m_allowConvSiteAffOverride; bool m_restrictGrantToAffOnly; bool m_enableInCallCtrl; + bool m_rejectUnknownRID; bool m_filterHeaders; bool m_filterTerminators; diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index a0f7fb86..31a45e9a 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -739,7 +739,32 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI } // report In-Call Control to the peer sending traffic - m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getSlotNo()); + m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo()); + return false; + } + } + else { + // if this is a default radio -- and we are rejecting undefined radios + // report call error + if (m_network->m_rejectUnknownRID) { + // 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(data.getSrcId())) + .tag("dstId", std::to_string(data.getDstId())) + .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) + .field("slot", data.getSlotNo()) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .request(m_network->m_influxServer); + } + + LogWarning(LOG_NET, "DMR slot %s, illegal/unknown RID attempted access, srcId = %u, dstId = %u", data.getSlotNo(), data.getSrcId(), data.getDstId()); + + // report In-Call Control to the peer sending traffic + m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo()); return false; } } @@ -771,6 +796,31 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI return false; } } + else { + // if this is a default radio -- and we are rejecting undefined radios + // report call error + if (m_network->m_rejectUnknownRID) { + // 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(data.getSrcId())) + .tag("dstId", std::to_string(data.getDstId())) + .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) + .field("slot", data.getSlotNo()) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .request(m_network->m_influxServer); + } + + LogWarning(LOG_NET, "DMR slot %s, illegal/unknown RID attempted access, srcId = %u, dstId = %u", data.getSlotNo(), data.getSrcId(), data.getDstId()); + + // report In-Call Control to the peer sending traffic + m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo()); + return false; + } + } } // is this a group call? diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index aecd8918..f7d72537 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -557,6 +557,30 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u return false; } } + else { + // if this is a default radio -- and we are rejecting undefined radios + // report call error + if (m_network->m_rejectUnknownRID) { + // 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(lc.getSrcId())) + .tag("dstId", std::to_string(lc.getDstId())) + .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .request(m_network->m_influxServer); + } + + LogWarning(LOG_NET, "NXDN, illegal/unknown RID attempted access, srcId = %u, dstId = %u", lc.getSrcId(), lc.getDstId()); + + // report In-Call Control to the peer sending traffic + m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId()); + return false; + } + } // always validate a terminator if the source is valid if (messageType == MessageType::RTCH_TX_REL || messageType == MessageType::RTCH_TX_REL_EX) @@ -586,6 +610,30 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u return false; } } + else { + // if this is a default radio -- and we are rejecting undefined radios + // report call error + if (m_network->m_rejectUnknownRID) { + // 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(lc.getSrcId())) + .tag("dstId", std::to_string(lc.getDstId())) + .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .request(m_network->m_influxServer); + } + + LogWarning(LOG_NET, "NXDN, illegal/unknown RID attempted access, srcId = %u, dstId = %u", lc.getSrcId(), lc.getDstId()); + + // report In-Call Control to the peer sending traffic + m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId()); + return false; + } + } return true; } diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index bac8b3d0..e1a6f591 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -1017,26 +1017,59 @@ 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) { + bool skipRidCheck = false; + if ((control.getMFId() == MFG_MOT && control.getSrcId() == 0U) || control.getSrcId() > WUID_FNE) { + 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()); + // is the source ID a blacklisted ID? - lookups::RadioId rid = m_network->m_ridLookup->find(control.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(control.getSrcId())) - .tag("dstId", std::to_string(control.getDstId())) - .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) - .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) - .request(m_network->m_influxServer); + if (!skipRidCheck) { + lookups::RadioId rid = m_network->m_ridLookup->find(control.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(control.getSrcId())) + .tag("dstId", std::to_string(control.getDstId())) + .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .request(m_network->m_influxServer); + } + + // report In-Call Control to the peer sending traffic + m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId()); + return false; } + } + else { + // if this is a default radio -- and we are rejecting undefined radios + // report call error + if (m_network->m_rejectUnknownRID) { + // 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(control.getSrcId())) + .tag("dstId", std::to_string(control.getDstId())) + .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .request(m_network->m_influxServer); + } - // report In-Call Control to the peer sending traffic - m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId()); - return false; + LogWarning(LOG_NET, "P25, illegal/unknown RID attempted access, srcId = %u, dstId = %u", control.getSrcId(), control.getDstId()); + + // report In-Call Control to the peer sending traffic + m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId()); + return false; + } } } @@ -1072,6 +1105,30 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const return false; } } + else { + // if this is a default radio -- and we are rejecting undefined radios + // report call error + if (m_network->m_rejectUnknownRID) { + // 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(control.getSrcId())) + .tag("dstId", std::to_string(control.getDstId())) + .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .request(m_network->m_influxServer); + } + + LogWarning(LOG_NET, "P25, illegal/unknown RID attempted access, srcId = %u, dstId = %u", control.getSrcId(), control.getDstId()); + + // report In-Call Control to the peer sending traffic + m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId()); + return false; + } + } return true; } diff --git a/src/host/Host.DMR.cpp b/src/host/Host.DMR.cpp index f2106790..1bdaaa05 100644 --- a/src/host/Host.DMR.cpp +++ b/src/host/Host.DMR.cpp @@ -62,8 +62,14 @@ void* Host::threadDMRReader1(void* arg) ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE + StopWatch stopWatch; + stopWatch.start(); + if (host->m_dmr != nullptr) { while (!g_killed) { + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + // scope is intentional { // ------------------------------------------------------ @@ -148,6 +154,23 @@ void* Host::threadDMRReader1(void* arg) LogWarning(LOG_HOST, "DMR modem data received, state = %u", host->m_state); } } + + // were frames received while still in an In-Call reject state? if so, reset the timer + if (host->m_dmr->getRFState(1U) == RS_RF_REJECTED) { + host->m_dmr1RejectTimer.start(); + host->m_dmr1RejCnt++; + } + } else { + // if we're receiving no more frames, and we're in a in-call reject state, clear the state + 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); + host->m_dmr1RejectTimer.stop(); + host->m_dmr->clearRFReject(1U); + host->m_dmr1RejCnt = 0U; + } + } } } } @@ -324,8 +347,14 @@ void* Host::threadDMRReader2(void* arg) ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE + StopWatch stopWatch; + stopWatch.start(); + if (host->m_dmr != nullptr) { while (!g_killed) { + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + // scope is intentional { // ------------------------------------------------------ @@ -409,6 +438,23 @@ void* Host::threadDMRReader2(void* arg) LogWarning(LOG_HOST, "DMR modem data received, state = %u", host->m_state); } } + + // were frames received while still in an In-Call reject state? if so, reset the timer + if (host->m_dmr->getRFState(2U) == RS_RF_REJECTED) { + host->m_dmr2RejectTimer.start(); + host->m_dmr2RejCnt++; + } + } else { + // if we're receiving no more frames, and we're in a in-call reject state, clear the state + 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); + host->m_dmr2RejectTimer.stop(); + host->m_dmr->clearRFReject(2U); + host->m_dmr2RejCnt = 0U; + } + } } } } diff --git a/src/host/Host.NXDN.cpp b/src/host/Host.NXDN.cpp index 35d4bcec..4bdeb6fe 100644 --- a/src/host/Host.NXDN.cpp +++ b/src/host/Host.NXDN.cpp @@ -49,8 +49,14 @@ void* Host::threadNXDNReader(void* arg) ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE + StopWatch stopWatch; + stopWatch.start(); + if (host->m_nxdn != nullptr) { while (!g_killed) { + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + // scope is intentional { // ------------------------------------------------------ @@ -97,6 +103,23 @@ void* Host::threadNXDNReader(void* arg) LogWarning(LOG_HOST, "NXDN modem data received, state = %u", host->m_state); } } + + // were frames received while still in an reject state? if so, reset the timer + if (host->m_nxdn->getRFState() == RS_RF_REJECTED) { + host->m_nxdnRejectTimer.start(); + host->m_nxdnRejCnt++; + } + } else { + // if we're receiving no more frames, and we're in a reject state, clear the state + 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); + host->m_nxdnRejectTimer.stop(); + host->m_nxdn->clearRFReject(); + host->m_nxdnRejCnt = 0U; + } + } } } } diff --git a/src/host/Host.P25.cpp b/src/host/Host.P25.cpp index 5af51fb7..9f24fdbd 100644 --- a/src/host/Host.P25.cpp +++ b/src/host/Host.P25.cpp @@ -50,8 +50,14 @@ void* Host::threadP25Reader(void* arg) ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE + StopWatch stopWatch; + stopWatch.start(); + if (host->m_p25 != nullptr) { while (!g_killed) { + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + // scope is intentional { // ------------------------------------------------------ @@ -138,6 +144,23 @@ void* Host::threadP25Reader(void* arg) else if (host->m_state != HOST_STATE_LOCKOUT) { LogWarning(LOG_HOST, "P25 modem data received, state = %u", host->m_state); } + + // were frames received while still in an In-Call reject state? if so, reset the timer + if (host->m_p25->getRFState() == RS_RF_REJECTED) { + host->m_p25RejectTimer.start(); + host->m_p25RejCnt++; + } + } + } else { + // if we're receiving no more frames, and we're in a in-call reject state, clear the state + 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); + host->m_p25RejectTimer.stop(); + host->m_p25->clearRFReject(); + host->m_p25RejCnt = 0U; + } } } } diff --git a/src/host/Host.cpp b/src/host/Host.cpp index 383551ea..e4865c9b 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -125,12 +125,20 @@ Host::Host(const std::string& confFile) : m_nxdnQueueSizeBytes(1488U), // 31 frames m_authoritative(true), m_supervisor(false), + m_dmr1RejCnt(0U), + m_dmr2RejCnt(0U), + m_p25RejCnt(0U), + m_nxdnRejCnt(0U), m_dmrBeaconDurationTimer(1000U), m_dmrDedicatedTxTestTimer(1000U, 0U, 125U), + m_dmr1RejectTimer(1000U, 0U, 500U), + m_dmr2RejectTimer(1000U, 0U, 500U), m_p25BcastDurationTimer(1000U), m_p25DedicatedTxTestTimer(1000U, 0U, 125U), + m_p25RejectTimer(1000U, 0U, 500U), m_nxdnBcastDurationTimer(1000U), m_nxdnDedicatedTxTestTimer(1000U, 0U, 125U), + m_nxdnRejectTimer(1000U, 0U, 500U), m_dmrTx1WatchdogTimer(1000U, 1U), m_dmrTx1LoopMS(0U), m_dmrTx2WatchdogTimer(1000U, 1U), diff --git a/src/host/Host.h b/src/host/Host.h index 50117cfa..685a092b 100644 --- a/src/host/Host.h +++ b/src/host/Host.h @@ -180,12 +180,21 @@ private: bool m_authoritative; bool m_supervisor; + uint32_t m_dmr1RejCnt; + uint32_t m_dmr2RejCnt; + uint32_t m_p25RejCnt; + uint32_t m_nxdnRejCnt; + Timer m_dmrBeaconDurationTimer; Timer m_dmrDedicatedTxTestTimer; + Timer m_dmr1RejectTimer; + Timer m_dmr2RejectTimer; Timer m_p25BcastDurationTimer; Timer m_p25DedicatedTxTestTimer; + Timer m_p25RejectTimer; Timer m_nxdnBcastDurationTimer; Timer m_nxdnDedicatedTxTestTimer; + Timer m_nxdnRejectTimer; /* Watchdog Timers */ diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index df6c182e..9feef4ba 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -471,6 +471,38 @@ dmr::lookups::DMRAffiliationLookup* Control::affiliations() return nullptr; } +/* Returns the current operating RF state of the DMR controller. */ + +RPT_RF_STATE Control::getRFState(uint32_t slotNo) const +{ + switch (slotNo) { + case 1U: + return m_slot1->getRFState(); + case 2U: + return m_slot2->getRFState(); + default: + LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", slotNo); + break; + } + + return RS_RF_INVALID; +} + +/* Clears the current operating RF state back to idle (with no data reset!). */ + +void Control::clearRFReject(uint32_t slotNo) +{ + switch (slotNo) { + case 1U: + return m_slot1->clearRFReject(); + case 2U: + return m_slot2->clearRFReject(); + default: + LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", slotNo); + break; + } +} + /* Helper to return the slot carrying the TSCC. */ Slot* Control::getTSCCSlot() const diff --git a/src/host/dmr/Control.h b/src/host/dmr/Control.h index 1b7cad2c..806d9ffd 100644 --- a/src/host/dmr/Control.h +++ b/src/host/dmr/Control.h @@ -52,7 +52,7 @@ namespace dmr // --------------------------------------------------------------------------- /** - * @brief This class implements core logic for handling DMR. + * @brief This class implements core controller logic for handling DMR. * @ingroup host_dmr */ class HOST_SW_API Control { @@ -215,6 +215,18 @@ namespace dmr */ lookups::DMRAffiliationLookup* affiliations(); + /** + * @brief Returns the current operating RF state of the DMR controller. + * @param slotNo DMR slot number. + * @returns RPT_RF_STATE + */ + RPT_RF_STATE getRFState(uint32_t slotNo) const; + /** + * @brief Clears the current operating RF state back to idle (with no data reset!). + * @param slotNo DMR slot number. + */ + void clearRFReject(uint32_t slotNo); + /** * @brief Helper to return the slot carrying the TSCC. * @returns Slot* Instance of Slot carrying the TSCC. diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index 39980092..1f9ccbb0 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -461,6 +461,8 @@ void Slot::processInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId) case network::NET_ICC::REJECT_TRAFFIC: { 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); + processFrameLoss(); m_rfLastDstId = 0U; @@ -677,22 +679,9 @@ void Slot::clock() } } - if (m_rfState == RS_RF_REJECTED) { - if (!m_enableTSCC) { - m_txQueue.clear(); - } - - m_rfFrames = 0U; - m_rfErrs = 0U; - m_rfBits = 1U; - - m_netFrames = 0U; - m_netLost = 0U; - - if (m_network != nullptr) - m_network->resetDMR(m_slotNo); - - m_rfState = RS_RF_LISTENING; + // reset states if we're in a rejected state and we're a control channel + if (m_rfState == RS_RF_REJECTED && m_enableTSCC) { + clearRFReject(); } if (m_frameLossCnt > 0U && m_rfState == RS_RF_LISTENING) @@ -832,6 +821,29 @@ void Slot::touchGrantTG(uint32_t dstId) } } +/* Clears the current operating RF state back to idle. */ + +void Slot::clearRFReject() +{ + if (m_rfState == RS_RF_REJECTED) { + if (!m_enableTSCC) { + m_txQueue.clear(); + } + + m_rfFrames = 0U; + m_rfErrs = 0U; + m_rfBits = 1U; + + m_netFrames = 0U; + m_netLost = 0U; + + if (m_network != nullptr) + m_network->resetDMR(m_slotNo); + + m_rfState = RS_RF_LISTENING; + } +} + /* Helper to change the debug and verbose state. */ void Slot::setDebugVerbose(bool debug, bool verbose) diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 98cbad0d..5f6673e3 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -214,6 +214,16 @@ namespace dmr */ packet::ControlSignaling* control() { return m_control; } + /** + * @brief Returns the current operating RF state of the NXDN controller. + * @returns RPT_RF_STATE + */ + RPT_RF_STATE getRFState() const { return m_rfState; } + /** + * @brief Clears the current operating RF state back to idle (with no data reset!). + */ + void clearRFReject(); + /** * @brief Helper to change the debug and verbose state. * @param debug Flag indicating whether debug is enabled or not. diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index ca663113..e8d4c09f 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -442,6 +442,10 @@ bool Control::processFrame(uint8_t* data, uint32_t len) return false; } + // if the controller is currently in an reject state; block any RF traffic + if (valid && m_rfState == RS_RF_REJECTED) + return false; + RFChannelType::E rfct = m_rfLastLICH.getRFCT(); FuncChannelType::E fct = m_rfLastLICH.getFCT(); ChOption::E option = m_rfLastLICH.getOption(); @@ -693,19 +697,9 @@ void Control::clock() } } - // reset states if we're in a rejected state - if (m_rfState == RS_RF_REJECTED) { - m_txQueue.clear(); - - m_voice->resetRF(); - m_voice->resetNet(); - - m_data->resetRF(); - - if (m_network != nullptr) - m_network->resetNXDN(); - - m_rfState = RS_RF_LISTENING; + // reset states if we're in a rejected state and we're a control channel + if (m_rfState == RS_RF_REJECTED && m_enableControl && m_dedicatedControl) { + clearRFReject(); } if (m_frameLossCnt > 0U && m_rfState == RS_RF_LISTENING) @@ -801,6 +795,25 @@ void Control::touchGrantTG(uint32_t dstId) } } +/* Clears the current operating RF state back to idle. */ + +void Control::clearRFReject() +{ + if (m_rfState == RS_RF_REJECTED) { + m_txQueue.clear(); + + m_voice->resetRF(); + m_voice->resetNet(); + + m_data->resetRF(); + + if (m_network != nullptr) + m_network->resetNXDN(); + + m_rfState = RS_RF_LISTENING; + } +} + /* Flag indicating whether the process or is busy or not. */ bool Control::isBusy() const @@ -1045,6 +1058,8 @@ void Control::processInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId) case network::NET_ICC::REJECT_TRAFFIC: { if (m_rfState == RS_RF_AUDIO && m_rfLC.getDstId() == dstId) { + LogWarning(LOG_P25, "network requested in-call traffic reject, dstId = %u", dstId); + processFrameLoss(); m_rfLastDstId = 0U; diff --git a/src/host/nxdn/Control.h b/src/host/nxdn/Control.h index d4cb4e95..08b272b3 100644 --- a/src/host/nxdn/Control.h +++ b/src/host/nxdn/Control.h @@ -61,7 +61,7 @@ namespace nxdn // --------------------------------------------------------------------------- /** - * @brief This class implements core logic for handling NXDN. + * @brief This class implements core controller logic for handling NXDN. * @ingroup host_nxdn */ class HOST_SW_API Control { @@ -212,6 +212,16 @@ namespace nxdn */ lookups::AffiliationLookup affiliations() { return m_affiliations; } + /** + * @brief Returns the current operating RF state of the NXDN controller. + * @returns RPT_RF_STATE + */ + RPT_RF_STATE getRFState() const { return m_rfState; } + /** + * @brief Clears the current operating RF state back to idle. + */ + void clearRFReject(); + /** * @brief Flag indicating whether the processor or is busy or not. * @returns bool True, if processor is busy, otherwise false. diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index 69cc43dd..abed572d 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -596,6 +596,10 @@ bool Control::processFrame(uint8_t* data, uint32_t len) if (!valid && m_rfState == RS_RF_LISTENING) return false; + // if the controller is currently in an reject state; block any RF traffic + if (valid && m_rfState == RS_RF_REJECTED) + return false; + DUID::E duid = m_nid.getDUID(); // Have we got RSSI bytes on the end of a P25 LDU? @@ -932,19 +936,9 @@ void Control::clock() } } - // reset states if we're in a rejected state - if (m_rfState == RS_RF_REJECTED) { - m_txQueue.clear(); - - m_voice->resetRF(); - m_voice->resetNet(); - - m_data->resetRF(); - - if (m_network != nullptr) - m_network->resetP25(); - - m_rfState = RS_RF_LISTENING; + // reset states if we're in a rejected state and we're a control channel + if (m_rfState == RS_RF_REJECTED && m_enableControl && m_dedicatedControl) { + clearRFReject(); } if (m_frameLossCnt > 0U && m_rfState == RS_RF_LISTENING) @@ -1119,6 +1113,25 @@ void Control::touchGrantTG(uint32_t dstId) } } +/* Clears the current operating RF state back to idle. */ + +void Control::clearRFReject() +{ + if (m_rfState == RS_RF_REJECTED) { + m_txQueue.clear(); + + m_voice->resetRF(); + m_voice->resetNet(); + + m_data->resetRF(); + + if (m_network != nullptr) + m_network->resetP25(); + + m_rfState = RS_RF_LISTENING; + } +} + /* Flag indicating whether the processor or is busy or not. */ bool Control::isBusy() const @@ -1547,6 +1560,8 @@ void Control::processInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId) case network::NET_ICC::REJECT_TRAFFIC: { if (m_rfState == RS_RF_AUDIO && m_voice->m_rfLC.getDstId() == dstId) { + LogWarning(LOG_P25, "network requested in-call traffic reject, dstId = %u", dstId); + processFrameLoss(); m_rfLastDstId = 0U; diff --git a/src/host/p25/Control.h b/src/host/p25/Control.h index d8ed72d1..47c2ebef 100644 --- a/src/host/p25/Control.h +++ b/src/host/p25/Control.h @@ -66,7 +66,7 @@ namespace p25 // --------------------------------------------------------------------------- /** - * @brief This class implements core logic for handling P25. + * @brief This class implements core controller logic for handling P25. * @ingroup host_p25 */ class HOST_SW_API Control { @@ -239,6 +239,16 @@ namespace p25 */ lookups::P25AffiliationLookup affiliations() { return m_affiliations; } + /** + * @brief Returns the current operating RF state of the P25 controller. + * @returns RPT_RF_STATE + */ + RPT_RF_STATE getRFState() const { return m_rfState; } + /** + * @brief Clears the current operating RF state back to idle. + */ + void clearRFReject(); + /** * @brief Flag indicating whether the processor or is busy or not. * @returns bool True, if processor is busy, otherwise false. diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index 2dd63ebe..8a8f530f 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -375,6 +375,12 @@ 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()); + srcId = WUID_FNE; + } + // send network grant demand TDU if (m_p25->m_network != nullptr) { if (!m_p25->m_dedicatedControl && m_p25->m_convNetGrantDemand) {