refactor call rejection to be timer based to reject a call until incoming data drops; correct bug with non-standard MFId's being used for the first LDU1; implement enforced unknown/undefined RID ACL support;

pull/86/head
Bryan Biedenkapp 1 year ago
parent 8f74260d5d
commit 0b0dc32c94

@ -88,7 +88,11 @@ master:
# Flag indicating whether or not a conventional site can override affiliation rules. # Flag indicating whether or not a conventional site can override affiliation rules.
allowConvSiteAffOverride: true allowConvSiteAffOverride: true
# Flag indicating whether or not In-Call Control feedback is enabled. # 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 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). # Flag indicating that traffic headers will be filtered by destination ID (i.e. valid RID or valid TGID).
filterHeaders: true filterHeaders: true

@ -83,6 +83,8 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port,
m_disallowExtAdjStsBcast(true), m_disallowExtAdjStsBcast(true),
m_allowConvSiteAffOverride(false), m_allowConvSiteAffOverride(false),
m_restrictGrantToAffOnly(false), m_restrictGrantToAffOnly(false),
m_enableInCallCtrl(true),
m_rejectUnknownRID(false),
m_filterHeaders(true), m_filterHeaders(true),
m_filterTerminators(true), m_filterTerminators(true),
m_dropU2UPeerTable(), m_dropU2UPeerTable(),
@ -125,6 +127,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions)
m_disallowExtAdjStsBcast = conf["disallowExtAdjStsBcast"].as<bool>(true); m_disallowExtAdjStsBcast = conf["disallowExtAdjStsBcast"].as<bool>(true);
m_allowConvSiteAffOverride = conf["allowConvSiteAffOverride"].as<bool>(true); m_allowConvSiteAffOverride = conf["allowConvSiteAffOverride"].as<bool>(true);
m_enableInCallCtrl = conf["enableInCallCtrl"].as<bool>(true); m_enableInCallCtrl = conf["enableInCallCtrl"].as<bool>(true);
m_rejectUnknownRID = conf["rejectUnknownRID"].as<bool>(false);
m_softConnLimit = conf["connectionLimit"].as<uint32_t>(MAX_HARD_CONN_CAP); m_softConnLimit = conf["connectionLimit"].as<uint32_t>(MAX_HARD_CONN_CAP);
if (m_softConnLimit > 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(" 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(" 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 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(" Restrict grant response by affiliation: %s", m_restrictGrantToAffOnly ? "yes" : "no");
LogInfo(" Traffic Headers Filtered by Destination ID: %s", m_filterHeaders ? "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"); LogInfo(" Traffic Terminators Filtered by Destination ID: %s", m_filterTerminators ? "yes" : "no");

@ -459,6 +459,7 @@ namespace network
bool m_allowConvSiteAffOverride; bool m_allowConvSiteAffOverride;
bool m_restrictGrantToAffOnly; bool m_restrictGrantToAffOnly;
bool m_enableInCallCtrl; bool m_enableInCallCtrl;
bool m_rejectUnknownRID;
bool m_filterHeaders; bool m_filterHeaders;
bool m_filterTerminators; bool m_filterTerminators;

@ -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 // 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::nanoseconds>(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; return false;
} }
} }
@ -771,6 +796,31 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
return false; 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::nanoseconds>(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? // is this a group call?

@ -557,6 +557,30 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
return false; 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::nanoseconds>(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 // always validate a terminator if the source is valid
if (messageType == MessageType::RTCH_TX_REL || messageType == MessageType::RTCH_TX_REL_EX) 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; 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::nanoseconds>(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; return true;
} }

@ -1017,7 +1017,15 @@ 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 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? // is the source ID a blacklisted ID?
if (!skipRidCheck) {
lookups::RadioId rid = m_network->m_ridLookup->find(control.getSrcId()); lookups::RadioId rid = m_network->m_ridLookup->find(control.getSrcId());
if (!rid.radioDefault()) { if (!rid.radioDefault()) {
if (!rid.radioEnabled()) { if (!rid.radioEnabled()) {
@ -1039,6 +1047,31 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
return false; 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::nanoseconds>(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;
}
}
}
// always validate a PDU if the source is valid // always validate a PDU if the source is valid
if (duid == DUID::PDU) if (duid == DUID::PDU)
@ -1072,6 +1105,30 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
return false; 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::nanoseconds>(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; return true;
} }

@ -62,8 +62,14 @@ void* Host::threadDMRReader1(void* arg)
::pthread_setname_np(th->thread, threadName.c_str()); ::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE #endif // _GNU_SOURCE
StopWatch stopWatch;
stopWatch.start();
if (host->m_dmr != nullptr) { if (host->m_dmr != nullptr) {
while (!g_killed) { while (!g_killed) {
uint32_t ms = stopWatch.elapsed();
stopWatch.start();
// scope is intentional // scope is intentional
{ {
// ------------------------------------------------------ // ------------------------------------------------------
@ -148,6 +154,23 @@ void* Host::threadDMRReader1(void* arg)
LogWarning(LOG_HOST, "DMR modem data received, state = %u", host->m_state); 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()); ::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE #endif // _GNU_SOURCE
StopWatch stopWatch;
stopWatch.start();
if (host->m_dmr != nullptr) { if (host->m_dmr != nullptr) {
while (!g_killed) { while (!g_killed) {
uint32_t ms = stopWatch.elapsed();
stopWatch.start();
// scope is intentional // scope is intentional
{ {
// ------------------------------------------------------ // ------------------------------------------------------
@ -409,6 +438,23 @@ void* Host::threadDMRReader2(void* arg)
LogWarning(LOG_HOST, "DMR modem data received, state = %u", host->m_state); 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;
}
}
} }
} }
} }

@ -49,8 +49,14 @@ void* Host::threadNXDNReader(void* arg)
::pthread_setname_np(th->thread, threadName.c_str()); ::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE #endif // _GNU_SOURCE
StopWatch stopWatch;
stopWatch.start();
if (host->m_nxdn != nullptr) { if (host->m_nxdn != nullptr) {
while (!g_killed) { while (!g_killed) {
uint32_t ms = stopWatch.elapsed();
stopWatch.start();
// scope is intentional // scope is intentional
{ {
// ------------------------------------------------------ // ------------------------------------------------------
@ -97,6 +103,23 @@ void* Host::threadNXDNReader(void* arg)
LogWarning(LOG_HOST, "NXDN modem data received, state = %u", host->m_state); 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;
}
}
} }
} }
} }

@ -50,8 +50,14 @@ void* Host::threadP25Reader(void* arg)
::pthread_setname_np(th->thread, threadName.c_str()); ::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE #endif // _GNU_SOURCE
StopWatch stopWatch;
stopWatch.start();
if (host->m_p25 != nullptr) { if (host->m_p25 != nullptr) {
while (!g_killed) { while (!g_killed) {
uint32_t ms = stopWatch.elapsed();
stopWatch.start();
// scope is intentional // scope is intentional
{ {
// ------------------------------------------------------ // ------------------------------------------------------
@ -138,6 +144,23 @@ void* Host::threadP25Reader(void* arg)
else if (host->m_state != HOST_STATE_LOCKOUT) { else if (host->m_state != HOST_STATE_LOCKOUT) {
LogWarning(LOG_HOST, "P25 modem data received, state = %u", host->m_state); 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;
}
} }
} }
} }

@ -125,12 +125,20 @@ Host::Host(const std::string& confFile) :
m_nxdnQueueSizeBytes(1488U), // 31 frames m_nxdnQueueSizeBytes(1488U), // 31 frames
m_authoritative(true), m_authoritative(true),
m_supervisor(false), m_supervisor(false),
m_dmr1RejCnt(0U),
m_dmr2RejCnt(0U),
m_p25RejCnt(0U),
m_nxdnRejCnt(0U),
m_dmrBeaconDurationTimer(1000U), m_dmrBeaconDurationTimer(1000U),
m_dmrDedicatedTxTestTimer(1000U, 0U, 125U), m_dmrDedicatedTxTestTimer(1000U, 0U, 125U),
m_dmr1RejectTimer(1000U, 0U, 500U),
m_dmr2RejectTimer(1000U, 0U, 500U),
m_p25BcastDurationTimer(1000U), m_p25BcastDurationTimer(1000U),
m_p25DedicatedTxTestTimer(1000U, 0U, 125U), m_p25DedicatedTxTestTimer(1000U, 0U, 125U),
m_p25RejectTimer(1000U, 0U, 500U),
m_nxdnBcastDurationTimer(1000U), m_nxdnBcastDurationTimer(1000U),
m_nxdnDedicatedTxTestTimer(1000U, 0U, 125U), m_nxdnDedicatedTxTestTimer(1000U, 0U, 125U),
m_nxdnRejectTimer(1000U, 0U, 500U),
m_dmrTx1WatchdogTimer(1000U, 1U), m_dmrTx1WatchdogTimer(1000U, 1U),
m_dmrTx1LoopMS(0U), m_dmrTx1LoopMS(0U),
m_dmrTx2WatchdogTimer(1000U, 1U), m_dmrTx2WatchdogTimer(1000U, 1U),

@ -180,12 +180,21 @@ private:
bool m_authoritative; bool m_authoritative;
bool m_supervisor; bool m_supervisor;
uint32_t m_dmr1RejCnt;
uint32_t m_dmr2RejCnt;
uint32_t m_p25RejCnt;
uint32_t m_nxdnRejCnt;
Timer m_dmrBeaconDurationTimer; Timer m_dmrBeaconDurationTimer;
Timer m_dmrDedicatedTxTestTimer; Timer m_dmrDedicatedTxTestTimer;
Timer m_dmr1RejectTimer;
Timer m_dmr2RejectTimer;
Timer m_p25BcastDurationTimer; Timer m_p25BcastDurationTimer;
Timer m_p25DedicatedTxTestTimer; Timer m_p25DedicatedTxTestTimer;
Timer m_p25RejectTimer;
Timer m_nxdnBcastDurationTimer; Timer m_nxdnBcastDurationTimer;
Timer m_nxdnDedicatedTxTestTimer; Timer m_nxdnDedicatedTxTestTimer;
Timer m_nxdnRejectTimer;
/* Watchdog Timers */ /* Watchdog Timers */

@ -471,6 +471,38 @@ dmr::lookups::DMRAffiliationLookup* Control::affiliations()
return nullptr; 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. */ /* Helper to return the slot carrying the TSCC. */
Slot* Control::getTSCCSlot() const Slot* Control::getTSCCSlot() const

@ -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 * @ingroup host_dmr
*/ */
class HOST_SW_API Control { class HOST_SW_API Control {
@ -215,6 +215,18 @@ namespace dmr
*/ */
lookups::DMRAffiliationLookup* affiliations(); 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. * @brief Helper to return the slot carrying the TSCC.
* @returns Slot* Instance of Slot carrying the TSCC. * @returns Slot* Instance of Slot carrying the TSCC.

@ -461,6 +461,8 @@ void Slot::processInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId)
case network::NET_ICC::REJECT_TRAFFIC: case network::NET_ICC::REJECT_TRAFFIC:
{ {
if (m_rfState == RS_RF_AUDIO && m_rfLC->getDstId() == 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);
processFrameLoss(); processFrameLoss();
m_rfLastDstId = 0U; m_rfLastDstId = 0U;
@ -677,22 +679,9 @@ void Slot::clock()
} }
} }
if (m_rfState == RS_RF_REJECTED) { // reset states if we're in a rejected state and we're a control channel
if (!m_enableTSCC) { if (m_rfState == RS_RF_REJECTED && m_enableTSCC) {
m_txQueue.clear(); clearRFReject();
}
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;
} }
if (m_frameLossCnt > 0U && m_rfState == RS_RF_LISTENING) 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. */ /* Helper to change the debug and verbose state. */
void Slot::setDebugVerbose(bool debug, bool verbose) void Slot::setDebugVerbose(bool debug, bool verbose)

@ -214,6 +214,16 @@ namespace dmr
*/ */
packet::ControlSignaling* control() { return m_control; } 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. * @brief Helper to change the debug and verbose state.
* @param debug Flag indicating whether debug is enabled or not. * @param debug Flag indicating whether debug is enabled or not.

@ -442,6 +442,10 @@ bool Control::processFrame(uint8_t* data, uint32_t len)
return false; 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(); RFChannelType::E rfct = m_rfLastLICH.getRFCT();
FuncChannelType::E fct = m_rfLastLICH.getFCT(); FuncChannelType::E fct = m_rfLastLICH.getFCT();
ChOption::E option = m_rfLastLICH.getOption(); ChOption::E option = m_rfLastLICH.getOption();
@ -693,19 +697,9 @@ void Control::clock()
} }
} }
// reset states if we're in a rejected state // reset states if we're in a rejected state and we're a control channel
if (m_rfState == RS_RF_REJECTED) { if (m_rfState == RS_RF_REJECTED && m_enableControl && m_dedicatedControl) {
m_txQueue.clear(); clearRFReject();
m_voice->resetRF();
m_voice->resetNet();
m_data->resetRF();
if (m_network != nullptr)
m_network->resetNXDN();
m_rfState = RS_RF_LISTENING;
} }
if (m_frameLossCnt > 0U && m_rfState == RS_RF_LISTENING) 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. */ /* Flag indicating whether the process or is busy or not. */
bool Control::isBusy() const 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: case network::NET_ICC::REJECT_TRAFFIC:
{ {
if (m_rfState == RS_RF_AUDIO && m_rfLC.getDstId() == dstId) { if (m_rfState == RS_RF_AUDIO && m_rfLC.getDstId() == dstId) {
LogWarning(LOG_P25, "network requested in-call traffic reject, dstId = %u", dstId);
processFrameLoss(); processFrameLoss();
m_rfLastDstId = 0U; m_rfLastDstId = 0U;

@ -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 * @ingroup host_nxdn
*/ */
class HOST_SW_API Control { class HOST_SW_API Control {
@ -212,6 +212,16 @@ namespace nxdn
*/ */
lookups::AffiliationLookup affiliations() { return m_affiliations; } 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. * @brief Flag indicating whether the processor or is busy or not.
* @returns bool True, if processor is busy, otherwise false. * @returns bool True, if processor is busy, otherwise false.

@ -596,6 +596,10 @@ bool Control::processFrame(uint8_t* data, uint32_t len)
if (!valid && m_rfState == RS_RF_LISTENING) if (!valid && m_rfState == RS_RF_LISTENING)
return false; 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(); DUID::E duid = m_nid.getDUID();
// Have we got RSSI bytes on the end of a P25 LDU? // 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 // reset states if we're in a rejected state and we're a control channel
if (m_rfState == RS_RF_REJECTED) { if (m_rfState == RS_RF_REJECTED && m_enableControl && m_dedicatedControl) {
m_txQueue.clear(); clearRFReject();
m_voice->resetRF();
m_voice->resetNet();
m_data->resetRF();
if (m_network != nullptr)
m_network->resetP25();
m_rfState = RS_RF_LISTENING;
} }
if (m_frameLossCnt > 0U && m_rfState == RS_RF_LISTENING) 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. */ /* Flag indicating whether the processor or is busy or not. */
bool Control::isBusy() const 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: case network::NET_ICC::REJECT_TRAFFIC:
{ {
if (m_rfState == RS_RF_AUDIO && m_voice->m_rfLC.getDstId() == dstId) { 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(); processFrameLoss();
m_rfLastDstId = 0U; m_rfLastDstId = 0U;

@ -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 * @ingroup host_p25
*/ */
class HOST_SW_API Control { class HOST_SW_API Control {
@ -239,6 +239,16 @@ namespace p25
*/ */
lookups::P25AffiliationLookup affiliations() { return m_affiliations; } 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. * @brief Flag indicating whether the processor or is busy or not.
* @returns bool True, if processor is busy, otherwise false. * @returns bool True, if processor is busy, otherwise false.

@ -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 // send network grant demand TDU
if (m_p25->m_network != nullptr) { if (m_p25->m_network != nullptr) {
if (!m_p25->m_dedicatedControl && m_p25->m_convNetGrantDemand) { if (!m_p25->m_dedicatedControl && m_p25->m_convNetGrantDemand) {

Loading…
Cancel
Save

Powered by TurnKey Linux.