Merge branch 'master' into incall_ctrl

pull/86/head
Bryan Biedenkapp 11 months ago
commit 6812029491

@ -76,6 +76,8 @@ network:
# Flag indicating UDP audio should be RTP framed.
# NOTE: This flag is only applicable when encoding G.711 uLaw.
udpRTPFrames: false
# Flag indicating UDP audio should follow the USRP format.
udpUsrp: false
# Source "Radio ID" for transmitted audio frames.
sourceId: 1234567
@ -85,6 +87,9 @@ network:
# Flag indicating the source "Radio ID" will be overridden from the received
# UDP SRC ID.
overrideSourceIdFromUDP: false
# Flag indicating if the source "Radio ID" is being overriden, and changes mid-call, bridge should
# terminate previous call an initiate a new one. (This applies only when both udpMetadata and overrideSourceIdFromUDP is set.)
resetCallForSourceIdChange: false
# Talkgroup ID for transmitted/received audio frames.
destinationId: 1
# Slot for received/transmitted audio frames.

@ -129,8 +129,14 @@ protocols:
# Flag indicating whether or not the source ID validation before granting disabled.
disableGrantSourceIdCheck: false
# Flag indicating whether or not network calls will generate a channel grant.
# (This applies only in conventional operations where channel granting is utilized and RF-only talkgroup
# steering is required.)
disableNetworkGrant: false
# Flag indicating whether or not a TGID will be tested for affiliations before being granted.
ignoreAffiliationCheck: false
# Flag indicating the host should send a network grant demand for conventional traffic.
convNetGrantDemand: false
# Flag indicating whether or not received RF embedded LC data only should be transmitted.
embeddedLCOnly: false
# Flag indicating whether talker alias data should be dumped to the log.

@ -91,7 +91,7 @@ master:
allowConvSiteAffOverride: true
# Flag indicating whether or not In-Call Control feedback is enabled.
# (This will enforce RID ACLs network wide, regardless of local peer RID ACL setting.)
enableInCallCtrl: true
enableInCallCtrl: false
# Flag indicating whether or not unknown/undefined RIDs will be rejected by the FNE.
# (This is a strict rejection, any unknown or undefined RID not in the RID ACL list will be hard rejected.)
rejectUnknownRID: false
@ -123,6 +123,19 @@ master:
# Flag indicating whether TSBK/CSBK/RCCH messages will be logged to InfluxDB.
influxLogRawData: false
#
# Crypto Container Configuration
#
crypto_container:
# Flag indicating whether or not crypto services are enabled.
enabled: false
# Full path to the talkgroup rules file.
file: key_container.ekc
# Container password.
password: "PASSWORD"
# Amount of time between updates of crypto container file. (minutes)
time: 30
#
# Talkgroup Rules Configuration
#

@ -661,6 +661,21 @@ The payload for a DMR protocol data message is formatted:
| BER | RSSI |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
[discrete]
==== Control Flags
[cols="2,1,2"]
|===
|Name |Flag |Description
|Grant Demand
|$80 (0x80)
|This control flag indicates the packet contains a remote grant demand request.
|Unit to Unit
|$01 (0x01)
|This control flag indicates any included control request is a unit-to-unit request.
|===
=== 3.3 P25 Protocol Data
For both reception and transmission of P25 protocol data on the network, simple packets are formed using the Protocol function and P25 sub-function.
@ -707,6 +722,29 @@ All messages (with the exception of PDUs) carry this message header:
| |
+-+-+-+-+-+-+-+-+
[discrete]
==== Control Flags
[cols="2,1,2"]
|===
|Name |Flag |Description
|Grant Demand
|$80 (0x80)
|This control flag indicates the packet contains a remote grant demand request.
|Grant Denial
|$40 (0x40)
|This control flag indicates any included grant demand request is to be denied.
|Encrypted
|$08 (0x08)
|This control flag indicates any included control request is for encrypted traffic to follow.
|Unit to Unit
|$01 (0x01)
|This control flag indicates any included control request is a unit-to-unit request.
|===
==== 3.3.2 PDU Message Header
[listing]
Below is the representation of the data layout for the P25 frame
@ -1188,4 +1226,8 @@ Once connected, a Peer-Linked CFNE will transparently pass peer list, peer statu
On DVM, for P25 a special network TDU exists called a "grant demand". The "grant demand" TDUs set a flag in the control bit of the P25 network header (bit $80) to flag that the TDU is meant to trigger a channel grant on a end-point.
For DMR, "grant demands", are carried along side the VOICE_LC_HEADER or DATA_HEADER packets with the control bytes set. Like, P25, the "grant demand" set a flag in the control bit of the DMR network header (bit $80) to flag that the packet is meant to trigger a channel grant on a end-point.
Because these grants are considered "network sourced" end-points should issue a grant locally and not repeat that grant to the network.
(See Section 3.3.1 for P25, and Section 3.2 for DMR)

@ -32,6 +32,10 @@ using namespace lookups;
// Global Variables
// ---------------------------------------------------------------------------
#ifndef SIGHUP
#define SIGHUP 1
#endif
int g_signal = 0;
std::string g_progExe = std::string(__EXE_NAME__);
std::string g_iniFile = std::string(DEFAULT_CONF_FILE);
@ -293,15 +297,15 @@ int main(int argc, char** argv)
ret = bridge->run();
delete bridge;
if (g_signal == 2)
if (g_signal == SIGINT)
::LogInfoEx(LOG_HOST, "Exited on receipt of SIGINT");
if (g_signal == 15)
if (g_signal == SIGTERM)
::LogInfoEx(LOG_HOST, "Exited on receipt of SIGTERM");
if (g_signal == 1)
if (g_signal == SIGHUP)
::LogInfoEx(LOG_HOST, "Restarting on receipt of SIGHUP");
} while (g_signal == 1);
} while (g_signal == SIGHUP);
::LogFinalise();
::ActivityLogFinalise();

