handle replicating radio aliases from FNE to FNE; implement RADIO_ALIAS_SYNC for fnecore consoles to be able to request the radio alias list from the core;

pull/128/head
Bryan Biedenkapp 1 month ago
parent f5f86ef9c7
commit 193055330a

@ -411,6 +411,17 @@ system:
# Flag indicating whether or not the Radio ID ACL updates are logged.
verbose: false
#
# Radio Alias Configuration
#
radio_alias:
# Full path to the RID Alias file.
file: rid_alias.dat
# Amount of time between updates of RID Alias file. (minutes)
time: 2
# Flag indicating whether or not the RID ACL updates are logged.
verbose: false
#
# Peer ACL configuration
#

@ -70,6 +70,9 @@ namespace network
KEY_LLA_REQ = 0x80U, //!< Encryption Key LLA Request
KEY_LLA_RSP = 0x81U, //!< Encryption Key LLA Response
RADIO_ALIAS_SYNC = 0x85U, //!< Radio Alias List Sync
KEYS_INVENTORY = 0x8EU, //!< Encryption Key Container Inventory
KEYS_UPDATE = 0x8FU, //!< Encryption Key Container Update
@ -119,6 +122,7 @@ namespace network
REPL_TALKGROUP_LIST = 0x00U, //!< FNE Replication Talkgroup Transfer
REPL_RID_LIST = 0x01U, //!< FNE Replication Radio ID Transfer
REPL_PEER_LIST = 0x02U, //!< FNE Replication Peer List Transfer
REPL_RID_ALIAS_LIST = 0x03U, //!< FNE Replication Radio Alias Transfer
REPL_ACT_PEER_LIST = 0xA2U, //!< FNE Replication Active Peer List Transfer
REPL_HA_PARAMS = 0xA3U, //!< FNE Replication HA Parameters

