ensure if the network changes the destination ID for P25 and NXDN in-flight the LC data is properly replaced; implement talkgroup mutation by peer on conference bridge FNE;

pull/48/head
Bryan Biedenkapp 2 years ago
parent 1766121686
commit 21a4a97ec3

@ -20,6 +20,8 @@ groupVoice:
inclusion: []
# List of peer IDs excluded for this talkgroup (peers listed here will be ignored for traffic).
exclusion: []
# List of peer talkgroup mutations.
mutations: []
#
# Source Configuration
#
@ -43,6 +45,8 @@ groupVoice:
inclusion: []
# List of peer IDs excluded for this talkgroup (peers listed here will be ignored for traffic).
exclusion: []
# List of peer talkgroup mutations.
mutations: []
#
# Source Configuration
#
@ -52,6 +56,37 @@ groupVoice:
# DMR slot number.
slot: 1
# Textual name of the talkgroup.
- name: Parrot Mutation Example
#
# Talkgroup Configuration
#
config:
# Flag indicating whether this talkgroup is active or not.
active: true
# Flag indicating whether or not this talkgroup is a parrot talkgroup.
parrot: true
# List of peer IDs included for this talkgroup (peers listed here will be selected for traffic).
inclusion: []
# List of peer IDs excluded for this talkgroup (peers listed here will be ignored for traffic).
exclusion: []
# List of peer talkgroup mutations.
mutations:
# Network Peer ID to mutate.
- peerid: 9000990
# Numerical talkgroup ID number.
tgid: 9999
# DMR slot number.
slot: 1
#
# Source Configuration
#
source:
# Numerical talkgroup ID number.
tgid: 9999
# DMR slot number.
slot: 1
# Textual name of the talkgroup.
- name: System Wide P25
#
@ -64,6 +99,8 @@ groupVoice:
inclusion: []
# List of peer IDs excluded for this talkgroup (peers listed here will be ignored for traffic).
exclusion: []
# List of peer talkgroup mutations.
mutations: []
#
# Source Configuration
#
@ -85,6 +122,8 @@ groupVoice:
inclusion: []
# List of peer IDs excluded for this talkgroup (peers listed here will be ignored for traffic).
exclusion: []
# List of peer talkgroup mutations.
mutations: []
#
# Source Configuration
#
@ -106,6 +145,8 @@ groupVoice:
inclusion: []
# List of peer IDs excluded for this talkgroup (peers listed here will be ignored for traffic).
exclusion: []
# List of peer talkgroup mutations.
mutations: []
#
# Source Configuration
#

@ -7,7 +7,7 @@
*
*/
/*
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
* Copyright (C) 2023-2024 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -244,14 +244,58 @@ TalkgroupRuleGroupVoice TalkgroupRulesLookup::find(uint32_t id, uint8_t slot)
m_mutex.lock();
{
auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(),
[&](TalkgroupRuleGroupVoice x)
{
auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(),
[&](TalkgroupRuleGroupVoice x)
{
if (slot != 0U) {
return x.source().tgId() == id && x.source().tgSlot() == slot;
}
return x.source().tgId() == id;
return x.source().tgId() == id;
});
if (it != m_groupVoice.end()) {
entry = *it;
} else {
entry = TalkgroupRuleGroupVoice();
}
}
m_mutex.unlock();
return entry;
}
/// <summary>
/// Finds a table entry in this lookup table.
/// </summary>
/// <param name="peerId">Unique identifier for table entry.</param>
/// <param name="id">Unique identifier for table entry.</param>
/// <param name="slot">DMR slot this talkgroup is valid on.</param>
/// <returns>Table entry.</returns>
TalkgroupRuleGroupVoice TalkgroupRulesLookup::findByMutation(uint32_t peerId, uint32_t id, uint8_t slot)
{
TalkgroupRuleGroupVoice entry;
m_mutex.lock();
{
auto it = std::find_if(m_groupVoice.begin(), m_groupVoice.end(),
[&](TalkgroupRuleGroupVoice x)
{
if (x.config().mutation().size() == 0)
return false;
auto innerIt = std::find_if(x.config().mutation().begin(), x.config().mutation().end(),
[&](TalkgroupRuleMutation y)
{
if (slot != 0U) {
return y.peerId() == peerId && y.tgId() == id && y.tgSlot() == slot;
}
return y.peerId() == peerId && y.tgId() == id;
});
if (innerIt != x.config().mutation().end())
return true;
return false;
});
if (it != m_groupVoice.end()) {
entry = *it;

@ -7,7 +7,7 @@
*
*/
/*
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
* Copyright (C) 2023-2024 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -79,6 +79,52 @@ namespace lookups
__PROPERTY_PLAIN(uint8_t, tgSlot, tgSlot);
};
// ---------------------------------------------------------------------------
// Class Declaration
// Represents an mutation block for a routing rule.
// ---------------------------------------------------------------------------
class HOST_SW_API TalkgroupRuleMutation {
public:
/// <summary>Initializes a new insatnce of the TalkgroupRuleMutation class.</summary>
TalkgroupRuleMutation() :
m_peerId(0U),
m_tgId(0U),
m_tgSlot(0U)
{
/* stub */
}
/// <summary>Initializes a new insatnce of the TalkgroupRuleMutation class.</summary>
/// <param name="node"></param>
TalkgroupRuleMutation(yaml::Node& node) :
TalkgroupRuleMutation()
{
m_peerId = node["peerId"].as<uint32_t>(0U);
m_tgId = node["tgid"].as<uint32_t>(0U);
m_tgSlot = (uint8_t)node["slot"].as<uint32_t>(0U);
}
/// <summary>Equals operator. Copies this TalkgroupRuleMutation to another TalkgroupRuleMutation.</summary>
virtual TalkgroupRuleMutation& operator=(const TalkgroupRuleMutation& data)
{
if (this != &data) {
m_peerId = data.m_peerId;
m_tgId = data.m_tgId;
m_tgSlot = data.m_tgSlot;
}
return *this;
}
public:
/// <summary>Peer ID.</summary>
__PROPERTY_PLAIN(uint32_t, peerId, peerId);
/// <summary>Talkgroup ID.</summary>
__PROPERTY_PLAIN(uint32_t, tgId, tgId);
/// <summary>Talkgroup DMR slot.</summary>
__PROPERTY_PLAIN(uint8_t, tgSlot, tgSlot);
};
// ---------------------------------------------------------------------------
// Class Declaration
// Represents an configuration block for a routing rule.
@ -91,7 +137,8 @@ namespace lookups
m_active(false),
m_parrot(false),
m_inclusion(),
m_exclusion()
m_exclusion(),
m_mutation()
{
/* stub */
}
@ -118,6 +165,14 @@ namespace lookups
m_exclusion.push_back(peerId);
}
}
yaml::Node& mutationList = node["mutations"];
if (mutationList.size() > 0U) {
for (size_t i = 0; i < mutationList.size(); i++) {
TalkgroupRuleMutation mutation = TalkgroupRuleMutation(mutationList[i]);
m_mutation.push_back(mutation);
}
}
}
/// <summary>Equals operator. Copies this TalkgroupRuleConfig to another TalkgroupRuleConfig.</summary>
@ -142,6 +197,8 @@ namespace lookups
__PROPERTY_PLAIN(std::vector<uint32_t>, inclusion, inclusion);
/// <summary>List of peer IDs excluded by this rule.</summary>
__PROPERTY_PLAIN(std::vector<uint32_t>, exclusion, exclusion);
/// <summary>List of mutations performed by this rule.</summary>
__PROPERTY_PLAIN(std::vector<TalkgroupRuleMutation>, mutation, mutation);
};
// ---------------------------------------------------------------------------
@ -230,6 +287,8 @@ namespace lookups
void eraseEntry(uint32_t id, uint8_t slot);
/// <summary>Finds a table entry in this lookup table.</summary>
virtual TalkgroupRuleGroupVoice find(uint32_t id, uint8_t slot = 0U);
/// <summary>Finds a table entry in this lookup table by mutation.</summary>
virtual TalkgroupRuleGroupVoice findByMutation(uint32_t peerId, uint32_t id, uint8_t slot = 0U);
/// <summary>Flag indicating whether talkgroup ID access control is enabled or not.</summary>
bool getACL();

