implement trunking site preference support for TGIDs, this implements a feature by which TGIDs can be gated to allow affiliation to preferred sites, all other non-preferred sites will DENY affiliation causing the radio to attempt to roam to another site (this is a trunking only feature and does not change conventional operation);

pull/51/head
Bryan Biedenkapp 2 years ago
parent 7d1af0235b
commit 96a9ba1c03

@ -24,6 +24,10 @@ groupVoice:
exclusion: []
# List of peer talkgroup rewrites.
rewrite: []
# List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access,
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers a preferred. (Trunking Only)
preferred: []
#
# Source Configuration
#
@ -51,6 +55,10 @@ groupVoice:
exclusion: []
# List of peer talkgroup rewrites.
rewrite: []
# List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access,
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers a preferred. (Trunking Only)
preferred: []
#
# Source Configuration
#
@ -84,6 +92,10 @@ groupVoice:
tgid: 9999
# DMR slot number.
slot: 1
# List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access,
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers a preferred. (Trunking Only)
preferred: []
#
# Source Configuration
#
@ -109,6 +121,10 @@ groupVoice:
exclusion: []
# List of peer talkgroup rewrites.
rewrite: []
# List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access,
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers a preferred. (Trunking Only)
preferred: []
#
# Source Configuration
#
@ -134,6 +150,10 @@ groupVoice:
exclusion: []
# List of peer talkgroup rewrites.
rewrite: []
# List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access,
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers a preferred. (Trunking Only)
preferred: []
#
# Source Configuration
#
@ -159,6 +179,10 @@ groupVoice:
exclusion: []
# List of peer talkgroup rewrites.
rewrite: []
# List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access,
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers a preferred. (Trunking Only)
preferred: []
#
# Source Configuration
#

@ -10,7 +10,7 @@
*
* Copyright (C) 2016 Simon Rune, G7RZU
* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017,2019 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017,2019,2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -95,3 +95,28 @@ bool AccessControl::validateTGId(uint32_t slotNo, uint32_t id)
return true;
}
}
/// <summary>
/// Helper to determine if a talkgroup ID is non-preferred.
/// </summary>
/// <param name="slotNo">DMR slot number.</param>
/// <param name="id">Talkgroup ID.</param>
/// <returns>True, if talkgroup ID is valid, otherwise false.</returns>
bool AccessControl::tgidNonPreferred(uint32_t slotNo, uint32_t id)
{
// TG0 is never valid
if (id == 0U)
return false;
// check if TID ACLs are enabled
if (!m_tidLookup->getACL()) {
return false;
}
// lookup TID and perform test for validity
TalkgroupRuleGroupVoice tid = m_tidLookup->find(id);
if (tid.config().nonPreferred())
return true;
return false;
}

@ -10,7 +10,7 @@
*
* Copyright (C) 2016 Simon Rune, G7RZU
* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017,2019 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017,2019,2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__DMR_ACL__ACCESS_CONTROL_H__)
@ -41,6 +41,9 @@ namespace dmr
/// <summary>Helper to validate a talkgroup ID.</summary>
static bool validateTGId(uint32_t slotNo, uint32_t id);
/// <summary>Helper to determine if a talkgroup ID is non-preferred.</summary>
static bool tgidNonPreferred(uint32_t slotNo, uint32_t id);
private:
static RadioIdLookup* m_ridLookup;
static TalkgroupRulesLookup* m_tidLookup;