@ -5,6 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL
* Copyright (C) 2025 Caleb, K4PHP
*
*/
#include "Defines.h"
@ -293,10 +294,12 @@ HostBridge::HostBridge(const std::string& confFile) :
m_udpNoIncludeLength(false),
m_udpUseULaw(false),
m_udpRTPFrames(false),
m_udpUsrp(false),
m_srcId(p25::defines::WUID_FNE),
m_srcIdOverride(0U),
m_overrideSrcIdFromMDC(false),
m_overrideSrcIdFromUDP(false),
m_resetCallForSourceIdChange(false),
m_dstId(1U),
m_slot(1U),
m_identity(),
@ -353,7 +356,8 @@ HostBridge::HostBridge(const std::string& confFile) :
m_trace(false),
m_debug(false),
m_rtpSeqNo(0U),
m_rtpTimestamp(INVALID_TS)
m_rtpTimestamp(INVALID_TS),
m_usrpSeqNo(0U)
#if defined(_WIN32)
,
m_encoderState(nullptr),
@ -654,8 +658,7 @@ int HostBridge::run()
// -- Network Clocking --
// ------------------------------------------------------
if (m_network != nullptr)
{
if (m_network != nullptr) {
std::lock_guard<std::mutex> lock(HostBridge::m_networkMutex);
m_network->clock(ms);
}
@ -1019,10 +1022,19 @@ bool HostBridge::createNetwork()
m_udpReceivePort = (uint16_t)networkConf["udpReceivePort"].as<uint32_t>(34001);
m_udpReceiveAddress = networkConf["udpReceiveAddress"].as<std::string>();
m_udpUseULaw = networkConf["udpUseULaw"].as<bool>(false);
m_udpUsrp = networkConf["udpUsrp"].as<bool>(false);
if (m_udpUsrp) {
m_udpMetadata = false; // USRP disables metadata due to USRP always having metadata
m_udpRTPFrames = false; // USRP disables RTP
m_udpNoIncludeLength = true; // USRP disables length
m_udpUseULaw = false; // USRP disables ULaw
}
if (m_udpUseULaw) {
m_udpNoIncludeLength = networkConf["udpNoIncludeLength"].as<bool>(false);
m_udpRTPFrames = networkConf["udpRTPFrames"].as<bool>(false);
m_udpUsrp = false; // ULaw disables USRP
if (m_udpRTPFrames)
m_udpNoIncludeLength = true; // RTP disables the length being included
}
@ -1033,9 +1045,15 @@ bool HostBridge::createNetwork()
m_srcId = (uint32_t)networkConf["sourceId"].as<uint32_t>(p25::defines::WUID_FNE);
m_overrideSrcIdFromMDC = networkConf["overrideSourceIdFromMDC"].as<bool>(false);
m_overrideSrcIdFromUDP = networkConf["overrideSourceIdFromUDP"].as<bool>(false);
m_resetCallForSourceIdChange = networkConf["resetCallForSourceIdChange"].as<bool>(false);
m_dstId = (uint32_t)networkConf["destinationId"].as<uint32_t>(1U);
m_slot = (uint8_t)networkConf["slot"].as<uint32_t>(1U);
if (!m_udpMetadata && m_resetCallForSourceIdChange)
m_resetCallForSourceIdChange = false; // only applies to UDP audio with metadata
if (!m_overrideSrcIdFromUDP && m_resetCallForSourceIdChange)
m_resetCallForSourceIdChange = false; // only applies to UDP audio when overriding source ID
bool encrypted = networkConf["encrypted"].as<bool>(false);
std::string key = networkConf["presharedKey"].as<std::string>();
uint8_t presharedKey[AES_WRAPPED_PCKT_KEY_LEN];
@ -1096,6 +1114,7 @@ bool HostBridge::createNetwork()
LogInfo(" UDP Audio No Length Header: %s", m_udpNoIncludeLength ? "yes" : "no");
LogInfo(" UDP Audio RTP Framed: %s", m_udpRTPFrames ? "yes" : "no");
}
LogInfo(" UDP Audio USRP: %s", m_udpUsrp ? "yes" : "no");
}
LogInfo(" Source ID: %u", m_srcId);
@ -1103,6 +1122,9 @@ bool HostBridge::createNetwork()
LogInfo(" DMR Slot: %u", m_slot);
LogInfo(" Override Source ID from MDC: %s", m_overrideSrcIdFromMDC ? "yes" : "no");
LogInfo(" Override Source ID from UDP Audio: %s", m_overrideSrcIdFromUDP ? "yes" : "no");
if (m_resetCallForSourceIdChange) {
LogInfo(" Reset Call if Source ID Changes from UDP Audio: %s", m_resetCallForSourceIdChange ? "yes" : "no");
}
if (debug) {
LogInfo(" Debug: yes");
@ -1178,12 +1200,13 @@ void HostBridge::processUDPAudio()
pcmLength = __GET_UINT32(buffer, 0U);
}
if (m_udpRTPFrames)
if (m_udpRTPFrames || m_udpUsrp)
pcmLength = MBE_SAMPLES_LENGTH * 2U;
UInt8Array __pcm = std::make_unique<uint8_t[]>(pcmLength);
uint8_t* pcm = __pcm.get();
if (!m_udpUsrp) {
if (m_udpRTPFrames) {
RTPHeader rtpHeader = RTPHeader();
rtpHeader.decode(buffer);
@ -1203,16 +1226,36 @@ void HostBridge::processUDPAudio()
::memcpy(pcm, buffer + 4U, pcmLength);
}
}
}
else {
uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH];
::memcpy(usrpHeader, buffer, USRP_HEADER_LENGTH);
if (usrpHeader[15U] == 1U && length > USRP_HEADER_LENGTH) // PTT state true and ensure we did not just receive a USRP header
::memcpy(pcm, buffer + USRP_HEADER_LENGTH, pcmLength);
delete[] usrpHeader;
}
// Utils::dump(1U, "PCM RECV BYTE BUFFER", pcm, pcmLength);
m_udpSrcId = m_srcId;
m_udpDstId = m_dstId;
if (m_udpMetadata) {
if (m_overrideSrcIdFromUDP)
m_udpSrcId = __GET_UINT32(buffer, pcmLength + 8U);
}
if (m_overrideSrcIdFromUDP) {
uint32_t udpSrcId = __GET_UINT32(buffer, pcmLength + 8U);
// if the UDP source ID now doesn't match the current call ID, reset call states
if (m_resetCallForSourceIdChange && (udpSrcId != m_udpSrcId)) {
callEnd(m_udpSrcId, m_dstId);
m_udpDstId = m_dstId;
}
m_udpSrcId = udpSrcId;
}
} else {
m_udpSrcId = m_srcId;
}
std::lock_guard<std::mutex> lock(m_audioMutex);
@ -1444,6 +1487,10 @@ void HostBridge::processDMRNetwork(uint8_t* buffer, uint32_t length)
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
uint64_t diff = now - m_rxStartTime;
// send USRP end of transmission
if (m_udpUsrp)
sendUsrpEot();
LogMessage(LOG_HOST, "P25, call end (T), srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U);
}
@ -1539,14 +1586,16 @@ void HostBridge::decodeDMRAudioFrame(uint8_t* ambe, uint32_t srcId, uint32_t dst
uint32_t length = (MBE_SAMPLES_LENGTH * 2U) + 4U;
uint8_t* audioData = nullptr;
if (!m_udpUsrp) {
if (!m_udpMetadata) {
audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length)
if (m_udpUseULaw) {
length = (MBE_SAMPLES_LENGTH) + 4U;
length = (MBE_SAMPLES_LENGTH)+4U;
if (m_udpNoIncludeLength) {
length = MBE_SAMPLES_LENGTH;
::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH);
} else {
}
else {
__SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U);
::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH);
}
@ -1582,6 +1631,21 @@ void HostBridge::decodeDMRAudioFrame(uint8_t* ambe, uint32_t srcId, uint32_t dst
__SET_UINT32(dstId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 4U));
__SET_UINT32(srcId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 8U));
}
}
else {
uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH];
length = (MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH;
audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH]; // PCM + 32 bytes (USRP Header)
m_usrpSeqNo++;
usrpHeader[15U] = 1; // set PTT state to true
__SET_UINT32(m_usrpSeqNo, usrpHeader, 4U);
::memcpy(usrpHeader, "USRP", 4);
::memcpy(audioData, usrpHeader, USRP_HEADER_LENGTH); // copy USRP header into the UDP payload
::memcpy(audioData + USRP_HEADER_LENGTH, pcm, MBE_SAMPLES_LENGTH * 2U);
}
sockaddr_storage addr;
uint32_t addrLen;
@ -1644,6 +1708,11 @@ void HostBridge::encodeDMRAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_
dmrData.setSrcId(srcId);
dmrData.setDstId(dstId);
dmrData.setFLCO(FLCO::GROUP);
if (m_grantDemand) {
dmrData.setControl(0x80U); // DMR remote grant demand flag
} else {
dmrData.setControl(0U);
}
dmrData.setN(m_dmrN);
dmrData.setSeqNo(m_dmrSeqNo);
dmrData.setBER(0U);
@ -1863,6 +1932,11 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length)
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
uint64_t diff = now - m_rxStartTime;
// send USRP end of transmission
if (m_udpUsrp) {
sendUsrpEot();
}
LogMessage(LOG_HOST, "P25, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U);
}
@ -2130,14 +2204,17 @@ void HostBridge::decodeP25AudioFrame(uint8_t* ldu, uint32_t srcId, uint32_t dstI
uint32_t length = (MBE_SAMPLES_LENGTH * 2U) + 4U;
uint8_t* audioData = nullptr;
if (!m_udpUsrp) {
if (!m_udpMetadata) {
audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length)
if (m_udpUseULaw) {
length = (MBE_SAMPLES_LENGTH) + 4U;
length = (MBE_SAMPLES_LENGTH)+4U;
if (m_udpNoIncludeLength) {
length = MBE_SAMPLES_LENGTH;
::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH);
} else {
}
else {
__SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U);
::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH);
}
@ -2173,6 +2250,21 @@ void HostBridge::decodeP25AudioFrame(uint8_t* ldu, uint32_t srcId, uint32_t dstI
__SET_UINT32(dstId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 4U));
__SET_UINT32(srcId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 8U));
}
}
else {
uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH];
length = (MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH;
audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH]; // PCM + 32 bytes (USRP Header)
m_usrpSeqNo++;
usrpHeader[15U] = 1; // set PTT state to true
__SET_UINT32(m_usrpSeqNo, usrpHeader, 4U);
::memcpy(usrpHeader, "USRP", 4);
::memcpy(audioData, usrpHeader, USRP_HEADER_LENGTH); // copy USRP header into the UDP payload
::memcpy(audioData + USRP_HEADER_LENGTH, pcm, MBE_SAMPLES_LENGTH * 2U);
}
sockaddr_storage addr;
uint32_t addrLen;
@ -2341,6 +2433,23 @@ void HostBridge::encodeP25AudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_
m_p25N++;
}
/* Helper to send USRP end of transmission */
void HostBridge::sendUsrpEot()
{
sockaddr_storage addr;
uint32_t addrLen;
uint8_t usrpHeader[USRP_HEADER_LENGTH];
m_usrpSeqNo = 0U;
::memcpy(usrpHeader, "USRP", 4);
if (udp::Socket::lookup(m_udpSendAddress, m_udpSendPort, addr, addrLen) == 0) {
m_udpAudioSocket->write(usrpHeader, USRP_HEADER_LENGTH, addr, addrLen);
}
}
/* Helper to generate the single-tone preamble tone. */
void HostBridge::generatePreambleTone()
@ -2381,7 +2490,7 @@ uint8_t* HostBridge::generateRTPHeaders(uint8_t msgLen, uint16_t& rtpSeq)
if (timestamp != INVALID_TS) {
timestamp += (RTP_GENERIC_CLOCK_RATE / 50);
if (m_debug)
LogDebug(LOG_NET, "HostBridge::generateRTPHeaders() RTP, previous TS = %u, TS = %u, rtpSeq = %u", m_rtpTimestamp, timestamp, rtpSeq);
LogDebugEx(LOG_NET, "HostBridge::generateRTPHeaders()", "RTP, previous TS = %u, TS = %u, rtpSeq = %u", m_rtpTimestamp, timestamp, rtpSeq);
m_rtpTimestamp = timestamp;
}
@ -2400,7 +2509,7 @@ uint8_t* HostBridge::generateRTPHeaders(uint8_t msgLen, uint16_t& rtpSeq)
if (timestamp == INVALID_TS) {
if (m_debug)
LogDebug(LOG_NET, "HostBridge::generateRTPHeaders() RTP, initial TS = %u, rtpSeq = %u", header.getTimestamp(), rtpSeq);
LogDebugEx(LOG_NET, "HostBridge::generateRTPHeaders()", "RTP, initial TS = %u, rtpSeq = %u", header.getTimestamp(), rtpSeq);
m_rtpTimestamp = header.getTimestamp();
}
@ -2501,7 +2610,7 @@ void* HostBridge::threadAudioProcess(void* arg)
HostBridge* bridge = static_cast<HostBridge*>(th->obj);
if (bridge == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -2509,23 +2618,17 @@ void* HostBridge::threadAudioProcess(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
StopWatch stopWatch;
stopWatch.start();
while (!g_killed) {
if (!bridge->m_running) {
Thread::sleep(1U);
continue;
}
uint32_t ms = stopWatch.elapsed();
stopWatch.start();
// scope is intentional
{
std::lock_guard<std::mutex> lock(m_audioMutex);
@ -2636,7 +2739,7 @@ void* HostBridge::threadAudioProcess(void* arg)
Thread::sleep(1U);
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -2659,7 +2762,7 @@ void* HostBridge::threadNetworkProcess(void* arg)
HostBridge* bridge = static_cast<HostBridge*>(th->obj);
if (bridge == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -2667,23 +2770,17 @@ void* HostBridge::threadNetworkProcess(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
StopWatch stopWatch;
stopWatch.start();
while (!g_killed) {
if (!bridge->m_running) {
Thread::sleep(1U);
continue;
}
uint32_t ms = stopWatch.elapsed();
stopWatch.start();
uint32_t length = 0U;
bool netReadRet = false;
if (bridge->m_txMode == TX_MODE_DMR) {
@ -2705,7 +2802,7 @@ void* HostBridge::threadNetworkProcess(void* arg)
Thread::sleep(1U);
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -2728,7 +2825,7 @@ void* HostBridge::threadCallWatchdog(void* arg)
HostBridge* bridge = static_cast<HostBridge*>(th->obj);
if (bridge == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -2736,7 +2833,7 @@ void* HostBridge::threadCallWatchdog(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -2790,7 +2887,7 @@ void* HostBridge::threadCallWatchdog(void* arg)
Thread::sleep(5U);
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}

@ -56,6 +56,8 @@
#define DECSTATE_SIZE 2048
#define ENCSTATE_SIZE 6144
#define USRP_HEADER_LENGTH 32
const uint8_t FULL_RATE_MODE = 0x00U;
const uint8_t HALF_RATE_MODE = 0x01U;
@ -162,11 +164,13 @@ private:
bool m_udpNoIncludeLength;
bool m_udpUseULaw;
bool m_udpRTPFrames;
bool m_udpUsrp;
uint32_t m_srcId;
uint32_t m_srcIdOverride;
bool m_overrideSrcIdFromMDC;
bool m_overrideSrcIdFromUDP;
bool m_resetCallForSourceIdChange;
uint32_t m_dstId;
uint8_t m_slot;
@ -245,6 +249,8 @@ private:
uint16_t m_rtpSeqNo;
uint32_t m_rtpTimestamp;
uint32_t m_usrpSeqNo;
static std::mutex m_audioMutex;
static std::mutex m_networkMutex;
@ -446,6 +452,11 @@ private:
*/
uint8_t* generateRTPHeaders(uint8_t msgLen, uint16_t& rtpSeq);
/**
* @brief Helper to generate USRP end of transmission
*/
void sendUsrpEot();
/**
* @brief Helper to generate the single-tone preamble tone.
*/

@ -266,7 +266,7 @@ AES::AES(const AESKeyLength keyLength) {
uint8_t* AES::encryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[])
{
if (inLen % BLOCK_BYTES_LEN != 0) {
LogDebug(LOG_HOST, "AES::encryptECB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
LogDebugEx(LOG_HOST, "AES::encryptECB()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
return nullptr;
}
@ -289,7 +289,7 @@ uint8_t* AES::encryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[]
uint8_t* AES::decryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[])
{
if (inLen % BLOCK_BYTES_LEN != 0) {
LogDebug(LOG_HOST, "AES::decryptECB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
LogDebugEx(LOG_HOST, "AES::decryptECB()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
return nullptr;
}
@ -312,7 +312,7 @@ uint8_t* AES::decryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[]
uint8_t* AES::encryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t* iv)
{
if (inLen % BLOCK_BYTES_LEN != 0) {
LogDebug(LOG_HOST, "AES::encryptCBC() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
LogDebugEx(LOG_HOST, "AES::encryptCBC()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
return nullptr;
}
@ -339,7 +339,7 @@ uint8_t* AES::encryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[]
uint8_t* AES::decryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t *iv)
{
if (inLen % BLOCK_BYTES_LEN != 0) {
LogDebug(LOG_HOST, "AES::decryptCBC() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
LogDebugEx(LOG_HOST, "AES::decryptCBC()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
return nullptr;
}
@ -366,7 +366,7 @@ uint8_t* AES::decryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[]
uint8_t* AES::encryptCFB(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t *iv)
{
if (inLen % BLOCK_BYTES_LEN != 0) {
LogDebug(LOG_HOST, "AES::encryptCFB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
LogDebugEx(LOG_HOST, "AES::encryptCFB()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
return nullptr;
}
@ -394,7 +394,7 @@ uint8_t* AES::encryptCFB(const uint8_t in[], uint32_t inLen, const uint8_t key[]
uint8_t* AES::decryptCFB(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t *iv)
{
if (inLen % BLOCK_BYTES_LEN != 0) {
LogDebug(LOG_HOST, "AES::decryptCFB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
LogDebugEx(LOG_HOST, "AES::decryptCFB()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
return nullptr;
}

@ -28,6 +28,7 @@ file(GLOB common_SRC
"src/common/p25/lc/tsbk/*.cpp"
"src/common/p25/lc/tsbk/mbt/*.cpp"
"src/common/p25/sndcp/*.cpp"
"src/common/p25/kmm/*.cpp"
"src/common/p25/lookups/*.cpp"
# NXDN module
@ -73,6 +74,7 @@ file(GLOB common_INCLUDE
"src/common/p25/lc/tsbk/*.h"
"src/common/p25/lc/tsbk/mbt/*.h"
"src/common/p25/sndcp/*.h"
"src/common/p25/kmm/*.h"
"src/common/p25/lookups/*.h"
# NXDN module

@ -109,7 +109,7 @@ typedef unsigned long long ulong64_t;
#define VERSION_MAJOR "04"
#define VERSION_MINOR "11"
#define VERSION_REV "F"
#define VERSION_REV "G"
#define __NETVER__ "DVM_R" VERSION_MAJOR VERSION_REV VERSION_MINOR
#define __VER__ VERSION_MAJOR "." VERSION_MINOR VERSION_REV " (R" VERSION_MAJOR VERSION_REV VERSION_MINOR " " __GIT_VER__ ")"

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2018-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2018-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "Log.h"
@ -201,7 +201,7 @@ void LogFinalise()
/* Writes a new entry to the diagnostics log. */
void Log(uint32_t level, const char *module, const char* fmt, ...)
void Log(uint32_t level, const char *module, const char* file, const int lineNo, const char* func, const char* fmt, ...)
{
assert(fmt != nullptr);
#if defined(CATCH2_TEST_COMPILATION)
@ -217,25 +217,86 @@ void Log(uint32_t level, const char *module, const char* fmt, ...)
::gettimeofday(&nowMillis, NULL);
if (module != nullptr) {
// level 1 is DEBUG
if (level == 1U) {
// if we have a file and line number -- add that to the log entry
if (file != nullptr && lineNo > 0) {
// if we have a function name add that to the log entry
if (func != nullptr) {
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s)[%s:%u][%s] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module, file, lineNo, func);
}
else {
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s)[%s:%u] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module, file, lineNo);
}
} else {
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s) ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module);
}
} else {
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s) ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module);
}
}
else {
// level 1 is DEBUG
if (level == 1U) {
// if we have a file and line number -- add that to the log entry
if (file != nullptr && lineNo > 0) {
// if we have a function name add that to the log entry
if (func != nullptr) {
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu [%s:%u][%s] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, file, lineNo, func);
}
else {
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu [%s:%u] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, file, lineNo);
}
} else {
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U);
}
} else {
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U);
}
}
}
else {
if (module != nullptr) {
// level 1 is DEBUG
if (level == 1U) {
// if we have a file and line number -- add that to the log entry
if (file != nullptr && lineNo > 0) {
// if we have a function name add that to the log entry
if (func != nullptr) {
::sprintf(buffer, "%c: (%s)[%s:%u][%s] ", LEVELS[level], module, file, lineNo, func);
}
else {
::sprintf(buffer, "%c: (%s)[%s:%u] ", LEVELS[level], module, file, lineNo);
}
}
else {
::sprintf(buffer, "%c: (%s) ", LEVELS[level], module);
}
} else {
::sprintf(buffer, "%c: (%s) ", LEVELS[level], module);
}
}
else {
if (level >= 9999U) {
::sprintf(buffer, "U: ");
}
else {
// if we have a file and line number -- add that to the log entry
if (file != nullptr && lineNo > 0) {
// if we have a function name add that to the log entry
if (func != nullptr) {
::sprintf(buffer, "%c: [%s:%u][%s] ", LEVELS[level], file, lineNo, func);
}
else {
::sprintf(buffer, "%c: [%s:%u] ", LEVELS[level], file, lineNo);
}
}
else {
::sprintf(buffer, "%c: ", LEVELS[level]);
}
}
}
}
va_list vl, vl_len;
va_start(vl, fmt);

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2018-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2018-2025 Bryan Biedenkapp, N2PLL
*
*/
/**
@ -62,7 +62,16 @@
*
* This is a variable argument function.
*/
#define LogDebug(_module, fmt, ...) Log(1U, _module, fmt, ##__VA_ARGS__)
#define LogDebug(_module, fmt, ...) Log(1U, _module, __FILE__, __LINE__, nullptr, fmt, ##__VA_ARGS__)
/**
* @brief Macro helper to create a debug log entry.
* @param _module Name of module generating log entry.
* @param _func Name of function generating log entry.
* @param fmt String format.
*
* This is a variable argument function.
*/
#define LogDebugEx(_module, _func, fmt, ...) Log(1U, _module, __FILE__, __LINE__, _func, fmt, ##__VA_ARGS__)
/**
* @brief Macro helper to create a message log entry.
* @param _module Name of module generating log entry.
@ -70,7 +79,7 @@
*
* This is a variable argument function.
*/
#define LogMessage(_module, fmt, ...) Log(2U, _module, fmt, ##__VA_ARGS__)
#define LogMessage(_module, fmt, ...) Log(2U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__)
/**
* @brief Macro helper to create a informational log entry.
* @param _module Name of module generating log entry.
@ -79,7 +88,7 @@
* This is a variable argument function. LogInfo() does not use a module
* name when creating a log entry.
*/
#define LogInfo(fmt, ...) Log(3U, nullptr, fmt, ##__VA_ARGS__)
#define LogInfo(fmt, ...) Log(3U, nullptr, nullptr, 0, nullptr, fmt, ##__VA_ARGS__)
/**
* @brief Macro helper to create a informational log entry with module name.
* @param _module Name of module generating log entry.
@ -87,7 +96,7 @@
*
* This is a variable argument function.
*/
#define LogInfoEx(_module, fmt, ...) Log(3U, _module, fmt, ##__VA_ARGS__)
#define LogInfoEx(_module, fmt, ...) Log(3U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__)
/**
* @brief Macro helper to create a warning log entry.
* @param _module Name of module generating log entry.
@ -95,7 +104,7 @@
*
* This is a variable argument function.
*/
#define LogWarning(_module, fmt, ...) Log(4U, _module, fmt, ##__VA_ARGS__)
#define LogWarning(_module, fmt, ...) Log(4U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__)
/**
* @brief Macro helper to create a error log entry.
* @param _module Name of module generating log entry.
@ -103,7 +112,7 @@
*
* This is a variable argument function.
*/
#define LogError(_module, fmt, ...) Log(5U, _module, fmt, ##__VA_ARGS__)
#define LogError(_module, fmt, ...) Log(5U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__)
/**
* @brief Macro helper to create a fatal log entry.
* @param _module Name of module generating log entry.
@ -111,7 +120,7 @@
*
* This is a variable argument function.
*/
#define LogFatal(_module, fmt, ...) Log(6U, _module, fmt, ##__VA_ARGS__)
#define LogFatal(_module, fmt, ...) Log(6U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__)
// ---------------------------------------------------------------------------
// Externs
@ -186,11 +195,14 @@ extern HOST_SW_API void LogFinalise();
* @brief Writes a new entry to the diagnostics log.
* @param level Log level for entry.
* @param module Name of module generating log entry.
* @param file Name of source code file generating log entry.
* @param line Line number in source code file generating log entry.
* @param func Name of function generating log entry.
* @param fmt String format.
*
* This is a variable argument function.
* This is a variable argument function. This shouldn't be called directly, utilize the LogXXXX macros above, instead.
*/
extern HOST_SW_API void Log(uint32_t level, const char* module, const char* fmt, ...);
extern HOST_SW_API void Log(uint32_t level, const char* module, const char* file, const int lineNo, const char* func, const char* fmt, ...);
/** @} */
#endif // __LOG_H__

@ -82,7 +82,7 @@ public:
m_iPtr = 0U;
}
#if DEBUG_RINGBUFFER
LogDebug(LOG_HOST, "RingBuffer::addData(%s): iPtr_Before = %u, iPtr_After = %u, oPtr = %u, len = %u, len_Written = %u", m_name, iPtr_BeforeWrite, m_iPtr, m_oPtr, m_length, (m_iPtr - iPtr_BeforeWrite));
LogDebugEx(LOG_HOST, "RingBuffer::addData()", "(%s): iPtr_Before = %u, iPtr_After = %u, oPtr = %u, len = %u, len_Written = %u", m_name, iPtr_BeforeWrite, m_iPtr, m_oPtr, m_length, (m_iPtr - iPtr_BeforeWrite));
#endif
return true;
}
@ -109,7 +109,7 @@ public:
m_oPtr = 0U;
}
#if DEBUG_RINGBUFFER
LogDebug(LOG_HOST, "RingBuffer::getData(%s): iPtr = %u, oPtr_Before = %u, oPtr_After = %u, len = %u, len_Read = %u", m_name, m_iPtr, oPtr_BeforeRead, m_oPtr, m_length, (m_oPtr - oPtr_BeforeRead));
LogDebugEx(LOG_HOST, "RingBuffer::getData()", "(%s): iPtr = %u, oPtr_Before = %u, oPtr_After = %u, len = %u, len_Read = %u", m_name, m_iPtr, oPtr_BeforeRead, m_oPtr, m_length, (m_oPtr - oPtr_BeforeRead));
#endif
return true;
}

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2009,2014,2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2018-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2018-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "Utils.h"
@ -72,7 +72,7 @@ void Utils::dump(int level, const std::string& title, const uint8_t* data, uint3
{
assert(data != nullptr);
::Log(level, "DUMP", "%s (len %u)", title.c_str(), length);
::Log(level, "DUMP", nullptr, 0, nullptr, "%s (len %u)", title.c_str(), length);
uint32_t offset = 0U;
@ -103,7 +103,7 @@ void Utils::dump(int level, const std::string& title, const uint8_t* data, uint3
output += '*';
#endif
::Log(level, "DUMP", "%04X: %s", offset, output.c_str());
::Log(level, "DUMP", nullptr, 0, nullptr, "%04X: %s", offset, output.c_str());
offset += 16U;
@ -143,7 +143,7 @@ void Utils::symbols(const std::string& title, const uint8_t* data, uint32_t leng
{
assert(data != nullptr);
::Log(2U, "SYMBOLS", "%s (len %u)", title.c_str(), length);
::Log(2U, "SYMBOLS", nullptr, 0, nullptr, "%s (len %u)", title.c_str(), length);
uint32_t offset = 0U;
uint32_t count = 0U;
@ -158,7 +158,7 @@ void Utils::symbols(const std::string& title, const uint8_t* data, uint32_t leng
microslotHeader += temp;
}
::Log(2U, "SYMBOLS", "MCR: %s", microslotHeader.c_str());
::Log(2U, "SYMBOLS", nullptr, 0, nullptr, "MCR: %s", microslotHeader.c_str());
uint32_t bufLen = length;
while (bufLen > 0U) {
@ -188,7 +188,7 @@ void Utils::symbols(const std::string& title, const uint8_t* data, uint32_t leng
symOffset += 9;
}
::Log(2U, "SYMBOLS", "%03u: %s", count, output.c_str());
::Log(2U, "SYMBOLS", nullptr, 0, nullptr, "%03u: %s", count, output.c_str());
offset += 18U;
count += 2U;

@ -22,7 +22,9 @@
#include "common/Defines.h"
// Shorthand macro to dmr::defines -- keeps source code that doesn't use "using" concise
#if !defined(DMRDEF)
#define DMRDEF dmr::defines
#endif // DMRDEF
namespace dmr
{
namespace defines

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -30,6 +30,7 @@ NetData::NetData(const NetData& data) :
m_srcId(data.m_srcId),
m_dstId(data.m_dstId),
m_flco(data.m_flco),
m_control(data.m_control),
m_n(data.m_n),
m_seqNo(data.m_seqNo),
m_dataType(data.m_dataType),
@ -48,6 +49,7 @@ NetData::NetData() :
m_srcId(0U),
m_dstId(0U),
m_flco(FLCO::GROUP),
m_control(0U),
m_n(0U),
m_seqNo(0U),
m_dataType(DataType::IDLE),
@ -76,6 +78,7 @@ NetData& NetData::operator=(const NetData& data)
m_srcId = data.m_srcId;
m_dstId = data.m_dstId;
m_flco = data.m_flco;
m_control = data.m_control;
m_dataType = data.m_dataType;
m_seqNo = data.m_seqNo;
m_n = data.m_n;

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL
*
*/
/**
@ -85,6 +85,11 @@ namespace dmr
*/
__PROPERTY(defines::FLCO::E, flco, FLCO);
/**
* @brief
*/
__PROPERTY(uint8_t, control, Control);
/**
* @brief
*/

@ -571,7 +571,7 @@ uint32_t AMBEFEC::regenerate(uint32_t& a, uint32_t& b, uint32_t& c) const
if (!valid) {
uint32_t errsA = Utils::countBits32(data ^ a);
#if DEBUG_AMBEFEC
LogDebug(LOG_HOST, "AMBEFEC::regnerate() invalid A block, errsA = %u, a = %6X, b = %6X, c = %6X", errsA, a, b, c);
LogDebugEx(LOG_HOST, "AMBEFEC::regnerate()", "invalid A block, errsA = %u, a = %6X, b = %6X, c = %6X", errsA, a, b, c);
#endif
a = 0xF00292U;
b = 0x0E0B20U;
@ -596,7 +596,7 @@ uint32_t AMBEFEC::regenerate(uint32_t& a, uint32_t& b, uint32_t& c) const
v = b ^ old_b;
uint32_t errsB = Utils::countBits32(v);
#if DEBUG_AMBEFEC
LogDebug(LOG_HOST, "AMBEFEC::regnerate() errsA = %u, a = %6X, errsB = %u, b = %6X, c = %6X", errsA, a, errsB, b, c);
LogDebugEx(LOG_HOST, "AMBEFEC::regnerate()", "errsA = %u, a = %6X, errsB = %u, b = %6X, c = %6X", errsA, a, errsB, b, c);
#endif
if (errsA >= 4U || ((errsA + errsB) >= 6U && errsA >= 2U)) {
a = 0xF00292U;

@ -202,7 +202,7 @@ bool CRC::checkCCITT162(const uint8_t *in, uint32_t length)
#if DEBUG_CRC_CHECK
uint16_t inCrc = (in[length - 2U] << 8) | (in[length - 1U] << 0);
LogDebug(LOG_HOST, "CRC::checkCCITT162(), crc = $%04X, in = $%04X, len = %u", crc16, inCrc, length);
LogDebugEx(LOG_HOST, "CRC::checkCCITT162()", "crc = $%04X, in = $%04X, len = %u", crc16, inCrc, length);
#endif
return crc8[0U] == in[length - 1U] && crc8[1U] == in[length - 2U];
@ -228,7 +228,7 @@ void CRC::addCCITT162(uint8_t* in, uint32_t length)
crc16 = ~crc16;
#if DEBUG_CRC_ADD
LogDebug(LOG_HOST, "CRC::addCCITT162(), crc = $%04X, len = %u", crc16, length);
LogDebugEx(LOG_HOST, "CRC::addCCITT162()", "crc = $%04X, len = %u", crc16, length);
#endif
in[length - 1U] = crc8[0U];
@ -256,7 +256,7 @@ bool CRC::checkCCITT161(const uint8_t *in, uint32_t length)
#if DEBUG_CRC_CHECK
uint16_t inCrc = (in[length - 2U] << 8) | (in[length - 1U] << 0);
LogDebug(LOG_HOST, "CRC::checkCCITT161(), crc = $%04X, in = $%04X, len = %u", crc16, inCrc, length);
LogDebugEx(LOG_HOST, "CRC::checkCCITT161()", "crc = $%04X, in = $%04X, len = %u", crc16, inCrc, length);
#endif
return crc8[0U] == in[length - 2U] && crc8[1U] == in[length - 1U];
@ -282,7 +282,7 @@ void CRC::addCCITT161(uint8_t* in, uint32_t length)
crc16 = ~crc16;
#if DEBUG_CRC_ADD
LogDebug(LOG_HOST, "CRC::addCCITT161(), crc = $%04X, len = %u", crc16, length);
LogDebugEx(LOG_HOST, "CRC::addCCITT161()", "crc = $%04X, len = %u", crc16, length);
#endif
in[length - 2U] = crc8[0U];
@ -314,7 +314,7 @@ bool CRC::checkCRC32(const uint8_t *in, uint32_t length)
#if DEBUG_CRC_CHECK
uint32_t inCrc = (in[length - 4U] << 24) | (in[length - 3U] << 16) | (in[length - 2U] << 8) | (in[length - 1U] << 0);
LogDebug(LOG_HOST, "CRC::checkCRC32(), crc = $%08X, in = $%08X, len = %u", crc32, inCrc, length);
LogDebugEx(LOG_HOST, "CRC::checkCRC32()", "crc = $%08X, in = $%08X, len = %u", crc32, inCrc, length);
#endif
return crc8[0U] == in[length - 1U] && crc8[1U] == in[length - 2U] && crc8[2U] == in[length - 3U] && crc8[3U] == in[length - 4U];
@ -344,7 +344,7 @@ void CRC::addCRC32(uint8_t* in, uint32_t length)
crc32 &= 0xFFFFFFFFU;
#if DEBUG_CRC_ADD
LogDebug(LOG_HOST, "CRC::addCRC32(), crc = $%08X, len = %u", crc32, length);
LogDebugEx(LOG_HOST, "CRC::addCRC32()", "crc = $%08X, len = %u", crc32, length);
#endif
in[length - 1U] = crc8[0U];
@ -365,7 +365,7 @@ uint8_t CRC::crc8(const uint8_t *in, uint32_t length)
crc = CRC8_TABLE[crc ^ in[i]];
#if DEBUG_CRC_CHECK
LogDebug(LOG_HOST, "CRC::crc8(), crc = $%02X, len = %u", crc, length);
LogDebugEx(LOG_HOST, "CRC::crc8()", "crc = $%02X, len = %u", crc, length);
#endif
return crc;
@ -389,7 +389,7 @@ bool CRC::checkCRC6(const uint8_t* in, uint32_t bitLength)
#if DEBUG_CRC_CHECK
uint32_t inCrc = temp[0U];
LogDebug(LOG_HOST, "CRC::checkCRC6(), crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength);
LogDebugEx(LOG_HOST, "CRC::checkCRC6()", "crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength);
#endif
return crc == temp[0U];
@ -411,7 +411,7 @@ uint8_t CRC::addCRC6(uint8_t* in, uint32_t bitLength)
}
#if DEBUG_CRC_ADD
LogDebug(LOG_HOST, "CRC::addCRC6(), crc = $%04X, bitlen = %u", crc[0U], bitLength);
LogDebugEx(LOG_HOST, "CRC::addCRC6()", "crc = $%04X, bitlen = %u", crc[0U], bitLength);
#endif
return crc[0U];
}
@ -438,7 +438,7 @@ bool CRC::checkCRC12(const uint8_t* in, uint32_t bitLength)
#if DEBUG_CRC_CHECK
uint16_t inCrc = (temp2[0U] << 8) | (temp2[1U] << 0);
LogDebug(LOG_HOST, "CRC:checkCRC12(), crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength);
LogDebugEx(LOG_HOST, "CRC:checkCRC12()", "crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength);
#endif
return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U];
@ -463,7 +463,7 @@ uint16_t CRC::addCRC12(uint8_t* in, uint32_t bitLength)
}
#if DEBUG_CRC_ADD
LogDebug(LOG_HOST, "CRC::addCRC12(), crc = $%04X, bitlen = %u", crc, bitLength);
LogDebugEx(LOG_HOST, "CRC::addCRC12()", "crc = $%04X, bitlen = %u", crc, bitLength);
#endif
return crc;
}
@ -490,7 +490,7 @@ bool CRC::checkCRC15(const uint8_t* in, uint32_t bitLength)
#if DEBUG_CRC_CHECK
uint16_t inCrc = (temp2[0U] << 8) | (temp2[1U] << 0);
LogDebug(LOG_HOST, "CRC:checkCRC15(), crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength);
LogDebugEx(LOG_HOST, "CRC:checkCRC15()", "crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength);
#endif
return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U];
@ -515,7 +515,7 @@ uint16_t CRC::addCRC15(uint8_t* in, uint32_t bitLength)
}
#if DEBUG_CRC_ADD
LogDebug(LOG_HOST, "CRC::addCRC15(), crc = $%04X, bitlen = %u", crc, bitLength);
LogDebugEx(LOG_HOST, "CRC::addCRC15()", "crc = $%04X, bitlen = %u", crc, bitLength);
#endif
return crc;
}
@ -542,7 +542,7 @@ bool CRC::checkCRC16(const uint8_t* in, uint32_t bitLength)
#if DEBUG_CRC_CHECK
uint16_t inCrc = (temp2[0U] << 8) | (temp2[1U] << 0);
LogDebug(LOG_HOST, "CRC:checkCRC16(), crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength);
LogDebugEx(LOG_HOST, "CRC:checkCRC16()", "crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength);
#endif
return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U];
@ -567,7 +567,7 @@ uint16_t CRC::addCRC16(uint8_t* in, uint32_t bitLength)
}
#if DEBUG_CRC_ADD
LogDebug(LOG_HOST, "CRC::addCRC16(), crc = $%04X, bitlen = %u", crc, bitLength);
LogDebugEx(LOG_HOST, "CRC::addCRC16()", "crc = $%04X, bitlen = %u", crc, bitLength);
#endif
return crc;
}

@ -155,7 +155,7 @@ bool RS634717::decode241213(uint8_t* data)
int ec = rs241213.decode(codeword);
#if DEBUG_RS
LogDebug(LOG_HOST, "RS634717::decode241213(), errors = %d", ec);
LogDebugEx(LOG_HOST, "RS634717::decode241213()", "errors = %d", ec);
#endif
offset = 0U;
for (uint32_t i = 0U; i < 12U; i++, offset += 6)
@ -205,7 +205,7 @@ bool RS634717::decode24169(uint8_t* data)
int ec = rs24169.decode(codeword);
#if DEBUG_RS
LogDebug(LOG_HOST, "RS634717::decode24169(), errors = %d\n", ec);
LogDebugEx(LOG_HOST, "RS634717::decode24169()", "errors = %d\n", ec);
#endif
offset = 0U;
for (uint32_t i = 0U; i < 16U; i++, offset += 6)
@ -255,7 +255,7 @@ bool RS634717::decode362017(uint8_t* data)
int ec = rs634717.decode(codeword);
#if DEBUG_RS
LogDebug(LOG_HOST, "RS634717::decode362017(), errors = %d\n", ec);
LogDebugEx(LOG_HOST, "RS634717::decode362017()", "errors = %d\n", ec);
#endif
offset = 0U;
for (uint32_t i = 0U; i < 20U; i++, offset += 6)

@ -453,7 +453,7 @@ void Trellis::dibitsToBits(const uint8_t* dibits, uint8_t* payload) const
bool Trellis::fixCode34(uint8_t* points, uint32_t failPos, uint8_t* payload) const
{
#if DEBUG_TRELLIS
::LogDebug(LOG_HOST, "Trellis::fixCode34() failPos = %u, val = %01X", failPos, points[failPos]);
::LogDebugEx(LOG_HOST, "Trellis::fixCode34()", "failPos = %u, val = %01X", failPos, points[failPos]);
#endif
for (unsigned j = 0U; j < 20U; j++) {
uint32_t bestPos = 0U;
@ -466,7 +466,7 @@ bool Trellis::fixCode34(uint8_t* points, uint32_t failPos, uint8_t* payload) con
uint32_t pos = checkCode34(points, tribits);
if (pos == 999U) {
#if DEBUG_TRELLIS
::LogDebug(LOG_HOST, "Trellis::fixCode34() fixed, failPos = %u, pos = %u, val = %01X", failPos, bestPos, bestVal);
::LogDebugEx(LOG_HOST, "Trellis::fixCode34()", "fixed, failPos = %u, pos = %u, val = %01X", failPos, bestPos, bestVal);
#endif
tribitsToBits(tribits, payload);
return true;
@ -519,7 +519,7 @@ uint32_t Trellis::checkCode34(const uint8_t* points, uint8_t* tribits) const
bool Trellis::fixCode12(uint8_t* points, uint32_t failPos, uint8_t* payload) const
{
#if DEBUG_TRELLIS
::LogDebug(LOG_HOST, "Trellis::fixCode12() failPos = %u, val = %01X", failPos, points[failPos]);
::LogDebugEx(LOG_HOST, "Trellis::fixCode12()", "failPos = %u, val = %01X", failPos, points[failPos]);
#endif
for (unsigned j = 0U; j < 20U; j++) {
uint32_t bestPos = 0U;
@ -532,7 +532,7 @@ bool Trellis::fixCode12(uint8_t* points, uint32_t failPos, uint8_t* payload) con
uint32_t pos = checkCode12(points, dibits);
if (pos == 999U) {
#if DEBUG_TRELLIS
::LogDebug(LOG_HOST, "Trellis::fixCode12() fixed, failPos = %u, pos = %u, val = %01X", failPos, bestPos, bestVal);
::LogDebugEx(LOG_HOST, "Trellis::fixCode12()", "fixed, failPos = %u, pos = %u, val = %01X", failPos, bestPos, bestVal);
#endif
dibitsToBits(dibits, payload);
return true;

@ -338,7 +338,7 @@ namespace edac
return;
}
#if DEBUG_RS
LogDebug(LOG_HOST, "reed_solomon_tabs::reed_solomon_tabs() RS(%d,*): initialized for %d symbols size, %d modulo table", SIZE, NN, MODS);
LogDebugEx(LOG_HOST, "reed_solomon_tabs::reed_solomon_tabs()", "RS(%d,*): initialized for %d symbols size, %d modulo table", SIZE, NN, MODS);
#endif
// Generate Galois field lookup tables
index_of[0] = A0; // log(zero) = -inf
@ -478,7 +478,7 @@ namespace edac
return;
}
#if DEBUG_RS
LogDebug(LOG_HOST, "reed_solomon::reed_solomon() RS(%d,%d): initialized for %d roots", SIZE, LOAD, NROOTS);
LogDebugEx(LOG_HOST, "reed_solomon::reed_solomon()", "RS(%d,%d): initialized for %d roots", SIZE, LOAD, NROOTS);
#endif
std::array<TYP, NROOTS + 1> tmppoly; // uninitialized
@ -896,25 +896,25 @@ namespace edac
}
if (count != no_eras) {
LogDebug(LOG_HOST, "reed_solomon::decode(): count = %d, no_eras = %d, lambda(x) is WRONG", count, no_eras);
LogDebugEx(LOG_HOST, "reed_solomon::decode()", "count = %d, no_eras = %d, lambda(x) is WRONG", count, no_eras);
count = -1;
goto finish;
}
if (count) {
std::stringstream ss;
ss << "reed_solomon::decode(): Erasure positions as determined by roots of Eras Loc Poly: ";
ss << "Erasure positions as determined by roots of Eras Loc Poly: ";
for (int i = 0; i < count; i++) {
ss << loc[i] << ' ';
}
LogDebug(LOG_HOST, "%s", ss.str().c_str());
LogDebugEx(LOG_HOST, "reed_solomon::decode()", "%s", ss.str().c_str());
ss.clear();
ss << "reed_solomon::decode(): Erasure positions as determined by roots of eras_pos array: ";
ss << "Erasure positions as determined by roots of eras_pos array: ";
for (int i = 0; i < no_eras; i++) {
ss << eras_pos[i] << ' ';
}
LogDebug(LOG_HOST, "%s", ss.str().c_str());
LogDebugEx(LOG_HOST, "reed_solomon::decode()", "%s", ss.str().c_str());
}
#endif
@ -1052,7 +1052,7 @@ namespace edac
#if DEBUG_RS
if (den == 0) {
LogDebug(LOG_HOST, "reed_solomon::decode(): ERROR: denominator = 0");
LogDebugEx(LOG_HOST, "reed_solomon::decode()", "ERROR: denominator = 0");
count = -1;
goto finish;
}
@ -1066,9 +1066,9 @@ namespace edac
// correction location outside of the data and parity we've been provided!
#if DEBUG_RS
std::stringstream ss;
ss << "reed_solomon::decode(): ERROR: RS(" << SIZE <<"," << LOAD << ") computed error location: " << loc[j] <<
ss << "ERROR: RS(" << SIZE <<"," << LOAD << ") computed error location: " << loc[j] <<
" within " << pad << " pad symbols, not within " << LOAD - pad << " data or " << NROOTS << " parity";
LogDebug(LOG_HOST, "%s", ss.str().c_str());
LogDebugEx(LOG_HOST, "reed_solomon::decode()", "%s", ss.str().c_str());
#endif
count = -1;
goto finish;
@ -1098,7 +1098,7 @@ namespace edac
finish:
#if DEBUG_RS
if (count > NROOTS) {
LogDebug(LOG_HOST, "reed_solomon::decode(): ERROR: number of corrections %d exceeds NROOTS %d", count, NROOTS);
LogDebugEx(LOG_HOST, "reed_solomon::decode()", "ERROR: number of corrections %d exceeds NROOTS %d", count, NROOTS);
}
if (count > 0) {
@ -1114,8 +1114,8 @@ finish:
}
std::stringstream ss;
ss << "reed_solomon::decode(): e)rase, E)rror; count = " << count << ": " << std::endl << errors;
LogDebug(LOG_HOST, "%s", ss.str().c_str());
ss << "e)rase, E)rror; count = " << count << ": " << std::endl << errors;
LogDebugEx(LOG_HOST, "reed_solomon::decode()", "%s", ss.str().c_str());
}
#endif
if (eras_pos != NULL) {

@ -305,7 +305,9 @@ bool AffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTi
std::lock_guard<std::mutex> lock(m_mutex);
uint32_t chNo = m_chLookup->getFirstRFChannel();
m_chLookup->removeRFCh(chNo);
if (!m_chLookup->removeRFCh(chNo)) {
return false;
}
m_grantChTable[dstId] = chNo;
m_grantSrcIdTable[dstId] = srcId;

@ -71,12 +71,18 @@ bool ChannelLookup::addRFCh(uint32_t chNo, bool force)
/* Helper to remove a RF channel. */
void ChannelLookup::removeRFCh(uint32_t chNo)
bool ChannelLookup::removeRFCh(uint32_t chNo)
{
if (chNo == 0U) {
return;
return false;
}
try {
auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo);
m_rfChTable.erase(it);
} catch (...) {
return false;
}
return true;
}

@ -206,8 +206,9 @@ namespace lookups
/**
* @brief Helper to remove a RF channel.
* @param chNo Channel Number.
* @returns bool True, if channel remove, otherwise false.
*/
void removeRFCh(uint32_t chNo);
bool removeRFCh(uint32_t chNo);
/**
* @brief Helper to determine if there are any RF channels available..
* @returns bool True, if any RF channels are available for use, otherwise false.

@ -182,7 +182,7 @@ namespace lookups
* @brief Helper to set the reload time of this lookup table.
* @param reloadTime Lookup time in seconds.
*/
void setReloadTime(uint32_t reloadTime) { m_reloadTime = 0U; }
void setReloadTime(uint32_t reloadTime) { m_reloadTime = reloadTime; }
protected:
std::string m_filename;

@ -152,6 +152,20 @@ PeerListLookup::Mode PeerListLookup::getMode() const
return m_mode;
}
/* Gets the entire peer ID table. */
std::vector<PeerId> PeerListLookup::tableAsList() const
{
std::vector<PeerId> ret = std::vector<PeerId>();
std::lock_guard<std::mutex> lock(m_mutex);
for (auto entry : m_table) {
ret.push_back(entry.second);
}
return ret;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
@ -221,7 +235,7 @@ bool PeerListLookup::load()
m_table[id] = PeerId(id, alias, password, peerLink, false);
// Log depending on what was loaded
LogDebug(LOG_HOST, "Loaded peer ID %u%s into peer ID lookup table, %s%s", id,
LogMessage(LOG_HOST, "Loaded peer ID %u%s into peer ID lookup table, %s%s", id,
(!alias.empty() ? (" (" + alias + ")").c_str() : ""),
(!password.empty() ? "using unique peer password" : "using master password"),
(peerLink) ? ", Peer-Link Enabled" : "");
@ -234,7 +248,7 @@ bool PeerListLookup::load()
if (size == 0U)
return false;
LogInfoEx(LOG_HOST, "Loaded %lu peers into list", size);
LogInfoEx(LOG_HOST, "Loaded %lu entries into peer list lookup table", size);
return true;
}
@ -242,8 +256,6 @@ bool PeerListLookup::load()
bool PeerListLookup::save()
{
LogDebug(LOG_HOST, "Saving peer lookup file to %s", m_filename.c_str());
if (m_filename.empty()) {
return false;
}
@ -254,6 +266,8 @@ bool PeerListLookup::save()
return false;
}
LogMessage(LOG_HOST, "Saving peer lookup file to %s", m_filename.c_str());
// Counter for lines written
unsigned int lines = 0;

@ -110,19 +110,19 @@ namespace lookups
/**
* @brief Peer ID.
*/
__READONLY_PROPERTY_PLAIN(uint32_t, peerId);
__PROPERTY_PLAIN(uint32_t, peerId);
/**
* @breif Peer Alias
*/
__READONLY_PROPERTY_PLAIN(std::string, peerAlias);
__PROPERTY_PLAIN(std::string, peerAlias);
/**
* @brief Per Peer Password.
*/
__READONLY_PROPERTY_PLAIN(std::string, peerPassword);
__PROPERTY_PLAIN(std::string, peerPassword);
/**
* @brief Flag indicating if the peer participates in peer link and should be sent configuration.
*/
__READONLY_PROPERTY_PLAIN(bool, peerLink);
__PROPERTY_PLAIN(bool, peerLink);
/**
* @brief Flag indicating if the peer is default.
*/
@ -221,6 +221,17 @@ namespace lookups
*/
Mode getMode() const;
/**
* @brief Gets the entire peer ID table.
* @returns std::unordered_map<uint32_t, PeerId>
*/
std::unordered_map<uint32_t, PeerId> table() const { return m_table; }
/**
* @brief Gets the entire peer ID table.
* @returns std::vector<PeerId>
*/
std::vector<PeerId> tableAsList() const;
protected:
bool m_acl;

@ -195,11 +195,6 @@ bool RadioIdLookup::load()
}
m_table[id] = RadioId(radioEnabled, false, alias, ipAddress);
/*if (alias != "") {
LogDebug(LOG_HOST, "Loaded RID %u (%s) into RID lookup table", id, parsed[2].c_str());
} else {
LogDebug(LOG_HOST, "Loaded RID %u into RID lookup table", id);
}*/
}
}
@ -209,7 +204,7 @@ bool RadioIdLookup::load()
if (size == 0U)
return false;
LogInfoEx(LOG_HOST, "Loaded %u entries into lookup table", size);
LogInfoEx(LOG_HOST, "Loaded %lu entries into radio ID lookup table", size);
return true;
}
@ -218,8 +213,6 @@ bool RadioIdLookup::load()
bool RadioIdLookup::save()
{
LogDebug(LOG_HOST, "Saving RID lookup file to %s", m_filename.c_str());
if (m_filename.empty()) {
return false;
}
@ -230,6 +223,8 @@ bool RadioIdLookup::save()
return false;
}
LogMessage(LOG_HOST, "Saving RID lookup file to %s", m_filename.c_str());
// Counter for lines written
unsigned int lines = 0;

@ -336,7 +336,7 @@ bool TalkgroupRulesLookup::load()
if (size == 0U)
return false;
LogInfoEx(LOG_HOST, "Loaded %u entries into lookup table", size);
LogInfoEx(LOG_HOST, "Loaded %lu entries into talkgroup rules table", size);
return true;
}
@ -359,10 +359,10 @@ bool TalkgroupRulesLookup::save()
for (auto entry : m_groupVoice) {
yaml::Node& gv = groupVoiceList.push_back();
entry.getYaml(gv);
//LogDebug(LOG_HOST, "Added TGID %s to yaml TG list", gv["name"].as<std::string>().c_str());
//LogDebugEx(LOG_HOST, "TalkgroupRulesLookup::save()", "Added TGID %s to yaml TG list", gv["name"].as<std::string>().c_str());
}
//LogDebug(LOG_HOST, "Got final GroupVoiceList YAML size of %u", groupVoiceList.size());
//LogDebugEx(LOG_HOST, "TalkgroupRulesLookup::save()", "Got final GroupVoiceList YAML size of %u", groupVoiceList.size());
// Set the new rules
newRules["groupVoice"] = groupVoiceList;
@ -374,7 +374,7 @@ bool TalkgroupRulesLookup::save()
}
try {
//LogDebug(LOG_HOST, "Saving TGID file to %s", m_rulesFile.c_str());
LogMessage(LOG_HOST, "Saving talkgroup rules file to %s", m_rulesFile.c_str());
yaml::Serialize(newRules, m_rulesFile.c_str());
LogDebug(LOG_HOST, "Saved TGID config file to %s", m_rulesFile.c_str());
}

@ -632,7 +632,7 @@ namespace lookups
* @brief Helper to set the reload time of this lookup table.
* @param reloadTime Lookup time in seconds.
*/
void setReloadTime(uint32_t reloadTime) { m_reloadTime = 0U; }
void setReloadTime(uint32_t reloadTime) { m_reloadTime = reloadTime; }
private:
std::string m_rulesFile;

@ -544,12 +544,7 @@ bool BaseNetwork::writeP25TDU(const p25::lc::LC& control, const p25::data::LowSp
return false;
}
uint16_t seq = pktSeq(resetSeq);
if (controlByte == 0x00U) {
seq = RTP_END_OF_CALL_SEQ;
}
return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, seq, m_p25StreamId);
return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, m_p25StreamId);
}
/* Writes P25 TSDU frame data to the network. */
@ -742,7 +737,7 @@ UInt8Array BaseNetwork::createDMR_Message(uint32_t& length, const uint32_t strea
uint32_t slotNo = data.getSlotNo();
buffer[14U] = 0U; // Control Bits
buffer[14U] = data.getControl(); // Control Bits
// Individual slot disabling
if (slotNo == 1U && !m_slot1) {

@ -57,7 +57,13 @@ UInt8Array FrameQueue::read(int& messageLength, sockaddr_storage& address, uint3
::memset(buffer, 0x00U, DATA_PACKET_LENGTH);
int length = m_socket->read(buffer, DATA_PACKET_LENGTH, address, addrLen);
if (length < 0) {
LogError(LOG_NET, "Failed reading data from the network");
if (m_failedReadCnt <= MAX_FAILED_READ_CNT_LOGGING)
LogError(LOG_NET, "Failed reading data from the network, failedCnt = %u", m_failedReadCnt);
else {
if (m_failedReadCnt == MAX_FAILED_READ_CNT_LOGGING + 1U)
LogError(LOG_NET, "Failed reading data from the network -- exceeded 5 read errors, probable connection issue, silencing further errors");
}
m_failedReadCnt++;
return nullptr;
}
@ -65,6 +71,8 @@ UInt8Array FrameQueue::read(int& messageLength, sockaddr_storage& address, uint3
if (m_debug)
Utils::dump(1U, "Network Packet", buffer, length);
m_failedReadCnt = 0U;
if (length < RTP_HEADER_LENGTH_BYTES + RTP_EXTENSION_HEADER_LENGTH_BYTES) {
LogError(LOG_NET, "FrameQueue::read(), message received from network is malformed! %u bytes != %u bytes",
RTP_HEADER_LENGTH_BYTES + RTP_EXTENSION_HEADER_LENGTH_BYTES, length);
@ -203,7 +211,7 @@ uint8_t* FrameQueue::generateMessage(const uint8_t* message, uint32_t length, ui
if (timestamp != INVALID_TS) {
timestamp += (RTP_GENERIC_CLOCK_RATE / 133);
if (m_debug)
LogDebug(LOG_NET, "FrameQueue::generateMessage() RTP streamId = %u, previous TS = %u, TS = %u, rtpSeq = %u", streamId, m_streamTimestamps[streamId], timestamp, rtpSeq);
LogDebugEx(LOG_NET, "FrameQueue::generateMessage()", "RTP streamId = %u, previous TS = %u, TS = %u, rtpSeq = %u", streamId, m_streamTimestamps[streamId], timestamp, rtpSeq);
m_streamTimestamps[streamId] = timestamp;
}
}
@ -224,7 +232,7 @@ uint8_t* FrameQueue::generateMessage(const uint8_t* message, uint32_t length, ui
if (streamId != 0U && timestamp == INVALID_TS && rtpSeq != RTP_END_OF_CALL_SEQ) {
if (m_debug)
LogDebug(LOG_NET, "FrameQueue::generateMessage() RTP streamId = %u, initial TS = %u, rtpSeq = %u", streamId, header.getTimestamp(), rtpSeq);
LogDebugEx(LOG_NET, "FrameQueue::generateMessage()", "RTP streamId = %u, initial TS = %u, rtpSeq = %u", streamId, header.getTimestamp(), rtpSeq);
m_streamTimestamps[streamId] = header.getTimestamp();
}
@ -233,7 +241,7 @@ uint8_t* FrameQueue::generateMessage(const uint8_t* message, uint32_t length, ui
auto entry = m_streamTimestamps.find(streamId);
if (entry != m_streamTimestamps.end()) {
if (m_debug)
LogDebug(LOG_NET, "FrameQueue::generateMessage() RTP streamId = %u, rtpSeq = %u", streamId, rtpSeq);
LogDebugEx(LOG_NET, "FrameQueue::generateMessage()", "RTP streamId = %u, rtpSeq = %u", streamId, rtpSeq);
m_streamTimestamps.erase(streamId);
}
}

@ -33,6 +33,7 @@ std::mutex RawFrameQueue::m_flushMutex;
RawFrameQueue::RawFrameQueue(udp::Socket* socket, bool debug) :
m_socket(socket),
m_buffers(),
m_failedReadCnt(0U),
m_debug(debug)
{
/* stub */
@ -56,7 +57,13 @@ UInt8Array RawFrameQueue::read(int& messageLength, sockaddr_storage& address, ui
::memset(buffer, 0x00U, DATA_PACKET_LENGTH);
int length = m_socket->read(buffer, DATA_PACKET_LENGTH, address, addrLen);
if (length < 0) {
LogError(LOG_NET, "Failed reading data from the network");
if (m_failedReadCnt <= MAX_FAILED_READ_CNT_LOGGING)
LogError(LOG_NET, "Failed reading data from the network, failedCnt = %u", m_failedReadCnt);
else {
if (m_failedReadCnt == MAX_FAILED_READ_CNT_LOGGING + 1U)
LogError(LOG_NET, "Failed reading data from the network -- exceeded 5 read errors, probable connection issue, silencing further errors");
}
m_failedReadCnt++;
return nullptr;
}
@ -64,6 +71,8 @@ UInt8Array RawFrameQueue::read(int& messageLength, sockaddr_storage& address, ui
if (m_debug)
Utils::dump(1U, "Network Packet", buffer, length);
m_failedReadCnt = 0U;
// copy message
messageLength = length;
UInt8Array message = std::unique_ptr<uint8_t[]>(new uint8_t[length]);

@ -29,6 +29,7 @@ namespace network
// ---------------------------------------------------------------------------
const uint32_t DATA_PACKET_LENGTH = 8192U;
const uint8_t MAX_FAILED_READ_CNT_LOGGING = 5U;
// ---------------------------------------------------------------------------
// Class Declaration
@ -96,6 +97,8 @@ namespace network
static std::mutex m_flushMutex;
udp::BufferVector m_buffers;
uint32_t m_failedReadCnt;
bool m_debug;
private:

@ -322,9 +322,9 @@ namespace network
void handleRequest(const Request& request, Reply& reply)
{
for (auto header : request.headers.headers())
::LogDebug(LOG_REST, "DebugRequestDispatcher::handleRequest() header = %s, value = %s", header.name.c_str(), header.value.c_str());
::LogDebugEx(LOG_REST, "DebugRequestDispatcher::handleRequest()", "header = %s, value = %s", header.name.c_str(), header.value.c_str());
::LogDebug(LOG_REST, "DebugRequestDispatcher::handleRequest() content = %s", request.content.c_str());
::LogDebugEx(LOG_REST, "DebugRequestDispatcher::handleRequest()", "content = %s", request.content.c_str());
}
};

@ -97,7 +97,7 @@ namespace network
*/
void add(const std::string& name, const std::string& value)
{
//::LogDebug(LOG_REST, "HTTPHeaders::add(), header = %s, value = %s", name.c_str(), value.c_str());
//::LogDebugEx(LOG_REST, "HTTPHeaders::add()", "header = %s, value = %s", name.c_str(), value.c_str());
for (auto& header : m_headers) {
if (::strtolower(header.name) == ::strtolower(name)) {
header.value = value;
@ -107,7 +107,7 @@ namespace network
m_headers.push_back(Header(name, value));
//for (auto header : m_headers)
// ::LogDebug(LOG_REST, "HTTPHeaders::add() m_headers.header = %s, m_headers.value = %s", header.name.c_str(), header.value.c_str());
// ::LogDebugEx(LOG_REST, "HTTPHeaders::add()", "m_headers.header = %s, m_headers.value = %s", header.name.c_str(), header.value.c_str());
}
/**
* @brief Helper to remove a HTTP header.

@ -358,7 +358,7 @@ HTTPLexer::ResultType HTTPLexer::consume(HTTPPayload& req, char input)
case EXPECTING_NEWLINE_3:
if (input == '\n') {
for (auto header : m_headers) {
//::LogDebug(LOG_REST, "HTTPLexer::consume(), header = %s, value = %s", header.name.c_str(), header.value.c_str());
//::LogDebugEx(LOG_REST, "HTTPLexer::consume()", "header = %s, value = %s", header.name.c_str(), header.value.c_str());
req.headers.add(header.name, header.value);
}

@ -279,7 +279,7 @@ std::vector<asio::const_buffer> HTTPPayload::toBuffers()
// copy URI and erase zero terminator
uri.erase(std::find(uri.begin(), uri.end(), '\0'), uri.end());
#if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "HTTPPayload::toBuffers() method = %s, uri = %s", method.c_str(), uri.c_str());
::LogDebugEx(LOG_REST, "HTTPPayload::toBuffers()", "method = %s, uri = %s", method.c_str(), uri.c_str());
#endif
buffers.push_back(asio::buffer(method));
buffers.push_back(asio::buffer(misc_strings::request_method_separator));
@ -295,7 +295,7 @@ std::vector<asio::const_buffer> HTTPPayload::toBuffers()
for (std::size_t i = 0; i < headers.size(); ++i) {
HTTPHeaders::Header& h = headers.m_headers[i];
#if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "HTTPPayload::toBuffers() header = %s, value = %s", h.name.c_str(), h.value.c_str());
::LogDebugEx(LOG_REST, "HTTPPayload::toBuffers()", "header = %s, value = %s", h.name.c_str(), h.value.c_str());
#endif
buffers.push_back(asio::buffer(h.name));
@ -309,9 +309,9 @@ std::vector<asio::const_buffer> HTTPPayload::toBuffers()
buffers.push_back(asio::buffer(content));
#if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "HTTPPayload::toBuffers() content = %s", content.c_str());
::LogDebugEx(LOG_REST, "HTTPPayload::toBuffers()", "content = %s", content.c_str());
for (auto buffer : buffers)
Utils::dump("buffer[]", (uint8_t*)buffer.data(), buffer.size());
Utils::dump("[HTTPPayload::toBuffers()] buffer[]", (uint8_t*)buffer.data(), buffer.size());
#endif
return buffers;

@ -333,6 +333,7 @@ bool Socket::write(const uint8_t* buffer, uint32_t length, const sockaddr_storag
cryptedLen += alignment;
// reallocate buffer and copy
delete[] cryptoBuffer;
cryptoBuffer = new uint8_t[cryptedLen];
::memset(cryptoBuffer, 0x00U, cryptedLen);
::memcpy(cryptoBuffer, buffer, length);
@ -466,6 +467,7 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept
continue;
}
try {
// are we crypto wrapped?
if (m_isCryptoWrapped && m_presharedKey != nullptr) {
uint32_t cryptedLen = length * sizeof(uint8_t);
@ -525,6 +527,10 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept
headers[i].msg_hdr.msg_control = 0;
headers[i].msg_hdr.msg_controllen = 0;
}
catch (...) {
--size;
}
}
bool skip = false;
for (auto& buffer : buffers) {

@ -22,7 +22,9 @@
#include "common/Defines.h"
// Shorthand macro to nxdn::defines -- keeps source code that doesn't use "using" concise
#if !defined(NXDDEF)
#define NXDDEF nxdn::defines
#endif // NXDDEF
namespace nxdn
{
namespace defines

@ -86,7 +86,7 @@ bool LICH::decode(const uint8_t* data)
m_lich = lich[0U];
#if DEBUG_NXDN_LICH
LogDebug(LOG_NXDN, "LICH::decode(), m_lich = %02X", m_lich);
LogDebugEx(LOG_NXDN, "LICH::decode()", "m_lich = %02X", m_lich);
#endif
bool newParity = getParity();
@ -121,7 +121,7 @@ void LICH::encode(uint8_t* data)
m_lich |= (m_outbound ? 0x01U : 0x00U) << 1;
#if DEBUG_NXDN_LICH
LogDebug(LOG_NXDN, "LICH::encode(), m_lich = %02X", m_lich);
LogDebugEx(LOG_NXDN, "LICH::encode()", "m_lich = %02X", m_lich);
#endif
bool parity = getParity();

@ -22,7 +22,9 @@
#include "common/Defines.h"
// Shorthand macro to p25::defines -- keeps source code that doesn't use "using" concise
#if !defined(P25DEF)
#define P25DEF p25::defines
#endif // P25DEF
namespace p25
{
namespace defines
@ -411,6 +413,58 @@ namespace p25
};
}
/** @brief KMM Message Type */
namespace KMM_MessageType {
enum : uint8_t {
NULL_CMD = 0x00U, //! Null
CHANGE_RSI_CMD = 0x03U, //! Change RSI Command
CHANGE_RSI_RSP = 0x04U, //! Change RSI Response
CHANGEOVER_CMD = 0x05U, //! Changeover Command
CHANGEOVER_RSP = 0x06U, //! Changeover Response
HELLO = 0x0CU, //! Hello
INVENTORY_CMD = 0x0DU, //! Inventory Command
INVENTORY_RSP = 0x0EU, //! Inventory Response
MODIFY_KEY_CMD = 0x13U, //! Modify Key Command
NAK = 0x16U, //! Negative Ack
ZEROIZE_CMD = 0x21U, //! Zeroize Command
ZEROIZE_RSP = 0x22U, //! Zeroize Response
};
}
/** @brief KMM Response Kind */
namespace KMM_ResponseKind {
enum : uint8_t {
NONE = 0x00U, //! Response Kind 1 (None)
DELAYED = 0x01U, //! Response Kind 2 (Delayed)
IMMEDIATE = 0x02U, //! Response Kind 3 (Immediate)
};
}
/** @brief KMM Message Authentication */
namespace KMM_MAC {
enum : uint8_t {
NO_MAC = 0x00U, //! No Message Authentication
ENH_MAC = 0x02U, //! Enhanced Message Authentication
DES_MAC = 0x03U, //! DES Message Authentication
};
}
/** @brief KMM Decryption Instruction - None */
const uint8_t KMM_DECRYPT_INSTRUCT_NONE = 0x00U;
/** @brief KMM Decryption Instruction - Message Indicator */
const uint8_t KMM_DECRYPT_INSTRUCT_MI = 0x40U;
/** @brief KMM Key Format TEK */
const uint8_t KEY_FORMAT_TEK = 0x80U;
/** @brief KMM Key Format Delete Key */
const uint8_t KEY_FORMAT_DELETE = 0x20U;
/** @brief SNDCP version 1 */
const uint8_t SNDCP_VERSION_1 = 0x01U;
/** @brief 296 byte MTU */

@ -122,13 +122,13 @@ namespace p25
m_netId = dist(mt);
// netId clamping
netId = P25Utils::netId(netId);
m_netId = P25Utils::netId(netId);
dist = std::uniform_int_distribution<uint32_t>(0x01, 0xFFEU);
m_sysId = dist(mt);
// sysId clamping
sysId = P25Utils::sysId(sysId);
m_sysId = P25Utils::sysId(sysId);
}
m_rfssId = rfssId;

@ -353,7 +353,7 @@ void DataHeader::encodeExtAddr(uint8_t* data, bool noTrellis)
header[1U] = m_exSap & 0x3FU; // Service Access Point
header[1U] |= 0xC0U;
header[2U] = m_mfId; // Mfg Id.
//header[2U] = m_mfId; // Mfg Id.
header[3U] = (m_srcLlId >> 16) & 0xFFU; // Source Logical Link ID
header[4U] = (m_srcLlId >> 8) & 0xFFU;

@ -405,8 +405,8 @@ void LC::encodeLDU1(uint8_t* data, const uint8_t* imbe)
}
#if DEBUG_P25_DFSI
LogDebug(LOG_P25, "LC::encodeLDU1(), frameType = $%02X", m_frameType);
Utils::dump(2U, "LC::encodeLDU1(), DFSI LDU1 Frame", dfsiFrame, frameLength);
LogDebugEx(LOG_P25, "LC::encodeLDU1()", "frameType = $%02X", m_frameType);
Utils::dump(2U, "[LC::encodeLDU1()] DFSI LDU1 Frame", dfsiFrame, frameLength);
#endif
::memcpy(data, dfsiFrame, frameLength);
@ -646,8 +646,8 @@ void LC::encodeLDU2(uint8_t* data, const uint8_t* imbe)
}
#if DEBUG_P25_DFSI
LogDebug(LOG_P25, "LC::encodeLDU2(), frameType = $%02X", m_frameType);
Utils::dump(2U, "LC::encodeLDU2(), DFSI LDU2 Frame", dfsiFrame, frameLength);
LogDebugEx(LOG_P25, "LC::encodeLDU2()", "frameType = $%02X", m_frameType);
Utils::dump(2U, "[LC::encodeLDU2()] DFSI LDU2 Frame", dfsiFrame, frameLength);
#endif
::memcpy(data, dfsiFrame, frameLength);

@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "p25/P25Defines.h"
#include "p25/kmm/KMMFrame.h"
#include "Log.h"
using namespace p25;
using namespace p25::defines;
using namespace p25::kmm;
#include <cassert>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/* Initializes a copy instance of the KMMFrame class. */
KMMFrame::KMMFrame(const KMMFrame& data) : KMMFrame()
{
copy(data);
}
/* Initializes a new instance of the KMMFrame class. */
KMMFrame::KMMFrame() :
m_messageId(KMM_MessageType::NULL_CMD),
m_messageLength(7U),
m_respKind(KMM_ResponseKind::NONE),
m_complete(true),
m_mfMessageNumber(0U),
m_mfMac(KMM_MAC::NO_MAC)
{
/* stub */
}
/* Finalizes a instance of the KMMFrame class. */
KMMFrame::~KMMFrame() = default;
// ---------------------------------------------------------------------------
// Protected Class Members
// ---------------------------------------------------------------------------
/* Internal helper to decode a SNDCP header. */
bool KMMFrame::decodeHeader(const uint8_t* data, bool outbound)
{
assert(data != nullptr);
m_messageId = data[0U]; // Message ID
m_messageLength = __GET_UINT16B(data, 1U); // Message Length
m_respKind = (data[2U] >> 6U) & 0x03U; // Response Kind
m_mfMessageNumber = (data[2U] >> 4U) & 0x03U; // Message Number
m_mfMac = (data[2U] >> 2U) & 0x03U; // MAC
bool done = (data[2U] & 0x01U) == 0x01U; // Done Flag
if (!done)
m_complete = true;
else
m_complete = false;
m_dstLlId = __GET_UINT16(data, 4U); // Destination RSI
m_srcLlId = __GET_UINT16(data, 7U); // Source RSI
return true;
}
/* Internal helper to encode a SNDCP header. */
void KMMFrame::encodeHeader(uint8_t* data, bool outbound)
{
assert(data != nullptr);
data[0U] = m_messageId; // Message ID
__SET_UINT16B(m_messageLength, data, 2U); // Message Length
data[2U] = ((m_respKind & 0x03U) << 6U) + // Response Kind
((m_mfMessageNumber & 0x03U) << 4U) + // Message Number
((m_mfMac & 0x03U) << 2U) + // MAC
((!m_complete) ? 0x01U : 0x00U); // Done Flag
__SET_UINT16(m_dstLlId, data, 4U); // Destination RSI
__SET_UINT16(m_srcLlId, data, 7U); // Source RSI
}
/* Internal helper to copy the the class. */
void KMMFrame::copy(const KMMFrame& data)
{
m_messageId = data.m_messageId;
m_messageLength = data.m_messageLength;
m_respKind = data.m_respKind;
m_complete = data.m_complete;
m_mfMessageNumber = data.m_mfMessageNumber;
m_mfMac = data.m_mfMac;
}

@ -0,0 +1,140 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
/**
* @defgroup p25_kmm Key Management Message
* @brief Implementation for the data handling of the TIA-102.AACA Project 25 standard.
* @ingroup p25
*
* @file KMMFrame.h
* @ingroup p25_kmm
* @file KMMFrame.cpp
* @ingroup p25_kmm
*/
#if !defined(__P25_KMM__KMM_FRAME_H__)
#define __P25_KMM__KMM_FRAME_H__
#include "common/Defines.h"
#include "common/Utils.h"
#include <string>
namespace p25
{
namespace kmm
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
/**
* @addtogroup p25_kmm
* @{
*/
const uint32_t KMM_FRAME_LENGTH = 9U;
/** @} */
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief Represents a KMM frame packet header.
* @ingroup p25_kmm
*/
class HOST_SW_API KMMFrame {
public:
/**
* @brief Initializes a copy instance of the KMMFrame class.
* @param data Instance of KMMFrame to copy/
*/
KMMFrame(const KMMFrame& data);
/**
* @brief Initializes a new instance of the KMMFrame class.
*/
KMMFrame();
/**
* @brief Finalizes a instance of the KMMFrame class.
*/
~KMMFrame();
/**
* @brief Gets the byte length of this KMMFrame.
* @return uint32_t Length of KMMFrame.
*/
virtual uint32_t length() const { return KMM_FRAME_LENGTH; }
/**
* @brief Decode a KMM frame.
* @param[in] data Buffer containing KMM frame data to decode.
* @returns bool True, if decoded, otherwise false.
*/
virtual bool decode(const uint8_t* data) = 0;
/**
* @brief Encode a KMM frame.
* @param[out] data Buffer to encode KMM frame data to.
*/
virtual void encode(uint8_t* data) = 0;
public:
// Common Data
/**
* @brief
*/
__PROTECTED_PROPERTY(uint8_t, messageId, MessageId);
/**
* @brief
*/
__PROTECTED_PROPERTY(uint16_t, messageLength, MessageLength);
/**
* @brief
*/
__PROTECTED_PROPERTY(uint8_t, respKind, ResponseKind);
/**
* @brief Destination Logical link ID.
*/
__PROTECTED_PROPERTY(uint32_t, dstLlId, DstLLId);
/**
* @brief Source Logical link ID.
*/
__PROTECTED_PROPERTY(uint32_t, srcLlId, SrcLLId);
/**
* @brief
*/
__PROTECTED_PROPERTY(bool, complete, Complete);
protected:
uint8_t m_mfMessageNumber;
uint8_t m_mfMac;
/**
* @brief Internal helper to decode a KMM header.
* @param data Buffer containing KMM packet data to decode.
* @param outbound Flag indicating whether the packet is inbound or outbound.
* @returns bool True, if decoded, otherwise false.
*/
bool decodeHeader(const uint8_t* data, bool outbound = false);
/**
* @brief Internal helper to encode a KMM header.
* @param data Buffer to encode KMM packet data to.
* @param outbound Flag indicating whether the packet is inbound or outbound.
*/
void encodeHeader(uint8_t* data, bool outbound = false);
__PROTECTED_COPY(KMMFrame);
};
} // namespace kmm
} // namespace p25
#endif // __P25_KMM__KMM_FRAME_H__

@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "p25/P25Defines.h"
#include "p25/kmm/KMMModifyKey.h"
#include "Log.h"
using namespace p25;
using namespace p25::defines;
using namespace p25::kmm;
#include <cassert>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/* Initializes a new instance of the KMMModifyKey class. */
KMMModifyKey::KMMModifyKey() : KMMFrame(),
m_decryptInfoFmt(KMM_DECRYPT_INSTRUCT_NONE),
m_algId(ALGO_UNENCRYPT),
m_kId(0U),
m_keysetItem(),
m_miSet(false),
m_mi(nullptr)
{
m_mi = new uint8_t[MI_LENGTH_BYTES];
::memset(m_mi, 0x00U, MI_LENGTH_BYTES);
}
/* Finalizes a instance of the KMMModifyKey class. */
KMMModifyKey::~KMMModifyKey()
{
if (m_mi != nullptr) {
delete[] m_mi;
m_mi = nullptr;
}
}
/* Gets the byte length of this KMMFrame. */
uint32_t KMMModifyKey::length() const
{
uint32_t len = KMM_MODIFY_KEY_LENGTH;
if (m_miSet)
len += MI_LENGTH_BYTES;
len += m_keysetItem.length();
return len;
}
/* Decode a KMM modify key. */
bool KMMModifyKey::decode(const uint8_t* data)
{
assert(data != nullptr);
KMMFrame::decodeHeader(data, false);
m_decryptInfoFmt = data[10U]; // Decryption Instruction Format
m_algId = data[11U]; // Algorithm ID
m_kId = data[12U]; // Key ID
uint16_t offset = 0U;
if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) {
::memset(m_mi, 0x00U, MI_LENGTH_BYTES);
::memcpy(m_mi, data + 13U, MI_LENGTH_BYTES);
offset += 9U;
}
m_keysetItem.keysetId(data[13U + offset]);
m_keysetItem.algId(data[14U + offset]);
m_keysetItem.keyLength(data[15U + offset]);
uint8_t keyCount = data[16U + offset];
for (uint8_t i = 0U; i < keyCount; i++) {
KeyItem* key = new KeyItem();
UInt8Array __keyPayload = std::make_unique<uint8_t[]>(m_keysetItem.keyLength());
uint8_t* keyPayload = __keyPayload.get();
uint8_t keyFormat = data[17U + offset];
uint8_t keyNameLen = keyFormat & 0x1FU;
key->keyFormat(keyFormat & 0xE0U);
key->sln(data[18U + offset]);
key->kId(data[19U + offset]);
::memcpy(keyPayload, data + (20U + offset), m_keysetItem.keyLength());
key->setKey(keyPayload, m_keysetItem.keyLength());
m_keysetItem.push_back(key);
offset += 5U + keyNameLen + m_keysetItem.keyLength();
}
return true;
}
/* Encode a KMM modify key. */
void KMMModifyKey::encode(uint8_t* data)
{
assert(data != nullptr);
KMMFrame::encodeHeader(data, true);
if (!m_miSet && m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) {
m_decryptInfoFmt = KMM_DECRYPT_INSTRUCT_NONE;
}
data[10U] = m_decryptInfoFmt; // Decryption Instruction Format
data[11U] = m_algId; // Algorithm ID
data[12U] = m_kId; // Key ID
uint16_t offset = 0U;
if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) {
::memcpy(data + 13U, m_mi, MI_LENGTH_BYTES);
offset += 9U;
}
data[13U + offset] = m_keysetItem.keysetId();
data[14U + offset] = m_keysetItem.algId();
data[15U + offset] = m_keysetItem.keyLength();
uint8_t keyCount = m_keysetItem.keys().size();
data[16U + offset] = keyCount;
for (auto key : m_keysetItem.keys()) {
uint8_t keyNameLen = key->keyFormat() & 0x1FU;
data[17U + offset] = key->keyFormat();
data[18U + offset] = key->sln();
data[19U + offset] = key->kId();
UInt8Array __keyPayload = std::make_unique<uint8_t[]>(m_keysetItem.keyLength());
uint8_t* keyPayload = __keyPayload.get();
key->getKey(keyPayload);
::memcpy(data + (20U + offset), keyPayload, m_keysetItem.keyLength());
offset += 5U + keyNameLen + m_keysetItem.keyLength();
}
}
// ---------------------------------------------------------------------------
// Protected Class Members
// ---------------------------------------------------------------------------
/* Internal helper to copy the the class. */
void KMMModifyKey::copy(const KMMModifyKey& data)
{
m_decryptInfoFmt = data.m_decryptInfoFmt;
m_algId = data.m_algId;
m_kId = data.m_kId;
if (data.m_mi != nullptr) {
::memset(m_mi, 0x00U, MI_LENGTH_BYTES);
::memcpy(m_mi, data.m_mi, MI_LENGTH_BYTES);
}
m_keysetItem = data.m_keysetItem;
}

@ -0,0 +1,119 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file KMMModifyKey.h
* @ingroup p25_kmm
* @file KMMModifyKey.cpp
* @ingroup p25_kmm
*/
#if !defined(__P25_KMM__KMM_MODIFY_KEY_H__)
#define __P25_KMM__KMM_MODIFY_KEY_H__
#include "common/Defines.h"
#include "common/p25/kmm/KMMFrame.h"
#include "common/p25/kmm/KeysetItem.h"
#include "common/Utils.h"
#include <string>
#include <vector>
namespace p25
{
namespace kmm
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
/**
* @addtogroup p25_kmm
* @{
*/
const uint32_t KMM_MODIFY_KEY_LENGTH = KMM_FRAME_LENGTH + 4U;
/** @} */
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
class HOST_SW_API KMMModifyKey : public KMMFrame {
public:
/**
* @brief Initializes a new instance of the KMMModifyKey class.
*/
KMMModifyKey();
/**
* @brief Finalizes a instance of the KMMModifyKey class.
*/
~KMMModifyKey();
/**
* @brief Gets the byte length of this KMMFrame.
* @return uint32_t Length of KMMFrame.
*/
uint32_t length() const override;
/**
* @brief Decode a KMM modify key packet.
* @param[in] data Buffer containing KMM packet data to decode.
* @returns bool True, if decoded, otherwise false.
*/
bool decode(const uint8_t* data);
/**
* @brief Encode a KMM modify key packet.
* @param[out] data Buffer to encode KMM packet data to.
*/
void encode(uint8_t* data);
/** @name Encryption data */
/**
* @brief Sets the encryption message indicator.
* @param[in] mi Buffer containing the 9-byte Message Indicator.
*/
void setMI(const uint8_t* mi);
/**
* @brief Gets the encryption message indicator.
* @param[out] mi Buffer containing the 9-byte Message Indicator.
*/
void getMI(uint8_t* mi) const;
/** @} */
public:
/**
* @brief
*/
__PROPERTY(uint8_t, decryptInfoFmt, DecryptInfoFmt);
/**
* @brief Encryption algorithm ID.
*/
__PROPERTY(uint8_t, algId, AlgId);
/**
* @brief Encryption key ID.
*/
__PROPERTY(uint32_t, kId, KId);
/**
* @brief
*/
__PROPERTY(KeysetItem, keysetItem, KeysetItem);
__COPY(KMMModifyKey);
private:
// Encryption data
bool m_miSet;
uint8_t* m_mi;
};
} // namespace kmm
} // namespace p25
#endif // __P25_KMM__KMM_MODIFY_KEY_H__

@ -0,0 +1,228 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file KeysetItem.h
* @ingroup p25_kmm
*/
#if !defined(__P25_KMM__KEYSET_ITEM_H__)
#define __P25_KMM__KEYSET_ITEM_H__
#include "common/Defines.h"
#include "common/Utils.h"
#include <cassert>
#include <vector>
namespace p25
{
namespace kmm
{
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief Represents a key set item within a KMM frame packet.
* @ingroup p25_kmm
*/
class HOST_SW_API KeyItem {
public:
/**
* @brief Initializes a new instance of the KeyItem class.
*/
KeyItem() :
m_keyFormat(0x80U/*P25DEF::KEY_FORMAT_TEK*/),
m_sln(0U),
m_kId(0U),
m_keyLength(0U),
m_keyMaterial(nullptr)
{
/* stub */
}
/**
* @brief Finalizes a instance of the KeyItem class.
*/
~KeyItem()
{
if (m_keyMaterial != nullptr) {
delete[] m_keyMaterial;
m_keyMaterial = nullptr;
}
}
/**
* @brief Equals operator. Copies this KeyItem to another KeyItem.
* @param data Instance of KeyItem to copy.
*/
virtual KeyItem& operator= (const KeyItem& data)
{
if (this != &data) {
m_keyFormat = data.m_keyFormat;
m_sln = data.m_sln;
m_kId = data.m_kId;
if (data.m_keyLength > 0U) {
if (m_keyMaterial != nullptr) {
delete[] m_keyMaterial;
}
m_keyMaterial = new uint8_t[data.m_keyLength];
m_keyLength = data.m_keyLength;
::memcpy(m_keyMaterial, data.m_keyMaterial, data.m_keyLength);
}
}
return *this;
}
/**
* @brief Set the key material.
* @param key
* @param keyLength
*/
void setKey(const uint8_t* key, uint32_t keyLength)
{
assert(key != nullptr);
m_keyLength = keyLength;
::memcpy(m_keyMaterial, key, keyLength);
}
/**
* @brief Get the key material.
* @param key
*/
void getKey(uint8_t* key) const
{
assert(key != nullptr);
::memcpy(key, m_keyMaterial, m_keyLength);
}
public:
/**
* @brief
*/
__PROPERTY_PLAIN(uint8_t, keyFormat);
/**
* @brief
*/
__PROPERTY_PLAIN(uint16_t, sln);
/**
* @brief
*/
__PROPERTY_PLAIN(uint16_t, kId);
private:
uint32_t m_keyLength;
uint8_t* m_keyMaterial;
};
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief Represents a key set item within a KMM frame packet.
* @ingroup p25_kmm
*/
class HOST_SW_API KeysetItem {
public:
/**
* @brief Initializes a new instance of the KeysetItem class.
*/
KeysetItem() :
m_keysetId(0U),
m_algId(0U),
m_keyLength(0U),
m_keys()
{
/* stub */
}
/**
* @brief Finalizes a instance of the KeysetItem class.
*/
~KeysetItem()
{
for (auto key : m_keys) {
delete key;
}
}
/**
* @brief Equals operator. Copies this KeysetItem to another KeysetItem.
* @param data Instance of KeysetItem to copy.
*/
virtual KeysetItem& operator= (const KeysetItem& data)
{
if (this != &data) {
m_keysetId = data.m_keysetId;
m_algId = data.m_algId;
m_keyLength = data.m_keyLength;
for (auto key : m_keys) {
delete key;
}
m_keys.clear();
for (auto key : data.m_keys) {
KeyItem* copy = key;
m_keys.push_back(copy);
}
}
return *this;
}
/**
* @brief Gets the byte length of this keyset item.
* @return uint32_t Length of keyset item.
*/
uint32_t length() const
{
uint32_t len = 4U;
uint32_t keyItemLength = m_keys.size() * 5U;
uint32_t combinedKeyLength = m_keys.size() * m_keyLength;
len += keyItemLength + combinedKeyLength;
return len;
}
/**
* @brief Add a key to the key list.
* @param key
*/
void push_back(KeyItem* key)
{
m_keys.push_back(key);
}
public:
/**
* @brief
*/
__PROPERTY_PLAIN(uint8_t, keysetId);
/**
* @brief Encryption algorithm ID.
*/
__PROPERTY_PLAIN(uint8_t, algId);
/**
* @brief
*/
__PROPERTY_PLAIN(uint8_t, keyLength);
/**
* @brief List of keys.
*/
__PROPERTY_PLAIN(std::vector<KeyItem*>, keys);
};
} // namespace kmm
} // namespace p25
#endif // __P25_KMM__KEYSET_ITEM_H__

@ -114,8 +114,8 @@ bool AMBT::decode(const data::DataHeader& dataHeader, const data::DataBlock* blo
}
if (m_verbose) {
LogDebug(LOG_P25, "AMBT::decode(), mfId = $%02X, lco = $%02X, ambt8 = $%02X, ambt9 = $%02X", m_mfId, m_lco, dataHeader.getAMBTField8(), dataHeader.getAMBTField9());
Utils::dump(2U, "AMBT::decode(), pduUserData", pduUserData, P25_PDU_UNCONFIRMED_LENGTH_BYTES * dataHeader.getBlocksToFollow());
LogDebugEx(LOG_P25, "AMBT::decode()", "mfId = $%02X, lco = $%02X, ambt8 = $%02X, ambt9 = $%02X", m_mfId, m_lco, dataHeader.getAMBTField8(), dataHeader.getAMBTField9());
Utils::dump(2U, "[AMBT::decode()] pduUserData", pduUserData, P25_PDU_UNCONFIRMED_LENGTH_BYTES * dataHeader.getBlocksToFollow());
}
return true;

@ -18,6 +18,7 @@ file(GLOB dvmfne_SRC
"src/fne/network/influxdb/*.h"
"src/fne/network/*.h"
"src/fne/network/*.cpp"
"src/fne/xml/*.h"
"src/fne/*.h"
"src/fne/*.cpp"
)

@ -0,0 +1,573 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Converged FNE Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
#include "common/AESCrypto.h"
#include "common/Log.h"
#include "common/Timer.h"
#include "common/Utils.h"
#include "common/zlib/zlib.h"
#include "xml/rapidxml.h"
#include "CryptoContainer.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#if defined(ENABLE_TCP_SSL)
#include <openssl/bio.h>
#include <openssl/evp.h>
#endif // ENABLE_TCP_SSL
using namespace crypto;
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#define CHUNK 16384
// ---------------------------------------------------------------------------
// Global Functions
// ---------------------------------------------------------------------------
#if defined(ENABLE_TCP_SSL)
/**
* @brief Calculates the length of a decoded base64 string.
* @param b64input String containing the base64 encoded data.
* @returns int Length of buffer to contain base64 encoded data.
*/
int calcDecodeLength(const char* b64input)
{
int len = strlen(b64input);
int padding = 0;
// last two chars are =
if (b64input[len-1] == '=' && b64input[len-2] == '=')
padding = 2;
else if (b64input[len-1] == '=') // last char is =
padding = 1;
return (int)len * 0.75 - padding;
}
/**
* @brief Decodes a base64 encoded string.
* @param b64message String containing the base64 encoded data.
* @param buffer Buffer pointer to place encoded data.
* @returns int
*/
int base64Decode(char* b64message, uint8_t** buffer)
{
int decodeLen = calcDecodeLength(b64message), len = 0;
*buffer = (uint8_t*)malloc(decodeLen + 1);
FILE* stream = ::fmemopen(b64message, ::strlen(b64message), "r");
BIO* b64 = BIO_new(BIO_f_base64());
BIO* bio = BIO_new_fp(stream, BIO_NOCLOSE);
bio = BIO_push(b64, bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // do not use newlines to flush buffer
len = BIO_read(bio, *buffer, ::strlen(b64message));
// can test here if len == decodeLen - if not, then return an error
(*buffer)[len] = '\0';
BIO_free_all(bio);
::fclose(stream);
return decodeLen;
}
#endif // ENABLE_TCP_SSL
/**
* @brief
* @param buffer
* @param len
* @param target
* @return int
*/
int findFirstChar(const uint8_t* buffer, uint32_t len, char target)
{
for (uint32_t i = 0U; i < len; i++) {
if (buffer[i] == target) {
return (int)i;
}
}
return -1;
}
/**
* @brief
* @param buffer
* @param len
* @param target
* @return int
*/
int findLastChar(const uint8_t* buffer, uint32_t len, char target)
{
if (buffer == nullptr) {
return -1;
}
int lastIndex = -1;
for (uint32_t i = 0U; i < len; i++) {
if (buffer[i] == target) {
lastIndex = i;
}
}
return lastIndex;
}
// ---------------------------------------------------------------------------
// Static Class Members
// ---------------------------------------------------------------------------
std::mutex CryptoContainer::m_mutex;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/* Initializes a new instance of the CryptoContainer class. */
CryptoContainer::CryptoContainer(const std::string& filename, const std::string& password, uint32_t reloadTime, bool enabled) : Thread(),
m_file(filename),
m_password(password),
m_reloadTime(reloadTime),
#if !defined(ENABLE_TCP_SSL)
m_enabled(false),
#else
m_enabled(enabled),
#endif // !ENABLE_TCP_SSL
m_stop(false),
m_keys()
{
/* stub */
}
/* Finalizes a instance of the CryptoContainer class. */
CryptoContainer::~CryptoContainer() = default;
/* Thread entry point. This function is provided to run the thread for the lookup table. */
void CryptoContainer::entry()
{
if (m_reloadTime == 0U) {
return;
}
Timer timer(1U, 60U * m_reloadTime);
timer.start();
while (!m_stop) {
sleep(1000U);
timer.clock();
if (timer.hasExpired()) {
load();
timer.start();
}
}
}
/* Stops and unloads this lookup table. */
void CryptoContainer::stop(bool noDestroy)
{
if (!m_enabled)
return;
if (m_reloadTime == 0U) {
if (!noDestroy)
delete this;
return;
}
m_stop = true;
wait();
}
/* Reads the lookup table from the specified lookup table file. */
bool CryptoContainer::read()
{
if (!m_enabled)
return false;
bool ret = load();
if (m_reloadTime > 0U)
run();
setName("fne:crypto-lookup-tbl");
return ret;
}
/* Clears all entries from the lookup table. */
void CryptoContainer::clear()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_keys.clear();
}
/* Adds a new entry to the lookup table by the specified unique ID. */
void CryptoContainer::addEntry(KeyItem key)
{
if (key.isInvalid())
return;
KeyItem entry = key;
uint32_t id = entry.id();
uint32_t kId = entry.kId();
std::lock_guard<std::mutex> lock(m_mutex);
auto it = std::find_if(m_keys.begin(), m_keys.end(),
[&](KeyItem x)
{
return x.id() == id && x.kId() == kId;
});
if (it != m_keys.end()) {
m_keys[it - m_keys.begin()] = entry;
}
else {
m_keys.push_back(entry);
}
}
/* Erases an existing entry from the lookup table by the specified unique ID. */
void CryptoContainer::eraseEntry(uint32_t id)
{
std::lock_guard<std::mutex> lock(m_mutex);
auto it = std::find_if(m_keys.begin(), m_keys.end(), [&](KeyItem x) { return x.id() == id; });
if (it != m_keys.end()) {
m_keys.erase(it);
}
}
/* Finds a table entry in this lookup table. */
KeyItem CryptoContainer::find(uint32_t kId)
{
KeyItem entry;
std::lock_guard<std::mutex> lock(m_mutex);
auto it = std::find_if(m_keys.begin(), m_keys.end(),
[&](KeyItem x)
{
return x.kId() == kId;
});
if (it != m_keys.end()) {
entry = *it;
} else {
entry = KeyItem();
}
return entry;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/* Loads the table from the passed lookup table file. */
bool CryptoContainer::load()
{
#if !defined(ENABLE_TCP_SSL)
return false;
#else
if (!m_enabled) {
return false;
}
if (m_file.length() <= 0) {
return false;
}
if (m_password.length() <= 0) {
return false;
}
FILE* ekcFile = ::fopen(m_file.c_str(), "rb");
if (ekcFile == nullptr) {
LogError(LOG_HOST, "Cannot open the crypto container file - %s", m_file.c_str());
return false;
}
// inflate file
// compression structures
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
// set input data
strm.avail_in = 0U;
strm.next_in = Z_NULL;
// initialize decompression
int ret = inflateInit2(&strm, 16 + MAX_WBITS);
if (ret != Z_OK) {
LogError(LOG_HOST, "Error initializing ZLIB, ret = %d", ret);
::fclose(ekcFile);
return false;
}
// skip 4 bytes (C# adds a header on the GZIP stream for the decompressed length)
::fseek(ekcFile, 4, SEEK_SET);
// decompress data
std::vector<uint8_t> decompressedData;
uint8_t inBuffer[CHUNK];
uint8_t outBuffer[CHUNK];
do {
strm.avail_in = fread(inBuffer, 1, CHUNK, ekcFile);
if (::ferror(ekcFile)) {
inflateEnd(&strm);
::fclose(ekcFile);
return false;
}
if (strm.avail_in == 0)
break;
strm.next_in = inBuffer;
uint32_t have = 0U;
do {
strm.avail_out = CHUNK;
strm.next_out = outBuffer;
ret = inflate(&strm, Z_NO_FLUSH);
if (ret == Z_DATA_ERROR) {
// deflate stream invalid
LogError(LOG_HOST, "Error decompressing EKC: %s", (strm.msg == NULL) ? "compressed data error" : strm.msg);
inflateEnd(&strm);
::fclose(ekcFile);
return false;
}
if (ret == Z_STREAM_ERROR || ret < 0) {
LogError(LOG_HOST, "Error decompressing EKC, ret = %d", ret);
inflateEnd(&strm);
::fclose(ekcFile);
return false;
}
have = CHUNK - strm.avail_out;
decompressedData.insert(decompressedData.end(), outBuffer, outBuffer + have);
} while (strm.avail_out == 0);
} while (ret != Z_STREAM_END);
// cleanup
inflateEnd(&strm);
::fclose(ekcFile);
try {
// ensure zero termination
decompressedData.push_back(0U);
uint8_t* decompressed = decompressedData.data();
// parse outer container DOM
enum { PARSE_FLAGS = rapidxml::parse_full };
rapidxml::xml_document<> ekcOuterContainer;
ekcOuterContainer.parse<PARSE_FLAGS>(reinterpret_cast<char*>(decompressed));
rapidxml::xml_node<>* outerRoot = ekcOuterContainer.first_node("OuterContainer");
if (outerRoot != nullptr) {
// get EKC version
std::string version = "";
rapidxml::xml_attribute<>* versionAttr = outerRoot->first_attribute("version");
if (versionAttr != nullptr)
version = std::string(versionAttr->value());
// validate EKC version is set and is 1.0
if (version == "") {
::LogError(LOG_HOST, "Error opening EKC: incorrect version, expected 1.0 got none");
return false;
}
if (version != "1.0") {
::LogError(LOG_HOST, "Error opening EKC: incorrect version, expected 1.0 got %s", version.c_str());
return false;
}
// get key derivation node
rapidxml::xml_node<>* keyDerivation = outerRoot->first_node("KeyDerivation");
if (keyDerivation == nullptr) {
::LogError(LOG_HOST, "Error opening EKC: failed to process XML");
return false;
}
// retreive and parse salt
uint8_t* salt = nullptr;
rapidxml::xml_node<>* saltNode = keyDerivation->first_node("Salt");
if (saltNode == nullptr) {
::LogError(LOG_HOST, "Error opening EKC: failed to process XML");
return false;
}
int8_t saltBufLen = base64Decode(saltNode->value(), &salt);
// retrieve interation count
int32_t iterationCount = 0;
rapidxml::xml_node<>* iterNode = keyDerivation->first_node("IterationCount");
if (iterNode == nullptr) {
::LogError(LOG_HOST, "Error opening EKC: failed to process XML");
return false;
}
iterationCount = ::strtoul(iterNode->value(), NULL, 10);
// retrieve key length
int32_t keyLength = 0;
rapidxml::xml_node<>* keyLenNode = keyDerivation->first_node("KeyLength");
if (keyLenNode == nullptr) {
::LogError(LOG_HOST, "Error opening EKC: failed to process XML");
return false;
}
keyLength = ::strtoul(keyLenNode->value(), NULL, 10);
// generate crypto key to decrypt inner container
uint8_t key[EVP_MAX_KEY_LENGTH];
::memset(key, 0x00U, EVP_MAX_KEY_LENGTH);
uint8_t iv[EVP_MAX_IV_LENGTH];
::memset(iv, 0x00U, EVP_MAX_IV_LENGTH);
uint8_t keyIv[EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH];
::memset(keyIv, 0x00U, EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH);
if (PKCS5_PBKDF2_HMAC(m_password.c_str(), m_password.size(), salt, saltBufLen, iterationCount, EVP_sha512(), keyLength + EVP_MAX_IV_LENGTH, keyIv)) {
::memcpy(key, keyIv, keyLength);
::memcpy(iv, keyIv + keyLength, EVP_MAX_IV_LENGTH);
}
// get inner container encrypted data
// bryanb: annoying levels of XML encapsulation...
rapidxml::xml_node<>* encryptedDataNode = outerRoot->first_node("EncryptedData");
if (encryptedDataNode == nullptr) {
::LogError(LOG_HOST, "Error opening EKC: failed to process XML");
return false;
}
rapidxml::xml_node<>* cipherDataNode = encryptedDataNode->first_node("CipherData");
if (cipherDataNode == nullptr) {
::LogError(LOG_HOST, "Error opening EKC: failed to process XML");
return false;
}
rapidxml::xml_node<>* cipherValue = cipherDataNode->first_node("CipherValue");
if (cipherValue == nullptr) {
::LogError(LOG_HOST, "Error opening EKC: failed to process XML");
return false;
}
uint8_t* innerContainerCrypted = nullptr;
int innerContainerLen = base64Decode(cipherValue->value(), &innerContainerCrypted);
// decrypt inner container
AES aes = AES(AESKeyLength::AES_256);
uint8_t* innerContainer = aes.decryptCBC(innerContainerCrypted, innerContainerLen, key, iv);
/*
** bryanb: this is probably slightly error prone...
*/
int xmlFirstTagChar = findFirstChar(innerContainer, innerContainerLen, '<');
int xmlLastTagChar = findLastChar(innerContainer, innerContainerLen, '>');
// zero all bytes after the last > character
::memset(innerContainer + xmlLastTagChar + 1U, 0x00U, innerContainerLen - xmlLastTagChar);
rapidxml::xml_document<> ekcInnerContainer;
ekcInnerContainer.parse<PARSE_FLAGS>(reinterpret_cast<char*>(innerContainer + xmlFirstTagChar));
rapidxml::xml_node<>* innerRoot = ekcInnerContainer.first_node("InnerContainer");
if (innerRoot != nullptr) {
// clear table
clear();
std::lock_guard<std::mutex> lock(m_mutex);
// get keys node
rapidxml::xml_node<>* keys = innerRoot->first_node("Keys");
if (keys != nullptr) {
uint32_t i = 0U;
for (rapidxml::xml_node<>* keyNode = keys->first_node("KeyItem"); keyNode; keyNode = keyNode->next_sibling()) {
KeyItem key = KeyItem();
key.id(i);
// get name
rapidxml::xml_node<>* nameNode = keyNode->first_node("Name");
if (nameNode == nullptr) {
continue;
}
key.name(nameNode->value());
// get keyset ID
rapidxml::xml_node<>* keysetIdNode = keyNode->first_node("KeysetId");
if (keysetIdNode == nullptr) {
continue;
}
key.keysetId(::strtoul(keysetIdNode->value(), NULL, 10));
// get SLN
rapidxml::xml_node<>* slnNode = keyNode->first_node("Sln");
if (slnNode == nullptr) {
continue;
}
key.sln(::strtoul(slnNode->value(), NULL, 10));
// get algorithm ID
rapidxml::xml_node<>* algIdNode = keyNode->first_node("AlgorithmId");
if (algIdNode == nullptr) {
continue;
}
key.algId(::strtoul(algIdNode->value(), NULL, 10));
// get key ID
rapidxml::xml_node<>* kIdNode = keyNode->first_node("KeyId");
if (kIdNode == nullptr) {
continue;
}
key.kId(::strtoul(kIdNode->value(), NULL, 10));
// get key material
rapidxml::xml_node<>* keyMatNode = keyNode->first_node("Key");
if (keyMatNode == nullptr) {
continue;
}
key.keyMaterial(keyMatNode->value());
::LogInfoEx(LOG_HOST, "Key NAME: %s SLN: %u ALGID: $%02X, KID: $%04X", key.name().c_str(), key.sln(), key.algId(), key.kId());
m_keys.push_back(key);
i++;
}
}
}
}
} catch(const std::exception& e) {
::LogError(LOG_HOST, "Error opening EKC: %s", e.what());
return false;
}
if (m_keys.size() == 0U) {
::LogError(LOG_HOST, "No encryption keys defined!");
return false;
}
size_t size = m_keys.size();
if (size == 0U)
return false;
LogInfoEx(LOG_HOST, "Loaded %lu entries into crypto lookup table", size);
return true;
#endif // !ENABLE_TCP_SSL
}

@ -0,0 +1,246 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Converged FNE Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file CryptoContainer.h
* @ingroup fne
* @file CryptoContainer.cpp
* @ingroup fne
*/
#if !defined(__CRYPTO_CONTAINER_H__)
#define __CRYPTO_CONTAINER_H__
#include "common/Defines.h"
#include "common/Thread.h"
#include "common/Utils.h"
#include <string>
#include <mutex>
#include <unordered_map>
#include <vector>
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief Represents an key item.
* @ingroup fne
*/
class HOST_SW_API KeyItem {
public:
/**
* @brief Initializes a new instance of the KeyItem class.
*/
KeyItem() :
m_id(0U),
m_name(),
m_keysetId(0U),
m_sln(0U),
m_algId(0U),
m_kId(0U),
m_keyMaterial()
{
/* stub */
}
/**
* @brief Equals operator. Copies this KeyItem to another KeyItem.
* @param data Instance of KeyItem to copy.
*/
virtual KeyItem& operator= (const KeyItem& data)
{
if (this != &data) {
m_id = data.m_id;
m_name = data.m_name;
m_keysetId = data.m_keysetId;
m_sln = data.m_sln;
m_algId = data.m_algId;
m_kId = data.m_kId;
m_keyMaterial = data.m_keyMaterial;
}
return *this;
}
/**
* @brief Helper to quickly determine if a key item entry is valid.
* @returns bool True, if key item entry is valid, otherwise false.
*/
bool isInvalid() const
{
if (m_sln == 0U)
return true;
if (m_algId == 0U)
return true;
if (m_kId == 0U)
return true;
if (m_keyMaterial.size() == 0U)
return true;
return false;
}
/**
* @brief Gets the encryption key.
* @param[out] key Buffer containing the key.
*/
void getKey(uint8_t* key) const
{
assert(key != nullptr);
const char* rawKey = m_keyMaterial.c_str();
::memset(key, 0x00U, 32U);
for (uint8_t i = 0U; i < 32U; i++) {
char t[4] = {rawKey[0], rawKey[1], 0};
key[i] = (uint8_t)::strtoul(t, NULL, 16);
if (i + 2U > m_keyMaterial.size())
break;
rawKey += 2 * sizeof(char);
}
}
public:
/**
* @brief
*/
__PROPERTY_PLAIN(uint32_t, id);
/**
* @brief
*/
__PROPERTY_PLAIN(std::string, name);
/**
* @brief
*/
__PROPERTY_PLAIN(uint32_t, keysetId);
/**
* @brief
*/
__PROPERTY_PLAIN(uint32_t, sln);
/**
* @brief Encryption algorithm ID.
*/
__PROPERTY_PLAIN(uint8_t, algId);
/**
* @brief Encryption key ID.
*/
__PROPERTY_PLAIN(uint32_t, kId);
/**
* @brief Encryption key material.
*/
__PROPERTY_PLAIN(std::string, keyMaterial);
};
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief Implements a threading lookup table class that contains routing
* rules information.
* @ingroup lookups_tgid
*/
class HOST_SW_API CryptoContainer : public Thread {
public:
/**
* @brief Initializes a new instance of the CryptoContainer class.
* @param filename Full-path to the crypto container file.
* @param password Crypto container file access password.
* @param reloadTime Interval of time to reload the crypto container.
* @param enabled Flag indicating if crypto container is enabled.
*/
CryptoContainer(const std::string& filename, const std::string& password, uint32_t reloadTime, bool enabled);
/**
* @brief Finalizes a instance of the CryptoContainer class.
*/
~CryptoContainer() override;
/**
* @brief Thread entry point. This function is provided to run the thread
* for the lookup table.
*/
void entry() override;
/**
* @brief Stops and unloads this lookup table.
* @param noDestroy Flag indicating the lookup table should remain resident in memory after stopping.
*/
void stop(bool noDestroy = false);
/**
* @brief Reads the lookup table from the specified lookup table file.
* @returns bool True, if lookup table was read, otherwise false.
*/
bool read();
/**
* @brief Reads the lookup table from the specified lookup table file.
* @returns bool True, if lookup table was read, otherwise false.
*/
bool reload() { return load(); }
/**
* @brief Clears all entries from the lookup table.
*/
void clear();
/**
* @brief Adds a new entry to the lookup table.
* @param key Key Item.
*/
void addEntry(KeyItem key);
/**
* @brief Erases an existing entry from the lookup table by the specified unique ID.
* @param id Unique ID to erase.
*/
void eraseEntry(uint32_t id);
/**
* @brief Finds a table entry in this lookup table.
* @param kId Unique identifier for table entry.
* @returns KeyItem Table entry.
*/
virtual KeyItem find(uint32_t kId);
/**
* @brief Helper to return the flag indicating whether or not the crypto container is enabled.
* @returns bool
*/
bool isEnabled() const { return m_enabled; }
/**
* @brief Helper to set the reload time of this lookup table.
* @param reloadTime Lookup time in seconds.
*/
void setReloadTime(uint32_t reloadTime) { m_reloadTime = reloadTime; }
private:
std::string m_file;
std::string m_password;
uint32_t m_reloadTime;
bool m_enabled;
bool m_stop;
static std::mutex m_mutex;
/**
* @brief Loads the table from the passed lookup table file.
* @return True, if lookup table was loaded, otherwise false.
*/
bool load();
public:
/**
* @brief List of keys.
*/
__PROPERTY_PLAIN(std::vector<KeyItem>, keys);
};
#endif // __CRYPTO_CONTAINER_H__

@ -32,6 +32,10 @@ using namespace lookups;
// Global Variables
// ---------------------------------------------------------------------------
#ifndef SIGHUP
#define SIGHUP 1
#endif
int g_signal = 0;
std::string g_progExe = std::string(__EXE_NAME__);
std::string g_iniFile = std::string(DEFAULT_CONF_FILE);
@ -209,15 +213,15 @@ int main(int argc, char** argv)
ret = fne->run();
delete fne;
if (g_signal == 2)
if (g_signal == SIGINT)
::LogInfoEx(LOG_HOST, "[STOP] dvmfne:main SIGINT");
if (g_signal == 15)
if (g_signal == SIGTERM)
::LogInfoEx(LOG_HOST, "[STOP] dvmfne:main SIGTERM");
if (g_signal == 1)
if (g_signal == SIGHUP)
::LogInfoEx(LOG_HOST, "[RSTR] dvmfne:main SIGHUP");
} while (g_signal == 1);
} while (g_signal == SIGHUP);
::LogFinalise();
::ActivityLogFinalise();

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023,2024,2025 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -64,6 +64,7 @@ HostFNE::HostFNE(const std::string& confFile) :
m_ridLookup(nullptr),
m_tidLookup(nullptr),
m_peerListLookup(nullptr),
m_cryptoLookup(nullptr),
m_peerNetworks(),
m_pingTime(5U),
m_maxMissedPings(5U),
@ -281,18 +282,18 @@ int HostFNE::run()
}
if (m_tidLookup != nullptr) {
m_tidLookup->setReloadTime(0U); // no reload
m_tidLookup->stop();
delete m_tidLookup;
}
if (m_ridLookup != nullptr) {
m_ridLookup->setReloadTime(0U); // no reload
m_ridLookup->stop();
delete m_ridLookup;
}
if (m_peerListLookup != nullptr) {
m_peerListLookup->setReloadTime(0U); // no reload
m_peerListLookup->stop();
delete m_peerListLookup;
}
#if !defined(_WIN32)
if (m_tun != nullptr) {
@ -353,6 +354,15 @@ bool HostFNE::readParams()
std::string talkgroupConfig = talkgroupRules["file"].as<std::string>();
uint32_t talkgroupConfigReload = talkgroupRules["time"].as<uint32_t>(30U);
yaml::Node cryptoContainer = masterConf["crypto_container"];
bool cryptoContainerEnabled = cryptoContainer["enabled"].as<bool>(false);
#if !defined(ENABLE_TCP_SSL)
cryptoContainerEnabled = false;
#endif // ENABLE_TCP_SSL
std::string cryptoContainerEKC = cryptoContainer["file"].as<std::string>();
std::string cryptoContainerPassword = cryptoContainer["password"].as<std::string>();
uint32_t cryptoContainerReload = cryptoContainer["time"].as<uint32_t>(30U);
std::string peerListLookupFile = systemConf["peer_acl"]["file"].as<std::string>();
bool peerListLookupEnable = systemConf["peer_acl"]["enabled"].as<bool>(false);
std::string peerListModeStr = systemConf["peer_acl"]["mode"].as<std::string>("whitelist");
@ -385,6 +395,16 @@ bool HostFNE::readParams()
m_peerListLookup = new PeerListLookup(peerListLookupFile, peerListMode, peerListConfigReload, peerListLookupEnable);
m_peerListLookup->read();
// try to load peer whitelist/blacklist
LogInfo("Crypto Container Lookups");
LogInfo(" Enabled: %s", cryptoContainerEnabled ? "yes" : "no");
LogInfo(" File: %s", cryptoContainerEKC.length() > 0U ? cryptoContainerEKC.c_str() : "None");
if (cryptoContainerReload > 0U)
LogInfo(" Reload: %u mins", cryptoContainerReload);
m_cryptoLookup = new CryptoContainer(cryptoContainerEKC, cryptoContainerPassword, cryptoContainerReload, cryptoContainerEnabled);
m_cryptoLookup->read();
return true;
}
@ -608,7 +628,7 @@ void* HostFNE::threadMasterNetwork(void* arg)
HostFNE* fne = static_cast<HostFNE*>(th->obj);
if (fne == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -616,7 +636,7 @@ void* HostFNE::threadMasterNetwork(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -628,7 +648,7 @@ void* HostFNE::threadMasterNetwork(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -651,7 +671,7 @@ void* HostFNE::threadDiagNetwork(void* arg)
HostFNE* fne = static_cast<HostFNE*>(th->obj);
if (fne == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -664,7 +684,7 @@ void* HostFNE::threadDiagNetwork(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -676,7 +696,7 @@ void* HostFNE::threadDiagNetwork(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -842,7 +862,7 @@ void* HostFNE::threadVirtualNetworking(void* arg)
HostFNE* fne = static_cast<HostFNE*>(th->obj);
if (fne == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -855,7 +875,7 @@ void* HostFNE::threadVirtualNetworking(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -887,7 +907,7 @@ void* HostFNE::threadVirtualNetworking(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -906,7 +926,7 @@ void* HostFNE::threadVirtualNetworkingClock(void* arg)
HostFNE* fne = static_cast<HostFNE*>(th->obj);
if (fne == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -919,7 +939,7 @@ void* HostFNE::threadVirtualNetworkingClock(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -947,7 +967,7 @@ void* HostFNE::threadVirtualNetworkingClock(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -972,7 +992,7 @@ void HostFNE::processPeer(network::PeerNetwork* peerNetwork)
if (ret) {
uint32_t peerId = peerNetwork->getPeerId();
uint32_t slotNo = (data[15U] & 0x80U) == 0x80U ? 2U : 1U;
uint32_t streamId = peerNetwork->getDMRStreamId(slotNo);
uint32_t streamId = peerNetwork->getRxDMRStreamId(slotNo);
m_network->dmrTrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId, true);
}
@ -985,7 +1005,7 @@ void HostFNE::processPeer(network::PeerNetwork* peerNetwork)
UInt8Array data = peerNetwork->readP25(ret, length);
if (ret) {
uint32_t peerId = peerNetwork->getPeerId();
uint32_t streamId = peerNetwork->getP25StreamId();
uint32_t streamId = peerNetwork->getRxP25StreamId();
m_network->p25TrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId, true);
}
@ -998,7 +1018,7 @@ void HostFNE::processPeer(network::PeerNetwork* peerNetwork)
UInt8Array data = peerNetwork->readNXDN(ret, length);
if (ret) {
uint32_t peerId = peerNetwork->getPeerId();
uint32_t streamId = peerNetwork->getNXDNStreamId();
uint32_t streamId = peerNetwork->getRxNXDNStreamId();
m_network->nxdnTrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId, true);
}

@ -27,6 +27,7 @@
#include "network/DiagNetwork.h"
#include "network/PeerNetwork.h"
#include "network/RESTAPI.h"
#include "CryptoContainer.h"
#include <string>
#include <unordered_map>
@ -104,6 +105,8 @@ private:
lookups::TalkgroupRulesLookup* m_tidLookup;
lookups::PeerListLookup* m_peerListLookup;
CryptoContainer* m_cryptoLookup;
std::unordered_map<std::string, network::PeerNetwork*> m_peerNetworks;
uint32_t m_pingTime;

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "fne/Defines.h"
@ -162,7 +162,6 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
if (req->length > 0) {
uint32_t peerId = req->fneHeader.getPeerId();
uint32_t streamId = req->fneHeader.getStreamId();
std::stringstream peerName;
peerName << peerId << ":diag-rx-pckt";
@ -170,33 +169,6 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
::pthread_setname_np(req->thread, peerName.str().c_str());
#endif // _GNU_SOURCE
// update current peer packet sequence and stream ID
if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end()) && streamId != 0U) {
FNEPeerConnection* connection = network->m_peers[peerId];
uint16_t pktSeq = req->rtpHeader.getSequence();
if (connection != nullptr) {
if (pktSeq == RTP_END_OF_CALL_SEQ) {
connection->pktLastSeq(pktSeq);
connection->pktNextSeq(0U);
} else {
if ((connection->currStreamId() == streamId) && (pktSeq != connection->pktNextSeq()) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U))) {
LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; %u != %u", peerId, connection->identity().c_str(),
streamId, pktSeq, connection->pktNextSeq());
}
connection->currStreamId(streamId);
connection->pktLastSeq(pktSeq);
connection->pktNextSeq(pktSeq + 1);
if (connection->pktNextSeq() > (RTP_END_OF_CALL_SEQ - 1U)) {
connection->pktNextSeq(0U);
}
}
}
network->m_peers[peerId] = connection;
}
// process incoming message frame opcodes
switch (req->fneHeader.getFunction()) {
case NET_FUNC::TRANSFER:
@ -255,14 +227,10 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
for (auto peer : network->m_peers) {
if (peer.second != nullptr) {
if (peer.second->isSysView()) {
uint32_t peerStreamId = peer.second->currStreamId();
if (streamId == 0U) {
streamId = peerStreamId;
}
sockaddr_storage addr = peer.second->socketStorage();
uint32_t addrLen = peer.second->sockStorageLen();
network->m_frameQueue->write(req->buffer, req->length, streamId, pktPeerId, network->m_peerId,
network->m_frameQueue->write(req->buffer, req->length, network->createStreamId(), pktPeerId, network->m_peerId,
{ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_ACTIVITY }, RTP_END_OF_CALL_SEQ, addr, addrLen);
}
} else {
@ -277,14 +245,14 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
if (peer.second != nullptr) {
if (peer.second->isEnabled() && peer.second->isPeerLink()) {
peer.second->writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_ACTIVITY },
req->buffer, req->length, RTP_END_OF_CALL_SEQ, streamId, false, true, pktPeerId);
req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false, true, pktPeerId);
}
}
}
}
}
else {
network->writePeerNAK(pktPeerId, TAG_TRANSFER_ACT_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(pktPeerId, network->createStreamId(), TAG_TRANSFER_ACT_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
@ -307,7 +275,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
bool currState = g_disableTimeDisplay;
g_disableTimeDisplay = true;
::Log(9999U, nullptr, "%.9u (%8s) %s", peerId, connection->identity().c_str(), payload.c_str());
::Log(9999U, nullptr, nullptr, 0U, nullptr, "%.9u (%8s) %s", peerId, connection->identity().c_str(), payload.c_str());
g_disableTimeDisplay = currState;
// report diagnostic log to InfluxDB
@ -322,7 +290,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(peerId, TAG_TRANSFER_DIAG_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(peerId, network->createStreamId(), TAG_TRANSFER_DIAG_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
@ -341,10 +309,6 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
for (auto peer : network->m_peers) {
if (peer.second != nullptr) {
if (peer.second->isSysView()) {
uint32_t peerStreamId = peer.second->currStreamId();
if (streamId == 0U) {
streamId = peerStreamId;
}
sockaddr_storage addr = peer.second->socketStorage();
uint32_t addrLen = peer.second->sockStorageLen();
@ -352,7 +316,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
LogDebug(LOG_NET, "SysView, srcPeer = %u, dstPeer = %u, peer status message, len = %u",
pktPeerId, peer.first, req->length);
}
network->m_frameQueue->write(req->buffer, req->length, streamId, pktPeerId, network->m_peerId,
network->m_frameQueue->write(req->buffer, req->length, network->createStreamId(), pktPeerId, network->m_peerId,
{ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_STATUS }, RTP_END_OF_CALL_SEQ, addr, addrLen);
}
} else {
@ -366,7 +330,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
if (peer.second != nullptr) {
if (peer.second->isEnabled() && peer.second->isPeerLink()) {
peer.second->writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_STATUS },
req->buffer, req->length, RTP_END_OF_CALL_SEQ, streamId, false, true, pktPeerId);
req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false, true, pktPeerId);
}
}
}
@ -374,13 +338,13 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(pktPeerId, TAG_TRANSFER_STATUS, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(pktPeerId, network->createStreamId(), TAG_TRANSFER_STATUS, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
}
else {
network->writePeerNAK(peerId, TAG_TRANSFER, NET_CONN_NAK_ILLEGAL_PACKET);
network->writePeerNAK(peerId, network->createStreamId(), TAG_TRANSFER, NET_CONN_NAK_ILLEGAL_PACKET);
Utils::dump("unknown transfer opcode from the peer", req->buffer, req->length);
}
}
@ -420,7 +384,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(peerId, TAG_PEER_LINK, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(peerId, 0U, TAG_PEER_LINK, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "fne/Defines.h"
@ -129,7 +129,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions)
m_disallowAdjStsBcast = conf["disallowAdjStsBcast"].as<bool>(false);
m_disallowExtAdjStsBcast = conf["disallowExtAdjStsBcast"].as<bool>(true);
m_allowConvSiteAffOverride = conf["allowConvSiteAffOverride"].as<bool>(true);
m_enableInCallCtrl = conf["enableInCallCtrl"].as<bool>(true);
m_enableInCallCtrl = conf["enableInCallCtrl"].as<bool>(false);
m_rejectUnknownRID = conf["rejectUnknownRID"].as<bool>(false);
m_disallowCallTerm = conf["disallowCallTerm"].as<bool>(false);
m_softConnLimit = conf["connectionLimit"].as<uint32_t>(MAX_HARD_CONN_CAP);
@ -345,6 +345,24 @@ void FNENetwork::clock(uint32_t ms)
}
}
// send ACL updates forcibly to any Peer-Link peers
for (auto peer : m_peers) {
uint32_t id = peer.first;
FNEPeerConnection* connection = peer.second;
if (connection != nullptr) {
if (connection->connected() && connection->isPeerLink()) {
// does this peer need an ACL update?
uint64_t dt = connection->lastACLUpdate() + (m_updateLookupTime * 1000);
if (dt < now) {
LogInfoEx(LOG_NET, "PEER %u (%s) updating ACL list, dt = %u, now = %u", id, connection->identity().c_str(),
dt, now);
peerACLUpdate(id);
connection->lastACLUpdate(now);
}
}
}
}
m_maintainenceTimer.start();
}
@ -409,8 +427,9 @@ void FNENetwork::close()
uint8_t buffer[1U];
::memset(buffer, 0x00U, 1U);
uint32_t streamId = createStreamId();
for (auto peer : m_peers) {
writePeer(peer.first, { NET_FUNC::MST_CLOSING, NET_SUBFUNC::NOP }, buffer, 1U, (uint16_t)0U, 0U);
writePeer(peer.first, { NET_FUNC::MST_CLOSING, NET_SUBFUNC::NOP }, buffer, 1U, RTP_END_OF_CALL_SEQ, streamId, false);
}
}
@ -465,21 +484,18 @@ void* FNENetwork::threadedNetworkRx(void* arg)
// only reset packet sequences if we're a PROTOCOL or RPTC function
if ((req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) ||
(req->fneHeader.getFunction() == NET_FUNC::RPTC)) {
connection->pktLastSeq(pktSeq);
connection->pktNextSeq(0U);
connection->eraseStreamPktSeq(streamId); // attempt to erase packet sequence for the stream
}
} else {
if ((connection->currStreamId() == streamId) && (pktSeq != connection->pktNextSeq()) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U)) && pktSeq != 0U) {
if (connection->hasStreamPktSeq(streamId)) {
uint16_t currPkt = connection->getStreamPktSeq(streamId);
if ((pktSeq != currPkt) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U)) && pktSeq != 0U) {
LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; %u != %u", peerId, connection->identity().c_str(),
streamId, pktSeq, connection->pktNextSeq());
streamId, pktSeq, currPkt);
}
connection->currStreamId(streamId);
connection->pktLastSeq(pktSeq);
connection->pktNextSeq(pktSeq + 1);
if (connection->pktNextSeq() > (RTP_END_OF_CALL_SEQ - 1U)) {
connection->pktNextSeq(0U);
}
connection->incStreamPktSeq(streamId, pktSeq + 1U);
}
}
@ -487,7 +503,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
}
// if we don't have a stream ID and are receiving call data -- throw an error and discard
if (streamId == 0 && req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) {
if (streamId == 0U && req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) {
std::string peerIdentity = network->resolvePeerIdentity(peerId);
LogError(LOG_NET, "PEER %u (%s) malformed packet (no stream ID for a call?)", peerId, peerIdentity.c_str());
@ -516,7 +532,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
network->m_tagDMR->processFrame(req->buffer, req->length, peerId, req->rtpHeader.getSequence(), streamId);
}
} else {
network->writePeerNAK(peerId, TAG_DMR_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
network->writePeerNAK(peerId, streamId, TAG_DMR_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
}
}
}
@ -539,7 +555,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
network->m_tagP25->processFrame(req->buffer, req->length, peerId, req->rtpHeader.getSequence(), streamId);
}
} else {
network->writePeerNAK(peerId, TAG_P25_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
network->writePeerNAK(peerId, streamId, TAG_P25_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
}
}
}
@ -562,7 +578,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
network->m_tagNXDN->processFrame(req->buffer, req->length, peerId, req->rtpHeader.getSequence(), streamId);
}
} else {
network->writePeerNAK(peerId, TAG_NXDN_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
network->writePeerNAK(peerId, streamId, TAG_NXDN_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
}
}
}
@ -594,9 +610,8 @@ void* FNENetwork::threadedNetworkRx(void* arg)
FNEPeerConnection* connection = new FNEPeerConnection(peerId, req->address, req->addrLen);
connection->lastPing(now);
connection->currStreamId(streamId);
network->setupRepeaterLogin(peerId, connection);
network->setupRepeaterLogin(peerId, streamId, connection);
// check if the peer is in the peer ACL list
if (network->m_peerListLookup->getACL()) {
@ -631,10 +646,9 @@ void* FNENetwork::threadedNetworkRx(void* arg)
connection = new FNEPeerConnection(peerId, req->address, req->addrLen);
connection->lastPing(now);
connection->currStreamId(streamId);
network->erasePeerAffiliations(peerId);
network->setupRepeaterLogin(peerId, connection);
network->setupRepeaterLogin(peerId, streamId, connection);
// check if the peer is in the peer ACL list
if (network->m_peerListLookup->getACL()) {
@ -751,7 +765,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
if (validHash) {
connection->connectionState(NET_STAT_WAITING_CONFIG);
network->writePeerACK(peerId);
network->writePeerACK(peerId, streamId);
LogInfoEx(LOG_NET, "PEER %u RPTK ACK, completed the login exchange", peerId);
network->m_peers[peerId] = connection;
}
@ -828,7 +842,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
buffer[0U] = 0x80U;
}
network->writePeerACK(peerId, buffer, 1U);
network->writePeerACK(peerId, streamId, buffer, 1U);
LogInfoEx(LOG_NET, "PEER %u RPTC ACK, completed the configuration exchange", peerId);
json::object peerConfig = connection->config();
@ -949,11 +963,12 @@ void* FNENetwork::threadedNetworkRx(void* arg)
if (dt < now) {
LogInfoEx(LOG_NET, "PEER %u (%s) updating ACL list, dt = %u, now = %u", peerId, connection->identity().c_str(),
dt, now);
if (connection->pktLastSeq() == RTP_END_OF_CALL_SEQ) {
dt = connection->lastACLUpdate() + ((network->m_updateLookupTime * 1000) * 2);
if (connection->streamCount() <= 1 || (dt < now)) {
network->peerACLUpdate(peerId);
}
connection->lastACLUpdate(now);
}
}
uint8_t payload[8U];
::memset(payload, 0x00U, 8U);
@ -969,7 +984,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
payload[7U] = (uint8_t)((now >> 0) & 0xFFU);
network->m_peers[peerId] = connection;
network->writePeerCommand(peerId, { NET_FUNC::PONG, NET_SUBFUNC::NOP }, payload, 8U);
network->writePeerCommand(peerId, { NET_FUNC::PONG, NET_SUBFUNC::NOP }, payload, 8U, streamId, false);
if (network->m_reportPeerPing) {
LogInfoEx(LOG_NET, "PEER %u (%s) ping, pingsReceived = %u, lastPing = %u, now = %u", peerId, connection->identity().c_str(),
@ -977,7 +992,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(peerId, TAG_REPEATER_PING);
network->writePeerNAK(peerId, streamId, TAG_REPEATER_PING);
}
}
}
@ -1007,7 +1022,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
if (network->m_tagDMR != nullptr) {
network->m_tagDMR->processGrantReq(srcId, dstId, slot, unitToUnit, peerId, req->rtpHeader.getSequence(), streamId);
} else {
network->writePeerNAK(peerId, TAG_DMR_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
network->writePeerNAK(peerId, streamId, TAG_DMR_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
}
}
break;
@ -1016,7 +1031,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
if (network->m_tagP25 != nullptr) {
network->m_tagP25->processGrantReq(srcId, dstId, unitToUnit, peerId, req->rtpHeader.getSequence(), streamId);
} else {
network->writePeerNAK(peerId, TAG_P25_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
network->writePeerNAK(peerId, streamId, TAG_P25_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
}
}
break;
@ -1025,18 +1040,18 @@ void* FNENetwork::threadedNetworkRx(void* arg)
if (network->m_tagNXDN != nullptr) {
network->m_tagNXDN->processGrantReq(srcId, dstId, unitToUnit, peerId, req->rtpHeader.getSequence(), streamId);
} else {
network->writePeerNAK(peerId, TAG_NXDN_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
network->writePeerNAK(peerId, streamId, TAG_NXDN_DATA, NET_CONN_NAK_MODE_NOT_ENABLED);
}
}
break;
default:
network->writePeerNAK(peerId, TAG_REPEATER_GRANT, NET_CONN_NAK_ILLEGAL_PACKET);
network->writePeerNAK(peerId, streamId, TAG_REPEATER_GRANT, NET_CONN_NAK_ILLEGAL_PACKET);
Utils::dump("unknown state for grant request from the peer", req->buffer, req->length);
break;
}
}
else {
network->writePeerNAK(peerId, TAG_REPEATER_GRANT, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(peerId, streamId, TAG_REPEATER_GRANT, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
@ -1086,7 +1101,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(peerId, TAG_TRANSFER_ACT_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(peerId, streamId, TAG_TRANSFER_ACT_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
@ -1124,7 +1139,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(peerId, TAG_TRANSFER_DIAG_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(peerId, streamId, TAG_TRANSFER_DIAG_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
@ -1134,7 +1149,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
// main traffic port status transfers aren't supported for performance reasons
}
else {
network->writePeerNAK(peerId, TAG_TRANSFER, NET_CONN_NAK_ILLEGAL_PACKET);
network->writePeerNAK(peerId, streamId, TAG_TRANSFER, NET_CONN_NAK_ILLEGAL_PACKET);
Utils::dump("unknown transfer opcode from the peer", req->buffer, req->length);
}
}
@ -1150,7 +1165,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId];
if (aff == nullptr) {
LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str());
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID);
}
// validate peer (simple validation really)
@ -1173,7 +1188,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
@ -1186,7 +1201,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId];
if (aff == nullptr) {
LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str());
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID);
}
// validate peer (simple validation really)
@ -1207,7 +1222,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
@ -1220,7 +1235,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId];
if (aff == nullptr) {
LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str());
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID);
}
// validate peer (simple validation really)
@ -1241,7 +1256,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
@ -1254,7 +1269,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId];
if (aff == nullptr) {
LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str());
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID);
}
// validate peer (simple validation really)
@ -1275,7 +1290,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
@ -1291,7 +1306,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId];
if (aff == nullptr) {
LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str());
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID);
}
if (aff != nullptr) {
@ -1323,7 +1338,7 @@ void* FNENetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
@ -1368,13 +1383,13 @@ void* FNENetwork::threadedNetworkRx(void* arg)
}
}
else {
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
}
else {
network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_ILLEGAL_PACKET);
network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_ILLEGAL_PACKET);
Utils::dump("unknown announcement opcode from the peer", req->buffer, req->length);
}
}
@ -1408,6 +1423,19 @@ bool FNENetwork::checkU2UDroppedPeer(uint32_t peerId)
return false;
}
/* Erases a stream ID from the given peer ID connection. */
void FNENetwork::eraseStreamPktSeq(uint32_t peerId, uint32_t streamId)
{
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::lock_guard<std::mutex> lock(m_peerMutex);
connection->eraseStreamPktSeq(streamId);
}
}
}
/* Helper to create a peer on the peers affiliations list. */
void FNENetwork::createPeerAffiliations(uint32_t peerId, std::string peerName)
@ -1557,7 +1585,7 @@ std::string FNENetwork::resolvePeerIdentity(uint32_t peerId)
/* Helper to complete setting up a repeater login request. */
void FNENetwork::setupRepeaterLogin(uint32_t peerId, FNEPeerConnection* connection)
void FNENetwork::setupRepeaterLogin(uint32_t peerId, uint32_t streamId, FNEPeerConnection* connection)
{
std::uniform_int_distribution<uint32_t> dist(DVM_RAND_MIN, DVM_RAND_MAX);
connection->salt(dist(m_random));
@ -1572,7 +1600,7 @@ void FNENetwork::setupRepeaterLogin(uint32_t peerId, FNEPeerConnection* connecti
::memset(salt, 0x00U, 4U);
__SET_UINT32(connection->salt(), salt, 0U);
writePeerACK(peerId, salt, 4U);
writePeerACK(peerId, streamId, salt, 4U);
LogInfoEx(LOG_NET, "PEER %u RPTL ACK, challenge response sent for login", peerId);
}
@ -1619,26 +1647,24 @@ void* FNENetwork::threadedACLUpdate(void* arg)
FNEPeerConnection* connection = network->m_peers[req->peerId];
if (connection != nullptr) {
uint32_t aclStreamId = network->createStreamId();
// if the connection is an external peer, and peer is participating in peer link,
// send the peer proper configuration data
if (connection->isExternalPeer() && connection->isPeerLink()) {
LogInfoEx(LOG_NET, "PEER %u (%s) sending Peer-Link ACL list updates", req->peerId, peerIdentity.c_str());
network->writeWhitelistRIDs(req->peerId, true);
network->writeTGIDs(req->peerId, true);
connection->pktLastSeq(RTP_END_OF_CALL_SEQ - 1U);
network->writePeerList(req->peerId);
network->writeWhitelistRIDs(req->peerId, aclStreamId, true);
network->writeTGIDs(req->peerId, aclStreamId, true);
network->writePeerList(req->peerId, aclStreamId);
}
else {
LogInfoEx(LOG_NET, "PEER %u (%s) sending ACL list updates", req->peerId, peerIdentity.c_str());
network->writeWhitelistRIDs(req->peerId, false);
network->writeBlacklistRIDs(req->peerId);
network->writeTGIDs(req->peerId, false);
connection->pktLastSeq(RTP_END_OF_CALL_SEQ - 1U);
network->writeDeactiveTGIDs(req->peerId);
network->writeWhitelistRIDs(req->peerId, aclStreamId, false);
network->writeBlacklistRIDs(req->peerId, aclStreamId);
network->writeTGIDs(req->peerId, aclStreamId, false);
network->writeDeactiveTGIDs(req->peerId, aclStreamId);
}
}
@ -1650,7 +1676,7 @@ void* FNENetwork::threadedACLUpdate(void* arg)
/* Helper to send the list of whitelisted RIDs to the specified peer. */
void FNENetwork::writeWhitelistRIDs(uint32_t peerId, bool isExternalPeer)
void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool isExternalPeer)
{
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
@ -1755,7 +1781,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, bool isExternalPeer)
offs += PEER_LINK_BLOCK_SIZE;
writePeer(peerId, { NET_FUNC::PEER_LINK, NET_SUBFUNC::PL_RID_LIST },
payload, bufSize, 0U, false, true, true);
payload, bufSize, 0U, streamId, false, true, true);
}
connection->lastPing(now);
@ -1825,7 +1851,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, bool isExternalPeer)
}
writePeerCommand(peerId, { NET_FUNC::MASTER, NET_SUBFUNC::MASTER_SUBFUNC_WL_RID },
payload, bufSize, true);
payload, bufSize, streamId, true);
}
connection->lastPing(now);
@ -1834,7 +1860,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, bool isExternalPeer)
/* Helper to send the list of whitelisted RIDs to the specified peer. */
void FNENetwork::writeBlacklistRIDs(uint32_t peerId)
void FNENetwork::writeBlacklistRIDs(uint32_t peerId, uint32_t streamId)
{
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
@ -1899,7 +1925,7 @@ void FNENetwork::writeBlacklistRIDs(uint32_t peerId)
}
writePeerCommand(peerId, { NET_FUNC::MASTER, NET_SUBFUNC::MASTER_SUBFUNC_BL_RID },
payload, bufSize, true);
payload, bufSize, streamId, true);
}
connection->lastPing(now);
@ -1908,7 +1934,7 @@ void FNENetwork::writeBlacklistRIDs(uint32_t peerId)
/* Helper to send the list of active TGIDs to the specified peer. */
void FNENetwork::writeTGIDs(uint32_t peerId, bool isExternalPeer)
void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool isExternalPeer)
{
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
@ -2017,7 +2043,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId, bool isExternalPeer)
offs += PEER_LINK_BLOCK_SIZE;
writePeer(peerId, { NET_FUNC::PEER_LINK, NET_SUBFUNC::PL_TALKGROUP_LIST },
payload, bufSize, 0U, false, true, true);
payload, bufSize, 0U, streamId, false, true, true);
}
connection->lastPing(now);
@ -2098,12 +2124,12 @@ void FNENetwork::writeTGIDs(uint32_t peerId, bool isExternalPeer)
}
writePeerCommand(peerId, { NET_FUNC::MASTER, NET_SUBFUNC::MASTER_SUBFUNC_ACTIVE_TGS },
payload, 4U + (tgidList.size() * 5U), true);
payload, 4U + (tgidList.size() * 5U), streamId, true);
}
/* Helper to send the list of deactivated TGIDs to the specified peer. */
void FNENetwork::writeDeactiveTGIDs(uint32_t peerId)
void FNENetwork::writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId)
{
if (!m_tidLookup->sendTalkgroups()) {
return;
@ -2159,17 +2185,13 @@ void FNENetwork::writeDeactiveTGIDs(uint32_t peerId)
}
writePeerCommand(peerId, { NET_FUNC::MASTER, NET_SUBFUNC::MASTER_SUBFUNC_DEACTIVE_TGS },
payload, 4U + (tgidList.size() * 5U), true);
payload, 4U + (tgidList.size() * 5U), streamId, true);
}
/* Helper to send the list of peers to the specified peer. */
void FNENetwork::writePeerList(uint32_t peerId)
void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId)
{
if (!m_tidLookup->sendTalkgroups()) {
return;
}
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
// sending PEER_LINK style RID list to external peers
@ -2272,7 +2294,7 @@ void FNENetwork::writePeerList(uint32_t peerId)
offs += PEER_LINK_BLOCK_SIZE;
writePeer(peerId, { NET_FUNC::PEER_LINK, NET_SUBFUNC::PL_PEER_LIST },
payload, bufSize, 0U, false, true, true);
payload, bufSize, 0U, streamId, false, true, true);
}
connection->lastPing(now);
@ -2283,7 +2305,7 @@ void FNENetwork::writePeerList(uint32_t peerId)
/* Helper to send a In-Call Control command to the specified peer. */
bool FNENetwork::writePeerICC(uint32_t peerId, NET_SUBFUNC::ENUM subFunc, NET_ICC::ENUM command, uint32_t dstId, uint8_t slotNo)
bool FNENetwork::writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::ENUM subFunc, NET_ICC::ENUM command, uint32_t dstId, uint8_t slotNo)
{
assert(peerId > 0);
if (!m_enableInCallCtrl)
@ -2299,25 +2321,29 @@ bool FNENetwork::writePeerICC(uint32_t peerId, NET_SUBFUNC::ENUM subFunc, NET_IC
__SET_UINT16(dstId, buffer, 11U); // Destination ID
buffer[14U] = slotNo; // DMR Slot No
return writePeer(peerId, { NET_FUNC::INCALL_CTRL, subFunc }, buffer, 15U, RTP_END_OF_CALL_SEQ, false, true);
return writePeer(peerId, { NET_FUNC::INCALL_CTRL, subFunc }, buffer, 15U, RTP_END_OF_CALL_SEQ, streamId, false);
}
/* Helper to send a data message to the specified peer. */
/* Helper to send a data message to the specified peer with a explicit packet sequence. */
bool FNENetwork::writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data,
uint32_t length, uint16_t pktSeq, uint32_t streamId, bool queueOnly, bool directWrite) const
uint32_t length, uint16_t pktSeq, uint32_t streamId, bool queueOnly, bool incPktSeq, bool directWrite) const
{
if (streamId == 0U) {
LogError(LOG_NET, "BUGBUG: PEER %u, trying to send data with a streamId of 0?", peerId);
}
auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; });
if (it != m_peers.end()) {
FNEPeerConnection* connection = m_peers.at(peerId);
if (connection != nullptr) {
uint32_t peerStreamId = connection->currStreamId();
if (streamId == 0U) {
streamId = peerStreamId;
}
sockaddr_storage addr = connection->socketStorage();
uint32_t addrLen = connection->sockStorageLen();
if (incPktSeq) {
pktSeq = connection->incStreamPktSeq(streamId, pktSeq);
}
if (directWrite)
return m_frameQueue->write(data, length, streamId, peerId, m_peerId, opcode, pktSeq, addr, addrLen);
else {
@ -2332,31 +2358,10 @@ bool FNENetwork::writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const
return false;
}
/* Helper to send a data message to the specified peer. */
bool FNENetwork::writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data,
uint32_t length, uint32_t streamId, bool queueOnly, bool incPktSeq, bool directWrite) const
{
auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; });
if (it != m_peers.end()) {
FNEPeerConnection* connection = m_peers.at(peerId);
if (connection != nullptr) {
if (incPktSeq) {
connection->pktLastSeq(connection->pktLastSeq() + 1);
}
uint16_t pktSeq = connection->pktLastSeq();
return writePeer(peerId, opcode, data, length, pktSeq, streamId, queueOnly, directWrite);
}
}
return false;
}
/* Helper to send a command message to the specified peer. */
bool FNENetwork::writePeerCommand(uint32_t peerId, FrameQueue::OpcodePair opcode,
const uint8_t* data, uint32_t length, bool incPktSeq) const
const uint8_t* data, uint32_t length, uint32_t streamId, bool incPktSeq) const
{
assert(peerId > 0);
@ -2368,12 +2373,12 @@ bool FNENetwork::writePeerCommand(uint32_t peerId, FrameQueue::OpcodePair opcode
}
uint32_t len = length + 6U;
return writePeer(peerId, opcode, buffer, len, 0U, false, incPktSeq, true);
return writePeer(peerId, opcode, buffer, len, RTP_END_OF_CALL_SEQ, streamId, false, incPktSeq, true);
}
/* Helper to send a ACK response to the specified peer. */
bool FNENetwork::writePeerACK(uint32_t peerId, const uint8_t* data, uint32_t length)
bool FNENetwork::writePeerACK(uint32_t peerId, uint32_t streamId, const uint8_t* data, uint32_t length)
{
uint8_t buffer[DATA_PACKET_LENGTH];
::memset(buffer, 0x00U, DATA_PACKET_LENGTH);
@ -2384,7 +2389,8 @@ bool FNENetwork::writePeerACK(uint32_t peerId, const uint8_t* data, uint32_t len
::memcpy(buffer + 6U, data, length);
}
return writePeer(peerId, { NET_FUNC::ACK, NET_SUBFUNC::NOP }, buffer, length + 10U, RTP_END_OF_CALL_SEQ, false, true);
return writePeer(peerId, { NET_FUNC::ACK, NET_SUBFUNC::NOP }, buffer, length + 10U, RTP_END_OF_CALL_SEQ, streamId,
false);
}
/* Helper to log a warning specifying which NAK reason is being sent a peer. */
@ -2426,7 +2432,7 @@ void FNENetwork::logPeerNAKReason(uint32_t peerId, const char* tag, NET_CONN_NAK
/* Helper to send a NAK response to the specified peer. */
bool FNENetwork::writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REASON reason)
bool FNENetwork::writePeerNAK(uint32_t peerId, uint32_t streamId, const char* tag, NET_CONN_NAK_REASON reason)
{
assert(peerId > 0);
assert(tag != nullptr);
@ -2438,7 +2444,7 @@ bool FNENetwork::writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REA
__SET_UINT16B((uint16_t)reason, buffer, 10U); // Reason
logPeerNAKReason(peerId, tag, reason);
return writePeer(peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, buffer, 12U, RTP_END_OF_CALL_SEQ, false, true);
return writePeer(peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, buffer, 10U, RTP_END_OF_CALL_SEQ, streamId, false);
}
/* Helper to send a NAK response to the specified peer. */

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL
*
*/
/**
@ -104,7 +104,6 @@ namespace network
FNEPeerConnection() :
m_id(0U),
m_ccPeerId(0U),
m_currStreamId(0U),
m_socketStorage(),
m_sockStorageLen(0U),
m_address(),
@ -120,8 +119,8 @@ namespace network
m_isSysView(false),
m_isPeerLink(false),
m_config(),
m_pktLastSeq(RTP_END_OF_CALL_SEQ),
m_pktNextSeq(1U)
m_streamSeqMutex(),
m_streamSeqNos()
{
/* stub */
}
@ -134,7 +133,6 @@ namespace network
FNEPeerConnection(uint32_t id, sockaddr_storage& socketStorage, uint32_t sockStorageLen) :
m_id(id),
m_ccPeerId(0U),
m_currStreamId(0U),
m_socketStorage(socketStorage),
m_sockStorageLen(sockStorageLen),
m_address(udp::Socket::address(socketStorage)),
@ -150,8 +148,8 @@ namespace network
m_isSysView(false),
m_isPeerLink(false),
m_config(),
m_pktLastSeq(RTP_END_OF_CALL_SEQ),
m_pktNextSeq(1U)
m_streamSeqMutex(),
m_streamSeqNos()
{
assert(id > 0U);
assert(sockStorageLen > 0U);
@ -159,6 +157,118 @@ namespace network
assert(m_port > 0U);
}
/**
* @brief Helper to return the current count of mapped RTP streams.
* @returns size_t
*/
size_t streamCount()
{
return m_streamSeqNos.size();
}
/**
* @brief Helper to determine if the stream ID has a stored RTP sequence.
* @param streamId Stream ID.
* @returns bool
*/
bool hasStreamPktSeq(uint64_t streamId)
{
bool ret = false;
m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60));
// determine if the stream has a current sequence no and return
{
auto it = m_streamSeqNos.find(streamId);
if (it == m_streamSeqNos.end()) {
ret = false;
}
else {
ret = true;
}
}
m_streamSeqMutex.unlock();
return ret;
}
/**
* @brief Helper to get the stored RTP sequence for the given stream ID.
* @param streamId Stream ID.
* @returns uint16_t
*/
uint16_t getStreamPktSeq(uint64_t streamId)
{
m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60));
// find the current sequence no and return
uint32_t pktSeq = 0U;
{
auto it = m_streamSeqNos.find(streamId);
if (it == m_streamSeqNos.end()) {
pktSeq = RTP_END_OF_CALL_SEQ;
} else {
pktSeq = m_streamSeqNos[streamId];
}
}
m_streamSeqMutex.unlock();
return pktSeq;
}
/**
* @brief Helper to increment the stored RTP sequence for the given stream ID.
* @param streamId Stream ID.
* @param initialSeq Initial sequence number to set.
* @returns uint16_t
*/
uint16_t incStreamPktSeq(uint64_t streamId, uint16_t initialSeq)
{
m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60));
// find the current sequence no, increment and return
uint32_t pktSeq = 0U;
{
auto it = m_streamSeqNos.find(streamId);
if (it == m_streamSeqNos.end()) {
m_streamSeqNos.insert({streamId, initialSeq});
} else {
pktSeq = m_streamSeqNos[streamId];
++pktSeq;
if (pktSeq > RTP_END_OF_CALL_SEQ) {
pktSeq = 0U;
}
m_streamSeqNos[streamId] = pktSeq;
}
}
m_streamSeqMutex.unlock();
return pktSeq;
}
/**
* @brief Helper to erase the stored RTP sequence for the given stream ID.
* @param streamId Stream ID.
* @returns uint16_t
*/
void eraseStreamPktSeq(uint64_t streamId)
{
m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60));
// find the sequence no and erase
{
auto entry = m_streamSeqNos.find(streamId);
if (entry != m_streamSeqNos.end()) {
m_streamSeqNos.erase(streamId);
}
}
m_streamSeqMutex.unlock();
}
public:
/**
* @brief Peer ID.
@ -174,11 +284,6 @@ namespace network
*/
__PROPERTY_PLAIN(uint32_t, ccPeerId);
/**
* @brief Current Stream ID.
*/
__PROPERTY_PLAIN(uint32_t, currStreamId);
/**
* @brief Unix socket storage containing the connected address.
*/
@ -247,14 +352,9 @@ namespace network
*/
__PROPERTY_PLAIN(json::object, config);
/**
* @brief Last received RTP sequence.
*/
__PROPERTY_PLAIN(uint16_t, pktLastSeq);
/**
* @brief Calculated next RTP sequence.
*/
__PROPERTY_PLAIN(uint16_t, pktNextSeq);
private:
std::timed_mutex m_streamSeqMutex;
std::unordered_map<uint64_t, uint16_t> m_streamSeqNos;
};
// ---------------------------------------------------------------------------
@ -500,6 +600,13 @@ namespace network
*/
bool checkU2UDroppedPeer(uint32_t peerId);
/**
* @brief Erases a stream ID from the given peer ID connection.
* @param peerId Peer ID.
* @param streamId Stream ID.
*/
void eraseStreamPktSeq(uint32_t peerId, uint32_t streamId);
/**
* @brief Helper to create a peer on the peers affiliations list.
* @param peerId Peer ID.
@ -529,9 +636,10 @@ namespace network
/**
* @brief Helper to complete setting up a repeater login request.
* @param peerId Peer ID.
* @param streamId Stream ID for the login sequence.
* @param connection Instance of the FNEPeerConnection class.
*/
void setupRepeaterLogin(uint32_t peerId, FNEPeerConnection* connection);
void setupRepeaterLogin(uint32_t peerId, uint32_t streamId, FNEPeerConnection* connection);
/**
* @brief Helper to send the ACL lists to the specified peer in a separate thread.
@ -548,44 +656,50 @@ namespace network
/**
* @brief Helper to send the list of whitelisted RIDs to the specified peer.
* @param peerId Peer ID.
* @param streamId Stream ID for this message.
* @param sendISSI Flag indicating the RID transfer is to an external peer via ISSI.
*/
void writeWhitelistRIDs(uint32_t peerId, bool sendISSI);
void writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sendISSI);
/**
* @brief Helper to send the list of blacklisted RIDs to the specified peer.
* @param peerId Peer ID.
* @param streamId Stream ID for this message.
*/
void writeBlacklistRIDs(uint32_t peerId);
void writeBlacklistRIDs(uint32_t peerId, uint32_t streamId);
/**
* @brief Helper to send the list of active TGIDs to the specified peer.
* @param peerId Peer ID.
* @param streamId Stream ID for this message.
* @param sendISSI Flag indicating the TGID transfer is to an external peer via ISSI.
*/
void writeTGIDs(uint32_t peerId, bool sendISSI);
void writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendISSI);
/**
* @brief Helper to send the list of deactivated TGIDs to the specified peer.
* @param peerId Peer ID.
* @param streamId Stream ID for this message.
*/
void writeDeactiveTGIDs(uint32_t peerId);
void writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId);
/**
* @brief Helper to send the list of peers to the specified peer.
* @param peerId Peer ID.
* @param streamId Stream ID for this message.
*/
void writePeerList(uint32_t peerId);
void writePeerList(uint32_t peerId, uint32_t streamId);
/**
* @brief Helper to send a In-Call Control command to the specified peer.
* @param peerId Peer ID.
* @param streamId Stream ID for this message.
* @param subFunc Network Sub-Function.
* @param command In-Call Control Command.
* @param dstId Destination ID.
* @param slotNo DMR slot.
*/
bool writePeerICC(uint32_t peerId, NET_SUBFUNC::ENUM subFunc = NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR,
bool writePeerICC(uint32_t peerId, uint32_t streamId, NET_SUBFUNC::ENUM subFunc = NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR,
NET_ICC::ENUM command = NET_ICC::NOP, uint32_t dstId = 0U, uint8_t slotNo = 0U);
/**
* @brief Helper to send a data message to the specified peer.
* @brief Helper to send a data message to the specified peer with a explicit packet sequence.
* @param peerId Peer ID.
* @param opcode FNE network opcode pair.
* @param[in] data Buffer containing message to send to peer.
@ -593,23 +707,11 @@ namespace network
* @param pktSeq RTP packet sequence for this message.
* @param streamId Stream ID for this message.
* @param queueOnly Flag indicating this message should be queued for transmission.
* @param directWrite Flag indicating this message should be immediately directly written.
*/
bool writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length,
uint16_t pktSeq, uint32_t streamId, bool queueOnly = false, bool directWrite = false) const;
/**
* @brief Helper to send a data message to the specified peer.
* @param peerId Peer ID.
* @param opcode FNE network opcode pair.
* @param[in] data Buffer containing message to send to peer.
* @param length Length of buffer.
* @param streamId Stream ID for this message.
* @param queueOnly Flag indicating this message should be queued for transmission.
* @param incPktSeq Flag indicating the message should increment the packet sequence after transmission.
* @param directWrite Flag indicating this message should be immediately directly written.
*/
bool writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length,
uint32_t streamId, bool queueOnly = false, bool incPktSeq = false, bool directWrite = false) const;
uint16_t pktSeq, uint32_t streamId, bool queueOnly, bool incPktSeq = false, bool directWrite = false) const;
/**
* @brief Helper to send a command message to the specified peer.
@ -617,18 +719,20 @@ namespace network
* @param opcode FNE network opcode pair.
* @param[in] data Buffer containing message to send to peer.
* @param length Length of buffer.
* @param streamId Stream ID for this message.
* @param incPktSeq Flag indicating the message should increment the packet sequence after transmission.
*/
bool writePeerCommand(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data = nullptr, uint32_t length = 0U,
bool incPktSeq = false) const;
bool writePeerCommand(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length,
uint32_t streamId, bool incPktSeq) const;
/**
* @brief Helper to send a ACK response to the specified peer.
* @param peerId Peer ID.
* @param streamId Stream ID for this message.
* @param[in] data Buffer containing response data to send to peer.
* @param length Length of buffer.
*/
bool writePeerACK(uint32_t peerId, const uint8_t* data = nullptr, uint32_t length = 0U);
bool writePeerACK(uint32_t peerId, uint32_t streamId, const uint8_t* data = nullptr, uint32_t length = 0U);
/**
* @brief Helper to log a warning specifying which NAK reason is being sent a peer.
@ -640,10 +744,11 @@ namespace network
/**
* @brief Helper to send a NAK response to the specified peer.
* @param peerId Peer ID.
* @param streamId Stream ID for this message.
* @param tag Tag.
* @param reason NAK reason.
*/
bool writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REASON reason = NET_CONN_NAK_GENERAL_FAILURE);
bool writePeerNAK(uint32_t peerId, uint32_t streamId, const char* tag, NET_CONN_NAK_REASON reason = NET_CONN_NAK_GENERAL_FAILURE);
/**
* @brief Helper to send a NAK response to the specified peer.
* @param peerId Peer ID.

@ -58,6 +58,20 @@ void PeerNetwork::setPeerLookups(lookups::PeerListLookup* pidLookup)
m_pidLookup = pidLookup;
}
/* Gets the received DMR stream ID. */
uint32_t PeerNetwork::getRxDMRStreamId(uint32_t slotNo) const
{
assert(slotNo == 1U || slotNo == 2U);
if (slotNo == 1U) {
return m_rxDMRStreamId[0U];
}
else {
return m_rxDMRStreamId[1U];
}
}
/* Checks if the passed peer ID is blocked from sending to this peer. */
bool PeerNetwork::checkBlockedPeer(uint32_t peerId)

