P25 OTAR unit tests;

pull/129/head
Bryan Biedenkapp 2 weeks ago
parent 93876e6ab7
commit fc23017cf2

@ -28,7 +28,8 @@ KMMDeregistrationResponse::KMMDeregistrationResponse() : KMMFrame(),
m_status(KMM_Status::CMD_PERFORMED)
{
m_messageId = KMM_MessageType::DEREG_RSP;
m_respKind = KMM_ResponseKind::IMMEDIATE;
// TIA-102.AACA Table 2: response messages use Response Kind 1 (None).
m_respKind = KMM_ResponseKind::NONE;
}
/* Finalizes a instance of the KMMDeregistrationResponse class. */

@ -719,10 +719,11 @@ UInt8Array P25OTARService::write_KMM_Rekey_Command(uint32_t llId, uint32_t kmmRS
outKmm.getSrcLLId(), outKmm.getDstLLId(), ks.keys().size());
}
const uint32_t frameLength = outKmm.fullLength();
if (payloadSize != nullptr)
*payloadSize = outKmm.length();
*payloadSize = frameLength;
UInt8Array kmmFrame = std::make_unique<uint8_t[]>(outKmm.length());
UInt8Array kmmFrame = std::make_unique<uint8_t[]>(frameLength);
outKmm.encode(kmmFrame.get());
outKmm.generateMAC(kekKey, kmmFrame.get());
return kmmFrame;
@ -741,10 +742,11 @@ UInt8Array P25OTARService::write_KMM_Reg_Command(uint32_t llId, uint32_t kmmRSI,
outKmm.getSrcLLId(), outKmm.getDstLLId());
}
const uint32_t frameLength = outKmm.fullLength();
if (payloadSize != nullptr)
*payloadSize = outKmm.length();
*payloadSize = frameLength;
UInt8Array kmmFrame = std::make_unique<uint8_t[]>(outKmm.length());
UInt8Array kmmFrame = std::make_unique<uint8_t[]>(frameLength);
outKmm.encode(kmmFrame.get());
return kmmFrame;
}
@ -763,10 +765,11 @@ UInt8Array P25OTARService::write_KMM_Dereg_Response(uint32_t llId, uint32_t kmmR
outKmm.getSrcLLId(), outKmm.getDstLLId());
}
const uint32_t frameLength = outKmm.fullLength();
if (payloadSize != nullptr)
*payloadSize = outKmm.length();
*payloadSize = frameLength;
UInt8Array kmmFrame = std::make_unique<uint8_t[]>(outKmm.length());
UInt8Array kmmFrame = std::make_unique<uint8_t[]>(frameLength);
outKmm.encode(kmmFrame.get());
return kmmFrame;
}
@ -784,10 +787,11 @@ UInt8Array P25OTARService::write_KMM_NoService(uint32_t llId, uint32_t kmmRSI, u
outKmm.getSrcLLId(), outKmm.getDstLLId());
}
const uint32_t frameLength = outKmm.fullLength();
if (payloadSize != nullptr)
*payloadSize = outKmm.length();
*payloadSize = frameLength;
UInt8Array kmmFrame = std::make_unique<uint8_t[]>(outKmm.length());
UInt8Array kmmFrame = std::make_unique<uint8_t[]>(frameLength);
outKmm.encode(kmmFrame.get());
return kmmFrame;
}
@ -805,10 +809,11 @@ UInt8Array P25OTARService::write_KMM_Zeroize(uint32_t llId, uint32_t kmmRSI, uin
outKmm.getSrcLLId(), outKmm.getDstLLId());
}
const uint32_t frameLength = outKmm.fullLength();
if (payloadSize != nullptr)
*payloadSize = outKmm.length();
*payloadSize = frameLength;
UInt8Array kmmFrame = std::make_unique<uint8_t[]>(outKmm.length());
UInt8Array kmmFrame = std::make_unique<uint8_t[]>(frameLength);
outKmm.encode(kmmFrame.get());
return kmmFrame;
}

