diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 07baf74b..2199df2c 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -83,6 +83,14 @@ master: # Flag indicating whether or not a conventional site can override affiliation rules. allowConvSiteAffOverride: true + # Flag indicating that traffic headers will be filtered by destination ID (i.e. valid RID or valid TGID). + filterHeaders: true + # Flag indicating that terminators will be filtered by destination ID (i.e. valid RID or valid TGID). + filterTerminators: true + + # List of peers that unit to unit calls are dropped for. + dropUnitToUnit: [] + # Flag indicating whether or not InfluxDB logging and metrics recording is enabled. enableInflux: false # Hostname/IP address of the InfluxDB instance to connect to. diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 70453072..ba3ddd28 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -633,6 +633,7 @@ bool HostFNE::createPeerNetworks() /* ** Block Traffic To Peers */ + yaml::Node& blockTrafficTo = peerConf["blockTrafficTo"]; if (blockTrafficTo.size() > 0U) { for (size_t i = 0; i < blockTrafficTo.size(); i++) { diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index c8210be0..4a8b3626 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -98,6 +98,9 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_disallowExtAdjStsBcast(true), m_allowConvSiteAffOverride(false), m_restrictGrantToAffOnly(false), + m_filterHeaders(true), + m_filterTerminators(true), + m_dropU2UPeerTable(), m_enableInfluxDB(false), m_influxServerAddress("127.0.0.1"), m_influxServerPort(8086U), @@ -163,6 +166,22 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) m_parrotOnlyOriginating = conf["parrotOnlyToOrginiatingPeer"].as(false); m_restrictGrantToAffOnly = conf["restrictGrantToAffiliatedOnly"].as(false); + m_filterHeaders = conf["filterHeaders"].as(true); + m_filterTerminators = conf["filterTerminators"].as(true); + + /* + ** Drop Unit to Unit Peers + */ + + yaml::Node& dropUnitToUnit = conf["dropUnitToUnit"]; + if (dropUnitToUnit.size() > 0U) { + for (size_t i = 0; i < dropUnitToUnit.size(); i++) { + uint32_t peerId = (uint32_t)::strtoul(dropUnitToUnit[i].as("0").c_str(), NULL, 10); + if (peerId != 0U) { + m_dropU2UPeerTable.push_back(peerId); + } + } + } if (printOptions) { LogInfo(" Maximum Permitted Connections: %u", m_softConnLimit); @@ -173,6 +192,8 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) 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(" Restrict grant response by affiliation: %s", m_restrictGrantToAffOnly ? "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(" InfluxDB Reporting Enabled: %s", m_enableInfluxDB ? "yes" : "no"); if (m_enableInfluxDB) { LogInfo(" InfluxDB Address: %s", m_influxServerAddress.c_str()); @@ -1103,6 +1124,22 @@ void* FNENetwork::threadedNetworkRx(void* arg) return nullptr; } +/// +/// Checks if the passed peer ID is blocked from unit-to-unit traffic. +/// +/// +bool FNENetwork::checkU2UDroppedPeer(uint32_t peerId) +{ + if (m_dropU2UPeerTable.empty()) + return false; + + if (std::find(m_dropU2UPeerTable.begin(), m_dropU2UPeerTable.end(), peerId) != m_dropU2UPeerTable.end()) { + return true; + } + + return false; +} + /// /// Helper to create a peer on the peers affiliations list. /// diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 29aa2d6a..b672fe86 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -307,6 +307,11 @@ namespace network bool m_allowConvSiteAffOverride; bool m_restrictGrantToAffOnly; + bool m_filterHeaders; + bool m_filterTerminators; + + std::vector m_dropU2UPeerTable; + bool m_enableInfluxDB; std::string m_influxServerAddress; uint16_t m_influxServerPort; @@ -322,6 +327,9 @@ namespace network /// Entry point to process a given network packet. static void* threadedNetworkRx(void* arg); + /// Checks if the passed peer ID is blocked from unit-to-unit traffic. + bool checkU2UDroppedPeer(uint32_t peerId); + /// Helper to create a peer on the peers affiliations list. void createPeerAffiliations(uint32_t peerId, std::string peerName); /// Helper to erase the peer from the peers affiliations list. diff --git a/src/fne/network/fne/TagDMRData.cpp b/src/fne/network/fne/TagDMRData.cpp index 8df72ac8..02e2c05e 100644 --- a/src/fne/network/fne/TagDMRData.cpp +++ b/src/fne/network/fne/TagDMRData.cpp @@ -612,9 +612,10 @@ bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::Data& /// bool TagDMRData::isPeerPermitted(uint32_t peerId, data::Data& data, uint32_t streamId, bool external) { - // private calls are always permitted if (data.getDataType() == FLCO_PRIVATE) { - return true; + if (!m_network->checkU2UDroppedPeer(peerId)) + return true; + return false; } // is this a group call? diff --git a/src/fne/network/fne/TagNXDNData.cpp b/src/fne/network/fne/TagNXDNData.cpp index 985d13d4..ab0e5056 100644 --- a/src/fne/network/fne/TagNXDNData.cpp +++ b/src/fne/network/fne/TagNXDNData.cpp @@ -421,9 +421,10 @@ bool TagNXDNData::peerRewrite(uint32_t peerId, uint32_t& dstId, bool outbound) /// bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, uint32_t streamId, bool external) { - // private calls are always permitted if (!lc.getGroup()) { - return true; + if (!m_network->checkU2UDroppedPeer(peerId)) + return true; + return false; } // is this a group call? diff --git a/src/fne/network/fne/TagP25Data.cpp b/src/fne/network/fne/TagP25Data.cpp index 78d766ba..c858df44 100644 --- a/src/fne/network/fne/TagP25Data.cpp +++ b/src/fne/network/fne/TagP25Data.cpp @@ -684,6 +684,13 @@ bool TagP25Data::processTSDUFrom(uint8_t* buffer, uint32_t peerId, uint8_t duid) // handle standard P25 reference opcodes switch (tsbk->getLCO()) { + case TSBK_IOSP_UU_VCH: + case TSBK_IOSP_UU_ANS: + { + if (m_network->checkU2UDroppedPeer(peerId)) + return false; + } + break; case TSBK_OSP_ADJ_STS_BCAST: { if (m_network->m_disallowAdjStsBcast) { @@ -841,18 +848,76 @@ bool TagP25Data::processTSDUToExternal(uint8_t* buffer, uint32_t srcPeerId, uint /// bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, uint8_t duid, uint32_t streamId, bool external) { - // private calls are always permitted if (control.getLCO() == LC_PRIVATE) { - return true; + if (!m_network->checkU2UDroppedPeer(peerId)) + return true; + return false; } // always permit a TSDU or PDU if (duid == P25_DUID_TSDU || duid == P25_DUID_PDU) return true; - // always permit a terminator - if (duid == P25_DUID_TDU || duid == P25_DUID_TDULC) + if (duid == P25_DUID_HDU) { + if (m_network->m_filterHeaders) { + if (control.getSrcId() != 0U && control.getDstId() != 0U) { + // is this a group call? + lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(control.getDstId()); + if (!tg.isInvalid()) { + return true; + } + + tg = m_network->m_tidLookup->findByRewrite(peerId, control.getDstId()); + if (!tg.isInvalid()) { + return true; + } + + // is this a U2U call? + lookups::RadioId rid = m_network->m_ridLookup->find(control.getDstId()); + if (!rid.radioDefault() && rid.radioEnabled()) { + return true; + } + + return false; + } + } + + // always permit a headers return true; + } + + if (duid == P25_DUID_TDULC) { + // always permit a terminator + return true; + } + + if (duid == P25_DUID_TDU) { + if (m_network->m_filterTerminators) { + if (control.getSrcId() != 0U && control.getDstId() != 0U) { + // is this a group call? + lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(control.getDstId()); + if (!tg.isInvalid()) { + return true; + } + + tg = m_network->m_tidLookup->findByRewrite(peerId, control.getDstId()); + if (!tg.isInvalid()) { + return true; + } + + // is this a U2U call? + lookups::RadioId rid = m_network->m_ridLookup->find(control.getDstId()); + if (!rid.radioDefault() && rid.radioEnabled()) { + return true; + } + + return false; + } + } + + // always permit a terminator + return true; + } // is this a group call? lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(control.getDstId());