diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp index dd444613..b8f8c258 100644 --- a/src/host/Host.Config.cpp +++ b/src/host/Host.Config.cpp @@ -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); diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index d55b9996..f3d54285 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -441,6 +441,12 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw } }); + // 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); @@ -1266,6 +1272,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 @@ -1434,12 +1441,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? diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index c1bba2d3..8feb11e9 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -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,33 +1730,35 @@ void Voice::writeNet_LDU1() (m_netLC.getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag (m_netLC.getPriority() & 0x07U); // Priority - 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); + 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); - if ((!m_p25->m_networkWatchdog.isRunning() || m_p25->m_networkWatchdog.hasExpired()) && - m_p25->m_netLastDstId != 0U) { - if (m_p25->m_network != nullptr) - m_p25->m_network->resetP25(); + if ((!m_p25->m_networkWatchdog.isRunning() || m_p25->m_networkWatchdog.hasExpired()) && + m_p25->m_netLastDstId != 0U) { + 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(); + m_p25->m_netTimeout.stop(); + m_p25->m_networkWatchdog.stop(); - m_netLC = lc::LC(); - m_netLastLDU1 = lc::LC(); - m_netLastFrameType = FrameType::DATA_UNIT; + m_netLC = lc::LC(); + m_netLastLDU1 = lc::LC(); + m_netLastFrameType = FrameType::DATA_UNIT; - m_p25->m_netState = RS_NET_IDLE; - m_p25->m_netLastDstId = 0U; - m_p25->m_netLastSrcId = 0U; + m_p25->m_netState = RS_NET_IDLE; + m_p25->m_netLastDstId = 0U; + m_p25->m_netLastSrcId = 0U; - if (m_p25->m_rfState == RS_RF_REJECTED) { - m_p25->m_rfState = RS_RF_LISTENING; - } + if (m_p25->m_rfState == RS_RF_REJECTED) { + m_p25->m_rfState = RS_RF_LISTENING; + } - return; + return; + } } } @@ -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]) diff --git a/src/host/p25/packet/Voice.h b/src/host/p25/packet/Voice.h index c1452535..51d7f8e6 100644 --- a/src/host/p25/packet/Voice.h +++ b/src/host/p25/packet/Voice.h @@ -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.