From 7fa3c16fe149ee676fa039056e196376d3ec8b7d Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 1 Nov 2026 16:40:39 -0500 Subject: [PATCH] better handle logging grant failures on the host for network traffic in certain conditions (like TGs that are sent always regardless of aff status); correct parrot playback thread not testing if affiliations were present before repeating parrot frames; --- src/fne/network/callhandler/TagAnalogData.cpp | 41 +++++++++++++++++++ src/fne/network/callhandler/TagDMRData.cpp | 41 +++++++++++++++++++ src/fne/network/callhandler/TagNXDNData.cpp | 41 +++++++++++++++++++ src/fne/network/callhandler/TagP25Data.cpp | 41 +++++++++++++++++++ src/fne/network/callhandler/TagP25P2Data.cpp | 41 +++++++++++++++++++ src/host/p25/packet/ControlSignaling.cpp | 1 + src/host/p25/packet/Voice.cpp | 6 ++- 7 files changed, 211 insertions(+), 1 deletion(-) diff --git a/src/fne/network/callhandler/TagAnalogData.cpp b/src/fne/network/callhandler/TagAnalogData.cpp index 46aaf8e1..836f2234 100644 --- a/src/fne/network/callhandler/TagAnalogData.cpp +++ b/src/fne/network/callhandler/TagAnalogData.cpp @@ -520,6 +520,15 @@ void TagAnalogData::playbackParrot() m_lastParrotSrcId = pkt.srcId; m_lastParrotDstId = pkt.dstId; + lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(pkt.dstId); + if (tg.isInvalid()) { + LogWarning(LOG_ANALOG, "TagAnalogData::playbackParrot(), invalid talkgroup, dstId = %u", pkt.dstId); + delete[] pkt.buffer; + m_parrotFrames.unlock(); + m_parrotFrames.pop_front(); + return; + } + if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { @@ -538,6 +547,38 @@ void TagAnalogData::playbackParrot() m_network->m_frameQueue->flushQueue(&queue); } + // skip peers that are not affiliated with the target group if parrot TG affiliation is enabled + if (tg.config().affiliated()) { + // peer always send list takes priority over any other rules + bool skipChecks = false; + std::vector alwaysSend = tg.config().alwaysSend(); + if (alwaysSend.size() > 0) { + auto it = std::find(alwaysSend.begin(), alwaysSend.end(), peer.first); + if (it != alwaysSend.end()) { + skipChecks = true; // skip any following checks and always send traffic + } + } + + if (!skipChecks) { + TrafficNetwork::PeerConnectionPtr connection = peer.first > 0U ? m_network->findPeer(peer.first) : nullptr; + uint32_t lookupPeerId = peer.first; + if (connection != nullptr) { + if (connection->ccPeerId() > 0U) + lookupPeerId = connection->ccPeerId(); + } + + std::shared_ptr aff = m_network->getPeerAffiliations(lookupPeerId); + if (aff == nullptr) { + continue; + } + else { + if (!aff->hasGroupAff(pkt.dstId)) { + continue; + } + } + } + } + m_network->writePeerQueue(&queue, peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebugEx(LOG_ANALOG, "TagAnalogData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 09848e5a..01cc7024 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -822,6 +822,15 @@ void TagDMRData::playbackParrot() m_lastParrotSrcId = pkt.srcId; m_lastParrotDstId = pkt.dstId; + lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(pkt.dstId); + if (tg.isInvalid()) { + LogWarning(LOG_DMR, "TagDMRData::playbackParrot(), invalid talkgroup, dstId = %u", pkt.dstId); + delete[] pkt.buffer; + m_parrotFrames.unlock(); + m_parrotFrames.pop_front(); + return; + } + if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { @@ -840,6 +849,38 @@ void TagDMRData::playbackParrot() m_network->m_frameQueue->flushQueue(&queue); } + // skip peers that are not affiliated with the target group if parrot TG affiliation is enabled + if (tg.config().affiliated()) { + // peer always send list takes priority over any other rules + bool skipChecks = false; + std::vector alwaysSend = tg.config().alwaysSend(); + if (alwaysSend.size() > 0) { + auto it = std::find(alwaysSend.begin(), alwaysSend.end(), peer.first); + if (it != alwaysSend.end()) { + skipChecks = true; // skip any following checks and always send traffic + } + } + + if (!skipChecks) { + TrafficNetwork::PeerConnectionPtr connection = peer.first > 0U ? m_network->findPeer(peer.first) : nullptr; + uint32_t lookupPeerId = peer.first; + if (connection != nullptr) { + if (connection->ccPeerId() > 0U) + lookupPeerId = connection->ccPeerId(); + } + + std::shared_ptr aff = m_network->getPeerAffiliations(lookupPeerId); + if (aff == nullptr) { + continue; + } + else { + if (!aff->hasGroupAff(pkt.dstId)) { + continue; + } + } + } + } + m_network->writePeerQueue(&queue, peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebugEx(LOG_DMR, "TagDMRData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 71207133..130b929c 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -826,6 +826,15 @@ void TagNXDNData::playbackParrot() m_lastParrotSrcId = pkt.srcId; m_lastParrotDstId = pkt.dstId; + lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(pkt.dstId); + if (tg.isInvalid()) { + LogWarning(LOG_NXDN, "TagNXDNData::playbackParrot(), invalid talkgroup, dstId = %u", pkt.dstId); + delete[] pkt.buffer; + m_parrotFrames.unlock(); + m_parrotFrames.pop_front(); + return; + } + if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { @@ -844,6 +853,38 @@ void TagNXDNData::playbackParrot() m_network->m_frameQueue->flushQueue(&queue); } + // skip peers that are not affiliated with the target group if parrot TG affiliation is enabled + if (tg.config().affiliated()) { + // peer always send list takes priority over any other rules + bool skipChecks = false; + std::vector alwaysSend = tg.config().alwaysSend(); + if (alwaysSend.size() > 0) { + auto it = std::find(alwaysSend.begin(), alwaysSend.end(), peer.first); + if (it != alwaysSend.end()) { + skipChecks = true; // skip any following checks and always send traffic + } + } + + if (!skipChecks) { + TrafficNetwork::PeerConnectionPtr connection = peer.first > 0U ? m_network->findPeer(peer.first) : nullptr; + uint32_t lookupPeerId = peer.first; + if (connection != nullptr) { + if (connection->ccPeerId() > 0U) + lookupPeerId = connection->ccPeerId(); + } + + std::shared_ptr aff = m_network->getPeerAffiliations(lookupPeerId); + if (aff == nullptr) { + continue; + } + else { + if (!aff->hasGroupAff(pkt.dstId)) { + continue; + } + } + } + } + m_network->writePeerQueue(&queue, peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebugEx(LOG_NXDN, "TagNXDNData::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 4547f267..aa39cbb3 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -999,6 +999,15 @@ void TagP25Data::playbackParrot() m_lastParrotSrcId = pkt.srcId; m_lastParrotDstId = pkt.dstId; + lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(pkt.dstId); + if (tg.isInvalid()) { + LogWarning(LOG_P25, "TagP25Data::playbackParrot(), invalid talkgroup, dstId = %u", pkt.dstId); + delete[] pkt.buffer; + m_parrotFrames.unlock(); + m_parrotFrames.pop_front(); + return; + } + if (m_network->m_parrotOnlyOriginating) { uint32_t ccPeerId = 0U; @@ -1050,6 +1059,38 @@ void TagP25Data::playbackParrot() m_network->m_frameQueue->flushQueue(&queue); } + // skip peers that are not affiliated with the target group if parrot TG affiliation is enabled + if (tg.config().affiliated()) { + // peer always send list takes priority over any other rules + bool skipChecks = false; + std::vector alwaysSend = tg.config().alwaysSend(); + if (alwaysSend.size() > 0) { + auto it = std::find(alwaysSend.begin(), alwaysSend.end(), peer.first); + if (it != alwaysSend.end()) { + skipChecks = true; // skip any following checks and always send traffic + } + } + + if (!skipChecks) { + TrafficNetwork::PeerConnectionPtr connection = peer.first > 0U ? m_network->findPeer(peer.first) : nullptr; + uint32_t lookupPeerId = peer.first; + if (connection != nullptr) { + if (connection->ccPeerId() > 0U) + lookupPeerId = connection->ccPeerId(); + } + + std::shared_ptr aff = m_network->getPeerAffiliations(lookupPeerId); + if (aff == nullptr) { + continue; + } + else { + if (!aff->hasGroupAff(pkt.dstId)) { + continue; + } + } + } + } + m_network->writePeerQueue(&queue, peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebug(LOG_P25, "TagP25Data::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", diff --git a/src/fne/network/callhandler/TagP25P2Data.cpp b/src/fne/network/callhandler/TagP25P2Data.cpp index 86f81d06..d06326e0 100644 --- a/src/fne/network/callhandler/TagP25P2Data.cpp +++ b/src/fne/network/callhandler/TagP25P2Data.cpp @@ -726,6 +726,15 @@ void TagP25P2Data::playbackParrot() m_lastParrotSrcId = pkt.srcId; m_lastParrotDstId = pkt.dstId; + lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(pkt.dstId); + if (tg.isInvalid()) { + LogWarning(LOG_P25, "TagP25P2Data::playbackParrot(), invalid talkgroup, dstId = %u", pkt.dstId); + delete[] pkt.buffer; + m_parrotFrames.unlock(); + m_parrotFrames.pop_front(); + return; + } + if (m_network->m_parrotOnlyOriginating) { m_network->writePeer(pkt.peerId, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25_P2 }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { @@ -744,6 +753,38 @@ void TagP25P2Data::playbackParrot() m_network->m_frameQueue->flushQueue(&queue); } + // skip peers that are not affiliated with the target group if parrot TG affiliation is enabled + if (tg.config().affiliated()) { + // peer always send list takes priority over any other rules + bool skipChecks = false; + std::vector alwaysSend = tg.config().alwaysSend(); + if (alwaysSend.size() > 0) { + auto it = std::find(alwaysSend.begin(), alwaysSend.end(), peer.first); + if (it != alwaysSend.end()) { + skipChecks = true; // skip any following checks and always send traffic + } + } + + if (!skipChecks) { + TrafficNetwork::PeerConnectionPtr connection = peer.first > 0U ? m_network->findPeer(peer.first) : nullptr; + uint32_t lookupPeerId = peer.first; + if (connection != nullptr) { + if (connection->ccPeerId() > 0U) + lookupPeerId = connection->ccPeerId(); + } + + std::shared_ptr aff = m_network->getPeerAffiliations(lookupPeerId); + if (aff == nullptr) { + continue; + } + else { + if (!aff->hasGroupAff(pkt.dstId)) { + continue; + } + } + } + } + m_network->writePeerQueue(&queue, peer.first, pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25_P2 }, pkt.buffer, pkt.bufferLen, pkt.pktSeq, pkt.streamId); if (m_network->m_debug) { LogDebugEx(LOG_P25, "TagP25P2Data::playbackParrot()", "Parrot, dstPeer = %u, len = %u, pktSeq = %u, streamId = %u", diff --git a/src/host/p25/packet/ControlSignaling.cpp b/src/host/p25/packet/ControlSignaling.cpp index c8d1bdce..51f36b78 100644 --- a/src/host/p25/packet/ControlSignaling.cpp +++ b/src/host/p25/packet/ControlSignaling.cpp @@ -2355,6 +2355,7 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (!m_p25->m_hideRemoteGrantLog) { LogWarning(LOG_NET, P25_TSDU_STR ", TSBKO, IOSP_GRP_VCH (Group Voice Channel Request) ignored, no group affiliations, dstId = %u", dstId); } + return false; } } diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index f4166588..0b883c7f 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -1914,10 +1914,14 @@ void Voice::writeNet_LDU1() if (!m_p25->m_affiliations->isGranted(dstId)) { if (!m_p25->m_control->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, group, true)) { - LogError(LOG_NET, P25_HDU_STR " call rejected, network call not granted, dstId = %u", dstId); + if (!m_p25->m_hideRemoteGrantLog) { + LogError(LOG_NET, P25_HDU_STR " call rejected, network call not granted, dstId = %u", dstId); + } if ((!m_p25->m_networkWatchdog.isRunning() || m_p25->m_networkWatchdog.hasExpired()) && m_p25->m_netLastDstId != 0U) { + LogWarning(LOG_NET, P25_HDU_STR ", call rejected, network watchdog not running or expired, netLastDstId = %u", m_p25->m_netLastDstId); + if (m_p25->m_network != nullptr) m_p25->m_network->resetP25();