parent
6cbcd4240c
commit
47fff42173
@ -0,0 +1,91 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Digital Voice Modem - Test Suite
|
||||||
|
* 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 <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include "fne/FNETestHooks.h"
|
||||||
|
#include "fne/HostFNE.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test harness for FNE network tests.
|
||||||
|
*/
|
||||||
|
class FNEHarness {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Initializes a new instance of the FNEHarness class.
|
||||||
|
*/
|
||||||
|
FNEHarness() :
|
||||||
|
configFile("fne-test.yml"),
|
||||||
|
host(configFile),
|
||||||
|
traffic(&host, "127.0.0.1", 62031U, 999999U, "test-password", "test-fne",
|
||||||
|
false, false, false, false, true, true, true, true, true,
|
||||||
|
0U, false, true, true, 5U, 10U, 2U),
|
||||||
|
metadata(&host, &traffic, "127.0.0.1", 62032U, 2U)
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Finalizes a instance of the FNEHarness class.
|
||||||
|
*/
|
||||||
|
~FNEHarness()
|
||||||
|
{
|
||||||
|
FNETestHooks::clearPeers(traffic);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string configFile;
|
||||||
|
HostFNE host;
|
||||||
|
network::TrafficNetwork traffic;
|
||||||
|
network::MetadataNetwork metadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE("FNE test hooks expose peer connection state", "[fne][hooks]")
|
||||||
|
{
|
||||||
|
FNEHarness harness;
|
||||||
|
FNETestHooks::addPeer(harness.traffic, 1001U, network::NET_STAT_WAITING_AUTHORISATION);
|
||||||
|
|
||||||
|
REQUIRE(FNETestHooks::hasPeer(harness.traffic, 1001U));
|
||||||
|
REQUIRE(FNETestHooks::peerCount(harness.traffic) == 1U);
|
||||||
|
REQUIRE(FNETestHooks::peerState(harness.traffic, 1001U) == network::NET_STAT_WAITING_AUTHORISATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("FNE traffic dispatcher rejects truncated pre-authentication packets", "[fne][hooks][security]")
|
||||||
|
{
|
||||||
|
FNEHarness harness;
|
||||||
|
FNETestHooks::addPeer(harness.traffic, 1001U, network::NET_STAT_WAITING_AUTHORISATION);
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(FNETestHooks::dispatchTraffic(harness.traffic, network::NET_FUNC::RPTK,
|
||||||
|
network::NET_SUBFUNC::NOP, 1001U, std::vector<uint8_t>(8U)));
|
||||||
|
REQUIRE(FNETestHooks::peerState(harness.traffic, 1001U) == network::NET_STAT_WAITING_AUTHORISATION);
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(FNETestHooks::dispatchTraffic(harness.traffic, network::NET_FUNC::RPTC,
|
||||||
|
network::NET_SUBFUNC::NOP, 1001U, std::vector<uint8_t>(8U)));
|
||||||
|
REQUIRE(FNETestHooks::peerState(harness.traffic, 1001U) == network::NET_STAT_WAITING_AUTHORISATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("FNE metadata dispatcher rejects truncated log transfers", "[fne][hooks][security]")
|
||||||
|
{
|
||||||
|
FNEHarness harness;
|
||||||
|
FNETestHooks::addPeer(harness.traffic, 1001U, network::NET_STAT_RUNNING, true);
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(FNETestHooks::dispatchMetadata(harness.traffic, harness.metadata,
|
||||||
|
network::NET_FUNC::TRANSFER, network::NET_SUBFUNC::TRANSFER_SUBFUNC_ACTIVITY,
|
||||||
|
1001U, std::vector<uint8_t>(11U)));
|
||||||
|
REQUIRE_NOTHROW(FNETestHooks::dispatchMetadata(harness.traffic, harness.metadata,
|
||||||
|
network::NET_FUNC::TRANSFER, network::NET_SUBFUNC::TRANSFER_SUBFUNC_DIAG,
|
||||||
|
1001U, std::vector<uint8_t>(10U)));
|
||||||
|
REQUIRE(FNETestHooks::peerState(harness.traffic, 1001U) == network::NET_STAT_RUNNING);
|
||||||
|
}
|
||||||
@ -0,0 +1,183 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Digital Voice Modem - Test Suite
|
||||||
|
* 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/FNETestHooks.h"
|
||||||
|
#include "fne/HostFNE.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <chrono>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
using namespace network;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Global Variables
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool g_promiscuousHub = false;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes an instance of the HostFNE class.
|
||||||
|
*/
|
||||||
|
HostFNE::HostFNE(const std::string& confFile) :
|
||||||
|
m_confFile(confFile), m_conf(), m_network(nullptr), m_mdNetwork(nullptr),
|
||||||
|
m_vtunEnabled(false), m_packetDataMode(PacketDataMode::PROJECT25),
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
m_tun(nullptr),
|
||||||
|
#endif
|
||||||
|
m_dmrEnabled(false), m_p25Enabled(false), m_p25P2Enabled(false),
|
||||||
|
m_nxdnEnabled(false), m_analogEnabled(false), m_ridLookup(nullptr),
|
||||||
|
m_tidLookup(nullptr), m_peerListLookup(nullptr), m_adjSiteMapLookup(nullptr),
|
||||||
|
m_cryptoLookup(nullptr), m_peerNetworks(), m_pingTime(5U), m_maxMissedPings(5U),
|
||||||
|
m_updateLookupTime(10U), m_peerReplicaSavesACL(false),
|
||||||
|
m_allowActivityTransfer(false), m_allowDiagnosticTransfer(false), m_RESTAPI(nullptr)
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finalizes an instance of the HostFNE class.
|
||||||
|
*/
|
||||||
|
HostFNE::~HostFNE() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a peer to the specified TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @param peerId The ID of the peer to add.
|
||||||
|
* @param state The connection state of the peer.
|
||||||
|
* @param connected Whether the peer is connected.
|
||||||
|
* @return Reference to the added FNEPeerConnection.
|
||||||
|
*/
|
||||||
|
FNEPeerConnection& FNETestHooks::addPeer(TrafficNetwork& network, uint32_t peerId,
|
||||||
|
NET_CONN_STATUS state, bool connected)
|
||||||
|
{
|
||||||
|
FNEPeerConnection* connection = new FNEPeerConnection();
|
||||||
|
connection->m_id = peerId;
|
||||||
|
connection->m_connectionState = state;
|
||||||
|
connection->m_connected = connected;
|
||||||
|
network.m_peers[peerId] = connection;
|
||||||
|
return *connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Static Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the specified peer exists in the TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @param peerId The ID of the peer to check.
|
||||||
|
* @return True if the peer exists, false otherwise.
|
||||||
|
*/
|
||||||
|
bool FNETestHooks::hasPeer(const TrafficNetwork& network, uint32_t peerId)
|
||||||
|
{
|
||||||
|
return network.m_peers.find(peerId) != network.m_peers.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the number of peers in the TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @return The number of peers.
|
||||||
|
*/
|
||||||
|
size_t FNETestHooks::peerCount(const TrafficNetwork& network)
|
||||||
|
{
|
||||||
|
return network.m_peers.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the connection state of the specified peer.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @param peerId The ID of the peer.
|
||||||
|
* @return The connection state of the peer.
|
||||||
|
*/
|
||||||
|
NET_CONN_STATUS FNETestHooks::peerState(const TrafficNetwork& network, uint32_t peerId)
|
||||||
|
{
|
||||||
|
auto it = network.m_peers.find(peerId);
|
||||||
|
if (it == network.m_peers.end() || it->second == nullptr)
|
||||||
|
throw std::out_of_range("peer does not exist");
|
||||||
|
return it->second->connectionState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clears all peers from the specified TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
*/
|
||||||
|
void FNETestHooks::clearPeers(TrafficNetwork& network)
|
||||||
|
{
|
||||||
|
for (auto peer : network.m_peers)
|
||||||
|
delete peer.second;
|
||||||
|
network.m_peers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a NetPacketRequest for the specified network, metadata, function, subFunction, peerId, and packet.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @param metadata The MetadataNetwork instance (can be nullptr).
|
||||||
|
* @param function The network function.
|
||||||
|
* @param subFunction The network sub-function.
|
||||||
|
* @param peerId The ID of the peer.
|
||||||
|
* @param packet The packet data.
|
||||||
|
* @return Pointer to the created NetPacketRequest.
|
||||||
|
*/
|
||||||
|
static NetPacketRequest* makeRequest(TrafficNetwork& network, MetadataNetwork* metadata,
|
||||||
|
NET_FUNC::ENUM function, NET_SUBFUNC::ENUM subFunction, uint32_t peerId,
|
||||||
|
const std::vector<uint8_t>& packet)
|
||||||
|
{
|
||||||
|
NetPacketRequest* request = new NetPacketRequest();
|
||||||
|
request->obj = &network;
|
||||||
|
request->metadataObj = metadata;
|
||||||
|
request->peerId = peerId;
|
||||||
|
request->fneHeader.setFunction(function);
|
||||||
|
request->fneHeader.setSubFunction(subFunction);
|
||||||
|
request->fneHeader.setPeerId(peerId);
|
||||||
|
request->fneHeader.setStreamId(1U);
|
||||||
|
request->rtpHeader.setSSRC(peerId);
|
||||||
|
request->pktRxTime = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
request->length = static_cast<int>(packet.size());
|
||||||
|
if (!packet.empty()) {
|
||||||
|
request->buffer = new uint8_t[packet.size()];
|
||||||
|
::memcpy(request->buffer, packet.data(), packet.size());
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dispatches a traffic packet to the specified TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @param function The network function.
|
||||||
|
* @param subFunction The network sub-function.
|
||||||
|
* @param peerId The ID of the peer.
|
||||||
|
* @param packet The packet data.
|
||||||
|
*/
|
||||||
|
void FNETestHooks::dispatchTraffic(TrafficNetwork& network, NET_FUNC::ENUM function,
|
||||||
|
NET_SUBFUNC::ENUM subFunction, uint32_t peerId, const std::vector<uint8_t>& packet)
|
||||||
|
{
|
||||||
|
TrafficNetwork::taskNetworkRx(makeRequest(network, nullptr, function, subFunction, peerId, packet));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dispatches a metadata packet to the specified TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @param metadata The MetadataNetwork instance.
|
||||||
|
* @param function The network function.
|
||||||
|
* @param subFunction The network sub-function.
|
||||||
|
* @param peerId The ID of the peer.
|
||||||
|
* @param packet The packet data.
|
||||||
|
*/
|
||||||
|
void FNETestHooks::dispatchMetadata(TrafficNetwork& network, MetadataNetwork& metadata,
|
||||||
|
NET_FUNC::ENUM function, NET_SUBFUNC::ENUM subFunction, uint32_t peerId,
|
||||||
|
const std::vector<uint8_t>& packet)
|
||||||
|
{
|
||||||
|
MetadataNetwork::taskNetworkRx(makeRequest(network, &metadata, function, subFunction, peerId, packet));
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Digital Voice Modem - Test Suite
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__FNE_TEST_HOOKS_H__)
|
||||||
|
#define __FNE_TEST_HOOKS_H__
|
||||||
|
|
||||||
|
#include "fne/network/MetadataNetwork.h"
|
||||||
|
#include "fne/network/TrafficNetwork.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test hooks for the FNE network components.
|
||||||
|
*/
|
||||||
|
class FNETestHooks {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Adds a peer to the specified TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @param peerId The ID of the peer to add.
|
||||||
|
* @param state The connection state of the peer.
|
||||||
|
* @param connected Whether the peer is connected.
|
||||||
|
* @return Reference to the added FNEPeerConnection.
|
||||||
|
*/
|
||||||
|
static network::FNEPeerConnection& addPeer(network::TrafficNetwork& network, uint32_t peerId,
|
||||||
|
network::NET_CONN_STATUS state, bool connected = false);
|
||||||
|
/**
|
||||||
|
* @brief Checks if the specified peer exists in the TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @param peerId The ID of the peer to check.
|
||||||
|
* @return True if the peer exists, false otherwise.
|
||||||
|
*/
|
||||||
|
static bool hasPeer(const network::TrafficNetwork& network, uint32_t peerId);
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the number of peers in the TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @return The number of peers.
|
||||||
|
*/
|
||||||
|
static size_t peerCount(const network::TrafficNetwork& network);
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the connection state of the specified peer.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @param peerId The ID of the peer.
|
||||||
|
* @return The connection state of the peer.
|
||||||
|
*/
|
||||||
|
static network::NET_CONN_STATUS peerState(const network::TrafficNetwork& network, uint32_t peerId);
|
||||||
|
/**
|
||||||
|
* @brief Clears all peers from the TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
*/
|
||||||
|
static void clearPeers(network::TrafficNetwork& network);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dispatches a traffic packet to the specified peer in the TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @param function The network function.
|
||||||
|
* @param subFunction The network sub-function.
|
||||||
|
* @param peerId The ID of the peer.
|
||||||
|
* @param packet The packet data.
|
||||||
|
*/
|
||||||
|
static void dispatchTraffic(network::TrafficNetwork& network, network::NET_FUNC::ENUM function,
|
||||||
|
network::NET_SUBFUNC::ENUM subFunction, uint32_t peerId, const std::vector<uint8_t>& packet);
|
||||||
|
/**
|
||||||
|
* @brief Dispatches a metadata packet to the specified peer in the TrafficNetwork.
|
||||||
|
* @param network The TrafficNetwork instance.
|
||||||
|
* @param metadata The MetadataNetwork instance.
|
||||||
|
* @param function The network function.
|
||||||
|
* @param subFunction The network sub-function.
|
||||||
|
* @param peerId The ID of the peer.
|
||||||
|
* @param packet The packet data.
|
||||||
|
*/
|
||||||
|
static void dispatchMetadata(network::TrafficNetwork& network, network::MetadataNetwork& metadata,
|
||||||
|
network::NET_FUNC::ENUM function, network::NET_SUBFUNC::ENUM subFunction, uint32_t peerId,
|
||||||
|
const std::vector<uint8_t>& packet);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __FNE_TEST_HOOKS_H__
|
||||||
Loading…
Reference in new issue