@ -16,6 +16,10 @@
#if !defined(__P25_OTAR_SERVICE_H__)
#define __P25_OTAR_SERVICE_H__
#if defined(CATCH2_TEST_COMPILATION)
class FNETestHooks;
#endif
#include "fne/Defines.h"
#include "common/concurrent/unordered_map.h"
#include "common/p25/P25Defines.h"
@ -51,6 +55,9 @@ namespace network
* @ingroup fne_network
*/
class HOST_SW_API P25OTARService {
#if defined(CATCH2_TEST_COMPILATION)
friend class ::FNETestHooks;
#endif
public:
/**
* @brief Initializes a new instance of the P25OTARService class.

@ -9,6 +9,7 @@
*/
#include "fne/FNETestHooks.h"
#include "fne/HostFNE.h"
#include "fne/network/P25OTARService.h"
#include <cstring>
#include <chrono>
@ -69,6 +70,22 @@ FNEPeerConnection& FNETestHooks::addPeer(TrafficNetwork& network, uint32_t peerI
return *connection;
}
std::unique_ptr<uint8_t[]> FNETestHooks::processOTARKMM(TrafficNetwork& network,
const std::vector<uint8_t>& packet, uint32_t llId, uint32_t& payloadSize)
{
payloadSize = 0U;
if (packet.empty() || network.m_p25OTARService == nullptr)
return nullptr;
return network.m_p25OTARService->processKMM(packet.data(), (uint32_t)packet.size(), llId,
false, &payloadSize);
}
void FNETestHooks::setKMFServicesEnabled(TrafficNetwork& network, bool enabled)
{
network.m_kmfServicesEnabled = enabled;
}
// ---------------------------------------------------------------------------
// Static Class Members
// ---------------------------------------------------------------------------

@ -14,6 +14,7 @@
#include "fne/network/TrafficNetwork.h"
#include <cstdint>
#include <memory>
#include <vector>
// ---------------------------------------------------------------------------
@ -83,6 +84,19 @@ public:
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);
/**
* @brief Passes an encoded KMM through the FNE OTAR message dispatcher.
* @param network The TrafficNetwork that owns the OTAR service.
* @param packet Encoded KMM bytes.
* @param llId Logical Link ID associated with the message.
* @param[out] payloadSize Size of the returned KMM, or zero when no response is required.
* @return Encoded response, or nullptr when the dispatcher produces no response.
*/
static std::unique_ptr<uint8_t[]> processOTARKMM(network::TrafficNetwork& network,
const std::vector<uint8_t>& packet, uint32_t llId, uint32_t& payloadSize);
/** @brief Enables or disables KMF services for a test TrafficNetwork. */
static void setKMFServicesEnabled(network::TrafficNetwork& network, bool enabled);
};
#endif // __FNE_TEST_HOOKS_H__