@ -121,13 +121,15 @@ void TalkgroupRulesLookup::clear()
/// <param name="id">Unique ID to add.</param>
/// <param name="slot">DMR slot this talkgroup is valid on.</param>
/// <param name="enabled">Flag indicating if talkgroup ID is enabled or not.</param>
void TalkgroupRulesLookup::addEntry(uint32_t id, uint8_t slot, bool enabled)
/// <param name="nonPreferred">Flag indicating if the talkgroup ID is non-preferred.</param>
void TalkgroupRulesLookup::addEntry(uint32_t id, uint8_t slot, bool enabled, bool nonPreferred)
{
TalkgroupRuleGroupVoiceSource source;
TalkgroupRuleConfig config;
source.tgId(id);
source.tgSlot(slot);
config.active(enabled);
config.nonPreferred(nonPreferred);
std::lock_guard<std::mutex> lock(m_mutex);
auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(),
@ -146,6 +148,7 @@ void TalkgroupRulesLookup::addEntry(uint32_t id, uint8_t slot, bool enabled)
config = it->config();
config.active(enabled);
config.nonPreferred(nonPreferred);
TalkgroupRuleGroupVoice entry = *it;
entry.config(config);
@ -345,12 +348,13 @@ bool TalkgroupRulesLookup::load()
uint32_t incCount = groupVoice.config().inclusion().size();
uint32_t excCount = groupVoice.config().exclusion().size();
uint32_t rewrCount = groupVoice.config().rewrite().size();
uint32_t prefCount = groupVoice.config().preferred().size();
if (incCount > 0 && excCount > 0) {
::LogWarning(LOG_HOST, "Talkgroup (%s) defines both inclusions and exclusions! Inclusions take precedence and exclusions will be ignored.", groupName.c_str());
}
::LogInfoEx(LOG_HOST, "Talkgroup NAME: %s SRC_TGID: %u SRC_TS: %u ACTIVE: %u PARROT: %u INCLUSIONS: %u EXCLUSIONS: %u REWRITES: %u", groupName.c_str(), tgId, tgSlot, active, parrot, incCount, excCount, rewrCount);
::LogInfoEx(LOG_HOST, "Talkgroup NAME: %s SRC_TGID: %u SRC_TS: %u ACTIVE: %u PARROT: %u INCLUSIONS: %u EXCLUSIONS: %u REWRITES: %u PREFERRED: %u", groupName.c_str(), tgId, tgSlot, active, parrot, incCount, excCount, rewrCount, prefCount);
}
size_t size = m_groupVoice.size();

@ -142,7 +142,9 @@ namespace lookups
m_parrot(false),
m_inclusion(),
m_exclusion(),
m_rewrite()
m_rewrite(),
m_preferred(),
m_nonPreferred(false)
{
/* stub */
}
@ -178,6 +180,14 @@ namespace lookups
m_rewrite.push_back(rewrite);
}
}
yaml::Node& preferredList = node["preferred"];
if (preferredList.size() > 0U) {
for (size_t i = 0; i < preferredList.size(); i++) {
uint32_t peerId = preferredList[i].as<uint32_t>(0U);
m_preferred.push_back(peerId);
}
}
}
/// <summary>Equals operator. Copies this TalkgroupRuleConfig to another TalkgroupRuleConfig.</summary>
@ -190,6 +200,8 @@ namespace lookups
m_inclusion = data.m_inclusion;
m_exclusion = data.m_exclusion;
m_rewrite = data.m_rewrite;
m_preferred = data.m_preferred;
m_nonPreferred = data.m_nonPreferred;
}
return *this;
@ -201,6 +213,8 @@ namespace lookups
uint8_t exclusionSize() const { return m_exclusion.size(); }
/// <summary>Gets the count of rewrites.</summary>
uint8_t rewriteSize() const { return m_rewrite.size(); }
/// <summary>Gets the count of rewrites.</summary>
uint8_t preferredSize() const { return m_preferred.size(); }
/// <summary>Return the YAML structure for this TalkgroupRuleConfig.</summary>
void getYaml(yaml::Node &node)
@ -237,6 +251,15 @@ namespace lookups
}
}
node["rewrite"] = rewriteList;
yaml::Node preferredList;
if (m_preferred.size() > 0U) {
for (auto pref : m_preferred) {
yaml::Node& newPref = preferredList.push_back();
newPref = __INT_STR(pref);
}
}
node["preferred"] = preferredList;
}
public:
@ -252,6 +275,11 @@ namespace lookups
__PROPERTY_PLAIN(std::vector<uint32_t>, exclusion);
/// <summary>List of rewrites performed by this rule.</summary>
__PROPERTY_PLAIN(std::vector<TalkgroupRuleRewrite>, rewrite);
/// <summary>List of peer IDs preferred by this rule.</summary>
__PROPERTY_PLAIN(std::vector<uint32_t>, preferred);
/// <summary>Flag indicating whether or not the talkgroup is a non-preferred.</summary>
__PROPERTY_PLAIN(bool, nonPreferred);
};
// ---------------------------------------------------------------------------
@ -348,7 +376,7 @@ namespace lookups
void clear();
/// <summary>Adds a new entry to the lookup table.</summary>
void addEntry(uint32_t id, uint8_t slot, bool enabled);
void addEntry(uint32_t id, uint8_t slot, bool enabled, bool nonPreferred = false);
/// <summary>Adds a new entry to the lookup table.</summary>
void addEntry(TalkgroupRuleGroupVoice groupVoice);
/// <summary>Erases an existing entry from the lookup table by the specified unique ID.</summary>