@ -91,7 +91,6 @@ namespace network
sockaddr_storage address;
uint32_t addrLen;
};
/* Vector of buffers that contain a full frames */

@ -7,7 +7,7 @@
*
*/
/*
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
* Copyright (C) 2023-2024 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -431,7 +431,7 @@ void HostFNE::processPeer(network::Network* peerNetwork)
uint32_t slotNo = (data[15U] & 0x80U) == 0x80U ? 2U : 1U;
uint32_t streamId = peerNetwork->getDMRStreamId(slotNo);
m_network->dmrTrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId);
m_network->dmrTrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId, true);
}
}
@ -444,7 +444,7 @@ void HostFNE::processPeer(network::Network* peerNetwork)
uint32_t peerId = peerNetwork->getPeerId();
uint32_t streamId = peerNetwork->getP25StreamId();
m_network->p25TrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId);
m_network->p25TrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId, true);
}
}
@ -457,7 +457,7 @@ void HostFNE::processPeer(network::Network* peerNetwork)
uint32_t peerId = peerNetwork->getPeerId();
uint32_t streamId = peerNetwork->getNXDNStreamId();
m_network->nxdnTrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId);
m_network->nxdnTrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId, true);
}
}
}

@ -7,7 +7,7 @@
*
*/
/*
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
* Copyright (C) 2023-2024 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,6 +24,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Defines.h"
#include "common/dmr/lc/LC.h"
#include "common/dmr/lc/FullLC.h"
#include "common/Clock.h"
#include "common/Log.h"
#include "common/StopWatch.h"
@ -35,6 +37,7 @@
using namespace system_clock;
using namespace network;
using namespace network::fne;
using namespace dmr;
#include <cstdio>
#include <cassert>
@ -76,23 +79,28 @@ TagDMRData::~TagDMRData()
/// <param name="peerId">Peer ID</param>
/// <param name="pktSeq"></param>
/// <param name="streamId">Stream ID</param>
/// <param name="fromPeer">Flag indicating whether this traffic is from a peer connection or not.</param>
/// <returns></returns>
bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId)
bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool fromPeer)
{
hrc::hrc_t pktTime = hrc::now();
uint8_t buffer[len];
::memset(buffer, 0x00U, len);
::memcpy(buffer, data, len);
uint8_t seqNo = data[4U];
uint32_t srcId = __GET_UINT16(data, 5U);
uint32_t dstId = __GET_UINT16(data, 8U);
uint8_t flco = (data[15U] & 0x40U) == 0x40U ? dmr::FLCO_PRIVATE : dmr::FLCO_GROUP;
uint8_t flco = (data[15U] & 0x40U) == 0x40U ? FLCO_PRIVATE : FLCO_GROUP;
uint32_t slotNo = (data[15U] & 0x80U) == 0x80U ? 2U : 1U;
uint8_t dataType = data[15U] & 0x0FU;
dmr::data::Data dmrData;
data::Data dmrData;
dmrData.setSeqNo(seqNo);
dmrData.setSlotNo(slotNo);
dmrData.setSrcId(srcId);
@ -109,16 +117,22 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
}
else if (voiceSync) {
dmrData.setData(data + 20U);
dmrData.setDataType(dmr::DT_VOICE_SYNC);
dmrData.setDataType(DT_VOICE_SYNC);
dmrData.setN(0U);
}
else {
uint8_t n = data[15U] & 0x0FU;
dmrData.setData(data + 20U);
dmrData.setDataType(dmr::DT_VOICE);
dmrData.setDataType(DT_VOICE);
dmrData.setN(n);
}
// is this data from a peer connection?
if (fromPeer) {
// perform TGID mutations if configured
mutateBuffer(buffer, peerId, dmrData, dataType, dstId, slotNo, false);
}
// is the stream valid?
if (validate(peerId, dmrData, streamId)) {
// is this peer ignored?
@ -127,7 +141,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
}
// is this the end of the call stream?
if (dataSync && (dataType == dmr::DT_TERMINATOR_WITH_LC)) {
if (dataSync && (dataType == DT_TERMINATOR_WITH_LC)) {
RxStatus status;
auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { return (x.second.dstId == dstId && x.second.slotNo == slotNo); });
if (it == m_status.end()) {
@ -160,7 +174,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
}
// is this a new call stream?
if (dataSync && (dataType == dmr::DT_VOICE_LC_HEADER)) {
if (dataSync && (dataType == DT_VOICE_LC_HEADER)) {
auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { return (x.second.dstId == dstId && x.second.slotNo == slotNo); });
if (it != m_status.end()) {
RxStatus status = it->second;
@ -203,7 +217,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId);
if (tg.config().parrot()) {
uint8_t* copy = new uint8_t[len];
::memcpy(copy, data, len);
::memcpy(copy, buffer, len);
m_parrotFrames.push_back(std::make_tuple(copy, len, pktSeq, streamId));
}
@ -215,7 +229,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
continue;
}
m_network->writePeer(peer.first, { NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_DMR }, data, len, pktSeq, streamId, true);
m_network->writePeer(peer.first, { NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_DMR }, buffer, len, pktSeq, streamId, true);
if (m_network->m_debug) {
LogDebug(LOG_NET, "DMR, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, flco = $%02X, slotNo = %u, len = %u, pktSeq = %u, stream = %u",
peerId, peer.first, seqNo, srcId, dstId, flco, slotNo, len, pktSeq, streamId);
@ -227,7 +241,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
}
// repeat traffic to upstream peers
if (m_network->m_host->m_peerNetworks.size() > 0) {
if (m_network->m_host->m_peerNetworks.size() > 0 && !tg.config().parrot()) {
for (auto peer : m_network->m_host->m_peerNetworks) {
uint32_t peerId = peer.second->getPeerId();
@ -236,7 +250,14 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
continue;
}
peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_DMR }, data, len, pktSeq, streamId);
uint8_t outboundPeerBuffer[len];
::memset(outboundPeerBuffer, 0x00U, len);
::memcpy(outboundPeerBuffer, buffer, len);
// perform TGID mutations if configured
mutateBuffer(outboundPeerBuffer, peerId, dmrData, dataType, dstId, slotNo);
peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId);
}
}
@ -278,6 +299,111 @@ void TagDMRData::playbackParrot()
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Helper to mutate the network data buffer.
/// </summary>
/// <param name="buffer"></param>
/// <param name="peerId">Peer ID</param>
/// <param name="dmrData"></param>
/// <param name="dataType"></param>
/// <param name="dstId"></param>
/// <param name="slotNo"></param>
/// <param name="outbound"></param>
void TagDMRData::mutateBuffer(uint8_t* buffer, uint32_t peerId, dmr::data::Data dmrData, uint8_t dataType, uint32_t dstId, uint32_t slotNo, bool outbound)
{
uint32_t mutatedDstId = dstId;
uint32_t mutatedSlotNo = slotNo;
// does the data require mutation?
if (peerMutate(peerId, mutatedDstId, mutatedSlotNo, outbound)) {
// rewrite destination TGID in the frame
__SET_UINT16(mutatedDstId, buffer, 8U);
// set or clear the e.Slot flag (if 0x80 is set Slot 2 otherwise Slot 1)
if (mutatedSlotNo == 2 && (buffer[15U] & 0x80U) == 0x00U)
buffer[15U] |= 0x80;
if (mutatedSlotNo == 1 && (buffer[15U] & 0x80U) == 0x80U)
buffer[15U] = buffer[15U] & ~0x80U;
uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
dmrData.getData(data + 2U);
if (dataType == DT_VOICE_LC_HEADER ||
dataType == DT_TERMINATOR_WITH_LC) {
// decode and reconstruct embedded DMR data
lc::FullLC fullLC;
std::unique_ptr<lc::LC> lc = fullLC.decode(data + 2U, dataType);
if (lc == nullptr) {
LogWarning(LOG_NET, "DMR Slot %u, bad LC received from the network, replacing", slotNo);
lc = new_unique(lc::LC, dmrData.getFLCO(), dmrData.getSrcId(), mutatedDstId);
}
lc->setDstId(mutatedDstId);
// Regenerate the LC data
fullLC.encode(*lc, data + 2U, dataType);
dmrData.setData(data + 2U);
}
else if (dataType == DT_VOICE_PI_HEADER) {
// decode and reconstruct embedded DMR data
lc::FullLC fullLC;
std::unique_ptr<lc::PrivacyLC> lc = fullLC.decodePI(data + 2U);
if (lc == nullptr) {
LogWarning(LOG_NET, "DMR Slot %u, DT_VOICE_PI_HEADER, bad LC received, replacing", slotNo);
lc = new_unique(lc::PrivacyLC);
}
lc->setDstId(mutatedDstId);
// Regenerate the LC data
fullLC.encodePI(*lc, data + 2U);
dmrData.setData(data + 2U);
}
dmrData.getData(buffer + 20U);
}
}
/// <summary>
/// Helper to mutate destination ID and slot.
/// </summary>
/// <param name="peerId">Peer ID</param>
/// <param name="dstId"></param>
/// <param name="slotNo"></param>
/// <param name="outbound"></param>
bool TagDMRData::peerMutate(uint32_t peerId, uint32_t& dstId, uint32_t& slotNo, bool outbound)
{
lookups::TalkgroupRuleGroupVoice tg;
if (outbound) {
tg = m_network->m_tidLookup->find(dstId, slotNo);
}
else {
tg = m_network->m_tidLookup->findByMutation(peerId, dstId, slotNo);
}
std::vector<lookups::TalkgroupRuleMutation> mutations = tg.config().mutation();
bool mutated = false;
if (mutations.size() > 0) {
for (auto entry : mutations) {
if (entry.peerId() == peerId) {
if (outbound) {
dstId = tg.source().tgId();
slotNo = tg.source().tgSlot();
}
else {
dstId = entry.tgId();
slotNo = entry.tgSlot();
}
mutated = true;
break;
}
}
}
return mutated;
}
/// <summary>
/// Helper to determine if the peer is permitted for traffic.
/// </summary>
@ -285,16 +411,16 @@ void TagDMRData::playbackParrot()
/// <param name="data"></param>
/// <param name="streamId">Stream ID</param>
/// <returns></returns>
bool TagDMRData::isPeerPermitted(uint32_t peerId, dmr::data::Data& data, uint32_t streamId)
bool TagDMRData::isPeerPermitted(uint32_t peerId, data::Data& data, uint32_t streamId)
{
// private calls are always permitted
if (data.getDataType() == dmr::FLCO_PRIVATE) {
if (data.getDataType() == FLCO_PRIVATE) {
return true;
}
// is this a group call?
if (data.getDataType() == dmr::FLCO_GROUP) {
lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(data.getDstId());
if (data.getDataType() == FLCO_GROUP) {
lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(data.getDstId(), data.getSlotNo());
std::vector<uint32_t> inclusion = tg.config().inclusion();
std::vector<uint32_t> exclusion = tg.config().exclusion();
@ -326,7 +452,7 @@ bool TagDMRData::isPeerPermitted(uint32_t peerId, dmr::data::Data& data, uint32_
/// <param name="data"></param>
/// <param name="streamId">Stream ID</param>
/// <returns></returns>
bool TagDMRData::validate(uint32_t peerId, dmr::data::Data& data, uint32_t streamId)
bool TagDMRData::validate(uint32_t peerId, data::Data& data, uint32_t streamId)
{
// is the source ID a blacklisted ID?
lookups::RadioId rid = m_network->m_ridLookup->find(data.getSrcId());
@ -337,11 +463,11 @@ bool TagDMRData::validate(uint32_t peerId, dmr::data::Data& data, uint32_t strea
}
// always validate a terminator if the source is valid
if (data.getDataType() == dmr::DT_TERMINATOR_WITH_LC)
if (data.getDataType() == DT_TERMINATOR_WITH_LC)
return true;
// is this a private call?
if (data.getDataType() == dmr::FLCO_PRIVATE) {
if (data.getDataType() == FLCO_PRIVATE) {
// is the destination ID a blacklisted ID?
lookups::RadioId rid = m_network->m_ridLookup->find(data.getDstId());
if (!rid.radioDefault()) {
@ -352,7 +478,7 @@ bool TagDMRData::validate(uint32_t peerId, dmr::data::Data& data, uint32_t strea
}
// is this a group call?
if (data.getDataType() == dmr::FLCO_GROUP) {
if (data.getDataType() == FLCO_GROUP) {
lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(data.getDstId());
// check the DMR slot number

@ -7,7 +7,7 @@
*
*/
/*
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
* Copyright (C) 2023-2024 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -51,7 +51,7 @@ namespace network
~TagDMRData();
/// <summary>Process a data frame from the network.</summary>
bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId);
bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool fromPeer = false);
/// <summary>Helper to playback a parrot frame to the network.</summary>
void playbackParrot();
@ -77,6 +77,11 @@ namespace network
bool m_debug;
/// <summary>Helper to mutate the network data buffer.</summary>
void mutateBuffer(uint8_t* buffer, uint32_t peerId, dmr::data::Data dmrData, uint8_t dataType, uint32_t dstId, uint32_t slotNo, bool outbound = true);
/// <summary>Helper to mutate destination ID and slot.</summary>
bool peerMutate(uint32_t peerId, uint32_t& dstId, uint32_t& slotNo, bool outbound = true);
/// <summary>Helper to determine if the peer is permitted for traffic.</summary>
bool isPeerPermitted(uint32_t peerId, dmr::data::Data& data, uint32_t streamId);
/// <summary>Helper to validate the DMR call stream.</summary>

@ -7,7 +7,7 @@
*
*/
/*
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
* Copyright (C) 2023-2024 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -36,6 +36,7 @@
using namespace system_clock;
using namespace network;
using namespace network::fne;
using namespace nxdn;
#include <cstdio>
#include <cassert>
@ -77,17 +78,22 @@ TagNXDNData::~TagNXDNData()
/// <param name="peerId">Peer ID</param>
/// <param name="pktSeq"></param>
/// <param name="streamId">Stream ID</param>
/// <param name="fromPeer">Flag indicating whether this traffic is from a peer connection or not.</param>
/// <returns></returns>
bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId)
bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool fromPeer)
{
hrc::hrc_t pktTime = hrc::now();
uint8_t buffer[len];
::memset(buffer, 0x00U, len);
::memcpy(buffer, data, len);
uint8_t messageType = data[4U];
uint32_t srcId = __GET_UINT16(data, 5U);
uint32_t dstId = __GET_UINT16(data, 8U);
nxdn::lc::RTCH lc;
lc::RTCH lc;
lc.setMessageType(messageType);
lc.setSrcId((uint16_t)srcId & 0xFFFFU);
@ -96,6 +102,12 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
bool group = (data[15U] & 0x40U) == 0x40U ? false : true;
lc.setGroup(group);
// is this data from a peer connection?
if (fromPeer) {
// perform TGID mutations if configured
mutateBuffer(buffer, peerId, messageType, dstId, false);
}
// is the stream valid?
if (validate(peerId, lc, messageType, streamId)) {
// is this peer ignored?
@ -104,11 +116,11 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
}
// specifically only check the following logic for end of call, voice or data frames
if ((messageType == nxdn::RTCH_MESSAGE_TYPE_TX_REL || messageType == nxdn::RTCH_MESSAGE_TYPE_TX_REL_EX) ||
(messageType == nxdn::RTCH_MESSAGE_TYPE_VCALL || messageType == nxdn::RTCH_MESSAGE_TYPE_DCALL_HDR ||
messageType == nxdn::RTCH_MESSAGE_TYPE_DCALL_DATA)) {
if ((messageType == RTCH_MESSAGE_TYPE_TX_REL || messageType == RTCH_MESSAGE_TYPE_TX_REL_EX) ||
(messageType == RTCH_MESSAGE_TYPE_VCALL || messageType == RTCH_MESSAGE_TYPE_DCALL_HDR ||
messageType == RTCH_MESSAGE_TYPE_DCALL_DATA)) {
// is this the end of the call stream?
if (messageType == nxdn::RTCH_MESSAGE_TYPE_TX_REL || messageType == nxdn::RTCH_MESSAGE_TYPE_TX_REL_EX) {
if (messageType == RTCH_MESSAGE_TYPE_TX_REL || messageType == RTCH_MESSAGE_TYPE_TX_REL_EX) {
RxStatus status = m_status[dstId];
uint64_t duration = hrc::diff(pktTime, status.callStartTime);
@ -132,7 +144,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
}
// is this a new call stream?
if ((messageType != nxdn::RTCH_MESSAGE_TYPE_TX_REL && messageType != nxdn::RTCH_MESSAGE_TYPE_TX_REL_EX)) {
if ((messageType != RTCH_MESSAGE_TYPE_TX_REL && messageType != RTCH_MESSAGE_TYPE_TX_REL_EX)) {
auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { return x.second.dstId == dstId; });
if (it != m_status.end()) {
RxStatus status = m_status[dstId];
@ -175,7 +187,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId);
if (tg.config().parrot()) {
uint8_t *copy = new uint8_t[len];
::memcpy(copy, data, len);
::memcpy(copy, buffer, len);
m_parrotFrames.push_back(std::make_tuple(copy, len, pktSeq, streamId));
}
@ -187,7 +199,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
continue;
}
m_network->writePeer(peer.first, { NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_NXDN }, data, len, pktSeq, streamId, true);
m_network->writePeer(peer.first, { NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_NXDN }, buffer, len, pktSeq, streamId, true);
if (m_network->m_debug) {
LogDebug(LOG_NET, "NXDN, srcPeer = %u, dstPeer = %u, messageType = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u",
peerId, peer.first, messageType, srcId, dstId, len, pktSeq, streamId);
@ -199,7 +211,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
}
// repeat traffic to upstream peers
if (m_network->m_host->m_peerNetworks.size() > 0) {
if (m_network->m_host->m_peerNetworks.size() > 0 && !tg.config().parrot()) {
for (auto peer : m_network->m_host->m_peerNetworks) {
uint32_t peerId = peer.second->getPeerId();
@ -208,7 +220,14 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
continue;
}
peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_NXDN }, data, len, pktSeq, streamId);
uint8_t outboundPeerBuffer[len];
::memset(outboundPeerBuffer, 0x00U, len);
::memcpy(outboundPeerBuffer, buffer, len);
// perform TGID mutations if configured
mutateBuffer(outboundPeerBuffer, peerId, messageType, dstId);
peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId);
}
}
@ -250,6 +269,62 @@ void TagNXDNData::playbackParrot()
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Helper to mutate the network data buffer.
/// </summary>
/// <param name="buffer"></param>
/// <param name="peerId">Peer ID</param>
/// <param name="duid"></param>
/// <param name="dstId"></param>
/// <param name="outbound"></param>
void TagNXDNData::mutateBuffer(uint8_t* buffer, uint32_t peerId, uint8_t messageType, uint32_t dstId, bool outbound)
{
uint32_t mutatedDstId = dstId;
// does the data require mutation?
if (peerMutate(peerId, mutatedDstId, outbound)) {
// rewrite destination TGID in the frame
__SET_UINT16(mutatedDstId, buffer, 8U);
}
}
/// <summary>
/// Helper to mutate destination ID.
/// </summary>
/// <param name="peerId">Peer ID</param>
/// <param name="dstId"></param>
/// <param name="outbound"></param>
bool TagNXDNData::peerMutate(uint32_t peerId, uint32_t& dstId, bool outbound)
{
lookups::TalkgroupRuleGroupVoice tg;
if (outbound) {
tg = m_network->m_tidLookup->find(dstId);
}
else {
tg = m_network->m_tidLookup->findByMutation(peerId, dstId);
}
std::vector<lookups::TalkgroupRuleMutation> mutations = tg.config().mutation();
bool mutated = false;
if (mutations.size() > 0) {
for (auto entry : mutations) {
if (entry.peerId() == peerId) {
if (outbound) {
dstId = tg.source().tgId();
}
else {
dstId = entry.tgId();
}
mutated = true;
break;
}
}
}
return mutated;
}
/// <summary>
/// Helper to determine if the peer is permitted for traffic.
/// </summary>
@ -258,7 +333,7 @@ void TagNXDNData::playbackParrot()
/// <param name="messageType"></param>
/// <param name="streamId">Stream ID</param>
/// <returns></returns>
bool TagNXDNData::isPeerPermitted(uint32_t peerId, nxdn::lc::RTCH& lc, uint8_t messageType, uint32_t streamId)
bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, uint32_t streamId)
{
// private calls are always permitted
if (!lc.getGroup()) {
@ -300,7 +375,7 @@ bool TagNXDNData::isPeerPermitted(uint32_t peerId, nxdn::lc::RTCH& lc, uint8_t m
/// <param name="messageType"></param>
/// <param name="streamId">Stream ID</param>
/// <returns></returns>
bool TagNXDNData::validate(uint32_t peerId, nxdn::lc::RTCH& lc, uint8_t messageType, uint32_t streamId)
bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, uint32_t streamId)
{
// is the source ID a blacklisted ID?
lookups::RadioId rid = m_network->m_ridLookup->find(lc.getSrcId());
@ -311,7 +386,7 @@ bool TagNXDNData::validate(uint32_t peerId, nxdn::lc::RTCH& lc, uint8_t messageT
}
// always validate a terminator if the source is valid
if (messageType == nxdn::RTCH_MESSAGE_TYPE_TX_REL || messageType == nxdn::RTCH_MESSAGE_TYPE_TX_REL_EX)
if (messageType == RTCH_MESSAGE_TYPE_TX_REL || messageType == RTCH_MESSAGE_TYPE_TX_REL_EX)
return true;
// is this a private call?

@ -7,7 +7,7 @@
*
*/
/*
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
* Copyright (C) 2023-2024 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -51,7 +51,7 @@ namespace network
~TagNXDNData();
/// <summary>Process a data frame from the network.</summary>
bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId);
bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool fromPeer = false);
/// <summary>Helper to playback a parrot frame to the network.</summary>
void playbackParrot();
@ -76,6 +76,11 @@ namespace network
bool m_debug;
/// <summary>Helper to mutate the network data buffer.</summary>
void mutateBuffer(uint8_t* buffer, uint32_t peerId, uint8_t messageType, uint32_t dstId, bool outbound = true);
/// <summary>Helper to mutate destination ID.</summary>
bool peerMutate(uint32_t peerId, uint32_t& dstId, bool outbound = true);
/// <summary>Helper to determine if the peer is permitted for traffic.</summary>
bool isPeerPermitted(uint32_t peerId, nxdn::lc::RTCH& lc, uint8_t messageType, uint32_t streamId);
/// <summary>Helper to validate the NXDN call stream.</summary>

@ -7,7 +7,7 @@
*
*/
/*
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
* Copyright (C) 2023-2024 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,6 +24,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Defines.h"
#include "common/p25/lc/tsbk/TSBKFactory.h"
#include "common/p25/Sync.h"
#include "common/Clock.h"
#include "common/Log.h"
#include "common/StopWatch.h"
@ -79,11 +81,16 @@ TagP25Data::~TagP25Data()
/// <param name="peerId">Peer ID</param>
/// <param name="pktSeq"></param>
/// <param name="streamId">Stream ID</param>
/// <param name="fromPeer">Flag indicating whether this traffic is from a peer connection or not.</param>
/// <returns></returns>
bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId)
bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool fromPeer)
{
hrc::hrc_t pktTime = hrc::now();
uint8_t buffer[len];
::memset(buffer, 0x00U, len);
::memcpy(buffer, data, len);
uint8_t lco = data[4U];
uint32_t srcId = __GET_UINT16(data, 5U);
@ -95,34 +102,34 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
uint8_t lsd2 = data[21U];
uint8_t duid = data[22U];
uint8_t frameType = p25::P25_FT_DATA_UNIT;
uint8_t frameType = P25_FT_DATA_UNIT;
p25::lc::LC control;
p25::data::LowSpeedData lsd;
lc::LC control;
data::LowSpeedData lsd;
// is this a LDU1, is this the first of a call?
if (duid == p25::P25_DUID_LDU1) {
if (duid == P25_DUID_LDU1) {
frameType = data[180U];
if (m_debug) {
LogDebug(LOG_NET, "P25, frameType = $%02X", frameType);
}
if (frameType == p25::P25_FT_HDU_VALID) {
if (frameType == P25_FT_HDU_VALID) {
uint8_t algId = data[181U];
uint32_t kid = (data[182U] << 8) | (data[183U] << 0);
// copy MI data
uint8_t mi[p25::P25_MI_LENGTH_BYTES];
::memset(mi, 0x00U, p25::P25_MI_LENGTH_BYTES);
uint8_t mi[P25_MI_LENGTH_BYTES];
::memset(mi, 0x00U, P25_MI_LENGTH_BYTES);
for (uint8_t i = 0; i < p25::P25_MI_LENGTH_BYTES; i++) {
for (uint8_t i = 0; i < P25_MI_LENGTH_BYTES; i++) {
mi[i] = data[184U + i];
}
if (m_debug) {
LogDebug(LOG_NET, "P25, HDU algId = $%02X, kId = $%02X", algId, kid);
Utils::dump(1U, "P25 HDU Network MI", mi, p25::P25_MI_LENGTH_BYTES);
Utils::dump(1U, "P25 HDU Network MI", mi, P25_MI_LENGTH_BYTES);
}
control.setAlgId(algId);
@ -139,6 +146,12 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
lsd.setLSD1(lsd1);
lsd.setLSD2(lsd2);
// is this data from a peer connection?
if (fromPeer) {
// perform TGID mutations if configured
mutateBuffer(buffer, peerId, duid, dstId, false);
}
// is the stream valid?
if (validate(peerId, control, duid, streamId)) {
// is this peer ignored?
@ -147,9 +160,9 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
}
// specifically only check the following logic for end of call or voice frames
if (duid != p25::P25_DUID_TSDU && duid != p25::P25_DUID_PDU) {
if (duid != P25_DUID_TSDU && duid != P25_DUID_PDU) {
// is this the end of the call stream?
if ((duid == p25::P25_DUID_TDU) || (duid == p25::P25_DUID_TDULC)) {
if ((duid == P25_DUID_TDU) || (duid == P25_DUID_TDULC)) {
RxStatus status = m_status[dstId];
uint64_t duration = hrc::diff(pktTime, status.callStartTime);
@ -174,11 +187,11 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
}
// is this a new call stream?
if ((duid != p25::P25_DUID_TDU) && (duid != p25::P25_DUID_TDULC)) {
if ((duid != P25_DUID_TDU) && (duid != P25_DUID_TDULC)) {
auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { return x.second.dstId == dstId; });
if (it != m_status.end()) {
RxStatus status = m_status[dstId];
if (streamId != status.streamId && ((duid != p25::P25_DUID_TDU) && (duid != p25::P25_DUID_TDULC))) {
if (streamId != status.streamId && ((duid != P25_DUID_TDU) && (duid != P25_DUID_TDULC))) {
if (status.srcId != 0U && status.srcId != srcId) {
LogWarning(LOG_NET, "P25, Call Collision, peer = %u, srcId = %u, dstId = %u, streamId = %u", peerId, srcId, dstId, streamId);
return false;
@ -217,7 +230,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
lookups::TalkgroupRuleGroupVoice tg = m_network->m_tidLookup->find(dstId);
if (tg.config().parrot()) {
uint8_t *copy = new uint8_t[len];
::memcpy(copy, data, len);
::memcpy(copy, buffer, len);
m_parrotFrames.push_back(std::make_tuple(copy, len, pktSeq, streamId, srcId, dstId));
}
@ -229,7 +242,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
continue;
}
m_network->writePeer(peer.first, { NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_P25 }, data, len, pktSeq, streamId, true);
m_network->writePeer(peer.first, { NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_P25 }, buffer, len, pktSeq, streamId, true);
if (m_network->m_debug) {
LogDebug(LOG_NET, "P25, srcPeer = %u, dstPeer = %u, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u, pktSeq = %u, streamId = %u",
peerId, peer.first, duid, lco, MFId, srcId, dstId, len, pktSeq, streamId);
@ -241,7 +254,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
}
// repeat traffic to upstream peers
if (m_network->m_host->m_peerNetworks.size() > 0) {
if (m_network->m_host->m_peerNetworks.size() > 0 && !tg.config().parrot()) {
for (auto peer : m_network->m_host->m_peerNetworks) {
uint32_t peerId = peer.second->getPeerId();
@ -250,7 +263,14 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
continue;
}
peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_P25 }, data, len, pktSeq, streamId);
uint8_t outboundPeerBuffer[len];
::memset(outboundPeerBuffer, 0x00U, len);
::memcpy(outboundPeerBuffer, buffer, len);
// perform TGID mutations if configured
mutateBuffer(outboundPeerBuffer, peerId, duid, dstId);
peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId);
}
}
@ -323,6 +343,108 @@ void TagP25Data::playbackParrot()
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Helper to mutate the network data buffer.
/// </summary>
/// <param name="buffer"></param>
/// <param name="peerId">Peer ID</param>
/// <param name="duid"></param>
/// <param name="dstId"></param>
/// <param name="outbound"></param>
void TagP25Data::mutateBuffer(uint8_t* buffer, uint32_t peerId, uint8_t duid, uint32_t dstId, bool outbound)
{
uint32_t srcId = __GET_UINT16(buffer, 5U);
uint32_t frameLength = buffer[23U];
uint32_t mutatedDstId = dstId;
// does the data require mutation?
if (peerMutate(peerId, mutatedDstId, outbound)) {
// rewrite destination TGID in the frame
__SET_UINT16(mutatedDstId, buffer, 8U);
UInt8Array data = std::unique_ptr<uint8_t[]>(new uint8_t[frameLength]);
::memset(data.get(), 0x00U, frameLength);
::memcpy(data.get(), buffer + 24U, frameLength);
// are we receiving a TSDU?
if (duid == P25_DUID_TSDU) {
std::unique_ptr<lc::TSBK> tsbk = lc::tsbk::TSBKFactory::createTSBK(data.get());
if (tsbk != nullptr) {
// handle standard P25 reference opcodes
switch (tsbk->getLCO()) {
case TSBK_IOSP_GRP_VCH:
{
LogMessage(LOG_NET, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u",
tsbk->toString(true).c_str(), tsbk->getEmergency(), tsbk->getEncrypted(), tsbk->getPriority(), tsbk->getGrpVchNo(), srcId, dstId);
tsbk->setDstId(dstId);
}
break;
}
// regenerate TSDU
uint8_t data[P25_TSDU_FRAME_LENGTH_BYTES + 2U];
::memset(data + 2U, 0x00U, P25_TSDU_FRAME_LENGTH_BYTES);
// Generate Sync
Sync::addP25Sync(data + 2U);
// Generate TSBK block
tsbk->setLastBlock(true); // always set last block -- this a Single Block TSDU
tsbk->encode(data + 2U);
if (m_debug) {
LogDebug(LOG_RF, P25_TSDU_STR ", lco = $%02X, mfId = $%02X, lastBlock = %u, AIV = %u, EX = %u, srcId = %u, dstId = %u, sysId = $%03X, netId = $%05X",
tsbk->getLCO(), tsbk->getMFId(), tsbk->getLastBlock(), tsbk->getAIV(), tsbk->getEX(), tsbk->getSrcId(), tsbk->getDstId(),
tsbk->getSysId(), tsbk->getNetId());
Utils::dump(1U, "!!! *TSDU (SBF) TSBK Block Data", data + P25_PREAMBLE_LENGTH_BYTES + 2U, P25_TSBK_FEC_LENGTH_BYTES);
}
::memcpy(buffer + 24U, data + 2U, p25::P25_TSDU_FRAME_LENGTH_BYTES);
}
}
}
}
/// <summary>
/// Helper to mutate destination ID.
/// </summary>
/// <param name="peerId">Peer ID</param>
/// <param name="dstId"></param>
/// <param name="outbound"></param>
bool TagP25Data::peerMutate(uint32_t peerId, uint32_t& dstId, bool outbound)
{
lookups::TalkgroupRuleGroupVoice tg;
if (outbound) {
tg = m_network->m_tidLookup->find(dstId);
}
else {
tg = m_network->m_tidLookup->findByMutation(peerId, dstId);
}
std::vector<lookups::TalkgroupRuleMutation> mutations = tg.config().mutation();
bool mutated = false;
if (mutations.size() > 0) {
for (auto entry : mutations) {
if (entry.peerId() == peerId) {
if (outbound) {
dstId = tg.source().tgId();
}
else {
dstId = entry.tgId();
}
mutated = true;
break;
}
}
}
return mutated;
}
/// <summary>
/// Helper to determine if the peer is permitted for traffic.
/// </summary>
@ -331,19 +453,19 @@ void TagP25Data::playbackParrot()
/// <param name="duid"></param>
/// <param name="streamId">Stream ID</param>
/// <returns></returns>
bool TagP25Data::isPeerPermitted(uint32_t peerId, p25::lc::LC& control, uint8_t duid, uint32_t streamId)
bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, uint8_t duid, uint32_t streamId)
{
// private calls are always permitted
if (control.getLCO() == p25::LC_PRIVATE) {
if (control.getLCO() == LC_PRIVATE) {
return true;
}
// always permit a TSDU or PDU
if (duid == p25::P25_DUID_TSDU || duid == p25::P25_DUID_PDU)
if (duid == P25_DUID_TSDU || duid == P25_DUID_PDU)
return true;
// always permit a terminator
if (duid == p25::P25_DUID_TDU || duid == p25::P25_DUID_TDULC)
if (duid == P25_DUID_TDU || duid == P25_DUID_TDULC)
return true;
// is this a group call?
@ -379,7 +501,7 @@ bool TagP25Data::isPeerPermitted(uint32_t peerId, p25::lc::LC& control, uint8_t
/// <param name="duid"></param>
/// <param name="streamId">Stream ID</param>
/// <returns></returns>
bool TagP25Data::validate(uint32_t peerId, p25::lc::LC& control, uint8_t duid, uint32_t streamId)
bool TagP25Data::validate(uint32_t peerId, lc::LC& control, uint8_t duid, uint32_t streamId)
{
// is the source ID a blacklisted ID?
lookups::RadioId rid = m_network->m_ridLookup->find(control.getSrcId());
@ -390,15 +512,15 @@ bool TagP25Data::validate(uint32_t peerId, p25::lc::LC& control, uint8_t duid, u
}
// always validate a TSDU or PDU if the source is valid
if (duid == p25::P25_DUID_TSDU || duid == p25::P25_DUID_PDU)
if (duid == P25_DUID_TSDU || duid == P25_DUID_PDU)
return true;
// always validate a terminator if the source is valid
if (duid == p25::P25_DUID_TDU || duid == p25::P25_DUID_TDULC)
if (duid == P25_DUID_TDU || duid == P25_DUID_TDULC)
return true;
// is this a private call?
if (control.getLCO() == p25::LC_PRIVATE) {
if (control.getLCO() == LC_PRIVATE) {
// is the destination ID a blacklisted ID?
lookups::RadioId rid = m_network->m_ridLookup->find(control.getDstId());
if (!rid.radioDefault()) {

@ -7,7 +7,7 @@
*
*/
/*
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
* Copyright (C) 2023-2024 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -57,7 +57,7 @@ namespace network
~TagP25Data();
/// <summary>Process a data frame from the network.</summary>
bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId);
bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool fromPeer = false);
/// <summary>Helper to playback a parrot frame to the network.</summary>
void playbackParrot();
@ -83,6 +83,11 @@ namespace network
bool m_debug;
/// <summary>Helper to mutate the network data buffer.</summary>
void mutateBuffer(uint8_t* buffer, uint32_t peerId, uint8_t duid, uint32_t dstId, bool outbound = true);
/// <summary>Helper to mutate destination ID.</summary>
bool peerMutate(uint32_t peerId, uint32_t& dstId, bool outbound = true);
/// <summary>Helper to determine if the peer is permitted for traffic.</summary>
bool isPeerPermitted(uint32_t peerId, p25::lc::LC& control, uint8_t duid, uint32_t streamId);
/// <summary>Helper to validate the P25 call stream.</summary>

@ -319,6 +319,13 @@ bool Data::processNetwork(uint8_t option, lc::RTCH& netLC, uint8_t* data, uint32
uint16_t srcId = lc.getSrcId();
bool group = lc.getGroup();
// overwrite the destination ID if the network message header and
// decoded network LC data don't agree (this can happen if the network is dynamically
// altering the destination ID in-flight)
if (lc.getDstId() != netLC.getDstId()) {
lc.setDstId(netLC.getDstId());
}
if (m_nxdn->m_netState == RS_NET_IDLE) {
uint8_t type = lc.getMessageType();
if (type != RTCH_MESSAGE_TYPE_DCALL_HDR)

@ -710,6 +710,13 @@ bool Voice::processNetwork(uint8_t fct, uint8_t option, lc::RTCH& netLC, uint8_t
bool group = lc.getGroup();
bool encrypted = lc.getEncrypted();
// overwrite the destination ID if the network message header and
// decoded network LC data don't agree (this can happen if the network is dynamically
// altering the destination ID in-flight)
if (lc.getDstId() != netLC.getDstId()) {
lc.setDstId(netLC.getDstId());
}
// don't process network frames if this modem isn't authoritative
if (!m_nxdn->m_authoritative && m_nxdn->m_permittedDstId != dstId) {
if (m_nxdn->m_netState != RS_NET_AUDIO) {

@ -782,23 +782,6 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr
// handle standard P25 reference opcodes
switch (tsbk->getLCO()) {
case TSBK_IOSP_GRP_VCH:
{
if (m_p25->m_enableControl) {
if (!m_p25->m_affiliations.isGranted(dstId)) {
if (m_verbose) {
LogMessage(LOG_NET, P25_TSDU_STR ", %s, emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u",
tsbk->toString(true).c_str(), tsbk->getEmergency(), tsbk->getEncrypted(), tsbk->getPriority(), tsbk->getGrpVchNo(), srcId, dstId);
}
uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag
(tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
(tsbk->getPriority() & 0x07U); // Priority
writeRF_TSDU_Grant(srcId, dstId, serviceOptions, true, true);
}
}
}
return true; // don't allow this to write to the air
case TSBK_IOSP_UU_VCH:
{
if (m_p25->m_enableControl) {
@ -812,7 +795,7 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr
(tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
(tsbk->getPriority() & 0x07U); // Priority
writeRF_TSDU_Grant(srcId, dstId, serviceOptions, false, true);
writeRF_TSDU_Grant(srcId, dstId, serviceOptions, (tsbk->getLCO() == TSBK_IOSP_GRP_VCH), true);
}
}
}

@ -1034,6 +1034,13 @@ bool Voice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L
m_dfsiLC.control()->setNetId(control.getNetId());
m_dfsiLC.control()->setSysId(control.getSysId());
// overwrite the destination ID if the network message header and
// decoded DFSI data don't agree (this can happen if the network is dynamically
// altering the destination ID in-flight)
if (m_dfsiLC.control()->getDstId() != control.getDstId()) {
m_dfsiLC.control()->setDstId(control.getDstId());
}
m_netLastLDU1 = control;
m_netLastFrameType = frameType;

Loading…
Cancel
Save

Powered by TurnKey Linux.