@ -63,6 +63,23 @@ namespace network
*/
void setPeerLookups(lookups::PeerListLookup* pidLookup);
/**
* @brief Gets the received DMR stream ID.
* @param slotNo DMR slot to get stream ID for.
* @return uint32_t Stream ID for the given DMR slot.
*/
uint32_t getRxDMRStreamId(uint32_t slotNo) const;
/**
* @brief Gets the received P25 stream ID.
* @return uint32_t Stream ID.
*/
uint32_t getRxP25StreamId() const { return m_rxP25StreamId; }
/**
* @brief Gets the received NXDN stream ID.
* @return uint32_t Stream ID.
*/
uint32_t getRxNXDNStreamId() const { return m_rxNXDNStreamId; }
/**
* @brief Gets the blocked traffic peer ID table.
* @returns std::vector<uint32_t> List of peer IDs this peer network cannot send traffic to.

@ -653,7 +653,7 @@ bool RESTAPI::validateAuth(const HTTPPayload& request, HTTPPayload& reply)
std::string host = request.headers.find("RemoteHost");
std::string headerToken = request.headers.find("X-DVM-Auth-Token");
#if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "RESTAPI::validateAuth() token, host = %s, token = %s", host.c_str(), headerToken.c_str());
::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "token, host = %s, token = %s", host.c_str(), headerToken.c_str());
#endif
if (headerToken.empty()) {
errorPayload(reply, "no authentication token", HTTPPayload::UNAUTHORIZED);
@ -662,11 +662,11 @@ bool RESTAPI::validateAuth(const HTTPPayload& request, HTTPPayload& reply)
for (auto& token : m_authTokens) {
#if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "RESTAPI::validateAuth() valid list, host = %s, token = %s", token.first.c_str(), std::to_string(token.second).c_str());
::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "valid list, host = %s, token = %s", token.first.c_str(), std::to_string(token.second).c_str());
#endif
if (token.first.compare(host) == 0) {
#if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "RESTAPI::validateAuth() storedToken = %s, passedToken = %s", std::to_string(token.second).c_str(), headerToken.c_str());
::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "storedToken = %s, passedToken = %s", std::to_string(token.second).c_str(), headerToken.c_str());
#endif
if (std::to_string(token.second).compare(headerToken) == 0) {
return true;

@ -197,6 +197,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
.request(m_network->m_influxServer);
}
m_network->eraseStreamPktSeq(peerId, streamId);
m_network->m_callInProgress = false;
}
}
@ -768,7 +769,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
return false;
}
}
@ -793,7 +794,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
LogWarning(LOG_NET, "DMR slot %s, illegal/unknown RID attempted access, srcId = %u, dstId = %u", data.getSlotNo(), data.getSrcId(), data.getDstId());
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
return false;
}
}
@ -846,7 +847,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
LogWarning(LOG_NET, "DMR slot %s, illegal/unknown RID attempted access, srcId = %u, dstId = %u", data.getSlotNo(), data.getSrcId(), data.getDstId());
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
return false;
}
}
@ -871,7 +872,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
return false;
}
@ -892,7 +893,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
return false;
}
@ -913,7 +914,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
return false;
}
@ -936,7 +937,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
return false;
}
}
@ -1065,7 +1066,7 @@ void TagDMRData::write_CSBK(uint32_t peerId, uint8_t slot, lc::CSBK* csbk)
}
if (peerId > 0U) {
m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false, true);
m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false);
} else {
// repeat traffic to the connected peers
if (m_network->m_peers.size() > 0U) {

@ -150,6 +150,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
.request(m_network->m_influxServer);
}
m_network->eraseStreamPktSeq(peerId, streamId);
m_network->m_callInProgress = false;
}
}
@ -574,7 +575,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
}
@ -598,7 +599,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
LogWarning(LOG_NET, "NXDN, illegal/unknown RID attempted access, srcId = %u, dstId = %u", lc.getSrcId(), lc.getDstId());
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
}
@ -627,7 +628,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
}
@ -651,7 +652,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
LogWarning(LOG_NET, "NXDN, illegal/unknown RID attempted access, srcId = %u, dstId = %u", lc.getSrcId(), lc.getDstId());
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
}
@ -677,7 +678,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
@ -697,7 +698,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
@ -720,7 +721,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
}
@ -840,5 +841,5 @@ void TagNXDNData::write_Message(uint32_t peerId, lc::RCCH* rcch)
}
uint32_t streamId = m_network->createStreamId();
m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false, true);
m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false);
}

@ -223,6 +223,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
.request(m_network->m_influxServer);
}
m_network->eraseStreamPktSeq(peerId, streamId);
m_network->m_callInProgress = false;
}
}
@ -494,12 +495,14 @@ void TagP25Data::playbackParrot()
if (message != nullptr) {
if (m_network->m_parrotOnlyOriginating) {
LogMessage(LOG_NET, "P25, Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", pkt.peerId, srcId, dstId);
m_network->writePeer(pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, 0U, false);
m_network->writePeer(pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength,
RTP_END_OF_CALL_SEQ, m_network->createStreamId(), false);
} else {
// repeat traffic to the connected peers
for (auto peer : m_network->m_peers) {
LogMessage(LOG_NET, "P25, Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", peer.first, srcId, dstId);
m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, 0U, false);
m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength,
RTP_END_OF_CALL_SEQ, m_network->createStreamId(), false);
}
}
}
@ -1090,7 +1093,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
}
@ -1114,7 +1117,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
LogWarning(LOG_NET, "P25, illegal/unknown RID attempted access, srcId = %u, dstId = %u", control.getSrcId(), control.getDstId());
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
}
@ -1148,7 +1151,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
}
@ -1172,7 +1175,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
LogWarning(LOG_NET, "P25, illegal/unknown RID attempted access, srcId = %u, dstId = %u", control.getSrcId(), control.getDstId());
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
}
@ -1242,7 +1245,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
@ -1262,7 +1265,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
@ -1285,7 +1288,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
m_network->writePeerICC(peerId, streamId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
}
@ -1439,7 +1442,8 @@ void TagP25Data::write_TSDU(uint32_t peerId, lc::TSBK* tsbk)
uint32_t streamId = m_network->createStreamId();
if (peerId > 0U) {
m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false, true);
m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength,
RTP_END_OF_CALL_SEQ, streamId, false);
} else {
// repeat traffic to the connected peers
if (m_network->m_peers.size() > 0U) {
@ -1450,7 +1454,8 @@ void TagP25Data::write_TSDU(uint32_t peerId, lc::TSBK* tsbk)
m_network->m_frameQueue->flushQueue();
}
m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, true);
m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength,
RTP_END_OF_CALL_SEQ, streamId, true);
if (m_network->m_debug) {
LogDebug(LOG_NET, "P25, peer = %u, len = %u, streamId = %u",
peer.first, messageLength, streamId);

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "fne/Defines.h"

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "fne/Defines.h"
@ -1045,7 +1045,7 @@ bool P25PacketData::writeNetwork(uint32_t peerId, network::PeerNetwork* peerNet,
if (peerNet != nullptr) {
return peerNet->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq, streamId);
} else {
return m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq, streamId, false, true);
return m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq, streamId, false);
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,467 @@
// SPDX-License-Identifier: MIT
/*
* Digital Voice Modem - Common Library
* MIT Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2006, 2009 Marcin Kalicinski
* Copyright (C) 2019 https://github.com/Fe-Bell/RapidXML
*
*/
/**
* @defgroup xml Extensible Markup Langauge
* @brief Defines and implements XML handling.
* @ingroup common
*
* @file rapidxml_print.h
* @ingroup xml
*/
#if !defined(__RAPIDXML_PRINT_H__)
#define __RAPIDXML_PRINT_H__
// Copyright (C) 2006, 2009 Marcin Kalicinski
// Copyright (C) 2019 https://github.com/Fe-Bell/RapidXML
// Version 1.17
// Revision $DateTime: 2023/09/19 23:27:00 $
//! \file rapidxml_print.h This file contains rapidxml printer implementation
#include "xml/rapidxml.h"
// Only include streams if not disabled
#ifndef RAPIDXML_NO_STREAMS
#include <ostream>
#include <iterator>
#endif
namespace rapidxml
{
///////////////////////////////////////////////////////////////////////
// Printing flags
const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function.
///////////////////////////////////////////////////////////////////////
// Internal
//! \cond internal
namespace internal
{
///////////////////////////////////////////////////////////////////////////
// Internal character operations
// Copy characters from given range to given output iterator
template<class OutIt, class Ch>
inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out)
{
while (begin != end)
*out++ = *begin++;
return out;
}
// Copy characters from given range to given output iterator and expand
// characters into references (&lt; &gt; &apos; &quot; &amp;)
template<class OutIt, class Ch>
inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out)
{
while (begin != end)
{
if (*begin == noexpand)
{
*out++ = *begin; // No expansion, copy character
}
else
{
switch (*begin)
{
case Ch('<'):
*out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';');
break;
case Ch('>'):
*out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';');
break;
case Ch('\''):
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';');
break;
case Ch('"'):
*out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';');
break;
case Ch('&'):
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';');
break;
default:
*out++ = *begin; // No expansion, copy character
}
}
++begin; // Step to next character
}
return out;
}
// Fill given output iterator with repetitions of the same character
template<class OutIt, class Ch>
inline OutIt fill_chars(OutIt out, int n, Ch ch)
{
for (int i = 0; i < n; ++i)
*out++ = ch;
return out;
}
// Find character
template<class Ch, Ch ch>
inline bool find_char(const Ch *begin, const Ch *end)
{
while (begin != end)
if (*begin++ == ch)
return true;
return false;
}
///////////////////////////////////////////////////////////////////////////
// Internal printing operations
template<class OutIt, class Ch>
inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent);
template<class OutIt, class Ch>
inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags);
template<class OutIt, class Ch>
inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
template<class OutIt, class Ch>
inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
template<class OutIt, class Ch>
inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
template<class OutIt, class Ch>
inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
template<class OutIt, class Ch>
inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
template<class OutIt, class Ch>
inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
template<class OutIt, class Ch>
inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
// Print node
template<class OutIt, class Ch>
inline OutIt print_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
// Print proper node type
switch (node->type())
{
// Document
case node_type::node_document:
out = print_children(out, node, flags, indent);
break;
// Element
case node_type::node_element:
out = print_element_node(out, node, flags, indent);
break;
// Data
case node_type::node_data:
out = print_data_node(out, node, flags, indent);
break;
// CDATA
case node_type::node_cdata:
out = print_cdata_node(out, node, flags, indent);
break;
// Declaration
case node_type::node_declaration:
out = print_declaration_node(out, node, flags, indent);
break;
// Comment
case node_type::node_comment:
out = print_comment_node(out, node, flags, indent);
break;
// Doctype
case node_type::node_doctype:
out = print_doctype_node(out, node, flags, indent);
break;
// Pi
case node_type::node_pi:
out = print_pi_node(out, node, flags, indent);
break;
// Unknown
default:
assert(0);
break;
}
// If indenting not disabled, add line break after node
if (!(flags & print_no_indenting))
*out = Ch('\n'), ++out;
// Return modified iterator
return out;
}
// Print children of the node
template<class OutIt, class Ch>
inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
for (xml_node<Ch> *child = node->first_node(); child; child = child->next_sibling())
out = print_node(out, child, flags, indent);
return out;
}
// Print attributes of the node
template<class OutIt, class Ch>
inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags)
{
for (xml_attribute<Ch> *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute())
{
if (attribute->name() && attribute->value())
{
// Print attribute name
*out = Ch(' '), ++out;
out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out);
*out = Ch('='), ++out;
// Print attribute value using appropriate quote type
if (find_char<Ch, Ch('"')>(attribute->value(), attribute->value() + attribute->value_size()))
{
*out = Ch('\''), ++out;
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out);
*out = Ch('\''), ++out;
}
else
{
*out = Ch('"'), ++out;
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out);
*out = Ch('"'), ++out;
}
}
}
return out;
}
// Print data node
template<class OutIt, class Ch>
inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_type::node_data);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
return out;
}
// Print data node
template<class OutIt, class Ch>
inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_type::node_cdata);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'); ++out;
*out = Ch('!'); ++out;
*out = Ch('['); ++out;
*out = Ch('C'); ++out;
*out = Ch('D'); ++out;
*out = Ch('A'); ++out;
*out = Ch('T'); ++out;
*out = Ch('A'); ++out;
*out = Ch('['); ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch(']'); ++out;
*out = Ch(']'); ++out;
*out = Ch('>'); ++out;
return out;
}
// Print element node
template<class OutIt, class Ch>
inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_type::node_element);
// Print element name and attributes, if any
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
out = copy_chars(node->name(), node->name() + node->name_size(), out);
out = print_attributes(out, node, flags);
// If node is childless
if (node->value_size() == 0 && !node->first_node())
{
// Print childless node tag ending
*out = Ch('/'), ++out;
*out = Ch('>'), ++out;
}
else
{
// Print normal node tag ending
*out = Ch('>'), ++out;
// Test if node contains a single data node only (and no other nodes)
xml_node<Ch> *child = node->first_node();
if (!child)
{
// If node has no children, only print its value without indenting
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
}
else if (child->next_sibling() == 0 && child->type() == node_type::node_data)
{
// If node has a sole data child, only print its value without indenting
out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out);
}
else
{
// Print all children with full indenting
if (!(flags & print_no_indenting))
*out = Ch('\n'), ++out;
out = print_children(out, node, flags, indent + 1);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
}
// Print node end
*out = Ch('<'), ++out;
*out = Ch('/'), ++out;
out = copy_chars(node->name(), node->name() + node->name_size(), out);
*out = Ch('>'), ++out;
}
return out;
}
// Print declaration node
template<class OutIt, class Ch>
inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
// Print declaration start
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('?'), ++out;
*out = Ch('x'), ++out;
*out = Ch('m'), ++out;
*out = Ch('l'), ++out;
// Print attributes
out = print_attributes(out, node, flags);
// Print declaration end
*out = Ch('?'), ++out;
*out = Ch('>'), ++out;
return out;
}
// Print comment node
template<class OutIt, class Ch>
inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_type::node_comment);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('!'), ++out;
*out = Ch('-'), ++out;
*out = Ch('-'), ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch('-'), ++out;
*out = Ch('-'), ++out;
*out = Ch('>'), ++out;
return out;
}
// Print doctype node
template<class OutIt, class Ch>
inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_type::node_doctype);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('!'), ++out;
*out = Ch('D'), ++out;
*out = Ch('O'), ++out;
*out = Ch('C'), ++out;
*out = Ch('T'), ++out;
*out = Ch('Y'), ++out;
*out = Ch('P'), ++out;
*out = Ch('E'), ++out;
*out = Ch(' '), ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch('>'), ++out;
return out;
}
// Print pi node
template<class OutIt, class Ch>
inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_type::node_pi);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('?'), ++out;
out = copy_chars(node->name(), node->name() + node->name_size(), out);
*out = Ch(' '), ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch('?'), ++out;
*out = Ch('>'), ++out;
return out;
}
}
//! \endcond
///////////////////////////////////////////////////////////////////////////
// Printing
//! Prints XML to given output iterator.
//! \param out Output iterator to print to.
//! \param node Node to be printed. Pass xml_document to print entire document.
//! \param flags Flags controlling how XML is printed.
//! \return Output iterator pointing to position immediately after last character of printed text.
template<class OutIt, class Ch>
inline OutIt print(OutIt out, const xml_node<Ch> &node, int flags = 0)
{
return internal::print_node(out, &node, flags, 0);
}
#ifndef RAPIDXML_NO_STREAMS
//! Prints XML to given output stream.
//! \param out Output stream to print to.
//! \param node Node to be printed. Pass xml_document to print entire document.
//! \param flags Flags controlling how XML is printed.
//! \return Output stream.
template<class Ch>
inline std::basic_ostream<Ch> &print(std::basic_ostream<Ch> &out, const xml_node<Ch> &node, int flags = 0)
{
print(std::ostream_iterator<Ch>(out), node, flags);
return out;
}
//! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.
//! \param out Output stream to print to.
//! \param node Node to be printed.
//! \return Output stream.
template<class Ch>
inline std::basic_ostream<Ch> &operator <<(std::basic_ostream<Ch> &out, const xml_node<Ch> &node)
{
return print(out, node);
}
#endif
}
#endif // __RAPIDXML_PRINT_H__