@ -10,7 +10,7 @@
*
* Copyright (C) 2016 Simon Rune, G7RZU
* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017,2019 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017,2019,2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -64,7 +64,6 @@ bool AccessControl::validateSrcId(uint32_t id)
/// <summary>
/// Helper to validate a talkgroup ID.
/// </summary>
/// <param name="slotNo">DMR slot number.</param>
/// <param name="id">Talkgroup ID.</param>
/// <returns>True, if talkgroup ID is valid, otherwise false.</returns>
bool AccessControl::validateTGId(uint32_t id)
@ -88,3 +87,27 @@ bool AccessControl::validateTGId(uint32_t id)
return true;
}
/// <summary>
/// Helper to determine if a talkgroup ID is non-preferred.
/// </summary>
/// <param name="id">Talkgroup ID.</param>
/// <returns>True, if talkgroup ID is valid, otherwise false.</returns>
bool AccessControl::tgidNonPreferred(uint32_t id)
{
// TG0 is never valid
if (id == 0U)
return false;
// check if TID ACLs are enabled
if (!m_tidLookup->getACL()) {
return false;
}
// lookup TID and perform test for validity
TalkgroupRuleGroupVoice tid = m_tidLookup->find(id);
if (tid.config().nonPreferred())
return true;
return false;
}

@ -10,7 +10,7 @@
*
* Copyright (C) 2016 Simon Rune, G7RZU
* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017,2019 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017,2019,2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__NXDN_ACL__ACCESS_CONTROL_H__)
@ -41,6 +41,9 @@ namespace nxdn
/// <summary>Helper to validate a talkgroup ID.</summary>
static bool validateTGId(uint32_t id);
/// <summary>Helper to determine if a talkgroup ID is non-preferred.</summary>
static bool tgidNonPreferred(uint32_t id);
private:
static RadioIdLookup* m_ridLookup;
static TalkgroupRulesLookup* m_tidLookup;

@ -10,7 +10,7 @@
*
* Copyright (C) 2016 Simon Rune, G7RZU
* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017,2019,2022 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017,2019,2022,2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -64,7 +64,6 @@ bool AccessControl::validateSrcId(uint32_t id)
/// <summary>
/// Helper to validate a talkgroup ID.
/// </summary>
/// <param name="slotNo">DMR slot number.</param>
/// <param name="id">Talkgroup ID.</param>
/// <returns>True, if talkgroup ID is valid, otherwise false.</returns>
bool AccessControl::validateTGId(uint32_t id)
@ -88,3 +87,27 @@ bool AccessControl::validateTGId(uint32_t id)
return true;
}
/// <summary>
/// Helper to determine if a talkgroup ID is non-preferred.
/// </summary>
/// <param name="id">Talkgroup ID.</param>
/// <returns>True, if talkgroup ID is valid, otherwise false.</returns>
bool AccessControl::tgidNonPreferred(uint32_t id)
{
// TG0 is never valid
if (id == 0U)
return false;
// check if TID ACLs are enabled
if (!m_tidLookup->getACL()) {
return false;
}
// lookup TID and perform test for validity
TalkgroupRuleGroupVoice tid = m_tidLookup->find(id);
if (tid.config().nonPreferred())
return true;
return false;
}

@ -10,7 +10,7 @@
*
* Copyright (C) 2016 Simon Rune, G7RZU
* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017,2019 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017,2019,2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__P25_ACL__ACCESS_CONTROL_H__)
@ -41,6 +41,9 @@ namespace p25
/// <summary>Helper to validate a talkgroup ID.</summary>
static bool validateTGId(uint32_t id);
/// <summary>Helper to determine if a talkgroup ID is non-preferred.</summary>
static bool tgidNonPreferred(uint32_t id);
private:
static RadioIdLookup* m_ridLookup;
static TalkgroupRulesLookup* m_tidLookup;