@ -74,6 +74,7 @@ HostFNE::HostFNE(const std::string& confFile) :
m_nxdnEnabled(false),
m_analogEnabled(false),
m_ridLookup(nullptr),
m_ridAliasLookup(nullptr),
m_tidLookup(nullptr),
m_peerListLookup(nullptr),
m_cryptoLookup(nullptr),
@ -190,6 +191,20 @@ int HostFNE::run()
m_ridLookup = new RadioIdLookup(ridLookupFile, ridReloadTime, true, verboseRIDRules);
m_ridLookup->read();
// try to load radio aliases table
std::string ridAliasLookupFile = systemConf["radio_alias"]["file"].as<std::string>();
uint32_t ridAliasReloadTime = systemConf["radio_alias"]["time"].as<uint32_t>(0U);
bool verboseRIDAliasRules = systemConf["radio_alias"]["verbose"].as<bool>(false);
LogInfo("Radio Alias Lookups");
LogInfo(" File: %s", ridAliasLookupFile.length() > 0U ? ridAliasLookupFile.c_str() : "None");
if (ridReloadTime > 0U)
LogInfo(" Reload: %u mins", ridAliasReloadTime);
LogInfo(" Verbose: %s", verboseRIDAliasRules ? "true" : "false");
m_ridAliasLookup = new RadioAliasLookup(ridAliasLookupFile, ridAliasReloadTime, verboseRIDAliasRules);
m_ridAliasLookup->read();
// initialize master networking
ret = createMasterNetwork();
if (!ret)
@ -669,7 +684,7 @@ bool HostFNE::createMasterNetwork()
m_network->setOptions(masterConf, true);
m_network->setPacketDump(packetDump);
m_network->setLookups(m_ridLookup, m_tidLookup, m_peerListLookup, m_cryptoLookup, m_adjSiteMapLookup);
m_network->setLookups(m_ridLookup, m_ridAliasLookup, m_tidLookup, m_peerListLookup, m_cryptoLookup, m_adjSiteMapLookup);
bool ret = m_network->open();
if (!ret) {
@ -891,6 +906,7 @@ bool HostFNE::createPeerNetworks()
network->setMetadata(identity, 0U, 0U, 0.0F, 0.0F, 0, 0, 0, latitude, longitude, 0, location);
network->setLookups(m_ridLookup, m_tidLookup);
network->setMasterPeerId(masterPeerId);
network->setRadioAliasLookups(m_ridAliasLookup);
network->setPeerLookups(m_peerListLookup);
network->setPeerReplicationSaveACL(m_peerReplicaSavesACL);
network->setNakFallOver(nakFallOver, nakFallOverCount);

@ -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-2025 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023-2026 Bryan Biedenkapp, N2PLL
*
*/
/**
@ -18,6 +18,7 @@
#include "fne/Defines.h"
#include "common/lookups/RadioIdLookup.h"
#include "common/lookups/RadioAliasLookup.h"
#include "common/lookups/TalkgroupRulesLookup.h"
#include "common/lookups/PeerListLookup.h"
#include "common/lookups/AdjSiteMapLookup.h"
@ -116,6 +117,7 @@ private:
bool m_analogEnabled;
lookups::RadioIdLookup* m_ridLookup;
lookups::RadioAliasLookup* m_ridAliasLookup;
lookups::TalkgroupRulesLookup* m_tidLookup;
lookups::PeerListLookup* m_peerListLookup;
lookups::AdjSiteMapLookup* m_adjSiteMapLookup;

@ -253,6 +253,8 @@ void MetadataNetwork::taskNetworkRx(NetPacketRequest* req)
{ NET_FUNC::KEYS_INVENTORY, &MetadataNetwork::PacketHandler::keysInventory },
{ NET_FUNC::KEYS_UPDATE, &MetadataNetwork::PacketHandler::keysUpdate },
{ NET_FUNC::RADIO_ALIAS_SYNC, &MetadataNetwork::PacketHandler::radioAliasSync },
{ NET_FUNC::REPL, &MetadataNetwork::PacketHandler::replication },
{ NET_FUNC::NET_TREE, &MetadataNetwork::PacketHandler::networkTree }
};

@ -207,6 +207,17 @@ namespace network
*/
static void announce(TrafficNetwork* network, MetadataNetwork* mdNetwork, NetPacketRequest* req, uint32_t peerId, uint32_t ssrc, uint32_t streamId);
/**
* @brief Handles NET_FUNC::RADIO_ALIAS_SYNC packets.
* @param network Instance of the TrafficNetwork class.
* @param mdNetwork Instance of the MetadataNetwork class.
* @param req Instance of the NetPacketRequest structure.
* @param peerId Peer ID of the packet.
* @param ssrc SSRC of the packet.
* @param streamId Stream ID of the packet.
*/
static void radioAliasSync(TrafficNetwork* network, MetadataNetwork* mdNetwork, NetPacketRequest* req, uint32_t peerId, uint32_t ssrc, uint32_t streamId);
/**
* @brief Handles NET_FUNC::KEYS_INVENTORY packets.
* @param network Instance of the TrafficNetwork class.

@ -50,12 +50,14 @@ PeerNetwork::PeerNetwork(const std::string& address, uint16_t port, uint16_t loc
m_netTreeDiscCallback(nullptr),
m_peerReplicaCallback(nullptr),
m_masterPeerId(0U),
m_ridAliasLookup(nullptr),
m_pidLookup(nullptr),
m_peerReplica(false),
m_peerReplicaSavesACL(false),
m_tgidPkt(true, "Peer Replication, TGID List"),
m_ridPkt(true, "Peer Replication, RID List"),
m_pidPkt(true, "Peer Replication, PID List"),
m_ridAliasPkt(true, "Peer Replication, RID Alias List"),
m_threadPool(WORKER_CNT, "peer"),
m_prevSpanningTreeChildren(0U),
m_nakFallOver(false),
@ -91,6 +93,13 @@ PeerNetwork::~PeerNetwork()
m_threadPool.wait();
}
/* Sets the instnace of the Radio Alias lookup table. */
void PeerNetwork::setRadioAliasLookups(lookups::RadioAliasLookup* ridAliasLookup)
{
m_ridAliasLookup = ridAliasLookup;
}
/* Sets the instances of the Peer List lookup tables. */
void PeerNetwork::setPeerLookups(lookups::PeerListLookup* pidLookup)
@ -457,6 +466,65 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco
}
break;
case NET_SUBFUNC::REPL_RID_ALIAS_LIST: // Peer List
{
uint32_t decompressedLen = 0U;
uint8_t* decompressed = nullptr;
if (m_ridAliasPkt.decode(data, &decompressed, &decompressedLen)) {
if (m_ridAliasLookup == nullptr) {
LogError(LOG_PEER, "Radio Alias lookup not available yet.");
m_ridAliasPkt.clear();
delete[] decompressed;
break;
}
// store to file
DECLARE_CHAR_ARRAY(str, decompressedLen + 1U);
::memcpy(str, decompressed, decompressedLen);
str[decompressedLen] = 0; // null termination
// randomize filename
std::ostringstream s;
if (!m_peerReplicaSavesACL) {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<uint32_t> dist(0x00U, 0xFFFFFFFFU);
s << "/tmp/rid_alias.dat." << dist(mt);
} else {
s << m_ridAliasLookup->filename();
}
std::string filename = s.str();
std::ofstream file(filename, std::ofstream::out);
if (file.fail()) {
LogError(LOG_PEER, "Cannot open the radio alias lookup file - %s", filename.c_str());
m_ridAliasPkt.clear();
delete[] decompressed;
break;
}
file << str;
file.close();
m_ridAliasLookup->stop(true);
m_ridAliasLookup->setReloadTime(0U);
m_ridAliasLookup->filename(filename);
m_ridAliasLookup->reload();
// flag this peer as replica enabled
m_peerReplica = true;
if (m_peerReplicaCallback != nullptr)
m_peerReplicaCallback(this);
// cleanup temporary file
::remove(filename.c_str());
m_ridAliasPkt.clear();
delete[] decompressed;
}
}
break;
default:
break;
}