@ -0,0 +1,141 @@
// SPDX-License-Identifier: MIT
/*
* Digital Voice Modem - Common Library
* MIT Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2006, 2009 Marcin Kalicinski
* Copyright (C) 2019 https://github.com/Fe-Bell/RapidXML
*
*/
/**
* @defgroup xml Extensible Markup Langauge
* @brief Defines and implements XML handling.
* @ingroup common
*
* @file rapidxml_utils.h
* @ingroup xml
*/
#if !defined(__RAPIDXML_UTILS_H__)
#define __RAPIDXML_UTILS_H__
// Copyright (C) 2006, 2009 Marcin Kalicinski
// Copyright (C) 2019 https://github.com/Fe-Bell/RapidXML
// Version 1.17
// Revision $DateTime: 2023/09/19 23:27:00 $
//! \file rapidxml_utils.h This file contains high-level rapidxml utilities that can be useful
//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective.
#include "xml/rapidxml.h"
#include <vector>
#include <string>
#include <fstream>
#include <stdexcept>
namespace rapidxml
{
//! Represents data loaded from a file
template<class Ch = char>
class file
{
public:
//! Loads file into the memory. Data will be automatically destroyed by the destructor.
//! \param filename Filename to load.
file(const char *filename)
{
using namespace std;
// Open stream
basic_ifstream<Ch> stream(filename, ios::binary);
if (!stream)
throw runtime_error(string("cannot open file ") + filename);
stream.unsetf(ios::skipws);
// Determine stream size
stream.seekg(0, ios::end);
size_t size = stream.tellg();
stream.seekg(0);
// Load data and add terminating 0
m_data.resize(size + 1);
stream.read(&m_data.front(), static_cast<streamsize>(size));
m_data[size] = 0;
}
//! Loads file into the memory. Data will be automatically destroyed by the destructor
//! \param stream Stream to load from
file(std::basic_istream<Ch> &stream)
{
using namespace std;
// Load data and add terminating 0
stream.unsetf(ios::skipws);
m_data.assign(istreambuf_iterator<Ch>(stream), istreambuf_iterator<Ch>());
if (stream.fail() || stream.bad())
throw runtime_error("error reading stream");
m_data.push_back(0);
}
//! Gets file data.
//! \return Pointer to data of file.
Ch *data()
{
return &m_data.front();
}
//! Gets file data.
//! \return Pointer to data of file.
const Ch *data() const
{
return &m_data.front();
}
//! Gets file data size.
//! \return Size of file data, in characters.
std::size_t size() const
{
return m_data.size();
}
private:
std::vector<Ch> m_data; // File data
};
//! Counts children of node. Time complexity is O(n).
//! \return Number of children of node
template<class Ch>
inline std::size_t count_children(xml_node<Ch> *node)
{
xml_node<Ch> *child = node->first_node();
std::size_t count = 0;
while (child)
{
++count;
child = child->next_sibling();
}
return count;
}
//! Counts attributes of node. Time complexity is O(n).
//! \return Number of attributes of node
template<class Ch>
inline std::size_t count_attributes(xml_node<Ch> *node)
{
xml_attribute<Ch> *attr = node->first_attribute();
std::size_t count = 0;
while (attr)
{
++count;
attr = attr->next_attribute();
}
return count;
}
}
#endif // __RAPIDXML_UTILS_H__