@ -1295,6 +1295,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId)
for (auto entry : groupVoice) {
std::vector<uint32_t> inclusion = entry.config().inclusion();
std::vector<uint32_t> exclusion = entry.config().exclusion();
std::vector<uint32_t> preferred = entry.config().preferred();
// peer inclusion lists take priority over exclusion lists
if (inclusion.size() > 0) {
@ -1313,9 +1314,25 @@ void FNENetwork::writeTGIDs(uint32_t peerId)
}
}
}
// determine if the peer is non-preferred
bool nonPreferred = false;
if (preferred.size() > 0) {
auto it = std::find(preferred.begin(), preferred.end(), peerId);
if (it == preferred.end()) {
nonPreferred = true;
}
}
if (entry.config().active()) {
tgidList.push_back({ entry.source().tgId(), entry.source().tgSlot() });
uint8_t slotNo = entry.source().tgSlot();
// set upper bit of the slot number to flag non-preferred
if (nonPreferred) {
slotNo = 0x80U + (slotNo & 0x03U);
}
tgidList.push_back({ entry.source().tgId(), slotNo });
}
}

@ -397,8 +397,10 @@ void Network::clock(uint32_t ms)
m_ridLookup->toggleEntry(id, true);
offs += 4U;
}
LogMessage(LOG_NET, "Network Announced %u whitelisted RIDs", len);
// Save to file if enabled and we got RIDs
// save to file if enabled and we got RIDs
if (m_saveLookup && len > 0) {
m_ridLookup->commit();
}
@ -419,8 +421,10 @@ void Network::clock(uint32_t ms)
m_ridLookup->toggleEntry(id, false);
offs += 4U;
}
LogMessage(LOG_NET, "Network Announced %u blacklisted RIDs", len);
// Save to file if enabled and we got RIDs
// save to file if enabled and we got RIDs
if (m_saveLookup && len > 0) {
m_ridLookup->commit();
}
@ -438,22 +442,35 @@ void Network::clock(uint32_t ms)
uint32_t offs = 11U;
for (uint32_t i = 0; i < len; i++) {
uint32_t id = __GET_UINT16(buffer, offs);
uint8_t slot = (buffer[offs + 3U]);
uint8_t slot = (buffer[offs + 3U]) & 0x03U;
bool nonPreferred = (buffer[offs + 3U] & 0x80U) == 0x80U;
lookups::TalkgroupRuleGroupVoice tid = m_tidLookup->find(id, slot);
// if the TG is marked as non-preferred, and the TGID exists in the local entries
// erase the local and overwrite with the FNE data
if (nonPreferred) {
if (!tid.isInvalid()) {
m_tidLookup->eraseEntry(id, slot);
tid = m_tidLookup->find(id, slot);
}
}
if (tid.isInvalid()) {
if (!tid.config().active()) {
m_tidLookup->eraseEntry(id, slot);
}
LogMessage(LOG_NET, "Activated TG %u TS %u in TGID table", id, slot);
m_tidLookup->addEntry(id, slot, true);
LogMessage(LOG_NET, "Activated%s TG %u TS %u in TGID table", (nonPreferred) ? " non-preferred" : "", id, slot);
m_tidLookup->addEntry(id, slot, true, nonPreferred);
}
offs += 5U;
}
LogMessage(LOG_NET, "Activated %u TGs; loaded %u entries into lookup table", len, m_tidLookup->groupVoice().size());
// Save if saving from network is enabled
// save if saving from network is enabled
if (m_saveLookup && len > 0) {
m_tidLookup->commit();
}
@ -481,8 +498,10 @@ void Network::clock(uint32_t ms)
offs += 5U;
}
LogMessage(LOG_NET, "Deactivated %u TGs; loaded %u entries into lookup table", len, m_tidLookup->groupVoice().size());
// Save if saving from network is enabled
// save if saving from network is enabled
if (m_saveLookup && len > 0) {
m_tidLookup->commit();
}

@ -2671,6 +2671,14 @@ bool ControlSignaling::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId)
iosp->setResponse(P25_RSP_DENY);
noNet = true;
}
// deny affiliation if the TG is non-preferred on this site/CC
if (acl::AccessControl::tgidNonPreferred(dstId)) {
LogWarning(LOG_RF, P25_TSDU_STR ", %s non-preferred on this site, TGID rejection, dstId = %u", iosp->toString().c_str(), dstId);
::ActivityLog("P25", true, "group affiliation request from %u to %s %u denied", srcId, "TG ", dstId);
iosp->setResponse(P25_RSP_DENY);
noNet = true;
}
}
if (iosp->getResponse() == P25_RSP_ACCEPT) {

Loading…
Cancel
Save

Powered by TurnKey Linux.