@ -17,6 +17,7 @@
#define __PEER_NETWORK_H__
#include "fne/Defines.h"
#include "common/lookups/RadioAliasLookup.h"
#include "common/lookups/PeerListLookup.h"
#include "common/network/Network.h"
#include "common/network/PacketBuffer.h"
@ -93,6 +94,11 @@ namespace network
* @param masterPeerId Master Peer ID.
*/
void setMasterPeerId(uint32_t masterPeerId) { m_masterPeerId = masterPeerId; }
/**
* @brief Sets the instnace of the Radio Alias lookup table.
* @param ridAliasLookup Radio Alias Lookup Table Instance
*/
void setRadioAliasLookups(lookups::RadioAliasLookup* ridAliasLookup);
/**
* @brief Sets the instances of the Peer List lookup tables.
* @param pidLookup Peer List Lookup Table Instance
@ -339,6 +345,7 @@ namespace network
private:
uint32_t m_masterPeerId;
lookups::RadioAliasLookup* m_ridAliasLookup;
lookups::PeerListLookup* m_pidLookup;
bool m_peerReplica;
bool m_peerReplicaSavesACL;
@ -346,6 +353,7 @@ namespace network
PacketBuffer m_tgidPkt;
PacketBuffer m_ridPkt;
PacketBuffer m_pidPkt;
PacketBuffer m_ridAliasPkt;
ThreadPool m_threadPool;

@ -93,6 +93,7 @@ TrafficNetwork::TrafficNetwork(HostFNE* host, const std::string& address, uint16
m_kmfEncKeyRequest(false),
m_kmfPresharedKey(nullptr),
m_ridLookup(nullptr),
m_ridAliasLookup(nullptr),
m_tidLookup(nullptr),
m_peerListLookup(nullptr),
m_adjSiteMapLookup(nullptr),
@ -525,12 +526,13 @@ void TrafficNetwork::setOptions(yaml::Node& conf, bool printOptions)
}
}
/* Sets the instances of the Radio ID, Talkgroup ID Peer List, and Crypto lookup tables. */
/* Sets the instances of the Radio ID, Radio Alias, Talkgroup ID, Peer List, and Crypto lookup tables. */
void TrafficNetwork::setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupRulesLookup* tidLookup, lookups::PeerListLookup* peerListLookup,
CryptoContainer* cryptoLookup, lookups::AdjSiteMapLookup* adjSiteMapLookup)
void TrafficNetwork::setLookups(lookups::RadioIdLookup* ridLookup, lookups::RadioAliasLookup* ridAliasLookup, lookups::TalkgroupRulesLookup* tidLookup,
lookups::PeerListLookup* peerListLookup, CryptoContainer* cryptoLookup, lookups::AdjSiteMapLookup* adjSiteMapLookup)
{
m_ridLookup = ridLookup;
m_ridAliasLookup = ridAliasLookup;
m_tidLookup = tidLookup;
m_peerListLookup = peerListLookup;
m_cryptoLookup = cryptoLookup;
@ -1836,6 +1838,7 @@ void TrafficNetwork::taskMetadataUpdate(MetadataUpdateRequest* req)
network->writeWhitelistRIDs(req->peerId, streamId, true);
network->writeTGIDs(req->peerId, streamId, true);
network->writePeerList(req->peerId, streamId);
network->writeRadioAliasList(req->peerId, streamId);
network->writeHAParameters(req->peerId, streamId, true);
}
@ -2270,6 +2273,68 @@ void TrafficNetwork::writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId)
payload, 4U + (tgidList.size() * 5U), streamId, true);
}
/* Helper to send the list of radio aliases to the specified peer. */
void TrafficNetwork::writeRadioAliasList(uint32_t peerId, uint32_t streamId)
{
// sending REPL style RID alias list to replica neighbor FNE peers
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string tempFile;
if (m_isReplica) {
std::ostringstream s;
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<uint32_t> dist(0x00U, 0xFFFFFFFFU);
s << "/tmp/rid_alias.dat." << dist(mt);
tempFile = s.str();
std::string origFile = m_ridAliasLookup->filename();
m_ridAliasLookup->filename(tempFile);
m_ridAliasLookup->commit(true);
m_ridAliasLookup->filename(origFile);
} else {
tempFile = m_ridAliasLookup->filename();
}
// read entire file into string buffer
std::stringstream b;
std::ifstream stream(tempFile);
if (stream.is_open()) {
while (stream.peek() != EOF) {
b << (char)stream.get();
}
stream.close();
}
if (m_isReplica)
::remove(tempFile.c_str());
// convert to a byte array
uint32_t len = b.str().size();
DECLARE_UINT8_ARRAY(buffer, len);
::memcpy(buffer, b.str().data(), len);
PacketBuffer pkt(true, "Peer Replication, RID Alias List");
pkt.encode((uint8_t*)buffer, len);
LogInfoEx(LOG_REPL, "PEER %u (%s) Peer Replication, RID Alias List, blocks %u, streamId = %u", peerId, connection->identWithQualifier().c_str(),
pkt.fragments.size(), streamId);
if (pkt.fragments.size() > 0U) {
for (auto frag : pkt.fragments) {
writePeer(peerId, m_peerId, { NET_FUNC::REPL, NET_SUBFUNC::REPL_RID_ALIAS_LIST },
frag.second->data, FRAG_SIZE, 0U, streamId);
Thread::sleep(60U); // pace block transmission
}
}
pkt.clear();
}
return;
}
/* Helper to send the list of peers to the specified peer. */
void TrafficNetwork::writePeerList(uint32_t peerId, uint32_t streamId)