@ -10,17 +10,49 @@
#include "host/Defines.h"
#include "common/p25/P25Defines.h"
#include "common/p25/kmm/KMMFactory.h"
#include "common/p25/kmm/KMMDeregistrationCommand.h"
#include "common/p25/kmm/KMMDeregistrationResponse.h"
#include "common/p25/kmm/KMMHello.h"
#include "common/p25/kmm/KMMNoService.h"
#include "fne/FNETestHooks.h"
#include "fne/HostFNE.h"
#include <catch2/catch_test_macros.hpp>
#include <cstring>
#include <vector>
using namespace p25;
using namespace p25::defines;
using namespace p25::kmm;
namespace {
class KMMFNEHarness {
public:
KMMFNEHarness() :
host("fne-kmm-test.yml"),
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)
{
/* stub */
}
HostFNE host;
network::TrafficNetwork traffic;
};
template<typename T>
std::vector<uint8_t> encodeKMM(T& frame)
{
std::vector<uint8_t> bytes(frame.fullLength(), 0U);
frame.encode(bytes.data());
return bytes;
}
} // namespace
TEST_CASE("KMM factory decodes encoded HELLO and NO_SERVICE frames", "[p25][kmm][factory]")
{
SECTION("HELLO frame round-trips with message number") {
@ -74,3 +106,83 @@ TEST_CASE("KMM factory decodes encoded HELLO and NO_SERVICE frames", "[p25][kmm]
REQUIRE(frame == nullptr);
}
}
TEST_CASE("FNE OTAR dispatcher follows AACA response-kind procedures", "[p25][kmm][otar]")
{
KMMFNEHarness harness;
constexpr uint32_t SU_RSI = 0x654321U;
constexpr uint32_t KMF_RSI = 0x123456U;
SECTION("Response Kind 1 Hello produces no OTAR response") {
KMMHello request;
request.setDstLLId(KMF_RSI);
request.setSrcLLId(SU_RSI);
request.setResponseKind(KMM_ResponseKind::NONE);
uint32_t payloadSize = 99U;
UInt8Array response = FNETestHooks::processOTARKMM(harness.traffic,
encodeKMM(request), SU_RSI, payloadSize);
REQUIRE(response == nullptr);
REQUIRE(payloadSize == 0U);
}
SECTION("SU-originated Response Kind 2 Hello is discarded") {
KMMHello request;
request.setDstLLId(KMF_RSI);
request.setSrcLLId(SU_RSI);
request.setResponseKind(KMM_ResponseKind::DELAYED);
uint32_t payloadSize = 99U;
UInt8Array response = FNETestHooks::processOTARKMM(harness.traffic,
encodeKMM(request), SU_RSI, payloadSize);
REQUIRE(response == nullptr);
REQUIRE(payloadSize == 0U);
}
SECTION("Response Kind 3 Hello receives No-Service when KMF service is disabled") {
KMMHello request;
request.setDstLLId(KMF_RSI);
request.setSrcLLId(SU_RSI);
request.setResponseKind(KMM_ResponseKind::IMMEDIATE);
request.setFlag(KMM_HelloFlag::REKEY_REQUEST_UKEK);
uint32_t payloadSize = 0U;
UInt8Array response = FNETestHooks::processOTARKMM(harness.traffic,
encodeKMM(request), SU_RSI, payloadSize);
REQUIRE(response != nullptr);
REQUIRE(payloadSize == KMMNoService().fullLength());
std::unique_ptr<KMMFrame> decoded = KMMFactory::create(response.get());
REQUIRE(decoded != nullptr);
REQUIRE(decoded->getMessageId() == KMM_MessageType::NO_SERVICE);
REQUIRE(decoded->getResponseKind() == KMM_ResponseKind::NONE);
REQUIRE(decoded->getDstLLId() == SU_RSI);
REQUIRE(decoded->getSrcLLId() == WUID_FNE);
}
SECTION("Response Kind 3 Deregistration receives Deregistration-Response") {
FNETestHooks::setKMFServicesEnabled(harness.traffic, true);
KMMDeregistrationCommand request;
request.setDstLLId(KMF_RSI);
request.setSrcLLId(SU_RSI);
request.setResponseKind(KMM_ResponseKind::IMMEDIATE);
request.setKMFRSI(KMF_RSI);
uint32_t payloadSize = 0U;
UInt8Array response = FNETestHooks::processOTARKMM(harness.traffic,
encodeKMM(request), SU_RSI, payloadSize);
REQUIRE(response != nullptr);
KMMDeregistrationResponse expected;
REQUIRE(payloadSize == expected.fullLength());
std::unique_ptr<KMMFrame> decoded = KMMFactory::create(response.get());
REQUIRE(decoded != nullptr);
REQUIRE(decoded->getMessageId() == KMM_MessageType::DEREG_RSP);
REQUIRE(decoded->getResponseKind() == KMM_ResponseKind::NONE);
REQUIRE(decoded->getDstLLId() == SU_RSI);
REQUIRE(decoded->getSrcLLId() == WUID_FNE);
}
}

Loading…
Cancel
Save

Powered by TurnKey Linux.