@ -1 +1 @@
Subproject commit c45ec691a04741443f390354a366798db841900c
Subproject commit 718093aea3a5a6c80f7fceb1bbbc940168b8d98c

@ -1 +1 @@
Subproject commit ca0882a6de99c0dc05eac5de14a5e096c77232ff
Subproject commit 0a4c78445fb0c6929a3f351f3f4e693ec0b6d969

@ -637,7 +637,7 @@ bool Host::createModem()
LogInfo(" RX Coarse: %u, Fine: %u", rxCoarse, rxFine);
LogInfo(" TX Coarse: %u, Fine: %u", txCoarse, txFine);
LogInfo(" RSSI Coarse: %u, Fine: %u", rssiCoarse, rssiFine);
LogInfo(" RF Power Level: %u%", rfPower);
LogInfo(" RF Power Level: %u%%", rfPower);
LogInfo(" RX Level: %.1f%%", rxLevel);
LogInfo(" CW Id TX Level: %.1f%%", cwIdTXLevel);
LogInfo(" DMR TX Level: %.1f%%", dmrTXLevel);

@ -49,7 +49,7 @@ void* Host::threadDMRReader1(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -57,7 +57,7 @@ void* Host::threadDMRReader1(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -182,7 +182,7 @@ void* Host::threadDMRReader1(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -205,7 +205,7 @@ void* Host::threadDMRWriter1(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -213,7 +213,7 @@ void* Host::threadDMRWriter1(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -311,7 +311,7 @@ void* Host::threadDMRWriter1(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -334,7 +334,7 @@ void* Host::threadDMRReader2(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -342,7 +342,7 @@ void* Host::threadDMRReader2(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -466,7 +466,7 @@ void* Host::threadDMRReader2(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -489,7 +489,7 @@ void* Host::threadDMRWriter2(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -497,7 +497,7 @@ void* Host::threadDMRWriter2(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -595,7 +595,7 @@ void* Host::threadDMRWriter2(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}

@ -36,7 +36,7 @@ void* Host::threadNXDNReader(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -44,7 +44,7 @@ void* Host::threadNXDNReader(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -131,7 +131,7 @@ void* Host::threadNXDNReader(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -154,7 +154,7 @@ void* Host::threadNXDNWriter(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -162,7 +162,7 @@ void* Host::threadNXDNWriter(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -247,7 +247,7 @@ void* Host::threadNXDNWriter(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}

@ -37,7 +37,7 @@ void* Host::threadP25Reader(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -45,7 +45,7 @@ void* Host::threadP25Reader(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -173,7 +173,7 @@ void* Host::threadP25Reader(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -196,7 +196,7 @@ void* Host::threadP25Writer(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -204,7 +204,7 @@ void* Host::threadP25Writer(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -332,7 +332,7 @@ void* Host::threadP25Writer(void* arg)
}
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}

@ -1527,6 +1527,10 @@ bool Host::rmtPortModemHandler(Modem* modem, uint32_t ms, modem::RESP_TYPE_DVM r
if (modem->getTrace())
Utils::dump(1U, "TX Remote Data", buffer, len);
// never send less then 3 bytes
if (len < 3U)
return false;
// send entire modem packet over the remote port
m_modemRemotePort->write(buffer, len);
}
@ -1548,11 +1552,25 @@ bool Host::rmtPortModemHandler(Modem* modem, uint32_t ms, modem::RESP_TYPE_DVM r
return true;
}
uint8_t len = data[1U];
int ret = modem->write(data, len);
if (ret != int(len))
uint32_t pktLength = 0U;
switch (data[0U]) {
case DVM_SHORT_FRAME_START:
pktLength = data[1U];
break;
case DVM_LONG_FRAME_START:
pktLength = ((data[1U] & 0xFFU) << 8) + (data[2U] & 0xFFU);
break;
default:
LogError(LOG_MODEM, "Invalid start of modem frame!");
break;
}
if (pktLength > 0U) {
int ret = modem->write(data, pktLength);
if (ret != int(pktLength))
LogError(LOG_MODEM, "Error writing remote data");
}
}
// handled modem response
return true;
@ -1674,12 +1692,12 @@ void Host::setState(uint8_t state)
}
if (m_tidLookup != nullptr) {
m_tidLookup->setReloadTime(0U);
m_tidLookup->stop();
//delete m_tidLookup;
}
if (m_ridLookup != nullptr) {
m_tidLookup->setReloadTime(0U);
m_ridLookup->stop();
//delete m_ridLookup;
}
}
else {
@ -1705,7 +1723,7 @@ void* Host::threadModem(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -1713,7 +1731,7 @@ void* Host::threadModem(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -1742,7 +1760,7 @@ void* Host::threadModem(void* arg)
Thread::sleep(m_idleTickDelay);
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -1765,7 +1783,7 @@ void* Host::threadWatchdog(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -1773,7 +1791,7 @@ void* Host::threadWatchdog(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -1957,7 +1975,7 @@ void* Host::threadWatchdog(void* arg)
Thread::sleep(m_idleTickDelay);
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -1980,7 +1998,7 @@ void* Host::threadSiteData(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -1988,7 +2006,7 @@ void* Host::threadSiteData(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -2026,7 +2044,7 @@ void* Host::threadSiteData(void* arg)
Thread::sleep(m_idleTickDelay);
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}
@ -2049,7 +2067,7 @@ void* Host::threadPresence(void* arg)
Host* host = static_cast<Host*>(th->obj);
if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
LogError(LOG_HOST, "[FAIL] %s", threadName.c_str());
}
if (g_killed) {
@ -2057,7 +2075,7 @@ void* Host::threadPresence(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -2133,7 +2151,7 @@ void* Host::threadPresence(void* arg)
Thread::sleep(m_idleTickDelay);
}
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}

@ -36,6 +36,10 @@ using namespace lookups;
// Global Variables
// ---------------------------------------------------------------------------
#ifndef SIGHUP
#define SIGHUP 1
#endif
int g_signal = 0;
bool g_calibrate = false;
bool g_setup = false;
@ -302,15 +306,15 @@ int main(int argc, char** argv)
delete host;
}
if (g_signal == 2)
if (g_signal == SIGINT)
::LogInfoEx(LOG_HOST, "[STOP] dvmhost:main SIGINT");
if (g_signal == 15)
if (g_signal == SIGTERM)
::LogInfoEx(LOG_HOST, "[STOP] dvmhost:main SIGTERM");
if (g_signal == 1)
if (g_signal == SIGHUP)
::LogInfoEx(LOG_HOST, "[RSTR] dvmhost:main SIGHUP");
} while (g_signal == 1);
} while (g_signal == SIGHUP);
::LogFinalise();
::ActivityLogFinalise();

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -181,6 +181,18 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa
m_network->setDMRICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId, uint8_t slotNo) { processInCallCtrl(command, dstId, slotNo); });
}
/*
** Network Grant Disables
*/
bool disableNetworkGrant = dmrProtocol["disableNetworkGrant"].as<bool>(false);
m_slot1->m_disableNetworkGrant = disableNetworkGrant;
m_slot2->m_disableNetworkGrant = disableNetworkGrant;
bool convNetGrantDemand = dmrProtocol["convNetGrantDemand"].as<bool>(false);
m_slot1->m_convNetGrantDemand = convNetGrantDemand;
m_slot2->m_convNetGrantDemand = convNetGrantDemand;
if (printOptions) {
if (enableTSCC) {
LogInfo(" TSCC Slot: %u", m_tsccSlotNo);
@ -190,6 +202,9 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa
LogInfo(" TSCC Disable Grant Source ID Check: yes");
}
}
if (disableNetworkGrant) {
LogInfo(" Disable Network Grants: yes");
}
LogInfo(" Ignore Affiliation Check: %s", ignoreAffiliationCheck ? "yes" : "no");
LogInfo(" Notify Control: %s", notifyCC ? "yes" : "no");
@ -197,6 +212,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa
LogInfo(" Frame Loss Threshold: %u", frameLossThreshold);
LogInfo(" Verify Registration: %s", Slot::m_verifyReg ? "yes" : "no");
LogInfo(" Conventional Network Grant Demand: %s", convNetGrantDemand ? "yes" : "no");
}
}
@ -697,6 +713,8 @@ void Control::processNetwork()
uint32_t srcId = __GET_UINT16(buffer, 5U);
uint32_t dstId = __GET_UINT16(buffer, 8U);
uint8_t controlByte = buffer[14U];
FLCO::E flco = (buffer[15U] & 0x40U) == 0x40U ? FLCO::PRIVATE : FLCO::GROUP;
uint32_t slotNo = (buffer[15U] & 0x80U) == 0x80U ? 2U : 1U;
@ -728,6 +746,8 @@ void Control::processNetwork()
data.setDstId(dstId);
data.setFLCO(flco);
data.setControl(controlByte);
bool dataSync = (buffer[15U] & 0x20U) == 0x20U;
bool voiceSync = (buffer[15U] & 0x10U) == 0x10U;

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017,2020-2023 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017,2020-2025 Bryan Biedenkapp, N2PLL
*
*/
/**

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -152,6 +152,8 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz
m_enableTSCC(false),
m_dedicatedTSCC(false),
m_ignoreAffiliationCheck(false),
m_disableNetworkGrant(false),
m_convNetGrantDemand(false),
m_tsccPayloadDstId(0U),
m_tsccPayloadSrcId(0U),
m_tsccPayloadGroup(false),
@ -425,11 +427,74 @@ void Slot::processNetwork(const data::NetData& dmrData)
DataType::E dataType = dmrData.getDataType();
// ignore non-CSBK data destined for the TSCC slot
if (m_enableTSCC && m_dedicatedTSCC && m_slotNo == m_dmr->m_tsccSlotNo &&
dataType != DataType::CSBK) {
Slot* tscc = m_dmr->getTSCCSlot();
bool enableTSCC = false;
if (tscc != nullptr)
enableTSCC = tscc->m_enableTSCC;
bool dedicatedTSCC = false;
if (tscc != nullptr)
dedicatedTSCC = tscc->m_dedicatedTSCC;
// check if this host instance is TSCC enabled or not -- if it is, handle processing network grant demands
if (enableTSCC && dedicatedTSCC) {
switch (dataType)
{
case DataType::VOICE_LC_HEADER:
case DataType::DATA_HEADER:
{
bool grantDemand = (dmrData.getControl() & 0x80U) == 0x80U;
bool unitToUnit = (dmrData.getControl() & 0x01U) == 0x01U;
if (grantDemand) {
if (m_disableNetworkGrant) {
break;
}
// if we're non-dedicated control, and if we're not in a listening or idle state, ignore any grant
// demands
if (!dedicatedTSCC && (m_rfState != RS_RF_LISTENING || m_netState != RS_NET_IDLE)) {
break;
}
// validate source RID
if (!acl::AccessControl::validateSrcId(dmrData.getSrcId())) {
break;
}
// validate the target ID, if the target is a talkgroup
if (!acl::AccessControl::validateTGId(dmrData.getSlotNo(), dmrData.getDstId())) {
break;
}
if (m_verbose) {
LogMessage(LOG_NET, "DMR Slot %u, remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u",
m_slotNo, dmrData.getSrcId(), dmrData.getDstId(), unitToUnit);
}
// perform grant response logic
if (dataType == DataType::VOICE_LC_HEADER)
tscc->m_control->writeRF_CSBK_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true);
if (dataType == DataType::DATA_HEADER)
tscc->m_control->writeRF_CSBK_Data_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true);
}
}
break;
default:
break;
}
// if *this slot* is the TSCC slot, stop processing after this point
if (m_enableTSCC && m_dedicatedTSCC)
{
if (dataType != DataType::CSBK)
return;
else {
if (m_slotNo != m_dmr->m_tsccSlotNo)
return;
}
}
}
switch (dataType)
{
@ -1079,7 +1144,7 @@ void Slot::addFrame(const uint8_t *data, bool net, bool imm)
fifoSpace = m_modem->getDMRSpace2();
}
//LogDebug(LOG_DMR, "Slot %u, addFrame() fifoSpace = %u", m_slotNo, fifoSpace);
//LogDebugEx(LOG_DMR, "Slot::addFrame()", "Slot %u, fifoSpace = %u", m_slotNo, fifoSpace);
// is this immediate data?
if (imm) {
@ -1139,15 +1204,15 @@ void Slot::processFrameLoss()
m_slotNo, m_rfFrames, m_rfBits, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits));
// release trunked grant (if necessary)
Slot *m_tscc = m_dmr->getTSCCSlot();
if (m_tscc != nullptr) {
if (m_tscc->m_enableTSCC && m_rfLC != nullptr) {
m_tscc->m_affiliations->releaseGrant(m_rfLC->getDstId(), false);
Slot* tscc = m_dmr->getTSCCSlot();
if (tscc != nullptr) {
if (tscc->m_enableTSCC && m_rfLC != nullptr) {
tscc->m_affiliations->releaseGrant(m_rfLC->getDstId(), false);
}
clearTSCCActivated();
if (!m_tscc->m_enableTSCC) {
if (!tscc->m_enableTSCC) {
notifyCC_ReleaseGrant(m_rfLC->getDstId());
}
}
@ -1244,18 +1309,18 @@ void Slot::notifyCC_TouchGrant(uint32_t dstId)
/* Write data frame to the network. */
void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, uint8_t errors, bool noSequence)
void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, uint8_t control, uint8_t errors, bool noSequence)
{
assert(data != nullptr);
assert(m_rfLC != nullptr);
writeNetwork(data, dataType, m_rfLC->getFLCO(), m_rfLC->getSrcId(), m_rfLC->getDstId(), errors);
writeNetwork(data, dataType, m_rfLC->getFLCO(), m_rfLC->getSrcId(), m_rfLC->getDstId(), control, errors);
}
/* Write data frame to the network. */
void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, FLCO::E flco, uint32_t srcId,
uint32_t dstId, uint8_t errors, bool noSequence)
uint32_t dstId, uint8_t control, uint8_t errors, bool noSequence)
{
assert(data != nullptr);
@ -1271,6 +1336,7 @@ void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, FLCO::E flco,
dmrData.setSrcId(srcId);
dmrData.setDstId(dstId);
dmrData.setFLCO(flco);
dmrData.setControl(control);
dmrData.setN(m_voice->m_rfN);
dmrData.setSeqNo(m_rfSeqNo);
dmrData.setBER(errors);
@ -1372,6 +1438,20 @@ void Slot::writeEndNet(bool writeEnd)
}
}
// release trunked grant (if necessary)
Slot* tscc = m_dmr->getTSCCSlot();
if (tscc != nullptr) {
if (tscc->m_enableTSCC && m_netLC != nullptr) {
tscc->m_affiliations->releaseGrant(m_netLC->getDstId(), false);
}
clearTSCCActivated();
if (!tscc->m_enableTSCC) {
notifyCC_ReleaseGrant(m_netLC->getDstId());
}
}
m_data->m_pduDataOffset = 0U;
if (m_network != nullptr)
@ -1467,7 +1547,7 @@ void Slot::writeRF_ControlData(uint16_t frameCnt, uint8_t n)
bool grp = m_affiliations->isGroup(dstId);
if (m_debug) {
LogDebug(LOG_DMR, "writeRF_ControlData, frameCnt = %u, seq = %u, late entry, dstId = %u, srcId = %u", frameCnt, n, dstId, srcId);
LogDebugEx(LOG_DMR, "Slot::writeRF_ControlData()", "frameCnt = %u, seq = %u, late entry, dstId = %u, srcId = %u", frameCnt, n, dstId, srcId);
}
m_control->writeRF_CSBK_Grant_LateEntry(dstId, srcId, grp);
@ -1670,8 +1750,8 @@ void Slot::setShortLC_TSCC(SiteData siteData, uint16_t counter)
lc[3U] = (uint8_t)((lcValue >> 0) & 0xFFU);
lc[4U] = edac::CRC::crc8(lc, 4U);
//LogDebug(LOG_DMR, "setShortLC_TSCC, netId = %02X, siteId = %02X", siteData.netId(), siteData.siteId());
//Utils::dump(1U, "shortLC_TSCC", lc, 5U);
//LogDebugEx(LOG_DMR, "Slot::setShortLC_TSCC()", "netId = %02X, siteId = %02X", siteData.netId(), siteData.siteId());
//Utils::dump(1U, "[Slot::shortLC_TSCC()]", lc, 5U);
uint8_t sLC[9U];
@ -1730,8 +1810,8 @@ void Slot::setShortLC_Payload(SiteData siteData, uint16_t counter)
lc[3U] = (uint8_t)((lcValue >> 0) & 0xFFU);
lc[4U] = edac::CRC::crc8(lc, 4U);
//LogDebug(LOG_DMR, "setShortLC_Payload, netId = %02X, siteId = %02X", siteData.netId(), siteData.siteId());
//Utils::dump(1U, "setShortLC_Payload", lc, 5U);
//LogDebugEx(LOG_DMR, "Slot::setShortLC_Payload()", "netId = %02X, siteId = %02X", siteData.netId(), siteData.siteId());
//Utils::dump(1U, "[Slot::setShortLC_Payload()]", lc, 5U);
uint8_t sLC[9U];

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL
*
*/
/**
@ -403,6 +403,8 @@ namespace dmr
bool m_enableTSCC;
bool m_dedicatedTSCC;
bool m_ignoreAffiliationCheck;
bool m_disableNetworkGrant;
bool m_convNetGrantDemand;
uint32_t m_tsccPayloadDstId;
uint32_t m_tsccPayloadSrcId;
@ -507,10 +509,11 @@ namespace dmr
* @brief Write data frame to the network.
* @param[in] data Buffer containing frame data to write to the network.
* @param dataType DMR Data Type for this frame.
* @param control Control Byte.
* @param errors Number of bit errors detected for this frame.
* @param noSequence Flag indicating this frame carries no sequence number.
*/
void writeNetwork(const uint8_t* data, defines::DataType::E dataType, uint8_t errors = 0U, bool noSequence = false);
void writeNetwork(const uint8_t* data, defines::DataType::E dataType, uint8_t control, uint8_t errors = 0U, bool noSequence = false);
/**
* @brief Write data frame to the network.
* @param[in] data Buffer containing frame data to write to the network.
@ -518,11 +521,12 @@ namespace dmr
* @param flco Full-Link Control Opcode.
* @param srcId Source Radio ID.
* @param dstId Destination ID.
* @param control Control Byte.
* @param errors Number of bit errors detected for this frame.
* @param noSequence Flag indicating this frame carries no sequence number.
*/
void writeNetwork(const uint8_t* data, defines::DataType::E dataType, defines::FLCO::E flco, uint32_t srcId,
uint32_t dstId, uint8_t errors = 0U, bool noSequence = false);
uint32_t dstId, uint8_t control, uint8_t errors = 0U, bool noSequence = false);
/**
* @brief Helper to write RF end of frame data.

@ -36,7 +36,7 @@ DMRAffiliationLookup::~DMRAffiliationLookup() = default;
bool DMRAffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTimeout, bool grp, bool netGranted)
{
::LogDebug(LOG_HOST, "%s, DMRAffiliationLookup::grantCh() use grantChSlot() BUGBUG");
::LogDebugEx(LOG_HOST, "%s", "DMRAffiliationLookup::grantCh()", "use grantChSlot() BUGBUG");
return false;
}

@ -379,7 +379,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len)
if (m_slot->m_duplex)
m_slot->addFrame(data);
m_slot->writeNetwork(data, DataType::CSBK, gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId, 0U, true);
m_slot->writeNetwork(data, DataType::CSBK, gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId, 0U, 0U, true);
}
return true;
@ -684,7 +684,7 @@ void ControlSignaling::writeRF_Call_Alrt(uint32_t srcId, uint32_t dstId)
/* Initializes a new instance of the ControlSignaling class. */
ControlSignaling::ControlSignaling(Slot * slot, network::BaseNetwork * network, bool dumpCSBKData, bool debug, bool verbose) :
ControlSignaling::ControlSignaling(Slot* slot, network::BaseNetwork * network, bool dumpCSBKData, bool debug, bool verbose) :
m_slot(slot),
m_dumpCSBKData(dumpCSBKData),
m_verbose(verbose),
@ -765,7 +765,7 @@ void ControlSignaling::writeNet_CSBK(lc::CSBK* csbk)
if (m_slot->m_duplex)
m_slot->addFrame(data);
m_slot->writeNetwork(data, DataType::CSBK, csbk->getGI() ? FLCO::GROUP : FLCO::PRIVATE, csbk->getSrcId(), csbk->getDstId(), 0U, true);
m_slot->writeNetwork(data, DataType::CSBK, csbk->getGI() ? FLCO::GROUP : FLCO::PRIVATE, csbk->getSrcId(), csbk->getDstId(), 0U, 0U, true);
}
/*
@ -802,7 +802,7 @@ void ControlSignaling::writeRF_CSBK_NACK_RSP(uint32_t dstId, uint8_t reason, uin
bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp, bool net, bool skip, uint32_t chNo)
{
Slot *m_tscc = m_slot->m_dmr->getTSCCSlot();
Slot* tscc = m_slot->m_dmr->getTSCCSlot();
uint8_t slot = 0U;
@ -819,10 +819,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
if (!skip) {
if (m_slot->m_rfState != RS_RF_LISTENING && m_slot->m_rfState != RS_RF_DATA) {
if (!net) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId);
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", tscc->m_slotNo, dstId);
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", tscc->m_slotNo, srcId, dstId);
m_slot->m_rfState = RS_RF_REJECTED;
}
@ -831,10 +831,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
if (m_slot->m_netState != RS_NET_IDLE && dstId == m_slot->m_netLastDstId) {
if (!net) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId);
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", tscc->m_slotNo, dstId);
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", tscc->m_slotNo, srcId, dstId);
m_slot->m_rfState = RS_RF_REJECTED;
}
@ -853,37 +853,37 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
}
}
if (!m_tscc->m_affiliations->isGranted(dstId)) {
::lookups::TalkgroupRuleGroupVoice groupVoice = m_tscc->m_tidLookup->find(dstId);
if (!tscc->m_affiliations->isGranted(dstId)) {
::lookups::TalkgroupRuleGroupVoice groupVoice = tscc->m_tidLookup->find(dstId);
slot = groupVoice.source().tgSlot();
if (grp && !m_tscc->m_ignoreAffiliationCheck) {
if (grp && !tscc->m_ignoreAffiliationCheck) {
// is this an affiliation required group?
::lookups::TalkgroupRuleGroupVoice tid = m_tscc->m_tidLookup->find(dstId, slot);
::lookups::TalkgroupRuleGroupVoice tid = tscc->m_tidLookup->find(dstId, slot);
if (tid.config().affiliated()) {
if (!m_tscc->m_affiliations->hasGroupAff(dstId)) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_VOICE_CALL (Group Voice Call) ignored, no group affiliations, dstId = %u", m_tscc->m_slotNo, dstId);
if (!tscc->m_affiliations->hasGroupAff(dstId)) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_VOICE_CALL (Group Voice Call) ignored, no group affiliations, dstId = %u", tscc->m_slotNo, dstId);
return false;
}
}
}
if (!grp && !m_tscc->m_ignoreAffiliationCheck) {
if (!grp && !tscc->m_ignoreAffiliationCheck) {
// is this the target registered?
if (!m_tscc->m_affiliations->isUnitReg(dstId)) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_VOICE_CALL (Individual Voice Call) ignored, no unit registration, dstId = %u", m_tscc->m_slotNo, dstId);
if (!tscc->m_affiliations->isUnitReg(dstId)) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_VOICE_CALL (Individual Voice Call) ignored, no unit registration, dstId = %u", tscc->m_slotNo, dstId);
return false;
}
}
uint32_t availChNo = m_tscc->m_affiliations->getAvailableChannelForSlot(slot);
if (!m_tscc->m_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) {
uint32_t availChNo = tscc->m_affiliations->getAvailableChannelForSlot(slot);
if (!tscc->m_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) {
if (grp) {
if (!net) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_VOICE_CALL (Group Voice Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId);
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_VOICE_CALL (Group Voice Call) queued, no channels available, dstId = %u", tscc->m_slotNo, dstId);
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_QUEUED_RSN_NO_RESOURCE, (grp) ? 1U : 0U);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", tscc->m_slotNo, srcId, dstId);
m_slot->m_rfState = RS_RF_REJECTED;
}
@ -891,10 +891,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
}
else {
if (!net) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_VOICE_CALL (Individual Voice Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId);
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_VOICE_CALL (Individual Voice Call) queued, no channels available, dstId = %u", tscc->m_slotNo, dstId);
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_QUEUED_RSN_NO_RESOURCE, (grp) ? 1U : 0U);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", tscc->m_slotNo, srcId, dstId);
m_slot->m_rfState = RS_RF_REJECTED;
}
@ -902,24 +902,24 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
}
}
else {
if (m_tscc->m_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) {
chNo = m_tscc->m_affiliations->getGrantedCh(dstId);
slot = m_tscc->m_affiliations->getGrantedSlot(dstId);
//m_tscc->m_siteData.setChCnt(m_tscc->m_affiliations->getRFChCnt() + m_tscc->m_affiliations->getGrantedRFChCnt());
if (tscc->m_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) {
chNo = tscc->m_affiliations->getGrantedCh(dstId);
slot = tscc->m_affiliations->getGrantedSlot(dstId);
//tscc->m_siteData.setChCnt(tscc->m_affiliations->getRFChCnt() + tscc->m_affiliations->getGrantedRFChCnt());
}
}
}
else {
if (!m_tscc->m_disableGrantSrcIdCheck && !net) {
if (!tscc->m_disableGrantSrcIdCheck && !net) {
// do collision check between grants to see if a SU is attempting a "grant retry" or if this is a
// different source from the original grant
uint32_t grantedSrcId = m_tscc->m_affiliations->getGrantedSrcId(dstId);
uint32_t grantedSrcId = tscc->m_affiliations->getGrantedSrcId(dstId);
if (srcId != grantedSrcId) {
if (!net) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId);
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", tscc->m_slotNo, dstId);
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", tscc->m_slotNo, srcId, dstId);
m_slot->m_rfState = RS_RF_REJECTED;
}
@ -927,18 +927,18 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
}
}
chNo = m_tscc->m_affiliations->getGrantedCh(dstId);
slot = m_tscc->m_affiliations->getGrantedSlot(dstId);
chNo = tscc->m_affiliations->getGrantedCh(dstId);
slot = tscc->m_affiliations->getGrantedSlot(dstId);
m_tscc->m_affiliations->touchGrant(dstId);
tscc->m_affiliations->touchGrant(dstId);
}
}
else {
if (m_tscc->m_affiliations->isGranted(dstId)) {
chNo = m_tscc->m_affiliations->getGrantedCh(dstId);
slot = m_tscc->m_affiliations->getGrantedSlot(dstId);
if (tscc->m_affiliations->isGranted(dstId)) {
chNo = tscc->m_affiliations->getGrantedCh(dstId);
slot = tscc->m_affiliations->getGrantedSlot(dstId);
m_tscc->m_affiliations->touchGrant(dstId);
tscc->m_affiliations->touchGrant(dstId);
}
else {
return false;
@ -947,13 +947,13 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
if (grp) {
if (!net) {
::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", tscc->m_slotNo, srcId, dstId);
}
// callback REST API to permit the granted TG on the specified voice channel
if (m_tscc->m_authoritative && m_tscc->m_supervisor &&
m_tscc->m_channelNo != chNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (tscc->m_authoritative && tscc->m_supervisor &&
tscc->m_channelNo != chNo) {
::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object();
int state = modem::DVM_STATE::STATE_DMR;
@ -962,10 +962,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
req["slot"].set<uint8_t>(slot);
int ret = RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_PERMIT_TG, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug);
HTTP_PUT, PUT_PERMIT_TG, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug);
if (ret != network::rest::http::HTTPPayload::StatusType::OK) {
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
m_tscc->m_affiliations->releaseGrant(dstId, false);
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot);
tscc->m_affiliations->releaseGrant(dstId, false);
if (!net) {
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U);
m_slot->m_rfState = RS_RF_REJECTED;
@ -975,7 +975,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
}
}
else {
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot);
}
}
@ -989,7 +989,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
if (m_verbose) {
LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u",
m_tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId);
tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId);
}
csbk->setEmergency(emergency);
@ -1001,8 +1001,8 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
writeRF_CSBK_Imm(csbk.get());
// if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (chNo != tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object();
req["dstId"].set<uint32_t>(dstId);
@ -1013,10 +1013,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
req["voice"].set<bool>(voice);
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug);
HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug);
}
else {
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot);
}
}
else {
@ -1025,13 +1025,13 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
}
else {
if (!net) {
::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", tscc->m_slotNo, srcId, dstId);
}
// callback REST API to permit the granted TG on the specified voice channel
if (m_tscc->m_authoritative && m_tscc->m_supervisor &&
m_tscc->m_channelNo != chNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (tscc->m_authoritative && tscc->m_supervisor &&
tscc->m_channelNo != chNo) {
::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object();
int state = modem::DVM_STATE::STATE_DMR;
@ -1040,10 +1040,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
req["slot"].set<uint8_t>(slot);
int ret = RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_PERMIT_TG, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug);
HTTP_PUT, PUT_PERMIT_TG, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug);
if (ret != network::rest::http::HTTPPayload::StatusType::OK) {
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
m_tscc->m_affiliations->releaseGrant(dstId, false);
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot);
tscc->m_affiliations->releaseGrant(dstId, false);
if (!net) {
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U);
m_slot->m_rfState = RS_RF_REJECTED;
@ -1053,7 +1053,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
}
}
else {
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot);
}
}
@ -1065,7 +1065,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
if (m_verbose) {
LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u",
m_tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId);
tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId);
}
csbk->setEmergency(emergency);
@ -1077,8 +1077,8 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
writeRF_CSBK_Imm(csbk.get());
// if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (chNo != tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object();
req["dstId"].set<uint32_t>(dstId);
@ -1089,10 +1089,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
req["voice"].set<bool>(voice);
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug);
HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug);
}
else {
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot);
}
}
else {
@ -1107,7 +1107,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp, bool net, bool skip, uint32_t chNo)
{
Slot *m_tscc = m_slot->m_dmr->getTSCCSlot();
Slot* tscc = m_slot->m_dmr->getTSCCSlot();
uint8_t slot = 0U;
@ -1124,10 +1124,10 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
if (!skip) {
if (m_slot->m_rfState != RS_RF_LISTENING && m_slot->m_rfState != RS_RF_DATA) {
if (!net) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, DATA_CALL (Data Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId);
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, DATA_CALL (Data Call) denied, traffic in progress, dstId = %u", tscc->m_slotNo, dstId);
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", tscc->m_slotNo, srcId, dstId);
m_slot->m_rfState = RS_RF_REJECTED;
}
@ -1136,10 +1136,10 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
if (m_slot->m_netState != RS_NET_IDLE && dstId == m_slot->m_netLastDstId) {
if (!net) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, DATA_CALL (Data Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId);
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, DATA_CALL (Data Call) denied, traffic in progress, dstId = %u", tscc->m_slotNo, dstId);
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", tscc->m_slotNo, srcId, dstId);
m_slot->m_rfState = RS_RF_REJECTED;
}
@ -1158,18 +1158,18 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
}
}
if (!m_tscc->m_affiliations->isGranted(dstId)) {
::lookups::TalkgroupRuleGroupVoice groupVoice = m_tscc->m_tidLookup->find(dstId);
if (!tscc->m_affiliations->isGranted(dstId)) {
::lookups::TalkgroupRuleGroupVoice groupVoice = tscc->m_tidLookup->find(dstId);
slot = groupVoice.source().tgSlot();
uint32_t availChNo = m_tscc->m_affiliations->getAvailableChannelForSlot(slot);
if (!m_tscc->m_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) {
uint32_t availChNo = tscc->m_affiliations->getAvailableChannelForSlot(slot);
if (!tscc->m_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) {
if (grp) {
if (!net) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_DATA_CALL (Group Data Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId);
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_DATA_CALL (Group Data Call) queued, no channels available, dstId = %u", tscc->m_slotNo, dstId);
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_QUEUED_RSN_NO_RESOURCE, (grp) ? 1U : 0U);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", tscc->m_slotNo, srcId, dstId);
m_slot->m_rfState = RS_RF_REJECTED;
}
@ -1177,10 +1177,10 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
}
else {
if (!net) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_DATA_CALL (Individual Data Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId);
LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_DATA_CALL (Individual Data Call) queued, no channels available, dstId = %u", tscc->m_slotNo, dstId);
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_QUEUED_RSN_NO_RESOURCE, (grp) ? 1U : 0U);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", tscc->m_slotNo, srcId, dstId);
m_slot->m_rfState = RS_RF_REJECTED;
}
@ -1188,26 +1188,26 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
}
}
else {
if (m_tscc->m_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) {
chNo = m_tscc->m_affiliations->getGrantedCh(dstId);
slot = m_tscc->m_affiliations->getGrantedSlot(dstId);
if (tscc->m_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) {
chNo = tscc->m_affiliations->getGrantedCh(dstId);
slot = tscc->m_affiliations->getGrantedSlot(dstId);
//m_tscc->m_siteData.setChCnt(m_tscc->m_affiliations->getRFChCnt() + m_tscc->m_affiliations->getGrantedRFChCnt());
//tscc->m_siteData.setChCnt(tscc->m_affiliations->getRFChCnt() + tscc->m_affiliations->getGrantedRFChCnt());
}
}
}
else {
chNo = m_tscc->m_affiliations->getGrantedCh(dstId);
slot = m_tscc->m_affiliations->getGrantedSlot(dstId);
chNo = tscc->m_affiliations->getGrantedCh(dstId);
slot = tscc->m_affiliations->getGrantedSlot(dstId);
m_tscc->m_affiliations->touchGrant(dstId);
tscc->m_affiliations->touchGrant(dstId);
}
}
if (grp) {
if (!net) {
::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", tscc->m_slotNo, srcId, dstId);
}
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_ACK_RSN_MSG, (grp) ? 1U : 0U);
@ -1218,7 +1218,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
if (m_verbose) {
LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u",
m_tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId);
tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId);
}
csbk->setEmergency(emergency);
@ -1230,8 +1230,8 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
writeRF_CSBK_Imm(csbk.get());
// if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (chNo != tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object();
req["dstId"].set<uint32_t>(dstId);
@ -1242,10 +1242,10 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
req["voice"].set<bool>(voice);
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug);
HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug);
}
else {
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot);
}
}
else {
@ -1254,7 +1254,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
}
else {
if (!net) {
::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId);
::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", tscc->m_slotNo, srcId, dstId);
}
writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_ACK_RSN_MSG, (grp) ? 1U : 0U);
@ -1265,7 +1265,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
if (m_verbose) {
LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u",
m_tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId);
tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId);
}
csbk->setEmergency(emergency);
@ -1277,8 +1277,8 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
writeRF_CSBK_Imm(csbk.get());
// if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (chNo != tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object();
req["dstId"].set<uint32_t>(dstId);
@ -1289,10 +1289,10 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
req["voice"].set<bool>(voice);
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug);
HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug);
}
else {
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot);
}
}
else {
@ -1307,7 +1307,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOptions)
{
Slot *m_tscc = m_slot->m_dmr->getTSCCSlot();
Slot* tscc = m_slot->m_dmr->getTSCCSlot();
bool dereg = (serviceOptions & 0x01U) == 0x01U;
uint8_t powerSave = (serviceOptions >> 1) & 0x07U;
@ -1318,7 +1318,7 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt
csbk->setReason(ReasonCode::TS_DENY_RSN_REG_DENIED);
if (m_verbose) {
LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, SU power saving unsupported, srcId = %u, serviceOptions = $%02X", m_tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions);
LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, SU power saving unsupported, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions);
}
csbk->setSrcId(WUID_REGI);
@ -1334,7 +1334,7 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt
if (!dereg) {
if (m_verbose) {
LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", m_tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions);
LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions);
}
// remove dynamic unit registration table entry
@ -1351,14 +1351,14 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt
// validate the source RID
if (!acl::AccessControl::validateSrcId(srcId)) {
LogWarning(LOG_RF, "DMR Slot %u, CSBK, %s, denial, RID rejection, srcId = %u", m_tscc->m_slotNo, csbk->toString().c_str(), srcId);
LogWarning(LOG_RF, "DMR Slot %u, CSBK, %s, denial, RID rejection, srcId = %u", tscc->m_slotNo, csbk->toString().c_str(), srcId);
::ActivityLog("DMR", true, "unit registration request from %u denied", srcId);
csbk->setReason(ReasonCode::TS_DENY_RSN_REG_DENIED);
}
if (csbk->getReason() == ReasonCode::TS_ACK_RSN_REG) {
if (m_verbose) {
LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", m_tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions);
LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions);
}
::ActivityLog("DMR", true, "unit registration request from %u", srcId);
@ -1383,10 +1383,10 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt
void ControlSignaling::writeRF_CSBK_Grant_LateEntry(uint32_t dstId, uint32_t srcId, bool grp)
{
Slot *m_tscc = m_slot->m_dmr->getTSCCSlot();
Slot* tscc = m_slot->m_dmr->getTSCCSlot();
uint32_t chNo = m_tscc->m_affiliations->getGrantedCh(dstId);
uint8_t slot = m_tscc->m_affiliations->getGrantedSlot(dstId);
uint32_t chNo = tscc->m_affiliations->getGrantedCh(dstId);
uint8_t slot = tscc->m_affiliations->getGrantedSlot(dstId);
if (grp) {
std::unique_ptr<CSBK_TV_GRANT> csbk = std::make_unique<CSBK_TV_GRANT>();

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -105,7 +105,7 @@ bool Data::process(uint8_t* data, uint32_t len)
data[0U] = modem::TAG_EOT;
data[1U] = 0x00U;
m_slot->writeNetwork(data, DataType::TERMINATOR_WITH_LC);
m_slot->writeNetwork(data, DataType::TERMINATOR_WITH_LC, 0U);
if (m_slot->m_duplex) {
for (uint32_t i = 0U; i < m_slot->m_hangCount; i++)
@ -250,7 +250,11 @@ bool Data::process(uint8_t* data, uint32_t len)
if (m_slot->m_duplex && m_repeatDataPacket)
m_slot->addFrame(data);
m_slot->writeNetwork(data, DataType::DATA_HEADER);
uint8_t controlByte = 0U;
if (m_slot->m_convNetGrantDemand)
controlByte |= 0x80U; // Grant Demand Flag
m_slot->writeNetwork(data, DataType::DATA_HEADER, controlByte);
m_slot->m_rfState = RS_RF_DATA;
m_slot->m_rfLastDstId = dstId;
@ -323,7 +327,7 @@ bool Data::process(uint8_t* data, uint32_t len)
// convert the Data Sync to be from the BS or MS as needed
Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
m_slot->writeNetwork(data, dataType);
m_slot->writeNetwork(data, dataType, 0U);
if (m_slot->m_duplex && m_repeatDataPacket) {
m_slot->addFrame(data);

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -199,7 +199,11 @@ bool Voice::process(uint8_t* data, uint32_t len)
m_slot->addFrame(data);
}
m_slot->writeNetwork(data, DataType::VOICE_LC_HEADER);
uint8_t controlByte = 0U;
if (m_slot->m_convNetGrantDemand)
controlByte |= 0x80U; // Grant Demand Flag
m_slot->writeNetwork(data, DataType::VOICE_LC_HEADER, controlByte);
m_slot->m_rfState = RS_RF_AUDIO;
m_slot->m_rfLastDstId = dstId;
@ -246,7 +250,7 @@ bool Voice::process(uint8_t* data, uint32_t len)
if (m_slot->m_duplex)
m_slot->addFrame(data);
m_slot->writeNetwork(data, DataType::VOICE_PI_HEADER);
m_slot->writeNetwork(data, DataType::VOICE_PI_HEADER, 0U);
if (m_verbose) {
LogMessage(LOG_RF, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_slot->m_slotNo,
@ -304,7 +308,7 @@ bool Voice::process(uint8_t* data, uint32_t len)
if (m_slot->m_duplex)
m_slot->addFrame(data);
m_slot->writeNetwork(data, DataType::VOICE_SYNC, errors);
m_slot->writeNetwork(data, DataType::VOICE_SYNC, 0U, errors);
return true;
}
@ -458,7 +462,7 @@ bool Voice::process(uint8_t* data, uint32_t len)
data[0U] = modem::TAG_DATA;
data[1U] = 0x00U;
m_slot->writeNetwork(data, DataType::VOICE, errors);
m_slot->writeNetwork(data, DataType::VOICE, 0U, errors);
if (m_embeddedLCOnly) {
// Only send the previously received LC
@ -561,7 +565,11 @@ bool Voice::process(uint8_t* data, uint32_t len)
m_slot->addFrame(start);
}
m_slot->writeNetwork(start, DataType::VOICE_LC_HEADER);
uint8_t controlByte = 0U;
if (m_slot->m_convNetGrantDemand)
controlByte |= 0x80U; // Grant Demand Flag
m_slot->writeNetwork(start, DataType::VOICE_LC_HEADER, controlByte);
m_rfN = data[1U] & 0x0FU;
@ -610,7 +618,7 @@ bool Voice::process(uint8_t* data, uint32_t len)
if (m_slot->m_duplex)
m_slot->addFrame(data);
m_slot->writeNetwork(data, DataType::VOICE, errors);
m_slot->writeNetwork(data, DataType::VOICE, 0U, errors);
m_slot->m_rfState = RS_RF_AUDIO;
@ -645,6 +653,8 @@ void Voice::processNetwork(const data::NetData& dmrData)
if (m_slot->m_netState == RS_NET_AUDIO)
return;
lc::FullLC fullLC;
std::unique_ptr<lc::LC> lc = fullLC.decode(data + 2U, DataType::VOICE_LC_HEADER);
if (lc == nullptr) {

@ -581,7 +581,7 @@ void Modem::clock(uint32_t ms)
m_rxDMRQueue1.addData(m_buffer + 3U, m_length - 3U);
if (m_trace)
Utils::dump(1U, "Modem::clock() RX DMR Data 1", m_buffer + 3U, m_length - 3U);
Utils::dump(1U, "[Modem::clock()] RX DMR Data 1", m_buffer + 3U, m_length - 3U);
}
}
break;
@ -607,7 +607,7 @@ void Modem::clock(uint32_t ms)
m_rxDMRQueue2.addData(m_buffer + 3U, m_length - 3U);
if (m_trace)
Utils::dump(1U, "Modem::clock() RX DMR Data 2", m_buffer + 3U, m_length - 3U);
Utils::dump(1U, "[Modem::clock()] RX DMR Data 2", m_buffer + 3U, m_length - 3U);
}
}
break;
@ -669,7 +669,7 @@ void Modem::clock(uint32_t ms)
m_rxP25Queue.addData(m_buffer + (cmdOffset + 1U), m_length - (cmdOffset + 1U));
if (m_trace)
Utils::dump(1U, "Modem::clock() RX P25 Data", m_buffer + (cmdOffset + 1U), m_length - (cmdOffset + 1U));
Utils::dump(1U, "[Modem::clock()] RX P25 Data", m_buffer + (cmdOffset + 1U), m_length - (cmdOffset + 1U));
}
}
break;
@ -712,7 +712,7 @@ void Modem::clock(uint32_t ms)
m_rxNXDNQueue.addData(m_buffer + 3U, m_length - 3U);
if (m_trace)
Utils::dump(1U, "Modem::clock() RX NXDN Data", m_buffer + 3U, m_length - 3U);
Utils::dump(1U, "[Modem::clock()] RX NXDN Data", m_buffer + 3U, m_length - 3U);
}
}
break;
@ -834,9 +834,9 @@ void Modem::clock(uint32_t ms)
m_p25Space = m_buffer[10U] * (P25DEF::P25_LDU_FRAME_LENGTH_BYTES);
if (m_dumpModemStatus) {
LogDebug(LOG_MODEM, "Modem::clock(), CMD_GET_STATUS, isHotspot = %u, dmr = %u / %u, p25 = %u / %u, nxdn = %u / %u, modemState = %u, tx = %u, adcOverflow = %u, rxOverflow = %u, txOverflow = %u, dacOverflow = %u, dmrSpace1 = %u, dmrSpace2 = %u, p25Space = %u, nxdnSpace = %u",
LogDebugEx(LOG_MODEM, "Modem::clock()", "CMD_GET_STATUS, isHotspot = %u, dmr = %u / %u, p25 = %u / %u, nxdn = %u / %u, modemState = %u, tx = %u, adcOverflow = %u, rxOverflow = %u, txOverflow = %u, dacOverflow = %u, dmrSpace1 = %u, dmrSpace2 = %u, p25Space = %u, nxdnSpace = %u",
m_isHotspot, dmrEnable, m_dmrEnabled, p25Enable, m_p25Enabled, nxdnEnable, m_nxdnEnabled, m_modemState, m_tx, adcOverflow, rxOverflow, txOverflow, dacOverflow, m_dmrSpace1, m_dmrSpace2, m_p25Space, m_nxdnSpace);
LogDebug(LOG_MODEM, "Modem::clock(), CMD_GET_STATUS, rxDMRData1 size = %u, len = %u, free = %u; rxDMRData2 size = %u, len = %u, free = %u, rxP25Data size = %u, len = %u, free = %u, rxNXDNData size = %u, len = %u, free = %u",
LogDebugEx(LOG_MODEM, "Modem::clock()", "CMD_GET_STATUS, rxDMRData1 size = %u, len = %u, free = %u; rxDMRData2 size = %u, len = %u, free = %u, rxP25Data size = %u, len = %u, free = %u, rxNXDNData size = %u, len = %u, free = %u",
m_rxDMRQueue1.length(), m_rxDMRQueue1.dataSize(), m_rxDMRQueue1.freeSpace(), m_rxDMRQueue2.length(), m_rxDMRQueue2.dataSize(), m_rxDMRQueue2.freeSpace(),
m_rxP25Queue.length(), m_rxP25Queue.dataSize(), m_rxP25Queue.freeSpace(), m_rxNXDNQueue.length(), m_rxNXDNQueue.dataSize(), m_rxNXDNQueue.freeSpace());
}
@ -907,7 +907,7 @@ void Modem::clock(uint32_t ms)
void Modem::close()
{
LogDebug(LOG_MODEM, "Closing the modem");
LogMessage(LOG_MODEM, "Closing the modem");
m_port->close();
m_gotModemStatus = false;
@ -928,7 +928,7 @@ uint32_t Modem::peekDMRFrame1Length()
uint8_t len = 0U;
m_rxDMRQueue1.peek(&len, 1U);
#if DEBUG_MODEM
LogDebug(LOG_MODEM, "Modem::peekDMRFrame1Length() len = %u, dataSize = %u", len, m_rxDMRQueue1.dataSize());
LogDebugEx(LOG_MODEM, "Modem::peekDMRFrame1Length()", "len = %u, dataSize = %u", len, m_rxDMRQueue1.dataSize());
#endif
// this ensures we never get in a situation where we have length stuck on the queue
if (m_rxDMRQueue1.dataSize() == 1U && len > m_rxDMRQueue1.dataSize()) {
@ -982,7 +982,7 @@ uint32_t Modem::peekDMRFrame2Length()
uint8_t len = 0U;
m_rxDMRQueue2.peek(&len, 1U);
#if DEBUG_MODEM
LogDebug(LOG_MODEM, "Modem::peekDMRFrame2Length() len = %u, dataSize = %u", len, m_rxDMRQueue2.dataSize());
LogDebugEx(LOG_MODEM, "Modem::peekDMRFrame2Length()", "len = %u, dataSize = %u", len, m_rxDMRQueue2.dataSize());
#endif
// this ensures we never get in a situation where we have length stuck on the queue
if (m_rxDMRQueue2.dataSize() == 1U && len > m_rxDMRQueue2.dataSize()) {
@ -1040,7 +1040,7 @@ uint32_t Modem::peekP25FrameLength()
uint16_t len = 0U;
len = (length[0U] << 8) + length[1U];
#if DEBUG_MODEM
LogDebug(LOG_MODEM, "Modem::peekP25FrameLength() len = %u, dataSize = %u", len, m_rxP25Queue.dataSize());
LogDebugEx(LOG_MODEM, "Modem::peekP25FrameLength()", "len = %u, dataSize = %u", len, m_rxP25Queue.dataSize());
#endif
// this ensures we never get in a situation where we have length stuck on the queue
if (m_rxP25Queue.dataSize() == 2U && len > m_rxP25Queue.dataSize()) {
@ -1098,7 +1098,7 @@ uint32_t Modem::peekNXDNFrameLength()
uint8_t len = 0U;
m_rxNXDNQueue.peek(&len, 1U);
#if DEBUG_MODEM
LogDebug(LOG_MODEM, "Modem::peekNXDNFrameLength() len = %u, dataSize = %u", len, m_rxNXDNQueue.dataSize());
LogDebugEx(LOG_MODEM, "Modem::peekNXDNFrameLength()", "len = %u, dataSize = %u", len, m_rxNXDNQueue.dataSize());
#endif
// this ensures we never get in a situation where we have length stuck on the queue
if (m_rxNXDNQueue.dataSize() == 1U && len > m_rxNXDNQueue.dataSize()) {
@ -1222,7 +1222,7 @@ void Modem::clearDMRFrame1()
buffer[1U] = 3U;
buffer[2U] = CMD_DMR_CLEAR1;
#if DEBUG_MODEM
Utils::dump(1U, "Modem::clearDMRFrame1(), Written", buffer, 3U);
Utils::dump(1U, "[Modem::clearDMRFrame1()] Written", buffer, 3U);
#endif
write(buffer, 3U);
Thread::sleep(5); // 5ms delay
@ -1238,7 +1238,7 @@ void Modem::clearDMRFrame2()
buffer[1U] = 3U;
buffer[2U] = CMD_DMR_CLEAR2;
#if DEBUG_MODEM
Utils::dump(1U, "Modem::clearDMRFrame2(), Written", buffer, 3U);
Utils::dump(1U, "[Modem::clearDMRFrame2()] Written", buffer, 3U);
#endif
write(buffer, 3U);
Thread::sleep(5); // 5ms delay
@ -1254,7 +1254,7 @@ void Modem::clearP25Frame()
buffer[1U] = 3U;
buffer[2U] = CMD_P25_CLEAR;
#if DEBUG_MODEM
Utils::dump(1U, "Modem::clearP25Data(), Written", buffer, 3U);
Utils::dump(1U, "[Modem::clearP25Data()] Written", buffer, 3U);
#endif
write(buffer, 3U);
Thread::sleep(5); // 5ms delay
@ -1270,7 +1270,7 @@ void Modem::clearNXDNFrame()
buffer[1U] = 3U;
buffer[2U] = CMD_NXDN_CLEAR;
#if DEBUG_MODEM
Utils::dump(1U, "Modem::clearNXDNFrame(), Written", buffer, 3U);
Utils::dump(1U, "[Modem::clearNXDNFrame()] Written", buffer, 3U);
#endif
write(buffer, 3U);
Thread::sleep(5); // 5ms delay
@ -1381,8 +1381,8 @@ bool Modem::writeDMRFrame1(const uint8_t* data, uint32_t length)
if (data[0U] != TAG_DATA && data[0U] != TAG_EOT)
return false;
if (length > MAX_LENGTH) {
LogError(LOG_MODEM, "Modem::writeDMRData1(); request data to write >%u?, len = %u", MAX_LENGTH, length);
Utils::dump(1U, "Modem::writeDMRData1(); Attmpted Data", data, length);
LogError(LOG_MODEM, "Modem::writeDMRFrame1(); request data to write >%u?, len = %u", MAX_LENGTH, length);
Utils::dump(1U, "[Modem::writeDMRFrame1()] Attmpted Data", data, length);
return false;
}
@ -1399,9 +1399,9 @@ bool Modem::writeDMRFrame1(const uint8_t* data, uint32_t length)
// write or buffer DMR slot 1 data to air interface
if (m_dmrSpace1 >= length) {
if (m_debug)
LogDebug(LOG_MODEM, "Modem::writeDMRData1(); immediate write (len %u)", length);
LogDebugEx(LOG_MODEM, "Modem::writeDMRFrame1()", "immediate write (len %u)", length);
if (m_trace)
Utils::dump(1U, "Modem::writeDMRData1() Immediate TX DMR Data 1", buffer + 3U, length - 1U);
Utils::dump(1U, "[Modem::writeDMRFrame1()] Immediate TX DMR Data 1", buffer + 3U, length - 1U);
int ret = write(buffer, len);
if (ret != int(len)) {
@ -1435,8 +1435,8 @@ bool Modem::writeDMRFrame2(const uint8_t* data, uint32_t length)
if (data[0U] != TAG_DATA && data[0U] != TAG_EOT)
return false;
if (length > MAX_LENGTH) {
LogError(LOG_MODEM, "Modem::writeDMRData2(); request data to write >%u?, len = %u", MAX_LENGTH, length);
Utils::dump(1U, "Modem::writeDMRData2(); Attmpted Data", data, length);
LogError(LOG_MODEM, "Modem::writeDMRFrame2(); request data to write >%u?, len = %u", MAX_LENGTH, length);
Utils::dump(1U, "Modem::writeDMRFrame2(); Attmpted Data", data, length);
return false;
}
@ -1453,9 +1453,9 @@ bool Modem::writeDMRFrame2(const uint8_t* data, uint32_t length)
// write or buffer DMR slot 2 data to air interface
if (m_dmrSpace2 >= length) {
if (m_debug)
LogDebug(LOG_MODEM, "Modem::writeDMRData2(); immediate write (len %u)", length);
LogDebugEx(LOG_MODEM, "Modem::writeDMRFrame2()", "immediate write (len %u)", length);
if (m_trace)
Utils::dump(1U, "Modem::writeDMRData2() Immediate TX DMR Data 2", buffer + 3U, length - 1U);
Utils::dump(1U, "[Modem::writeDMRFrame2()] Immediate TX DMR Data 2", buffer + 3U, length - 1U);
int ret = write(buffer, len);
if (ret != int(len)) {
@ -1492,8 +1492,8 @@ bool Modem::writeP25Frame(const uint8_t* data, uint32_t length)
if (data[0U] != TAG_DATA && data[0U] != TAG_EOT)
return false;
if (length > MAX_LENGTH) {
LogError(LOG_MODEM, "Modem::writeP25Data(); request data to write >%u?, len = %u", MAX_LENGTH, length);
Utils::dump(1U, "Modem::writeP25Data(); Attmpted Data", data, length);
LogError(LOG_MODEM, "Modem::writeP25Frame(); request data to write >%u?, len = %u", MAX_LENGTH, length);
Utils::dump(1U, "[Modem::writeP25Frame()] Attmpted Data", data, length);
return false;
}
@ -1514,14 +1514,14 @@ bool Modem::writeP25Frame(const uint8_t* data, uint32_t length)
::memcpy(buffer + 4U, data + 1U, length - 1U);
}
uint8_t len = length + 2U;
uint32_t len = length + 2U;
// write or buffer P25 data to air interface
if (m_p25Space >= length) {
if (m_debug)
LogDebug(LOG_MODEM, "Modem::writeP25Data(); immediate write (len %u)", length);
LogDebugEx(LOG_MODEM, "Modem::writeP25Frame()", "immediate write (len %u)", length);
if (m_trace)
Utils::dump(1U, "Modem::writeP25Data() Immediate TX P25 Data", buffer + 3U, length - 3U);
Utils::dump(1U, "[Modem::writeP25Frame()] Immediate TX P25 Data", buffer + 3U, length - 3U);
int ret = write(buffer, len);
if (ret != int(len)) {
@ -1555,8 +1555,8 @@ bool Modem::writeNXDNFrame(const uint8_t* data, uint32_t length)
if (data[0U] != TAG_DATA && data[0U] != TAG_EOT)
return false;
if (length > MAX_LENGTH) {
LogError(LOG_MODEM, "Modem::writeNXDNData(); request data to write >%u?, len = %u", MAX_LENGTH, length);
Utils::dump(1U, "Modem::writeNXDNData(); Attmpted Data", data, length);
LogError(LOG_MODEM, "Modem::writeNXDNFrame(); request data to write >%u?, len = %u", MAX_LENGTH, length);
Utils::dump(1U, "[Modem::writeNXDNFrame()] Attmpted Data", data, length);
return false;
}
@ -1573,9 +1573,9 @@ bool Modem::writeNXDNFrame(const uint8_t* data, uint32_t length)
// write or buffer NXDN data to air interface
if (m_nxdnSpace >= length) {
if (m_debug)
LogDebug(LOG_MODEM, "Modem::writeNXDNData(); immediate write (len %u)", length);
LogDebugEx(LOG_MODEM, "Modem::writeNXDNFrame()", "immediate write (len %u)", length);
if (m_trace)
Utils::dump(1U, "Modem::writeNXDNData() Immediate TX NXDN Data", buffer + 3U, length - 1U);
Utils::dump(1U, "[Modem::writeNXDNFrame()] Immediate TX NXDN Data", buffer + 3U, length - 1U);
int ret = write(buffer, len);
if (ret != int(len)) {
@ -1613,7 +1613,7 @@ bool Modem::writeDMRStart(bool tx)
buffer[2U] = CMD_DMR_START;
buffer[3U] = tx ? 0x01U : 0x00U;
#if DEBUG_MODEM
Utils::dump(1U, "Modem::writeDMRStart(), Written", buffer, 4U);
Utils::dump(1U, "[Modem::writeDMRStart()] Written", buffer, 4U);
#endif
return write(buffer, 4U) == 4;
}
@ -1644,7 +1644,7 @@ bool Modem::writeDMRShortLC(const uint8_t* lc)
buffer[10U] = lc[7U];
buffer[11U] = lc[8U];
#if DEBUG_MODEM
Utils::dump(1U, "Modem::writeDMRShortLC(), Written", buffer, 12U);
Utils::dump(1U, "[Modem::writeDMRShortLC()] Written", buffer, 12U);
#endif
return write(buffer, 12U) == 12;
}
@ -1665,7 +1665,7 @@ bool Modem::writeDMRAbort(uint32_t slotNo)
buffer[2U] = CMD_DMR_ABORT;
buffer[3U] = slotNo;
#if DEBUG_MODEM
Utils::dump(1U, "Modem::writeDMRAbort(), Written", buffer, 4U);
Utils::dump(1U, "[Modem::writeDMRAbort()] Written", buffer, 4U);
#endif
return write(buffer, 4U) == 4;
}
@ -1689,7 +1689,7 @@ bool Modem::setDMRIgnoreCACH_AT(uint8_t slotNo)
// are we on a protocol version 3 firmware?
if (m_protoVer >= 3U) {
#if DEBUG_MODEM
Utils::dump(1U, "Modem::setDMRIgnoreCACH_AT(), Written", buffer, 4U);
Utils::dump(1U, "[Modem::setDMRIgnoreCACH_AT()] Written", buffer, 4U);
#endif
return write(buffer, 4U) == 4;
} else {
@ -1734,7 +1734,7 @@ bool Modem::setState(DVM_STATE state)
buffer[2U] = CMD_SET_MODE;
buffer[3U] = state;
#if DEBUG_MODEM
Utils::dump(1U, "Modem::setState(), Written", buffer, 4U);
Utils::dump(1U, "[Modem::setState()] Written", buffer, 4U);
#endif
return write(buffer, 4U) == 4;
}
@ -1876,7 +1876,7 @@ bool Modem::getStatus()
buffer[1U] = 3U;
buffer[2U] = CMD_GET_STATUS;
//LogDebug(LOG_MODEM, "getStatus(), polling modem status");
//LogDebugEx(LOG_MODEM, "Modem::getStatus()", "polling modem status");
return write(buffer, 3U) == 3;
}
@ -2372,7 +2372,7 @@ RESP_TYPE_DVM Modem::getResponse()
{
m_rspDoubleLength = false;
//LogDebug(LOG_MODEM, "Modem::getResponse(), checking if we have data");
//LogDebugEx(LOG_MODEM, "Modem::getResponse()", "checking if we have data");
// get the start of the frame or nothing at all
if (m_rspState == RESP_START) {
@ -2384,13 +2384,13 @@ RESP_TYPE_DVM Modem::getResponse()
}
if (ret == 0) {
//LogDebug(LOG_MODEM, "Modem::getResponse(), no data available");
//LogDebugEx(LOG_MODEM, "Modem::getResponse()", "no data available");
return RTM_TIMEOUT;
}
if (m_buffer[0U] != DVM_SHORT_FRAME_START &&
m_buffer[0U] != DVM_LONG_FRAME_START) {
//LogError(LOG_MODEM, "Modem::getResponse(), illegal response, first byte not a frame start; byte = %02X", m_buffer[0U]);
//LogErrorEx(LOG_MODEM, "Modem::getResponse()", "illegal response, first byte not a frame start; byte = %02X", m_buffer[0U]);
::memset(m_buffer, 0x00U, BUFFER_LENGTH);
m_rspState = RESP_START;
return RTM_ERROR;
@ -2400,12 +2400,12 @@ RESP_TYPE_DVM Modem::getResponse()
m_rspDoubleLength = true;
}
//LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_START");
//LogDebugEx(LOG_MODEM, "Modem::getResponse()", "RESP_START");
m_rspState = RESP_LENGTH1;
}
//LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame length 1/2, rspDoubleLength = %u", m_rspDoubleLength);
//LogDebugEx(LOG_MODEM, "Modem::getResponse()", "getting frame length 1/2, rspDoubleLength = %u", m_rspDoubleLength);
// get the length of the frame, 1/2
if (m_rspState == RESP_LENGTH1) {
int ret = m_port->read(m_buffer + 1U, 1U);
@ -2432,12 +2432,12 @@ RESP_TYPE_DVM Modem::getResponse()
m_length = m_buffer[1U];
}
//LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_LENGTH1, len = %u", m_length);
//LogDebugEx(LOG_MODEM, "Modem::getResponse()", "RESP_LENGTH1, len = %u", m_length);
m_rspOffset = 2U;
}
//LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame length 2/2");
//LogDebugEx(LOG_MODEM, "Modem::getResponse()", "getting frame length 2/2");
// get the length of the frame, 2/2
if (m_rspState == RESP_LENGTH2) {
int ret = m_port->read(m_buffer + 2U, 1U);
@ -2453,13 +2453,13 @@ RESP_TYPE_DVM Modem::getResponse()
m_length = (m_length + (m_buffer[2U] & 0xFFU));
m_rspState = RESP_TYPE;
//LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_LENGTH2, len = %u", m_length);
//LogDebugEx(LOG_MODEM, "Modem::getResponse()", "RESP_LENGTH2, len = %u", m_length);
m_rspDoubleLength = true;
m_rspOffset = 3U;
}
//LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame type");
//LogDebugEx(LOG_MODEM, "Modem::getResponse()", "getting frame type");
// get the frame type
if (m_rspState == RESP_TYPE) {
int ret = m_port->read(m_buffer + m_rspOffset, 1U);
@ -2474,18 +2474,18 @@ RESP_TYPE_DVM Modem::getResponse()
m_rspType = (DVM_COMMANDS)m_buffer[m_rspOffset];
//LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_TYPE, len = %u, type = %u", m_length, m_rspType);
//LogDebugEx(LOG_MODEM, "Modem::getResponse()", "RESP_TYPE, len = %u, type = %u", m_length, m_rspType);
m_rspState = RESP_DATA;
m_rspOffset++;
}
//LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame data");
//LogDebugEx(LOG_MODEM, "Modem::getResponse()", "getting frame data");
// get the frame data
if (m_rspState == RESP_DATA) {
if (m_respTrace)
LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_DATA, len = %u, offset = %u, type = %02X", m_length, m_rspOffset, m_rspType);
LogDebugEx(LOG_MODEM, "Modem::getResponse()", "RESP_DATA, len = %u, offset = %u, type = %02X", m_length, m_rspOffset, m_rspType);
while (m_rspOffset < m_length) {
int ret = m_port->read(m_buffer + m_rspOffset, m_length - m_rspOffset);
@ -2503,7 +2503,7 @@ RESP_TYPE_DVM Modem::getResponse()
}
if (m_respTrace)
Utils::dump(1U, "Modem::getResponse() Buffer", m_buffer, m_length);
Utils::dump(1U, "[Modem::getResponse()] Buffer", m_buffer, m_length);
}
m_rspState = RESP_START;

@ -320,9 +320,9 @@ void ModemV24::clock(uint32_t ms)
m_p25Space = m_buffer[10U] * (P25DEF::P25_LDU_FRAME_LENGTH_BYTES);
if (m_dumpModemStatus) {
LogDebug(LOG_MODEM, "ModemV24::clock(), CMD_GET_STATUS, isHotspot = %u, v24Connected = %u, dmr = %u / %u, p25 = %u / %u, nxdn = %u / %u, modemState = %u, tx = %u, adcOverflow = %u, rxOverflow = %u, txOverflow = %u, dacOverflow = %u, dmrSpace1 = %u, dmrSpace2 = %u, p25Space = %u, nxdnSpace = %u",
LogDebugEx(LOG_MODEM, "ModemV24::clock()", "CMD_GET_STATUS, isHotspot = %u, v24Connected = %u, dmr = %u / %u, p25 = %u / %u, nxdn = %u / %u, modemState = %u, tx = %u, adcOverflow = %u, rxOverflow = %u, txOverflow = %u, dacOverflow = %u, dmrSpace1 = %u, dmrSpace2 = %u, p25Space = %u, nxdnSpace = %u",
m_isHotspot, m_v24Connected, dmrEnable, m_dmrEnabled, p25Enable, m_p25Enabled, nxdnEnable, m_nxdnEnabled, m_modemState, m_tx, adcOverflow, rxOverflow, txOverflow, dacOverflow, m_dmrSpace1, m_dmrSpace2, m_p25Space, m_nxdnSpace);
LogDebug(LOG_MODEM, "ModemV24::clock(), CMD_GET_STATUS, rxDMRData1 size = %u, len = %u, free = %u; rxDMRData2 size = %u, len = %u, free = %u, rxP25Data size = %u, len = %u, free = %u, rxNXDNData size = %u, len = %u, free = %u",
LogDebugEx(LOG_MODEM, "ModemV24::clock()", "CMD_GET_STATUS, rxDMRData1 size = %u, len = %u, free = %u; rxDMRData2 size = %u, len = %u, free = %u, rxP25Data size = %u, len = %u, free = %u, rxNXDNData size = %u, len = %u, free = %u",
m_rxDMRQueue1.length(), m_rxDMRQueue1.dataSize(), m_rxDMRQueue1.freeSpace(), m_rxDMRQueue2.length(), m_rxDMRQueue2.dataSize(), m_rxDMRQueue2.freeSpace(),
m_rxP25Queue.length(), m_rxP25Queue.dataSize(), m_rxP25Queue.freeSpace(), m_rxNXDNQueue.length(), m_rxNXDNQueue.dataSize(), m_rxNXDNQueue.freeSpace());
}
@ -409,7 +409,7 @@ void ModemV24::clock(uint32_t ms)
void ModemV24::close()
{
LogDebug(LOG_MODEM, "Closing the modem");
LogMessage(LOG_MODEM, "Closing the modem");
m_port->close();
m_gotModemStatus = false;
@ -1553,9 +1553,9 @@ void ModemV24::queueP25Frame(uint8_t* data, uint16_t len, SERIAL_TX_TYPE msgType
assert(len > 0U);
if (m_debug)
LogDebug(LOG_MODEM, "ModemV24::queueP25Frame() msgType = $%02X", msgType);
LogDebugEx(LOG_MODEM, "ModemV24::queueP25Frame()", "msgType = $%02X", msgType);
if (m_trace)
Utils::dump(1U, "ModemV24::queueP25Frame() data", data, len);
Utils::dump(1U, "[ModemV24::queueP25Frame()] data", data, len);
// get current time in ms
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

@ -759,7 +759,7 @@ uint8_t* V24UDPPort::generateMessage(const uint8_t* message, uint32_t length, ui
if (timestamp != INVALID_TS) {
timestamp += (RTP_GENERIC_CLOCK_RATE / 133);
if (m_debug)
LogDebug(LOG_NET, "V24UDPPort::generateMessage() RTP streamId = %u, previous TS = %u, TS = %u, rtpSeq = %u", streamId, m_timestamp, timestamp, rtpSeq);
LogDebugEx(LOG_NET, "V24UDPPort::generateMessage()", "RTP streamId = %u, previous TS = %u, TS = %u, rtpSeq = %u", streamId, m_timestamp, timestamp, rtpSeq);
m_timestamp = timestamp;
}
@ -779,20 +779,20 @@ uint8_t* V24UDPPort::generateMessage(const uint8_t* message, uint32_t length, ui
if (streamId != 0U && timestamp == INVALID_TS && rtpSeq != RTP_END_OF_CALL_SEQ) {
if (m_debug)
LogDebug(LOG_NET, "V24UDPPort::generateMessage() RTP streamId = %u, initial TS = %u, rtpSeq = %u", streamId, header.getTimestamp(), rtpSeq);
LogDebugEx(LOG_NET, "V24UDPPort::generateMessage()", "RTP streamId = %u, initial TS = %u, rtpSeq = %u", streamId, header.getTimestamp(), rtpSeq);
m_timestamp = header.getTimestamp();
}
if (streamId != 0U && rtpSeq == RTP_END_OF_CALL_SEQ) {
m_timestamp = INVALID_TS;
if (m_debug)
LogDebug(LOG_NET, "V24UDPPort::generateMessage() RTP streamId = %u, rtpSeq = %u", streamId, rtpSeq);
LogDebugEx(LOG_NET, "V24UDPPort::generateMessage()", "RTP streamId = %u, rtpSeq = %u", streamId, rtpSeq);
}
::memcpy(buffer + RTP_HEADER_LENGTH_BYTES, message, length);
if (m_debug)
Utils::dump(1U, "V24UDPPort::generateMessage() Buffered Message", buffer, bufferLen);
Utils::dump(1U, "[V24UDPPort::generateMessage()] Buffered Message", buffer, bufferLen);
if (outBufferLen != nullptr) {
*outBufferLen = bufferLen;

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -229,7 +229,7 @@ void Network::clock(uint32_t ms)
}
if (m_debug) {
LogDebug(LOG_NET, "RTP, peerId = %u, seq = %u, streamId = %u, func = %02X, subFunc = %02X", fneHeader.getPeerId(), rtpHeader.getSequence(),
LogDebugEx(LOG_NET, "Network::clock()", "RTP, peerId = %u, seq = %u, streamId = %u, func = %02X, subFunc = %02X", fneHeader.getPeerId(), rtpHeader.getSequence(),
fneHeader.getStreamId(), fneHeader.getFunction(), fneHeader.getSubFunction());
}
@ -265,9 +265,26 @@ void Network::clock(uint32_t ms)
{
if (fneHeader.getSubFunction() == NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR) { // Encapsulated DMR data frame
if (m_enabled && m_dmrEnabled) {
uint32_t slotNo = (buffer[15U] & 0x80U) == 0x80U ? 2U : 1U;
uint32_t slotNo = (buffer[15U] & 0x80U) == 0x80U ? 1U : 0U; // this is the raw index for the stream ID array
if (m_debug) {
LogDebug(LOG_NET, "DMR Slot %u, peer = %u, len = %u, pktSeq = %u, streamId = %u",
slotNo + 1U, peerId, length, rtpHeader.getSequence(), streamId);
}
if (m_promiscuousPeer) {
m_rxDMRStreamId[slotNo] = streamId;
m_pktLastSeq = m_pktSeq;
}
else {
if (m_rxDMRStreamId[slotNo] == 0U) {
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
m_rxDMRStreamId[slotNo] = 0U;
}
else {
m_rxDMRStreamId[slotNo] = streamId;
}
m_pktLastSeq = m_pktSeq;
}
else {
@ -280,10 +297,15 @@ void Network::clock(uint32_t ms)
m_pktLastSeq = m_pktSeq;
}
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
m_rxDMRStreamId[slotNo] = 0U;
}
}
}
if (m_debug)
Utils::dump(1U, "Network Received, DMR", buffer.get(), length);
Utils::dump(1U, "[Network::clock()] Network Received, DMR", buffer.get(), length);
if (length > 255)
LogError(LOG_NET, "DMR Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, m_pktSeq, length);
@ -294,8 +316,24 @@ void Network::clock(uint32_t ms)
}
else if (fneHeader.getSubFunction() == NET_SUBFUNC::PROTOCOL_SUBFUNC_P25) { // Encapsulated P25 data frame
if (m_enabled && m_p25Enabled) {
if (m_debug) {
LogDebug(LOG_NET, "P25, peer = %u, len = %u, pktSeq = %u, streamId = %u",
peerId, length, rtpHeader.getSequence(), streamId);
}
if (m_promiscuousPeer) {
m_rxP25StreamId = streamId;
m_pktLastSeq = m_pktSeq;
}
else {
if (m_rxP25StreamId == 0U) {
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
m_rxP25StreamId = 0U;
}
else {
m_rxP25StreamId = streamId;
}
m_pktLastSeq = m_pktSeq;
}
else {
@ -308,10 +346,15 @@ void Network::clock(uint32_t ms)
m_pktLastSeq = m_pktSeq;
}
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
m_rxP25StreamId = 0U;
}
}
}
if (m_debug)
Utils::dump(1U, "Network Received, P25", buffer.get(), length);
Utils::dump(1U, "[Network::clock()] Network Received, P25", buffer.get(), length);
if (length > 255)
LogError(LOG_NET, "P25 Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, m_pktSeq, length);
@ -322,8 +365,24 @@ void Network::clock(uint32_t ms)
}
else if (fneHeader.getSubFunction() == NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN) { // Encapsulated NXDN data frame
if (m_enabled && m_nxdnEnabled) {
if (m_debug) {
LogDebug(LOG_NET, "NXDN, peer = %u, len = %u, pktSeq = %u, streamId = %u",
peerId, length, rtpHeader.getSequence(), streamId);
}
if (m_promiscuousPeer) {
m_rxNXDNStreamId = streamId;
m_pktLastSeq = m_pktSeq;
}
else {
if (m_rxNXDNStreamId == 0U) {
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
m_rxNXDNStreamId = 0U;
}
else {
m_rxNXDNStreamId = streamId;
}
m_pktLastSeq = m_pktSeq;
}
else {
@ -336,10 +395,15 @@ void Network::clock(uint32_t ms)
m_pktLastSeq = m_pktSeq;
}
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
m_rxNXDNStreamId = 0U;
}
}
}
if (m_debug)
Utils::dump(1U, "Network Received, NXDN", buffer.get(), length);
Utils::dump(1U, "[Network::clock()] Network Received, NXDN", buffer.get(), length);
if (length > 255)
LogError(LOG_NET, "NXDN Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, m_pktSeq, length);
@ -443,7 +507,7 @@ void Network::clock(uint32_t ms)
offs += 5U;
}
LogMessage(LOG_NET, "Activated %u TGs; loaded %u entries into lookup table", len, m_tidLookup->groupVoice().size());
LogMessage(LOG_NET, "Activated %u TGs; loaded %u entries into talkgroup rules table", len, m_tidLookup->groupVoice().size());
// save if saving from network is enabled
if (m_saveLookup && len > 0) {
@ -474,7 +538,7 @@ 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());
LogMessage(LOG_NET, "Deactivated %u TGs; loaded %u entries into talkgroup rules table", len, m_tidLookup->groupVoice().size());
// save if saving from network is enabled
if (m_saveLookup && len > 0) {
@ -590,7 +654,7 @@ void Network::clock(uint32_t ms)
{
switch (m_status) {
case NET_STAT_WAITING_LOGIN:
LogDebug(LOG_NET, "PEER %u RPTL ACK, performing login exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC());
LogMessage(LOG_NET, "PEER %u RPTL ACK, performing login exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC());
::memcpy(m_salt, buffer.get() + 6U, sizeof(uint32_t));
writeAuthorisation();
@ -600,7 +664,7 @@ void Network::clock(uint32_t ms)
m_retryTimer.start();
break;
case NET_STAT_WAITING_AUTHORISATION:
LogDebug(LOG_NET, "PEER %u RPTK ACK, performing configuration exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC());
LogMessage(LOG_NET, "PEER %u RPTK ACK, performing configuration exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC());
writeConfig();
@ -711,7 +775,7 @@ bool Network::open()
LogMessage(LOG_NET, "PEER %u opening network", m_peerId);
if (udp::Socket::lookup(m_address, m_port, m_addr, m_addrLen) != 0) {
LogMessage(LOG_NET, "Could not lookup the address of the master");
LogMessage(LOG_NET, "!!! Could not lookup the address of the master!");
return false;
}

@ -349,7 +349,7 @@ bool RESTAPI::validateAuth(const HTTPPayload& request, HTTPPayload& reply)
std::string host = request.headers.find("RemoteHost");
std::string headerToken = request.headers.find("X-DVM-Auth-Token");
#if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "RESTAPI::validateAuth() token, host = %s, token = %s", host.c_str(), headerToken.c_str());
::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "token, host = %s, token = %s", host.c_str(), headerToken.c_str());
#endif
if (headerToken.empty()) {
errorPayload(reply, "no authentication token", HTTPPayload::UNAUTHORIZED);
@ -358,11 +358,11 @@ bool RESTAPI::validateAuth(const HTTPPayload& request, HTTPPayload& reply)
for (auto& token : m_authTokens) {
#if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "RESTAPI::validateAuth() valid list, host = %s, token = %s", token.first.c_str(), std::to_string(token.second).c_str());
::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "valid list, host = %s, token = %s", token.first.c_str(), std::to_string(token.second).c_str());
#endif
if (token.first.compare(host) == 0) {
#if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "RESTAPI::validateAuth() storedToken = %s, passedToken = %s", std::to_string(token.second).c_str(), headerToken.c_str());
::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "storedToken = %s, passedToken = %s", std::to_string(token.second).c_str(), headerToken.c_str());
#endif
if (std::to_string(token.second).compare(headerToken) == 0) {
return true;
@ -1006,7 +1006,7 @@ void RESTAPI::restAPI_PutRegisterCCVC(const HTTPPayload& request, HTTPPayload& r
uint32_t peerId = req["peerId"].get<uint32_t>();
// LogDebug(LOG_REST, "restAPI_PutRegisterCCVC(): callback, channelNo = %u, peerId = %u", channelNo, peerId);
// LogDebugEx(LOG_REST, "RESTAPI::restAPI_PutRegisterCCVC()", "callback, channelNo = %u, peerId = %u", channelNo, peerId);
// validate restAddress is a string within the JSON blob
if (!req["restAddress"].is<std::string>()) {
@ -1087,7 +1087,7 @@ void RESTAPI::restAPI_PutReleaseGrant(const HTTPPayload& request, HTTPPayload& r
return;
}
// LogDebug(LOG_REST, "restAPI_PutReleaseGrant(): callback, state = %u, dstId = %u", state, dstId);
// LogDebugEx(LOG_REST, "RESTAPI::restAPI_PutReleaseGrant()", "callback, state = %u, dstId = %u", state, dstId);
switch (state) {
case STATE_DMR:
@ -1179,7 +1179,7 @@ void RESTAPI::restAPI_PutTouchGrant(const HTTPPayload& request, HTTPPayload& rep
return;
}
// LogDebug(LOG_REST, "restAPI_PutTouchGrant(): callback, state = %u, dstId = %u", state, dstId);
// LogDebugEx(LOG_REST, "RESTAPI::restAPI_PutTouchGrant()", "callback, state = %u, dstId = %u", state, dstId);
switch (state) {
case STATE_DMR:

@ -895,7 +895,7 @@ void Control::addFrame(const uint8_t *data, bool net, bool imm)
uint32_t fifoSpace = m_modem->getNXDNSpace();
//LogDebug(LOG_NXDN, "addFrame() fifoSpace = %u", fifoSpace);
//LogDebugEx(LOG_NXDN, "Control::addFrame()", "fifoSpace = %u", fifoSpace);
// is this immediate data?
if (imm) {

@ -398,7 +398,7 @@ void ControlSignaling::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adj
do
{
if (m_debug) {
LogDebug(LOG_NXDN, "writeRF_ControlData, frameCnt = %u, seq = %u", frameCnt, n);
LogDebugEx(LOG_NXDN, "ControlSignaling::writeRF_ControlData()", "frameCnt = %u, seq = %u", frameCnt, n);
}
switch (n)

@ -446,6 +446,12 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
m_network->setP25ICCCallback([=](network::NET_ICC::ENUM command, uint32_t dstId) { processInCallCtrl(command, dstId); });
}
// throw a warning if we are notifying a CC of our presence (this indicates we're a VC) *AND* we have the control
// enable flag set
if (m_enableControl && m_notifyCC) {
LogWarning(LOG_P25, "We are configured as a dedicated trunked voice channel but, have control data enabled. Don't do this, control data for a dedicated trunked voice channel should be disabled. This can cause unintended behavior.");
}
if (printOptions) {
LogInfo(" Silence Threshold: %u (%.1f%%)", m_voice->m_silenceThreshold, float(m_voice->m_silenceThreshold) / 12.33F);
LogInfo(" Frame Loss Threshold: %u", m_frameLossThreshold);
@ -1206,7 +1212,7 @@ void Control::addFrame(const uint8_t* data, uint32_t length, bool net, bool imm)
uint32_t fifoSpace = m_modem->getP25Space();
//LogDebug(LOG_P25, "addFrame() fifoSpace = %u", fifoSpace);
// LogDebugEx(LOG_P25, "Control::addFrame()", "fifoSpace = %u", fifoSpace);
// is this immediate data?
if (imm) {
@ -1284,6 +1290,7 @@ void Control::processNetwork()
bool grantDemand = (buffer[14U] & 0x80U) == 0x80U;
bool grantDenial = (buffer[14U] & 0x40U) == 0x40U;
bool grantEncrypt = (buffer[14U] & 0x08U) == 0x08U;
bool unitToUnit = (buffer[14U] & 0x01U) == 0x01U;
// process network message header
@ -1328,7 +1335,7 @@ void Control::processNetwork()
}
if (!m_dedicatedControl)
ret = m_data->processNetwork(data.get(), frameLength, blockLength);
m_data->processNetwork(data.get(), frameLength, blockLength);
return;
}
@ -1425,7 +1432,7 @@ void Control::processNetwork()
break;
}
ret = m_voice->processNetwork(data.get(), frameLength, control, lsd, duid, frameType);
m_voice->processNetwork(data.get(), frameLength, control, lsd, duid, frameType);
break;
case DUID::TDU:
@ -1452,12 +1459,15 @@ void Control::processNetwork()
return;
}
if (grantEncrypt)
control.setEncrypted(true); // make sure encrypted flag is set
uint8_t serviceOptions = (control.getEmergency() ? 0x80U : 0x00U) + // Emergency Flag
(control.getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
(control.getPriority() & 0x07U); // Priority
if (m_verbose) {
LogMessage(LOG_NET, P25_TSDU_STR " remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u", srcId, dstId, unitToUnit);
LogMessage(LOG_NET, P25_TSDU_STR " remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u, encrypted = %u", srcId, dstId, unitToUnit, grantEncrypt);
}
// are we denying the grant?
@ -1734,10 +1744,6 @@ void Control::writeRF_Nulls()
data[0U] = modem::TAG_EOT;
data[1U] = 0x00U;
if (m_debug) {
LogDebug(LOG_P25, "writeRF_Nulls()");
}
addFrame(data, NULLS_LENGTH_BYTES + 2U);
}

@ -1784,7 +1784,7 @@ void ControlSignaling::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adj
}
if (m_debug) {
LogDebug(LOG_P25, "writeRF_ControlData, mbfCnt = %u, frameCnt = %u, seq = %u, adjSS = %u", m_mbfCnt, frameCnt, n, adjSS);
LogDebugEx(LOG_P25, "ControlSignaling::writeRF_ControlData()", "mbfCnt = %u, frameCnt = %u, seq = %u, adjSS = %u", m_mbfCnt, frameCnt, n, adjSS);
}
// bryanb: this is just a simple counter because we treat the SYNC_BCST as unlocked
@ -1877,7 +1877,7 @@ void ControlSignaling::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adj
queueRF_TSBK_Ctrl(TSBKO::OSP_RFSS_STS_BCAST);
queueRF_TSBK_Ctrl(TSBKO::OSP_NET_STS_BCAST);
if (m_debug) {
LogDebug(LOG_P25, "writeRF_ControlData, have 1 pad 2, mbfCnt = %u", m_mbfCnt);
LogDebugEx(LOG_P25, "ControlSignaling::writeRF_ControlData()]", "have 1 pad 2, mbfCnt = %u", m_mbfCnt);
}
}
@ -1892,7 +1892,7 @@ void ControlSignaling::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adj
}
if (m_debug) {
LogDebug(LOG_P25, "writeRF_ControlData, have 2 pad 1, mbfCnt = %u", m_mbfCnt);
LogDebugEx(LOG_P25, "ControlSignaling::writeRF_ControlData()", "have 2 pad 1, mbfCnt = %u", m_mbfCnt);
}
}

@ -75,6 +75,9 @@ void Voice::resetNet()
m_netLastLDU1 = lc;
//m_netLastFrameType = P25_FT_DATA_UNIT;
m_gotNetLDU1 = false;
m_gotNetLDU2 = false;
m_netFrames = 0U;
m_netLost = 0U;
m_vocLDU1Count = 0U;
@ -385,7 +388,12 @@ bool Voice::process(uint8_t* data, uint32_t len)
// send network grant demand TDU
if (m_p25->m_network != nullptr) {
if (!m_p25->m_dedicatedControl && m_p25->m_convNetGrantDemand) {
uint8_t controlByte = 0x80U + ((group) ? 0x00U : 0x01U);
uint8_t controlByte = 0x80U; // Grant Demand Flag
if (encrypted)
controlByte |= 0x08U; // Grant Encrypt Flag
if (!group)
controlByte |= 0x01U; // Unit-to-unit Flag
LogMessage(LOG_RF, P25_HDU_STR " remote grant demand, srcId = %u, dstId = %u", srcId, dstId);
m_p25->m_network->writeP25TDU(lc, m_rfLSD, controlByte);
}
@ -437,7 +445,9 @@ bool Voice::process(uint8_t* data, uint32_t len)
// conventional registration or DVRS support?
if ((m_p25->m_enableControl && !m_p25->m_dedicatedControl) || m_p25->m_voiceOnControl) {
m_p25->m_control->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, group, true, true);
if (!m_p25->m_affiliations.isGranted(dstId)) {
m_p25->m_control->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, group, false, true);
}
// if voice on control; insert grant updates before voice traffic
if (m_p25->m_voiceOnControl) {
@ -1241,6 +1251,8 @@ bool Voice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L
m_dfsiLC.decodeLDU1(data + count, m_netLDU1 + 204U);
count += DFSI_LDU1_VOICE9_FRAME_LENGTH_BYTES;
m_gotNetLDU1 = true;
// these aren't set by the DFSI decoder, so we'll manually
// reset them
m_dfsiLC.control()->setNetId(control.getNetId());
@ -1321,6 +1333,8 @@ bool Voice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L
m_dfsiLC.decodeLDU2(data + count, m_netLDU2 + 204U);
count += DFSI_LDU2_VOICE18_FRAME_LENGTH_BYTES;
m_gotNetLDU2 = true;
if (m_p25->m_enableControl) {
lc::LC control = lc::LC(*m_dfsiLC.control());
m_p25->m_affiliations.touchGrant(control.getDstId());
@ -1431,7 +1445,9 @@ Voice::Voice(Control* p25, bool debug, bool verbose) :
m_rfLSD(),
m_netLSD(),
m_dfsiLC(),
m_gotNetLDU1(false),
m_netLDU1(nullptr),
m_gotNetLDU2(false),
m_netLDU2(nullptr),
m_lastDUID(DUID::TDU),
m_lastIMBE(nullptr),
@ -1449,7 +1465,9 @@ Voice::Voice(Control* p25, bool debug, bool verbose) :
m_netLDU2 = new uint8_t[9U * 25U];
::memset(m_netLDU1, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU1, false);
::memset(m_netLDU2, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU2, false);
m_lastIMBE = new uint8_t[RAW_IMBE_LENGTH_BYTES];
::memcpy(m_lastIMBE, NULL_IMBE, RAW_IMBE_LENGTH_BYTES);
@ -1556,8 +1574,8 @@ void Voice::writeNet_TDU()
if (m_p25->m_network != nullptr)
m_p25->m_network->resetP25();
::memset(m_netLDU1, 0x00U, 9U * 25U);
::memset(m_netLDU2, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU1, false);
resetWithNullAudio(m_netLDU2, false);
m_p25->m_netTimeout.stop();
m_p25->m_networkWatchdog.stop();
@ -1580,9 +1598,10 @@ void Voice::checkNet_LDU1()
return;
// check for an unflushed LDU1
if (m_netLDU1[10U] != 0x00U || m_netLDU1[26U] != 0x00U || m_netLDU1[55U] != 0x00U ||
if ((m_netLDU1[10U] != 0x00U || m_netLDU1[26U] != 0x00U || m_netLDU1[55U] != 0x00U ||
m_netLDU1[80U] != 0x00U || m_netLDU1[105U] != 0x00U || m_netLDU1[130U] != 0x00U ||
m_netLDU1[155U] != 0x00U || m_netLDU1[180U] != 0x00U || m_netLDU1[204U] != 0x00U)
m_netLDU1[155U] != 0x00U || m_netLDU1[180U] != 0x00U || m_netLDU1[204U] != 0x00U) &&
m_gotNetLDU1)
writeNet_LDU1();
}
@ -1711,6 +1730,7 @@ void Voice::writeNet_LDU1()
(m_netLC.getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
(m_netLC.getPriority() & 0x07U); // Priority
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);
@ -1719,8 +1739,8 @@ void Voice::writeNet_LDU1()
if (m_p25->m_network != nullptr)
m_p25->m_network->resetP25();
::memset(m_netLDU1, 0x00U, 9U * 25U);
::memset(m_netLDU2, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU1, false);
resetWithNullAudio(m_netLDU2, false);
m_p25->m_netTimeout.stop();
m_p25->m_networkWatchdog.stop();
@ -1740,6 +1760,7 @@ void Voice::writeNet_LDU1()
return;
}
}
}
m_p25->writeRF_Preamble(0, true);
@ -1933,7 +1954,8 @@ void Voice::writeNet_LDU1()
sysId, netId);
}
::memset(m_netLDU1, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU1, m_netLC.getAlgId() != P25DEF::ALGO_UNENCRYPT);
m_gotNetLDU1 = false;
m_netFrames += 9U;
}
@ -1946,10 +1968,12 @@ void Voice::checkNet_LDU2()
return;
// check for an unflushed LDU2
if (m_netLDU2[10U] != 0x00U || m_netLDU2[26U] != 0x00U || m_netLDU2[55U] != 0x00U ||
if ((m_netLDU2[10U] != 0x00U || m_netLDU2[26U] != 0x00U || m_netLDU2[55U] != 0x00U ||
m_netLDU2[80U] != 0x00U || m_netLDU2[105U] != 0x00U || m_netLDU2[130U] != 0x00U ||
m_netLDU2[155U] != 0x00U || m_netLDU2[180U] != 0x00U || m_netLDU2[204U] != 0x00U)
m_netLDU2[155U] != 0x00U || m_netLDU2[180U] != 0x00U || m_netLDU2[204U] != 0x00U) &&
m_gotNetLDU2) {
writeNet_LDU2();
}
}
/* Helper to write a network P25 LDU2 packet. */
@ -2022,14 +2046,15 @@ void Voice::writeNet_LDU2()
LogMessage(LOG_NET, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", m_netLC.getAlgId(), m_netLC.getKId());
}
::memset(m_netLDU2, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU2, m_netLC.getAlgId() != P25DEF::ALGO_UNENCRYPT);
m_gotNetLDU2 = false;
m_netFrames += 9U;
}
/* Helper to insert IMBE silence frames for missing audio. */
void Voice::insertMissingAudio(uint8_t *data)
void Voice::insertMissingAudio(uint8_t* data)
{
if (data[10U] == 0x00U) {
::memcpy(data + 10U, m_lastIMBE, 11U);
@ -2186,6 +2211,44 @@ void Voice::insertEncryptedNullAudio(uint8_t *data)
}
}
/* Helper to reset IMBE buffer with null frames. */
void Voice::resetWithNullAudio(uint8_t* data, bool encrypted)
{
if (data == nullptr)
return;
// clear buffer for next sequence
::memset(data, 0x00U, 9U * 25U);
// fill with null
if (!encrypted) {
::memcpy(data + 10U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 26U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 55U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 80U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 105U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 130U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 155U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 180U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 204U, P25DEF::NULL_IMBE, 11U);
} else {
::memcpy(data + 10U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 26U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 55U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 80U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 105U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 130U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 155U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 180U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 204U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
}
}
/* Given the last MI, generate the next MI using LFSR. */
void Voice::getNextMI(uint8_t lastMI[9U], uint8_t nextMI[9U])

@ -9,7 +9,7 @@
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL
*
*/
/**
@ -111,7 +111,9 @@ namespace p25
data::LowSpeedData m_netLSD;
dfsi::LC m_dfsiLC;
bool m_gotNetLDU1;
uint8_t* m_netLDU1;
bool m_gotNetLDU2;
uint8_t* m_netLDU2;
defines::DUID::E m_lastDUID;
@ -192,6 +194,14 @@ namespace p25
* @param data Buffer containing frame data.
*/
void insertEncryptedNullAudio(uint8_t* data);
/**
* @brief Helper to reset IMBE buffer with null frames.
* @param data Buffer containing frame data.
* @param encrypted Flag indicating whether or not the data is encrypted.
*/
void resetWithNullAudio(uint8_t* data, bool encrypted);
/**
* @brief Given the last MI, generate the next MI using LFSR.
* @param lastMI Last MI received.

@ -533,7 +533,7 @@ void* HostWS::threadWebSocket(void* arg)
return nullptr;
}
LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str());
#ifdef _GNU_SOURCE
::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
@ -542,7 +542,7 @@ void* HostWS::threadWebSocket(void* arg)
ws->m_wsServer.start_accept();
ws->m_wsServer.run();
LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str());
delete th;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save

Powered by TurnKey Linux.