diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 7ff4c9e2..8fdbdb2b 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -89,6 +89,8 @@ master: disallowExtAdjStsBcast: true # Flag indicating whether or not a conventional site can override affiliation rules. allowConvSiteAffOverride: true + # Flag indicating whether or not a TDULC call terminations will pass to any peers. + disallowCallTerm: 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 b0507f24..4c16dd79 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -82,6 +82,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_disallowAdjStsBcast(false), m_disallowExtAdjStsBcast(true), m_allowConvSiteAffOverride(false), + m_disallowCallTerm(false), m_restrictGrantToAffOnly(false), m_filterHeaders(true), m_filterTerminators(true), @@ -126,6 +127,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) m_disallowAdjStsBcast = conf["disallowAdjStsBcast"].as(false); m_disallowExtAdjStsBcast = conf["disallowExtAdjStsBcast"].as(true); m_allowConvSiteAffOverride = conf["allowConvSiteAffOverride"].as(true); + m_disallowCallTerm = conf["disallowCallTerm"].as(false); m_softConnLimit = conf["connectionLimit"].as(MAX_HARD_CONN_CAP); if (m_softConnLimit > MAX_HARD_CONN_CAP) { @@ -183,6 +185,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogInfo(" Disable Packet Data: %s", m_disablePacketData ? "yes" : "no"); LogInfo(" Dump Packet Data: %s", m_dumpPacketData ? "yes" : "no"); LogInfo(" Disable P25 ADJ_STS_BCAST to external peers: %s", m_disallowExtAdjStsBcast ? "yes" : "no"); + LogInfo(" Disable P25 TDULC call termination broadcasts to any peers: %s", m_disallowCallTerm ? "yes" : "no"); LogInfo(" Allow conventional sites to override affiliation and receive all traffic: %s", m_allowConvSiteAffOverride ? "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"); diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 2a36a0c6..8a2721f5 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -456,6 +456,7 @@ namespace network bool m_disallowAdjStsBcast; bool m_disallowExtAdjStsBcast; bool m_allowConvSiteAffOverride; + bool m_disallowCallTerm; bool m_restrictGrantToAffOnly; bool m_filterHeaders; diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index bd5c188f..9f33d9d3 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -778,7 +778,8 @@ bool TagP25Data::processTSDUFrom(uint8_t* buffer, uint32_t peerId, uint8_t duid) // handle standard P25 reference opcodes switch (tdulc->getLCO()) { case LCO::CALL_TERM: - return false; // discard call terms at the FNE + if (m_network->m_disallowCallTerm) + return false; default: break; } diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index 5141d2ec..56f763ff 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2016,2017,2018 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" @@ -1358,6 +1358,18 @@ bool Voice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L // currently ignored -- this is a TODO break; case DUID::TDU: + case DUID::TDULC: + if (duid == DUID::TDULC) { + std::unique_ptr tdulc = lc::tdulc::TDULCFactory::createTDULC(data); + if (tdulc == nullptr) { + LogWarning(LOG_NET, P25_TDULC_STR ", undecodable TDULC"); + } + else { + if (tdulc->getLCO() != LCO::CALL_TERM) + break; + } + } + // ignore a TDU that doesn't contain our destination ID if (control.getDstId() != m_p25->m_netLastDstId) { return false; @@ -1384,9 +1396,6 @@ bool Voice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L resetNet(); } break; - case DUID::TDULC: - // currently ignored - break; default: break;