@ -29,6 +29,7 @@
#include "common/concurrent/shared_unordered_map.h"
#include "common/json/json.h"
#include "common/lookups/RadioIdLookup.h"
#include "common/lookups/RadioAliasLookup.h"
#include "common/lookups/TalkgroupRulesLookup.h"
#include "common/lookups/PeerListLookup.h"
#include "common/lookups/AdjSiteMapLookup.h"
@ -236,15 +237,16 @@ namespace network
callhandler::TagAnalogData* analogTrafficHandler() const { return m_tagAnalog; }
/**
* @brief Sets the instances of the Radio ID, Talkgroup ID Peer List, and Crypto lookup tables.
* @brief Sets the instances of the Radio ID, Radio Alias, Talkgroup ID, Peer List, and Crypto lookup tables.
* @param ridLookup Radio ID Lookup Table Instance
* @param ridAliasLookup Radio Alias Lookup Table Instance
* @param tidLookup Talkgroup Rules Lookup Table Instance
* @param peerListLookup Peer List Lookup Table Instance
* @param cryptoLookup Crypto Container Lookup Table Instance
* @param adjSiteMapLookup Adjacent Site Map Lookup Table Instance
*/
void setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupRulesLookup* tidLookup, lookups::PeerListLookup* peerListLookup,
CryptoContainer* cryptoLookup, lookups::AdjSiteMapLookup* adjSiteMapLookup);
void setLookups(lookups::RadioIdLookup* ridLookup, lookups::RadioAliasLookup* ridAliasLookup, lookups::TalkgroupRulesLookup* tidLookup,
lookups::PeerListLookup* peerListLookup, CryptoContainer* cryptoLookup, lookups::AdjSiteMapLookup* adjSiteMapLookup);
/**
* @brief Sets endpoint preshared encryption key.
* @param presharedKey Encryption preshared key for networking.
@ -364,6 +366,7 @@ namespace network
uint8_t* m_kmfPresharedKey;
lookups::RadioIdLookup* m_ridLookup;
lookups::RadioAliasLookup* m_ridAliasLookup;
lookups::TalkgroupRulesLookup* m_tidLookup;
lookups::PeerListLookup* m_peerListLookup;
lookups::AdjSiteMapLookup* m_adjSiteMapLookup;
@ -853,6 +856,13 @@ namespace network
* @param streamId Stream ID for this message.
*/
void writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId);
/**
* @brief Helper to send the list of radio aliases to the specified peer.
* @note This doesn't have a data layout document because it is *only* sent as a packet buffered message.
* @param peerId Peer ID.
* @param streamId Stream ID for this message.
*/
void writeRadioAliasList(uint32_t peerId, uint32_t streamId);
/**
* @brief Helper to send the list of peers to the specified peer.
* @note This doesn't have a data layout document because it is *only* sent as a packet buffered message.

@ -0,0 +1,84 @@
// 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) 2026 Bryan Biedenkapp, N2PLL
*
*/
#include "fne/Defines.h"
#include "common/Log.h"
#include "network/MetadataNetwork.h"
#include "fne/ActivityLog.h"
#include "HostFNE.h"
using namespace network;
#include <fstream>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/* Handles NET_FUNC::RADIO_ALIAS_SYNC packets. */
void MetadataNetwork::PacketHandler::radioAliasSync(TrafficNetwork* network, MetadataNetwork* mdNetwork, NetPacketRequest* req, uint32_t peerId, uint32_t ssrc, uint32_t streamId)
{
(void)mdNetwork;
if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) {
FNEPeerConnection* connection = network->m_peers[peerId];
if (connection != nullptr) {
std::string ip = udp::Socket::address(req->address);
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
// read entire file into buffer
std::stringstream b;
std::ifstream stream(network->m_ridAliasLookup->filename(), std::ios::in | std::ios::binary);
uint32_t len = 0U;
UInt8Array bufferUInt8Array = nullptr;
uint8_t* buffer = nullptr;
if (stream.is_open()) {
stream.seekg(0, std::ios::end);
len = (uint32_t)stream.tellg();
stream.seekg(0, std::ios::beg);
bufferUInt8Array = std::make_unique<uint8_t[]>(len);
buffer = bufferUInt8Array.get();
::memset(buffer, 0x00U, len);
uint32_t i = 0U;
while (stream.peek() != EOF) {
buffer[i] = (uint8_t)stream.get();
i++;
}
stream.close();
}
PacketBuffer pkt(true, "Radio Alias Sync");
pkt.encode((uint8_t*)buffer, len);
LogInfoEx(LOG_REPL, "PEER %u Radio Alias Sync, blocks %u, streamId = %u", peerId, pkt.fragments.size(), streamId);
if (pkt.fragments.size() > 0U) {
for (auto frag : pkt.fragments) {
// violate most handling rules for responding to packets -- we need to directly respond to the calling peer as
// they may not be logged in as a standard peer
network->writePeer(peerId, network->m_peerId, { NET_FUNC::RADIO_ALIAS_SYNC, NET_SUBFUNC::NOP },
frag.second->data, FRAG_SIZE, 0U, streamId);
Thread::sleep(60U); // pace block transmission
}
}
pkt.clear();
}
else {
network->writePeerNAK(peerId, streamId, TAG_PEER_REPLICA, NET_CONN_NAK_FNE_UNAUTHORIZED);
}
}
}
}
Loading…
Cancel
Save

Powered by TurnKey Linux.