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.