refactor base Network class to follow same packet processing refactor that TrafficNetwork and MetadataNetwork did;
parent
a5bebe87f8
commit
4e7a08ea18
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,83 @@
|
||||
// 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) 2017-2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "common/Log.h"
|
||||
#include "network/Network.h"
|
||||
|
||||
using namespace network;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Handles NET_FUNC::ACK packets. */
|
||||
|
||||
bool Network::PacketHandler::ack(Network* network, uint32_t peerId, uint32_t streamId, uint64_t now,
|
||||
const frame::RTPFNEHeader& fneHeader, const frame::RTPHeader& rtpHeader, const uint8_t* buffer, int length)
|
||||
{
|
||||
(void)peerId;
|
||||
(void)streamId;
|
||||
(void)now;
|
||||
(void)fneHeader;
|
||||
|
||||
switch (network->m_status) {
|
||||
case NET_STAT_WAITING_LOGIN:
|
||||
LogInfoEx(LOG_NET, "PEER %u RPTL ACK, performing login exchange, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
|
||||
::memcpy(network->m_salt, buffer + 6U, sizeof(uint32_t));
|
||||
network->writeAuthorisation();
|
||||
|
||||
network->m_status = NET_STAT_WAITING_AUTHORISATION;
|
||||
network->m_timeoutTimer.start();
|
||||
network->m_retryTimer.start();
|
||||
break;
|
||||
case NET_STAT_WAITING_AUTHORISATION:
|
||||
LogInfoEx(LOG_NET, "PEER %u RPTK ACK, performing configuration exchange, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
|
||||
network->writeConfig();
|
||||
|
||||
network->m_status = NET_STAT_WAITING_CONFIG;
|
||||
network->m_timeoutTimer.start();
|
||||
network->m_retryTimer.start();
|
||||
break;
|
||||
case NET_STAT_WAITING_CONFIG:
|
||||
LogInfoEx(LOG_NET, "PEER %u RPTC ACK, logged into the master successfully, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
network->m_loginStreamId = 0U;
|
||||
network->m_remotePeerId = rtpHeader.getSSRC();
|
||||
|
||||
network->pktSeq(true);
|
||||
|
||||
// fire off peer connected callback if we have one
|
||||
if (network->m_peerConnectedCallback != nullptr) {
|
||||
network->m_peerConnectedCallback();
|
||||
}
|
||||
|
||||
network->m_status = NET_STAT_RUNNING;
|
||||
network->m_timeoutTimer.start();
|
||||
network->m_retryTimer.setTimeout(DEFAULT_RETRY_TIME);
|
||||
network->m_retryTimer.start();
|
||||
|
||||
if (length > 6) {
|
||||
bool useAlternatePortForDiagnostics = (buffer[6U] & 0x80U) == 0x80U;
|
||||
if (!useAlternatePortForDiagnostics) {
|
||||
// disable diagnostic and activity logging automatically if the master doesn't utilize the secondary port
|
||||
network->m_allowDiagnosticTransfer = false;
|
||||
network->m_allowActivityTransfer = false;
|
||||
LogError(LOG_NET, "PEER %u RPTC ACK, master does not enable secondary port for metadata, diagnostic and activity logging are disabled, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
LogError(LOG_NET, "PEER %u RPTC ACK, **please update your FNE**, secondary port for metadata, is required for all services as of R05A04, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
// 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) 2017-2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "common/Log.h"
|
||||
#include "network/Network.h"
|
||||
|
||||
using namespace network;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Handles NET_FUNC::INCALL_CTRL packets. */
|
||||
|
||||
bool Network::PacketHandler::inCallControl(Network* network, uint32_t peerId, uint32_t streamId, uint64_t now,
|
||||
const frame::RTPFNEHeader& fneHeader, const frame::RTPHeader& rtpHeader, const uint8_t* buffer, int length)
|
||||
{
|
||||
(void)now;
|
||||
(void)length;
|
||||
|
||||
uint32_t ssrc = rtpHeader.getSSRC();
|
||||
if (!network->m_promiscuousPeer && ssrc != peerId) {
|
||||
LogWarning(LOG_NET, "PEER %u, ignoring in-call control not destined for this peer SSRC %u", network->m_peerId, ssrc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// process incoming message subfunction opcodes
|
||||
switch (fneHeader.getSubFunction()) {
|
||||
case NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR: // DMR In-Call Control
|
||||
{
|
||||
if (network->m_enabled && network->m_dmrEnabled) {
|
||||
NET_ICC::ENUM command = (NET_ICC::ENUM)buffer[10U];
|
||||
uint32_t dstId = GET_UINT24(buffer, 11U);
|
||||
uint8_t slot = buffer[14U];
|
||||
|
||||
// fire off DMR in-call callback if we have one
|
||||
if (network->m_dmrInCallCallback != nullptr) {
|
||||
network->m_dmrInCallCallback(command, dstId, slot, peerId, ssrc, streamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NET_SUBFUNC::PROTOCOL_SUBFUNC_P25: // P25 In-Call Control
|
||||
{
|
||||
if (network->m_enabled && network->m_p25Enabled) {
|
||||
NET_ICC::ENUM command = (NET_ICC::ENUM)buffer[10U];
|
||||
uint32_t dstId = GET_UINT24(buffer, 11U);
|
||||
|
||||
// fire off P25 in-call callback if we have one
|
||||
if (network->m_p25InCallCallback != nullptr) {
|
||||
network->m_p25InCallCallback(command, dstId, peerId, ssrc, streamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN: // NXDN In-Call Control
|
||||
{
|
||||
if (network->m_enabled && network->m_nxdnEnabled) {
|
||||
NET_ICC::ENUM command = (NET_ICC::ENUM)buffer[10U];
|
||||
uint32_t dstId = GET_UINT24(buffer, 11U);
|
||||
|
||||
// fire off NXDN in-call callback if we have one
|
||||
if (network->m_nxdnInCallCallback != nullptr) {
|
||||
network->m_nxdnInCallCallback(command, dstId, peerId, ssrc, streamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG: // Analog In-Call Control
|
||||
{
|
||||
if (network->m_enabled && network->m_analogEnabled) {
|
||||
NET_ICC::ENUM command = (NET_ICC::ENUM)buffer[10U];
|
||||
uint32_t dstId = GET_UINT24(buffer, 11U);
|
||||
|
||||
// fire off analog in-call callback if we have one
|
||||
if (network->m_analogInCallCallback != nullptr) {
|
||||
network->m_analogInCallCallback(command, dstId, peerId, ssrc, streamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Utils::dump("unknown incall control opcode from the master", buffer, length);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
// 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) 2017-2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "common/p25/kmm/KMMFactory.h"
|
||||
#include "common/Log.h"
|
||||
#include "network/Network.h"
|
||||
|
||||
using namespace network;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Handles NET_FUNC::KEY_RSP packets. */
|
||||
|
||||
bool Network::PacketHandler::keyResponse(Network* network, uint32_t peerId, uint32_t streamId, uint64_t now,
|
||||
const frame::RTPFNEHeader& fneHeader, const frame::RTPHeader& rtpHeader, const uint8_t* buffer, int length)
|
||||
{
|
||||
(void)peerId;
|
||||
(void)streamId;
|
||||
(void)now;
|
||||
(void)fneHeader;
|
||||
(void)rtpHeader;
|
||||
(void)length;
|
||||
|
||||
if (network->m_enabled) {
|
||||
using namespace p25::kmm;
|
||||
|
||||
std::unique_ptr<KMMFrame> frame = KMMFactory::create(buffer + 11U);
|
||||
if (frame == nullptr) {
|
||||
LogWarning(LOG_NET, "PEER %u, undecodable KMM frame from master", network->m_peerId);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (frame->getMessageId()) {
|
||||
case P25DEF::KMM_MessageType::MODIFY_KEY_CMD:
|
||||
{
|
||||
KMMModifyKey* modifyKey = static_cast<KMMModifyKey*>(frame.get());
|
||||
if (modifyKey->getAlgId() > 0U) {
|
||||
KeysetItem ks = modifyKey->getKeysetItem();
|
||||
if (ks.keys().size() > 0U) {
|
||||
// fetch first key (a master response should never really send back more then one key)
|
||||
KeyItem ki = ks.keys()[0];
|
||||
LogInfoEx(LOG_NET, "PEER %u, master reported enc. key, algId = $%02X, kID = $%04X", network->m_peerId,
|
||||
ks.algId(), ki.kId());
|
||||
|
||||
if (!network->m_passKeysWithNoPresharedKey && (modifyKey->getDecryptInfoFmt() == KMM_DECRYPT_PEER_ENC)) {
|
||||
// if the to decrypt the key with a preshared key, do that now
|
||||
if (network->m_kmfPresharedKey != nullptr) {
|
||||
uint8_t encryptedKey[P25DEF::MAX_ENC_KEY_LENGTH_BYTES];
|
||||
::memset(encryptedKey, 0x00U, P25DEF::MAX_ENC_KEY_LENGTH_BYTES);
|
||||
ki.getKey(encryptedKey);
|
||||
|
||||
uint8_t* key = nullptr;
|
||||
crypto::AES aes = crypto::AES(crypto::AESKeyLength::AES_256);
|
||||
key = aes.decryptECB(encryptedKey, P25DEF::MAX_ENC_KEY_LENGTH_BYTES, network->m_kmfPresharedKey);
|
||||
|
||||
uint32_t keyLength = P25DEF::MAX_ENC_KEY_LENGTH_BYTES;
|
||||
switch (ks.algId()) {
|
||||
case P25DEF::ALGO_DES:
|
||||
keyLength = P25DEF::DES_ENC_KEY_LENGTH_BYTES;
|
||||
break;
|
||||
case P25DEF::ALGO_ARC4:
|
||||
keyLength = P25DEF::ARC4_ENC_KEY_LENGTH_BYTES;
|
||||
break;
|
||||
|
||||
case P25DEF::ALGO_AES_256:
|
||||
break;
|
||||
default:
|
||||
LogWarning(LOG_NET, "PEER %u, unknown algorithm ID $%02X, unable to determine key length", network->m_peerId, ks.algId());
|
||||
break;
|
||||
}
|
||||
|
||||
if (network->m_debug)
|
||||
Utils::dump(1U, "Network::clock(), Key", key, keyLength);
|
||||
|
||||
if (key != nullptr) {
|
||||
ki.setKey(key, keyLength);
|
||||
ks.keyLength(keyLength);
|
||||
delete[] key;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (modifyKey->getDecryptInfoFmt() == KMM_DECRYPT_PEER_ENC) {
|
||||
LogInfoEx(LOG_NET, "PEER %u, received encrypted enc. key, but no preshared key available, algId = $%02X, kID = $%04X", network->m_peerId,
|
||||
ks.algId(), ki.kId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fire off key response callback if we have one
|
||||
if (network->m_keyRespCallback != nullptr) {
|
||||
network->m_keyRespCallback(ki, ks.algId(), ks.keyLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
// 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) 2017-2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "common/p25/kmm/KMMFactory.h"
|
||||
#include "common/Log.h"
|
||||
#include "network/Network.h"
|
||||
|
||||
using namespace network;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Handles NET_FUNC::KEY_LLA_RSP packets. */
|
||||
|
||||
bool Network::PacketHandler::llaKeyResponse(Network* network, uint32_t peerId, uint32_t streamId, uint64_t now,
|
||||
const frame::RTPFNEHeader& fneHeader, const frame::RTPHeader& rtpHeader, const uint8_t* buffer, int length)
|
||||
{
|
||||
(void)peerId;
|
||||
(void)streamId;
|
||||
(void)now;
|
||||
(void)fneHeader;
|
||||
(void)rtpHeader;
|
||||
(void)length;
|
||||
|
||||
if (network->m_enabled) {
|
||||
using namespace p25::kmm;
|
||||
|
||||
std::unique_ptr<KMMFrame> frame = KMMFactory::create(buffer + 11U);
|
||||
if (frame == nullptr) {
|
||||
LogWarning(LOG_NET, "PEER %u, undecodable KMM frame from master", network->m_peerId);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (frame->getMessageId()) {
|
||||
case P25DEF::KMM_MessageType::MODIFY_KEY_CMD:
|
||||
{
|
||||
KMMModifyKey* modifyKey = static_cast<KMMModifyKey*>(frame.get());
|
||||
if (modifyKey->getAlgId() > 0U) {
|
||||
KeysetItem ks = modifyKey->getKeysetItem();
|
||||
if (ks.keys().size() > 0U) {
|
||||
// fetch first key (a master response should never really send back more then one key)
|
||||
KeyItem ki = ks.keys()[0];
|
||||
LogInfoEx(LOG_NET, "PEER %u, master reported LLA enc. key, algId = $%02X, kID = $%04X", network->m_peerId,
|
||||
ks.algId(), ki.kId());
|
||||
|
||||
if (!network->m_passKeysWithNoPresharedKey && (modifyKey->getDecryptInfoFmt() == KMM_DECRYPT_PEER_ENC)) {
|
||||
// if the to decrypt the key with a preshared key, do that now
|
||||
if (network->m_kmfPresharedKey != nullptr) {
|
||||
uint8_t encryptedKey[P25DEF::MAX_ENC_KEY_LENGTH_BYTES];
|
||||
::memset(encryptedKey, 0x00U, P25DEF::MAX_ENC_KEY_LENGTH_BYTES);
|
||||
ki.getKey(encryptedKey);
|
||||
|
||||
uint8_t* key = nullptr;
|
||||
crypto::AES aes = crypto::AES(crypto::AESKeyLength::AES_256);
|
||||
key = aes.decryptECB(encryptedKey, P25DEF::MAX_ENC_KEY_LENGTH_BYTES, network->m_kmfPresharedKey);
|
||||
|
||||
uint32_t keyLength = P25DEF::MAX_ENC_KEY_LENGTH_BYTES;
|
||||
switch (ks.algId()) {
|
||||
case P25DEF::ALGO_AES_128:
|
||||
keyLength = P25DEF::AES_128_ENC_KEY_LENGTH_BYTES;
|
||||
break;
|
||||
|
||||
case P25DEF::ALGO_AES_256:
|
||||
default:
|
||||
LogWarning(LOG_NET, "PEER %u, unknown algorithm ID $%02X, unable to determine key length", network->m_peerId, ks.algId());
|
||||
break;
|
||||
}
|
||||
|
||||
if (network->m_debug)
|
||||
Utils::dump(1U, "Network::clock(), Key", key, keyLength);
|
||||
|
||||
if (key != nullptr) {
|
||||
ki.setKey(key, keyLength);
|
||||
ks.keyLength(keyLength);
|
||||
delete[] key;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (modifyKey->getDecryptInfoFmt() == KMM_DECRYPT_PEER_ENC) {
|
||||
LogInfoEx(LOG_NET, "PEER %u, received encrypted LLA enc. key, but no preshared key available, algId = $%02X, kID = $%04X", network->m_peerId,
|
||||
ks.algId(), ki.kId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fire off key response callback if we have one
|
||||
if (network->m_llaKeyRespCallback != nullptr) {
|
||||
network->m_llaKeyRespCallback(modifyKey->getDstLLId(), ki, ks.keyLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,237 @@
|
||||
// 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) 2017-2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "common/Log.h"
|
||||
#include "network/Network.h"
|
||||
|
||||
using namespace network;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Handles NET_FUNC::MASTER packets. */
|
||||
|
||||
bool Network::PacketHandler::master(Network* network, uint32_t peerId, uint32_t streamId, uint64_t now,
|
||||
const frame::RTPFNEHeader& fneHeader, const frame::RTPHeader& rtpHeader, const uint8_t* buffer, int length)
|
||||
{
|
||||
(void)peerId;
|
||||
(void)streamId;
|
||||
(void)now;
|
||||
(void)fneHeader;
|
||||
(void)rtpHeader;
|
||||
|
||||
// process incoming message subfunction opcodes
|
||||
switch (fneHeader.getSubFunction()) {
|
||||
case NET_SUBFUNC::MASTER_SUBFUNC_WL_RID: // Radio ID Whitelist
|
||||
{
|
||||
if (network->m_enabled && network->m_updateLookup) {
|
||||
if (network->m_packetDump)
|
||||
Utils::dump(1U, "Network::clock(), Network Rx, WL RID", buffer, length);
|
||||
|
||||
if (network->m_ridLookup != nullptr) {
|
||||
// update RID lists
|
||||
uint32_t len = GET_UINT32(buffer, 6U);
|
||||
uint32_t offs = 11U;
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
uint32_t id = GET_UINT24(buffer, offs);
|
||||
network->m_ridLookup->toggleEntry(id, true);
|
||||
offs += 4U;
|
||||
}
|
||||
|
||||
LogInfoEx(LOG_NET, "Network Announced %u whitelisted RIDs", len);
|
||||
|
||||
// save to file if enabled and we got RIDs
|
||||
if (network->m_saveLookup && len > 0) {
|
||||
network->m_ridLookup->commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NET_SUBFUNC::MASTER_SUBFUNC_BL_RID: // Radio ID Blacklist
|
||||
{
|
||||
if (network->m_enabled && network->m_updateLookup) {
|
||||
if (network->m_packetDump)
|
||||
Utils::dump(1U, "Network::clock(), Network Rx, BL RID", buffer, length);
|
||||
|
||||
if (network->m_ridLookup != nullptr) {
|
||||
// update RID lists
|
||||
uint32_t len = GET_UINT32(buffer, 6U);
|
||||
uint32_t offs = 11U;
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
uint32_t id = GET_UINT24(buffer, offs);
|
||||
network->m_ridLookup->toggleEntry(id, false);
|
||||
offs += 4U;
|
||||
}
|
||||
|
||||
LogInfoEx(LOG_NET, "Network Announced %u blacklisted RIDs", len);
|
||||
|
||||
// save to file if enabled and we got RIDs
|
||||
if (network->m_saveLookup && len > 0) {
|
||||
network->m_ridLookup->commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NET_SUBFUNC::MASTER_SUBFUNC_ACTIVE_TGS: // Talkgroup Active IDs
|
||||
{
|
||||
if (network->m_enabled && network->m_updateLookup) {
|
||||
if (network->m_packetDump)
|
||||
Utils::dump(1U, "Network::clock(), Network Rx, ACTIVE TGS", buffer, length);
|
||||
|
||||
if (network->m_tidLookup != nullptr) {
|
||||
// update TGID lists
|
||||
uint32_t len = GET_UINT32(buffer, 6U);
|
||||
uint32_t offs = 11U;
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
uint32_t id = GET_UINT24(buffer, offs);
|
||||
uint8_t slot = (buffer[offs + 3U]) & 0x03U;
|
||||
bool affiliated = (buffer[offs + 3U] & 0x40U) == 0x40U;
|
||||
bool nonPreferred = (buffer[offs + 3U] & 0x80U) == 0x80U;
|
||||
|
||||
// encryption strapping bits
|
||||
bool strappedBit = (buffer[offs + 3U] & 0x20U) == 0x20U;
|
||||
bool clearBit = (buffer[offs + 3U] & 0x10U) == 0x10U;
|
||||
uint8_t strapping = lookups::TG_STRAPPING_SELECTABLE;
|
||||
|
||||
if (strappedBit) {
|
||||
strapping = lookups::TG_STRAPPING_STRAPPED;
|
||||
}
|
||||
|
||||
if (clearBit) {
|
||||
strapping = lookups::TG_STRAPPING_CLEAR;
|
||||
}
|
||||
|
||||
lookups::TalkgroupRuleGroupVoice tid = network->m_tidLookup->find(id, slot);
|
||||
|
||||
// if the TG is marked as non-preferred, and the TGID exists in the local entries
|
||||
// erase the local and overwrite with the FNE data
|
||||
if (nonPreferred) {
|
||||
if (!tid.isInvalid()) {
|
||||
network->m_tidLookup->eraseEntry(id, slot);
|
||||
tid = network->m_tidLookup->find(id, slot);
|
||||
}
|
||||
}
|
||||
|
||||
if (tid.isInvalid()) {
|
||||
if (!tid.config().active()) {
|
||||
network->m_tidLookup->eraseEntry(id, slot);
|
||||
}
|
||||
|
||||
LogInfoEx(LOG_NET, "Activated%s%s TG %u TS %u %s in TGID table",
|
||||
(nonPreferred) ? " non-preferred" : "", (affiliated) ? " affiliated" : "", id, slot,
|
||||
(strappedBit) ? "strapped" : (clearBit) ? "clear" : "selectable");
|
||||
network->m_tidLookup->addEntry(id, slot, true, affiliated, nonPreferred, strapping);
|
||||
}
|
||||
|
||||
offs += 5U;
|
||||
}
|
||||
|
||||
LogInfoEx(LOG_NET, "Activated %u TGs; loaded %u entries into talkgroup rules table", len, network->m_tidLookup->groupVoice().size());
|
||||
|
||||
// save if saving from network is enabled
|
||||
if (network->m_saveLookup && len > 0) {
|
||||
network->m_tidLookup->commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NET_SUBFUNC::MASTER_SUBFUNC_DEACTIVE_TGS: // Talkgroup Deactivated IDs
|
||||
{
|
||||
if (network->m_enabled && network->m_updateLookup) {
|
||||
if (network->m_packetDump)
|
||||
Utils::dump(1U, "Network::clock(), Network Rx, DEACTIVE TGS", buffer, length);
|
||||
|
||||
if (network->m_tidLookup != nullptr) {
|
||||
// update TGID lists
|
||||
uint32_t len = GET_UINT32(buffer, 6U);
|
||||
uint32_t offs = 11U;
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
uint32_t id = GET_UINT24(buffer, offs);
|
||||
uint8_t slot = (buffer[offs + 3U]);
|
||||
|
||||
lookups::TalkgroupRuleGroupVoice tid = network->m_tidLookup->find(id, slot);
|
||||
if (!tid.isInvalid()) {
|
||||
LogInfoEx(LOG_NET, "Deactivated TG %u TS %u in TGID table", id, slot);
|
||||
network->m_tidLookup->eraseEntry(id, slot);
|
||||
}
|
||||
|
||||
offs += 5U;
|
||||
}
|
||||
|
||||
LogInfoEx(LOG_NET, "Deactivated %u TGs; loaded %u entries into talkgroup rules table", len, network->m_tidLookup->groupVoice().size());
|
||||
|
||||
// save if saving from network is enabled
|
||||
if (network->m_saveLookup && len > 0) {
|
||||
network->m_tidLookup->commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NET_SUBFUNC::MASTER_HA_PARAMS: // HA Parameters
|
||||
{
|
||||
if (network->m_enabled) {
|
||||
if (network->m_packetDump)
|
||||
Utils::dump(1U, "Network::clock(), Network Rx, HA PARAMS", buffer, length);
|
||||
|
||||
network->m_haIPs.clear();
|
||||
network->m_currentHAIP = 0U;
|
||||
network->m_maxRetryCount = MAX_RETRY_HA_RECONNECT;
|
||||
|
||||
// always add the configured address to the HA IP list
|
||||
network->m_haIPs.push_back(PeerHAIPEntry(network->m_configuredAddress, network->m_configuredPort));
|
||||
if (network->m_debug)
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "HA PARAMS, 1, %s:%u", network->m_configuredAddress.c_str(), network->m_configuredPort);
|
||||
|
||||
uint32_t len = GET_UINT32(buffer, 6U);
|
||||
if (len > 0U) {
|
||||
len /= HA_PARAMS_ENTRY_LEN;
|
||||
}
|
||||
|
||||
uint8_t offs = 10U;
|
||||
for (uint8_t i = 0U; i < len; i++, offs += HA_PARAMS_ENTRY_LEN) {
|
||||
uint32_t ipAddr = GET_UINT32(buffer, offs + 4U);
|
||||
uint16_t port = GET_UINT16(buffer, offs + 8U);
|
||||
|
||||
std::string address = __IP_FROM_UINT(ipAddr);
|
||||
|
||||
if (address == network->m_configuredAddress && port == network->m_configuredPort) {
|
||||
continue; // skip if this is the same as our configured address
|
||||
}
|
||||
|
||||
if (network->m_debug)
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "HA PARAMS, %u, %s:%u", i + 2, address.c_str(), port);
|
||||
|
||||
network->m_haIPs.push_back(PeerHAIPEntry(address, port));
|
||||
}
|
||||
|
||||
if (network->m_haIPs.size() > 1U) {
|
||||
network->m_currentHAIP = 1U; // because the first entry is our configured entry, set
|
||||
// the current HA IP to the next available
|
||||
LogInfoEx(LOG_NET, "Loaded %u HA IPs from master", network->m_haIPs.size());
|
||||
LogInfoEx(LOG_NET, "Current HA IP %s:%u", network->m_haIPs[network->m_currentHAIP].masterAddress.c_str(), network->m_haIPs[network->m_currentHAIP].masterPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Utils::dump("unknown master control opcode from the master", buffer, length);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
// 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) 2017-2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "common/Log.h"
|
||||
#include "network/Network.h"
|
||||
|
||||
using namespace network;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Handles NET_FUNC::MST_DISC packets. */
|
||||
|
||||
bool Network::PacketHandler::masterDisconnect(Network* network, uint32_t peerId, uint32_t streamId, uint64_t now,
|
||||
const frame::RTPFNEHeader& fneHeader, const frame::RTPHeader& rtpHeader, const uint8_t* buffer, int length)
|
||||
{
|
||||
(void)peerId;
|
||||
(void)streamId;
|
||||
(void)now;
|
||||
(void)fneHeader;
|
||||
(void)rtpHeader;
|
||||
(void)buffer;
|
||||
(void)length;
|
||||
|
||||
LogError(LOG_NET, "PEER %u master disconnect, remotePeerId = %u", network->m_peerId, network->m_remotePeerId);
|
||||
network->m_status = NET_STAT_WAITING_CONNECT;
|
||||
|
||||
// fire off peer disconnected callback if we have one
|
||||
if (network->m_peerDisconnectedCallback != nullptr) {
|
||||
network->m_peerDisconnectedCallback();
|
||||
}
|
||||
|
||||
network->close();
|
||||
network->open();
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
// 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) 2017-2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "common/Log.h"
|
||||
#include "network/Network.h"
|
||||
|
||||
using namespace network;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Handles NET_FUNC::NAK packets. */
|
||||
|
||||
bool Network::PacketHandler::nak(Network* network, uint32_t peerId, uint32_t streamId, uint64_t now,
|
||||
const frame::RTPFNEHeader& fneHeader, const frame::RTPHeader& rtpHeader, const uint8_t* buffer, int length)
|
||||
{
|
||||
(void)peerId;
|
||||
(void)streamId;
|
||||
(void)now;
|
||||
|
||||
// DVM 3.6 adds support to respond with a NAK reason, as such we just check if the NAK response is greater
|
||||
// then 10 bytes and process the reason value
|
||||
uint16_t reason = NET_CONN_NAK_GENERAL_FAILURE;
|
||||
if (length > 10) {
|
||||
reason = GET_UINT16(buffer, 10U);
|
||||
switch (reason) {
|
||||
case NET_CONN_NAK_MODE_NOT_ENABLED:
|
||||
LogWarning(LOG_NET, "PEER %u master NAK; digital mode not enabled on FNE, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
break;
|
||||
case NET_CONN_NAK_ILLEGAL_PACKET:
|
||||
LogWarning(LOG_NET, "PEER %u master NAK; illegal/unknown packet, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
break;
|
||||
case NET_CONN_NAK_FNE_UNAUTHORIZED:
|
||||
LogWarning(LOG_NET, "PEER %u master NAK; unauthorized, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
break;
|
||||
case NET_CONN_NAK_BAD_CONN_STATE:
|
||||
LogWarning(LOG_NET, "PEER %u master NAK; bad connection state, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
break;
|
||||
case NET_CONN_NAK_INVALID_CONFIG_DATA:
|
||||
LogWarning(LOG_NET, "PEER %u master NAK; invalid configuration data, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
break;
|
||||
case NET_CONN_NAK_FNE_MAX_CONN:
|
||||
LogWarning(LOG_NET, "PEER %u master NAK; FNE has reached maximum permitted connections, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
break;
|
||||
case NET_CONN_NAK_PEER_RESET:
|
||||
LogWarning(LOG_NET, "PEER %u master NAK; FNE demanded connection reset, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
break;
|
||||
case NET_CONN_NAK_PEER_ACL:
|
||||
LogError(LOG_NET, "PEER %u master NAK; ACL rejection, network disabled, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
if (!network->m_neverDisableOnACLNAK) {
|
||||
network->m_status = NET_STAT_WAITING_LOGIN;
|
||||
network->m_enabled = false; // ACL rejection give up stop trying to connect
|
||||
}
|
||||
break;
|
||||
|
||||
case NET_CONN_NAK_FNE_DUPLICATE_CONN:
|
||||
LogWarning(LOG_NET, "PEER %u master NAK; duplicate connection to FNE, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
network->m_status = NET_STAT_WAITING_CONNECT;
|
||||
network->m_remotePeerId = 0U;
|
||||
network->m_flaggedDuplicateConn = true;
|
||||
network->m_maxRetryCount = MAX_RETRY_DUP_RECONNECT;
|
||||
network->m_retryTimer.start();
|
||||
return true;
|
||||
|
||||
case NET_CONN_NAK_GENERAL_FAILURE:
|
||||
default:
|
||||
LogWarning(LOG_NET, "PEER %u master NAK; general failure, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if the NAK reason is unhandled by the user code, perform default handling
|
||||
if (!network->userNakHandler(network->m_peerId, reason, fneHeader, rtpHeader)) {
|
||||
if (network->m_status == NET_STAT_RUNNING && (reason == NET_CONN_NAK_FNE_MAX_CONN)) {
|
||||
LogWarning(LOG_NET, "PEER %u master NAK; attemping to relogin, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
network->m_status = NET_STAT_WAITING_LOGIN;
|
||||
network->m_timeoutTimer.start();
|
||||
network->m_retryTimer.start();
|
||||
}
|
||||
else {
|
||||
if (network->m_enabled) {
|
||||
LogError(LOG_NET, "PEER %u master NAK; network reconnect, remotePeerId = %u", network->m_peerId, rtpHeader.getSSRC());
|
||||
network->close();
|
||||
network->open();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
// 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) 2017-2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "common/Log.h"
|
||||
#include "network/Network.h"
|
||||
|
||||
using namespace network;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define MAX_SERVER_DIFF 360ULL // maximum difference in time between a server timestamp and local timestamp in milliseconds
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Handles NET_FUNC::PONG packets. */
|
||||
|
||||
bool Network::PacketHandler::pong(Network* network, uint32_t peerId, uint32_t streamId, uint64_t now,
|
||||
const frame::RTPFNEHeader& fneHeader, const frame::RTPHeader& rtpHeader, const uint8_t* buffer, int length)
|
||||
{
|
||||
(void)peerId;
|
||||
(void)streamId;
|
||||
(void)fneHeader;
|
||||
(void)rtpHeader;
|
||||
|
||||
network->m_timeoutTimer.start();
|
||||
if (length >= 14) {
|
||||
if (network->m_packetDump)
|
||||
Utils::dump(1U, "Network::clock(), Network Rx, PONG", buffer, length);
|
||||
|
||||
ulong64_t serverNow = 0U;
|
||||
|
||||
// combine bytes into ulong64_t (8 byte) value
|
||||
serverNow = buffer[6U];
|
||||
serverNow = (serverNow << 8) + buffer[7U];
|
||||
serverNow = (serverNow << 8) + buffer[8U];
|
||||
serverNow = (serverNow << 8) + buffer[9U];
|
||||
serverNow = (serverNow << 8) + buffer[10U];
|
||||
serverNow = (serverNow << 8) + buffer[11U];
|
||||
serverNow = (serverNow << 8) + buffer[12U];
|
||||
serverNow = (serverNow << 8) + buffer[13U];
|
||||
|
||||
// check the ping RTT and report any over the maximum defined time
|
||||
uint64_t dt = (uint64_t)fabs((double)now - (double)serverNow);
|
||||
if (dt > MAX_SERVER_DIFF)
|
||||
LogWarning(LOG_NET, "PEER %u pong, time delay greater than %llums, now = %llu, server = %llu, dt = %llu", network->m_peerId, MAX_SERVER_DIFF, now, serverNow, dt);
|
||||
|
||||
++network->m_pingsReceived;
|
||||
|
||||
// if we've been connected for at least 10 PING/PONG cycles and we're flagged duplicate connection, clear the flag
|
||||
if (network->m_pingsReceived > 10U && network->m_flaggedDuplicateConn) {
|
||||
network->m_flaggedDuplicateConn = false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,488 @@
|
||||
// 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) 2017-2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "common/Log.h"
|
||||
#include "network/Network.h"
|
||||
|
||||
using namespace network;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Handles NET_FUNC::PROTOCOL packets. */
|
||||
|
||||
bool Network::PacketHandler::protocol(Network* network, uint32_t peerId, uint32_t streamId, uint64_t now,
|
||||
const frame::RTPFNEHeader& fneHeader, const frame::RTPHeader& rtpHeader, const uint8_t* buffer, int length)
|
||||
{
|
||||
(void)now;
|
||||
|
||||
// are protocol messages being user handled?
|
||||
if (network->m_userHandleProtocol) {
|
||||
network->userPacketHandler(fneHeader.getPeerId(), { fneHeader.getFunction(), fneHeader.getSubFunction() },
|
||||
buffer, length, fneHeader.getStreamId(), fneHeader, rtpHeader);
|
||||
return false;
|
||||
}
|
||||
|
||||
// process incoming message subfunction opcodes
|
||||
switch (fneHeader.getSubFunction()) {
|
||||
case NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR: // Encapsulated DMR data frame
|
||||
{
|
||||
if (network->m_enabled && network->m_dmrEnabled) {
|
||||
uint32_t slotNo = (buffer[15U] & 0x80U) == 0x80U ? 1U : 0U; // this is the raw index for the stream ID array
|
||||
|
||||
if (network->m_debug) {
|
||||
LogDebug(LOG_NET, "DMR Slot %u, peer = %u, len = %u, pktSeq = %u, streamId = %u",
|
||||
slotNo + 1U, peerId, length, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
|
||||
if (network->m_promiscuousPeer) {
|
||||
network->m_rxDMRStreamId[slotNo] = streamId;
|
||||
network->m_pktLastSeq = network->m_pktSeq;
|
||||
|
||||
uint16_t lastRxSeq = 0U;
|
||||
|
||||
MULTIPLEX_RET_CODE ret = network->m_mux->verifyStream(streamId, rtpHeader.getSequence(), fneHeader.getFunction(), &lastRxSeq);
|
||||
if (ret == MUX_LOST_FRAMES) {
|
||||
LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", peerId,
|
||||
streamId, rtpHeader.getSequence(), lastRxSeq, rtpHeader.getSequence());
|
||||
}
|
||||
else if (ret == MUX_OUT_OF_ORDER) {
|
||||
LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", peerId,
|
||||
streamId, rtpHeader.getSequence(), lastRxSeq);
|
||||
}
|
||||
#if DEBUG_RTP_MUX
|
||||
else {
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "PEER %u valid mux, seq = %u, streamId = %u", peerId, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
if (network->m_rxDMRStreamId[slotNo] == 0U) {
|
||||
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
|
||||
network->m_rxDMRStreamId[slotNo] = 0U;
|
||||
}
|
||||
else {
|
||||
network->m_rxDMRStreamId[slotNo] = streamId;
|
||||
}
|
||||
|
||||
network->m_pktLastSeq = network->m_pktSeq;
|
||||
}
|
||||
else {
|
||||
if (network->m_rxDMRStreamId[slotNo] == streamId) {
|
||||
uint16_t lastRxSeq = 0U;
|
||||
|
||||
MULTIPLEX_RET_CODE ret = network->verifyStream(&lastRxSeq);
|
||||
if (ret == MUX_LOST_FRAMES) {
|
||||
LogWarning(LOG_NET, "DMR Slot %u stream %u possible lost frames; got %u, expected %u",
|
||||
slotNo, streamId, network->m_pktSeq, lastRxSeq);
|
||||
}
|
||||
else if (ret == MUX_OUT_OF_ORDER) {
|
||||
LogWarning(LOG_NET, "DMR Slot %u stream %u out-of-order; got %u, expected %u",
|
||||
slotNo, streamId, network->m_pktSeq, lastRxSeq);
|
||||
}
|
||||
#if DEBUG_RTP_MUX
|
||||
else {
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "DMR Slot %u valid seq, seq = %u, streamId = %u", slotNo, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
#endif
|
||||
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
|
||||
network->m_rxDMRStreamId[slotNo] = 0U;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if we need to skip this stream -- a non-zero stream ID means the network client is locked
|
||||
// to receiving a specific stream; a zero stream ID means the network is promiscuously
|
||||
// receiving streams sent to this peer
|
||||
if (network->m_rxDMRStreamId[slotNo] != 0U && network->m_rxDMRStreamId[slotNo] != streamId &&
|
||||
rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (network->m_packetDump)
|
||||
Utils::dump(1U, "Network::clock(), Network Rx, DMR", buffer, length);
|
||||
if (length > (int)(DMR_PACKET_LENGTH + PACKET_PAD))
|
||||
LogError(LOG_NET, "DMR Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, network->m_pktSeq, length);
|
||||
|
||||
uint8_t len = length;
|
||||
network->m_rxDMRData.addData(&len, 1U);
|
||||
network->m_rxDMRData.addData(buffer, len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NET_SUBFUNC::PROTOCOL_SUBFUNC_P25: // Encapsulated P25 data frame
|
||||
{
|
||||
if (network->m_enabled && network->m_p25Enabled) {
|
||||
if (network->m_debug) {
|
||||
LogDebug(LOG_NET, "P25, peer = %u, len = %u, pktSeq = %u, streamId = %u",
|
||||
peerId, length, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
|
||||
if (network->m_promiscuousPeer) {
|
||||
network->m_rxP25StreamId = streamId;
|
||||
network->m_pktLastSeq = network->m_pktSeq;
|
||||
|
||||
uint16_t lastRxSeq = 0U;
|
||||
|
||||
MULTIPLEX_RET_CODE ret = network->m_mux->verifyStream(streamId, rtpHeader.getSequence(), fneHeader.getFunction(), &lastRxSeq);
|
||||
if (ret == MUX_LOST_FRAMES) {
|
||||
LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", peerId,
|
||||
streamId, rtpHeader.getSequence(), lastRxSeq, rtpHeader.getSequence());
|
||||
}
|
||||
else if (ret == MUX_OUT_OF_ORDER) {
|
||||
LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", peerId,
|
||||
streamId, rtpHeader.getSequence(), lastRxSeq);
|
||||
}
|
||||
#if DEBUG_RTP_MUX
|
||||
else {
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "PEER %u valid mux, seq = %u, streamId = %u", peerId, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
if (network->m_rxP25StreamId == 0U) {
|
||||
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
|
||||
network->m_rxP25StreamId = 0U;
|
||||
}
|
||||
else {
|
||||
network->m_rxP25StreamId = streamId;
|
||||
}
|
||||
|
||||
network->m_pktLastSeq = network->m_pktSeq;
|
||||
}
|
||||
else {
|
||||
if (network->m_rxP25StreamId == streamId) {
|
||||
uint16_t lastRxSeq = 0U;
|
||||
|
||||
MULTIPLEX_RET_CODE ret = network->verifyStream(&lastRxSeq);
|
||||
if (ret == MUX_LOST_FRAMES) {
|
||||
LogWarning(LOG_NET, "P25 stream %u possible lost frames; got %u, expected %u",
|
||||
streamId, network->m_pktSeq, lastRxSeq);
|
||||
}
|
||||
else if (ret == MUX_OUT_OF_ORDER) {
|
||||
LogWarning(LOG_NET, "P25 stream %u out-of-order; got %u, expected %u",
|
||||
streamId, network->m_pktSeq, lastRxSeq);
|
||||
}
|
||||
#if DEBUG_RTP_MUX
|
||||
else {
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "P25 valid seq, seq = %u, streamId = %u", rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
#endif
|
||||
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
|
||||
network->m_rxP25StreamId = 0U;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if we need to skip this stream -- a non-zero stream ID means the network client is locked
|
||||
// to receiving a specific stream; a zero stream ID means the network is promiscuously
|
||||
// receiving streams sent to this peer
|
||||
if (network->m_rxP25StreamId != 0U && network->m_rxP25StreamId != streamId &&
|
||||
rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (network->m_packetDump)
|
||||
Utils::dump(1U, "Network::clock(), Network Rx, P25", buffer, length);
|
||||
if (length > 512)
|
||||
LogError(LOG_NET, "P25 Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, network->m_pktSeq, length);
|
||||
|
||||
// P25 frames can be up to 512 bytes, but we need to handle the case where the frame is larger than 255 bytes
|
||||
uint8_t len = length;
|
||||
if (length > 254) {
|
||||
len = 254U;
|
||||
network->m_rxP25Data.addData(&len, 1U);
|
||||
len = length - 254U;
|
||||
network->m_rxP25Data.addData(&len, 1U);
|
||||
}
|
||||
else {
|
||||
network->m_rxP25Data.addData(&len, 1U);
|
||||
}
|
||||
|
||||
network->m_rxP25Data.addData(buffer, length);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NET_SUBFUNC::PROTOCOL_SUBFUNC_P25_P2: // Encapsulated P25 Phase 2 data frame
|
||||
{
|
||||
if (network->m_enabled && network->m_p25Enabled) {
|
||||
uint32_t slotNo = (buffer[19U] & 0x80U) == 0x80U ? 1U : 0U; // this is the raw index for the stream ID array
|
||||
|
||||
if (network->m_debug) {
|
||||
LogDebug(LOG_NET, "P25 Phase 2 Slot %u, peer = %u, len = %u, pktSeq = %u, streamId = %u",
|
||||
slotNo + 1U, peerId, length, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
|
||||
if (network->m_promiscuousPeer) {
|
||||
network->m_rxP25P2StreamId[slotNo] = streamId;
|
||||
network->m_pktLastSeq = network->m_pktSeq;
|
||||
|
||||
uint16_t lastRxSeq = 0U;
|
||||
|
||||
MULTIPLEX_RET_CODE ret = network->m_mux->verifyStream(streamId, rtpHeader.getSequence(), fneHeader.getFunction(), &lastRxSeq);
|
||||
if (ret == MUX_LOST_FRAMES) {
|
||||
LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", peerId,
|
||||
streamId, rtpHeader.getSequence(), lastRxSeq, rtpHeader.getSequence());
|
||||
}
|
||||
else if (ret == MUX_OUT_OF_ORDER) {
|
||||
LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", peerId,
|
||||
streamId, rtpHeader.getSequence(), lastRxSeq);
|
||||
}
|
||||
#if DEBUG_RTP_MUX
|
||||
else {
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "PEER %u valid mux, seq = %u, streamId = %u", peerId, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
if (network->m_rxP25P2StreamId[slotNo] == 0U) {
|
||||
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
|
||||
network->m_rxP25P2StreamId[slotNo] = 0U;
|
||||
}
|
||||
else {
|
||||
network->m_rxP25P2StreamId[slotNo] = streamId;
|
||||
}
|
||||
|
||||
network->m_pktLastSeq = network->m_pktSeq;
|
||||
}
|
||||
else {
|
||||
if (network->m_rxP25P2StreamId[slotNo] == streamId) {
|
||||
uint16_t lastRxSeq = 0U;
|
||||
|
||||
MULTIPLEX_RET_CODE ret = network->verifyStream(&lastRxSeq);
|
||||
if (ret == MUX_LOST_FRAMES) {
|
||||
LogWarning(LOG_NET, "DMR Slot %u stream %u possible lost frames; got %u, expected %u",
|
||||
slotNo, streamId, network->m_pktSeq, lastRxSeq);
|
||||
}
|
||||
else if (ret == MUX_OUT_OF_ORDER) {
|
||||
LogWarning(LOG_NET, "DMR Slot %u stream %u out-of-order; got %u, expected %u",
|
||||
slotNo, streamId, network->m_pktSeq, lastRxSeq);
|
||||
}
|
||||
#if DEBUG_RTP_MUX
|
||||
else {
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "P25 Phase 2 Slot %u valid seq, seq = %u, streamId = %u", slotNo, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
#endif
|
||||
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
|
||||
network->m_rxP25P2StreamId[slotNo] = 0U;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if we need to skip this stream -- a non-zero stream ID means the network client is locked
|
||||
// to receiving a specific stream; a zero stream ID means the network is promiscuously
|
||||
// receiving streams sent to this peer
|
||||
if (network->m_rxP25P2StreamId[slotNo] != 0U && network->m_rxP25P2StreamId[slotNo] != streamId &&
|
||||
rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (network->m_packetDump)
|
||||
Utils::dump(1U, "Network::clock(), Network Rx, P25 Phase 2", buffer, length);
|
||||
if (length > (int)(P25_P2_PACKET_LENGTH + PACKET_PAD))
|
||||
LogError(LOG_NET, "P25 Phase 2 Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, network->m_pktSeq, length);
|
||||
|
||||
uint8_t len = length;
|
||||
network->m_rxP25P2Data.addData(&len, 1U);
|
||||
network->m_rxP25P2Data.addData(buffer, len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN: // Encapsulated NXDN data frame
|
||||
{
|
||||
if (network->m_enabled && network->m_nxdnEnabled) {
|
||||
if (network->m_debug) {
|
||||
LogDebug(LOG_NET, "NXDN, peer = %u, len = %u, pktSeq = %u, streamId = %u",
|
||||
peerId, length, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
|
||||
if (network->m_promiscuousPeer) {
|
||||
network->m_rxNXDNStreamId = streamId;
|
||||
network->m_pktLastSeq = network->m_pktSeq;
|
||||
|
||||
uint16_t lastRxSeq = 0U;
|
||||
|
||||
MULTIPLEX_RET_CODE ret = network->m_mux->verifyStream(streamId, rtpHeader.getSequence(), fneHeader.getFunction(), &lastRxSeq);
|
||||
if (ret == MUX_LOST_FRAMES) {
|
||||
LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", peerId,
|
||||
streamId, rtpHeader.getSequence(), lastRxSeq, rtpHeader.getSequence());
|
||||
}
|
||||
else if (ret == MUX_OUT_OF_ORDER) {
|
||||
LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", peerId,
|
||||
streamId, rtpHeader.getSequence(), lastRxSeq);
|
||||
}
|
||||
#if DEBUG_RTP_MUX
|
||||
else {
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "PEER %u valid mux, seq = %u, streamId = %u", peerId, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
if (network->m_rxNXDNStreamId == 0U) {
|
||||
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
|
||||
network->m_rxNXDNStreamId = 0U;
|
||||
}
|
||||
else {
|
||||
network->m_rxNXDNStreamId = streamId;
|
||||
}
|
||||
|
||||
network->m_pktLastSeq = network->m_pktSeq;
|
||||
}
|
||||
else {
|
||||
if (network->m_rxNXDNStreamId == streamId) {
|
||||
uint16_t lastRxSeq = 0U;
|
||||
|
||||
MULTIPLEX_RET_CODE ret = network->verifyStream(&lastRxSeq);
|
||||
if (ret == MUX_LOST_FRAMES) {
|
||||
LogWarning(LOG_NET, "NXDN stream %u possible lost frames; got %u, expected %u",
|
||||
streamId, network->m_pktSeq, lastRxSeq);
|
||||
}
|
||||
else if (ret == MUX_OUT_OF_ORDER) {
|
||||
LogWarning(LOG_NET, "NXDN stream %u out-of-order; got %u, expected %u",
|
||||
streamId, network->m_pktSeq, lastRxSeq);
|
||||
}
|
||||
#if DEBUG_RTP_MUX
|
||||
else {
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "NXDN valid seq, seq = %u, streamId = %u", rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
#endif
|
||||
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
|
||||
network->m_rxNXDNStreamId = 0U;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if we need to skip this stream -- a non-zero stream ID means the network client is locked
|
||||
// to receiving a specific stream; a zero stream ID means the network is promiscuously
|
||||
// receiving streams sent to this peer
|
||||
if (network->m_rxNXDNStreamId != 0U && network->m_rxNXDNStreamId != streamId &&
|
||||
rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (network->m_packetDump)
|
||||
Utils::dump(1U, "Network::clock(), Network Rx, NXDN", buffer, length);
|
||||
if (length > (int)(NXDN_PACKET_LENGTH + PACKET_PAD))
|
||||
LogError(LOG_NET, "NXDN Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, network->m_pktSeq, length);
|
||||
|
||||
uint8_t len = length;
|
||||
network->m_rxNXDNData.addData(&len, 1U);
|
||||
network->m_rxNXDNData.addData(buffer, len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NET_SUBFUNC::PROTOCOL_SUBFUNC_ANALOG: // Encapsulated Analog data frame
|
||||
{
|
||||
if (network->m_enabled && network->m_analogEnabled) {
|
||||
if (network->m_debug) {
|
||||
LogDebug(LOG_NET, "Analog, peer = %u, len = %u, pktSeq = %u, streamId = %u",
|
||||
peerId, length, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
|
||||
if (network->m_promiscuousPeer) {
|
||||
network->m_rxAnalogStreamId = streamId;
|
||||
network->m_pktLastSeq = network->m_pktSeq;
|
||||
|
||||
uint16_t lastRxSeq = 0U;
|
||||
|
||||
MULTIPLEX_RET_CODE ret = network->m_mux->verifyStream(streamId, rtpHeader.getSequence(), fneHeader.getFunction(), &lastRxSeq);
|
||||
if (ret == MUX_LOST_FRAMES) {
|
||||
LogError(LOG_NET, "PEER %u stream %u possible lost frames; got %u, expected %u", peerId,
|
||||
streamId, rtpHeader.getSequence(), lastRxSeq, rtpHeader.getSequence());
|
||||
}
|
||||
else if (ret == MUX_OUT_OF_ORDER) {
|
||||
LogError(LOG_NET, "PEER %u stream %u out-of-order; got %u, expected >%u", peerId,
|
||||
streamId, rtpHeader.getSequence(), lastRxSeq);
|
||||
}
|
||||
#if DEBUG_RTP_MUX
|
||||
else {
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "PEER %u valid mux, seq = %u, streamId = %u", peerId, rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
if (network->m_rxAnalogStreamId == 0U) {
|
||||
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
|
||||
network->m_rxAnalogStreamId = 0U;
|
||||
}
|
||||
else {
|
||||
network->m_rxAnalogStreamId = streamId;
|
||||
}
|
||||
|
||||
network->m_pktLastSeq = network->m_pktSeq;
|
||||
}
|
||||
else {
|
||||
if (network->m_rxAnalogStreamId == streamId) {
|
||||
uint16_t lastRxSeq = 0U;
|
||||
|
||||
MULTIPLEX_RET_CODE ret = network->verifyStream(&lastRxSeq);
|
||||
if (ret == MUX_LOST_FRAMES) {
|
||||
LogWarning(LOG_NET, "Analog stream %u possible lost frames; got %u, expected %u",
|
||||
streamId, network->m_pktSeq, lastRxSeq);
|
||||
}
|
||||
else if (ret == MUX_OUT_OF_ORDER) {
|
||||
LogWarning(LOG_NET, "Analog stream %u out-of-order; got %u, expected %u",
|
||||
streamId, network->m_pktSeq, lastRxSeq);
|
||||
}
|
||||
#if DEBUG_RTP_MUX
|
||||
else {
|
||||
LogDebugEx(LOG_NET, "Network::clock()", "Analog valid seq, seq = %u, streamId = %u", rtpHeader.getSequence(), streamId);
|
||||
}
|
||||
#endif
|
||||
if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) {
|
||||
network->m_rxAnalogStreamId = 0U;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if we need to skip this stream -- a non-zero stream ID means the network client is locked
|
||||
// to receiving a specific stream; a zero stream ID means the network is promiscuously
|
||||
// receiving streams sent to this peer
|
||||
if (network->m_rxAnalogStreamId != 0U && network->m_rxAnalogStreamId != streamId &&
|
||||
rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (network->m_packetDump)
|
||||
Utils::dump(1U, "Network::clock(), Network Rx, Analog", buffer, length);
|
||||
if (length < (int)ANALOG_PACKET_LENGTH) {
|
||||
LogError(LOG_NET, "Analog Stream %u, frame too short? this shouldn't happen, pktSeq = %u, len = %u", streamId, network->m_pktSeq, length);
|
||||
}
|
||||
else {
|
||||
if (length > 512)
|
||||
LogError(LOG_NET, "Analog Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, network->m_pktSeq, length);
|
||||
|
||||
// Analog frames are larger then 254 bytes, but we need to handle the case where the frame is larger than 255 bytes
|
||||
uint8_t len = length - 254U;
|
||||
network->m_rxAnalogData.addData(&len, 1U);
|
||||
|
||||
network->m_rxAnalogData.addData(buffer, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Utils::dump("unknown protocol opcode from the master", buffer, length);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
Loading…
Reference in new issue