From e7ddfffed3d291a0a9cd13e7c3982522de1ac4fa Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 17 Jun 2024 15:39:03 -0400 Subject: [PATCH] add rudimentary support to process NET_FUNC_GRANT_REQ (network grant requests), this is the first step towards eventually supporting non-authorative CCs; --- src/fne/network/callhandler/TagDMRData.cpp | 109 ++++++++++++++++++- src/fne/network/callhandler/TagDMRData.h | 2 + src/fne/network/callhandler/TagNXDNData.cpp | 85 ++++++++++++++- src/fne/network/callhandler/TagNXDNData.h | 2 + src/fne/network/callhandler/TagP25Data.cpp | 112 +++++++++++++++++++- src/fne/network/callhandler/TagP25Data.h | 2 + 6 files changed, 306 insertions(+), 6 deletions(-) diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 43cd18c1..82032d9c 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -349,8 +349,40 @@ 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) { - // bryanb: TODO TODO TODO - return false; + // if we have an Rx status for the destination deny the grant + if (std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { return x.second.dstId == dstId; }) != m_status.end()) { + return false; + } + + // is the source ID a blacklisted ID? + lookups::RadioId rid = m_network->m_ridLookup->find(srcId); + if (!rid.radioDefault()) { + if (!rid.radioEnabled()) { + return false; + } + } + + lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId); + + // check TGID validity + if (tg.isInvalid()) { + return false; + } + + if (!tg.config().active()) { + return false; + } + + // repeat traffic to the connected peers + if (m_network->m_peers.size() > 0U) { + for (auto peer : m_network->m_peers) { + if (peerId != peer.first) { + write_CSBK_Grant(peer.first, srcId, dstId, 4U, !unitToUnit); + } + } + } + + return true; } /// @@ -816,6 +848,79 @@ bool TagDMRData::validate(uint32_t peerId, data::Data& data, uint32_t streamId) return true; } +/// +/// Helper to write a grant packet. +/// +/// +/// +/// +/// +/// +/// +bool TagDMRData::write_CSBK_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp) +{ + uint8_t slot = 0U; + + bool emergency = ((serviceOptions & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag + bool privacy = ((serviceOptions & 0xFFU) & 0x40U) == 0x40U; // Privacy Flag + bool broadcast = ((serviceOptions & 0xFFU) & 0x10U) == 0x10U; // Broadcast Flag + uint8_t priority = ((serviceOptions & 0xFFU) & 0x03U); // Priority + + if (dstId == DMR_WUID_ALL) { + return true; // do not generate grant packets for $FFFF (All Call) TGID + } + + // check the affiliations for this peer to see if we can grant traffic + 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()); + return false; // this will cause no traffic to pass for this peer now...I'm not sure this is good behavior + } + else { + if (!aff->hasGroupAff(dstId)) { + return false; + } + } + + if (grp) { + std::unique_ptr csbk = std::make_unique(); + if (broadcast) + csbk->setCSBKO(CSBKO_BTV_GRANT); + csbk->setLogicalCh1(0U); + csbk->setSlotNo(slot); + + if (m_network->m_verbose) { + LogMessage(LOG_NET, "DMR, DT_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); + } + + csbk->setEmergency(emergency); + csbk->setSrcId(srcId); + csbk->setDstId(dstId); + + write_CSBK(peerId, 1U, csbk.get()); + } + else { + std::unique_ptr csbk = std::make_unique(); + csbk->setLogicalCh1(0U); + csbk->setSlotNo(slot); + + if (m_network->m_verbose) { + LogMessage(LOG_NET, "DMR, DT_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); + } + + csbk->setEmergency(emergency); + csbk->setSrcId(srcId); + csbk->setDstId(dstId); + + write_CSBK(peerId, 1U, csbk.get()); + } + + return true; +} + /// /// Helper to write a NACK RSP packet. /// diff --git a/src/fne/network/callhandler/TagDMRData.h b/src/fne/network/callhandler/TagDMRData.h index 2abc88bd..f3c6abc4 100644 --- a/src/fne/network/callhandler/TagDMRData.h +++ b/src/fne/network/callhandler/TagDMRData.h @@ -100,6 +100,8 @@ namespace network /// Helper to validate the DMR call stream. bool validate(uint32_t peerId, dmr::data::Data& data, uint32_t streamId); + /// Helper to write a grant packet. + bool write_CSBK_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp); /// Helper to write a NACK RSP packet. void write_CSBK_NACK_RSP(uint32_t peerId, uint32_t dstId, uint8_t reason, uint8_t service); diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index be356863..4ea870bb 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -313,8 +313,40 @@ 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) { - // bryanb: TODO TODO TODO - return false; + // if we have an Rx status for the destination deny the grant + if (std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { return x.second.dstId == dstId; }) != m_status.end()) { + return false; + } + + // is the source ID a blacklisted ID? + lookups::RadioId rid = m_network->m_ridLookup->find(srcId); + if (!rid.radioDefault()) { + if (!rid.radioEnabled()) { + return false; + } + } + + lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId); + + // check TGID validity + if (tg.isInvalid()) { + return false; + } + + if (!tg.config().active()) { + return false; + } + + // repeat traffic to the connected peers + if (m_network->m_peers.size() > 0U) { + for (auto peer : m_network->m_peers) { + if (peerId != peer.first) { + write_Message_Grant(peer.first, srcId, dstId, 4U, !unitToUnit); + } + } + } + + return true; } /// @@ -604,6 +636,55 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u return true; } +/// +/// Helper to write a grant packet. +/// +/// +/// +/// +/// +/// +/// +bool TagNXDNData::write_Message_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp) +{ + bool emergency = ((serviceOptions & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag + bool encryption = ((serviceOptions & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag + uint8_t priority = ((serviceOptions & 0xFFU) & 0x07U); // Priority + + std::unique_ptr rcch = std::make_unique(); + + // check the affiliations for this peer to see if we can grant traffic + 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()); + return false; // this will cause no traffic to pass for this peer now...I'm not sure this is good behavior + } + else { + if (!aff->hasGroupAff(dstId)) { + return false; + } + } + + rcch->setMessageType(RTCH_MESSAGE_TYPE_VCALL); + rcch->setGrpVchNo(0U); + rcch->setGroup(grp); + rcch->setSrcId(srcId); + rcch->setDstId(dstId); + + rcch->setEmergency(emergency); + rcch->setEncrypted(encryption); + 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", + rcch->toString().c_str(), rcch->getEmergency(), rcch->getEncrypted(), rcch->getPriority(), rcch->getGrpVchNo(), rcch->getSrcId(), rcch->getDstId(), peerId); + } + + write_Message(peerId, rcch.get()); + return true; +} + /// /// Helper to write a deny packet. /// diff --git a/src/fne/network/callhandler/TagNXDNData.h b/src/fne/network/callhandler/TagNXDNData.h index 8b997186..a3e70a5b 100644 --- a/src/fne/network/callhandler/TagNXDNData.h +++ b/src/fne/network/callhandler/TagNXDNData.h @@ -89,6 +89,8 @@ namespace network /// Helper to validate the NXDN call stream. bool validate(uint32_t peerId, nxdn::lc::RTCH& control, uint8_t messageType, uint32_t streamId); + /// Helper to write a grant packet. + bool write_Message_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp); /// Helper to write a deny packet. void write_Message_Deny(uint32_t peerId, uint32_t srcId, uint32_t dstId, uint8_t reason, uint8_t service); diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 38d3e35c..46b3a5dc 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -29,6 +29,12 @@ using namespace p25; #include #include +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +const uint32_t GRANT_TIMER_TIMEOUT = 15U; + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -384,8 +390,40 @@ 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) { - // bryanb: TODO TODO TODO - return false; + // if we have an Rx status for the destination deny the grant + if (std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { return x.second.dstId == dstId; }) != m_status.end()) { + return false; + } + + // is the source ID a blacklisted ID? + lookups::RadioId rid = m_network->m_ridLookup->find(srcId); + if (!rid.radioDefault()) { + if (!rid.radioEnabled()) { + return false; + } + } + + lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId); + + // check TGID validity + if (tg.isInvalid()) { + return false; + } + + if (!tg.config().active()) { + return false; + } + + // repeat traffic to the connected peers + if (m_network->m_peers.size() > 0U) { + for (auto peer : m_network->m_peers) { + if (peerId != peer.first) { + write_TSDU_Grant(peer.first, srcId, dstId, 4U, !unitToUnit); + } + } + } + + return true; } /// @@ -1123,6 +1161,76 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, uint8_t duid, const return true; } + +/// +/// Helper to write a grant packet. +/// +/// +/// +/// +/// +/// +bool TagP25Data::write_TSDU_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp) +{ + bool emergency = ((serviceOptions & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag + bool encryption = ((serviceOptions & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag + uint8_t priority = ((serviceOptions & 0xFFU) & 0x07U); // Priority + + if (dstId == P25_TGID_ALL) { + return true; // do not generate grant packets for $FFFF (All Call) TGID + } + + // check the affiliations for this peer to see if we can grant traffic + 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()); + return false; // this will cause no traffic to pass for this peer now...I'm not sure this is good behavior + } + else { + if (!aff->hasGroupAff(dstId)) { + return false; + } + } + + if (grp) { + std::unique_ptr iosp = std::make_unique(); + iosp->setSrcId(srcId); + iosp->setDstId(dstId); + iosp->setGrpVchId(0U); + iosp->setGrpVchNo(0U); + iosp->setEmergency(emergency); + iosp->setEncrypted(encryption); + iosp->setPriority(priority); + + if (m_network->m_verbose) { + LogMessage(LOG_NET, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u, peerId = %u", + iosp->toString().c_str(), iosp->getEmergency(), iosp->getEncrypted(), iosp->getPriority(), iosp->getGrpVchNo(), iosp->getSrcId(), iosp->getDstId(), peerId); + } + + write_TSDU(peerId, iosp.get()); + } + else { + std::unique_ptr iosp = std::make_unique(); + iosp->setSrcId(srcId); + iosp->setDstId(dstId); + iosp->setGrpVchId(0U); + iosp->setGrpVchNo(0U); + iosp->setEmergency(emergency); + iosp->setEncrypted(encryption); + iosp->setPriority(priority); + + if (m_network->m_verbose) { + LogMessage(LOG_NET, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u, peerId = %u", + iosp->toString().c_str(), iosp->getEmergency(), iosp->getEncrypted(), iosp->getPriority(), iosp->getGrpVchNo(), iosp->getSrcId(), iosp->getDstId(), peerId); + } + + write_TSDU(peerId, iosp.get()); + } + + return true; +} + /// /// Helper to write a deny packet. /// diff --git a/src/fne/network/callhandler/TagP25Data.h b/src/fne/network/callhandler/TagP25Data.h index 3ee2c85e..b5cdba9e 100644 --- a/src/fne/network/callhandler/TagP25Data.h +++ b/src/fne/network/callhandler/TagP25Data.h @@ -113,6 +113,8 @@ namespace network /// Helper to validate the P25 call stream. bool validate(uint32_t peerId, p25::lc::LC& control, uint8_t duid, const p25::lc::TSBK* tsbk, uint32_t streamId); + /// Helper to write a grant packet. + bool write_TSDU_Grant(uint32_t peerId, uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp); /// Helper to write a deny packet. void write_TSDU_Deny(uint32_t peerId, uint32_t srcId, uint32_t dstId, uint8_t reason, uint8_t service, bool group = false, bool aiv = false); /// Helper to write a queue packet.