add synthetic U2U call tests;

pull/134/head
Bryan Biedenkapp 4 days ago
parent ab348543cf
commit b555b2a4f0

@ -31,6 +31,8 @@
#include <unordered_set>
#include <algorithm>
class HostTestHooks;
namespace p25
{
// ---------------------------------------------------------------------------
@ -155,6 +157,7 @@ namespace p25
void setTSBKVerbose(bool verbose);
protected:
friend class ::HostTestHooks;
friend class packet::Voice;
friend class packet::Data;
friend class p25::Control;

@ -18,6 +18,8 @@
#include "common/nxdn/channel/LICH.h"
#include "common/nxdn/channel/SACCH.h"
#include "common/nxdn/lc/RTCH.h"
#include "common/p25/lc/tsbk/TSBKFactory.h"
#include "host/p25/packet/ControlSignaling.h"
#include <cstring>
@ -384,6 +386,42 @@ bool HostTestHooks::p25SeedGrant(p25::Control& control, uint32_t srcId, uint32_t
return control.m_affiliations->grantCh(dstId, srcId, 15U, true, netGranted);
}
/* Seeds a P25 unit-to-unit grant for a call-procedure test. */
bool HostTestHooks::p25SeedU2UGrant(p25::Control& control, uint32_t srcId, uint32_t dstId)
{
return control.m_affiliations->grantCh(dstId, srcId, 15U, false, false);
}
/* Configures P25 unit-to-unit call-procedure policy for a test. */
void HostTestHooks::p25ConfigureU2U(p25::Control& control, bool availabilityCheck)
{
control.m_enableControl = true;
control.m_ignoreAffiliationCheck = false;
control.m_control->m_unitToUnitAvailCheck = availabilityCheck;
}
/* Registers a P25 unit for a call-procedure test. */
void HostTestHooks::p25RegisterUnit(p25::Control& control, uint32_t srcId)
{
control.m_affiliations->unitReg(srcId);
}
/* Injects an independently encoded raw TSBK into P25 control signaling. */
bool HostTestHooks::p25ProcessRawTSBK(p25::Control& control, const uint8_t* raw)
{
std::unique_ptr<p25::lc::TSBK> tsbk = p25::lc::tsbk::TSBKFactory::createTSBK(raw, true);
if (tsbk == nullptr) {
return false;
}
uint8_t frame[2U] = { 0U };
return control.m_control->process(frame, sizeof(frame), std::move(tsbk));
}
/* Releases a P25 grant for a steering-policy test. */
bool HostTestHooks::p25ReleaseGrant(p25::Control& control, uint32_t dstId)

@ -232,6 +232,33 @@ public:
* @returns bool True if the grant was created; otherwise, false.
*/
static bool p25SeedGrant(p25::Control& control, uint32_t srcId, uint32_t dstId, bool netGranted);
/**
* @brief Seeds a P25 unit-to-unit grant for a call-procedure test.
* @param control P25 control instance.
* @param srcId Source radio ID.
* @param dstId Destination radio ID.
* @returns bool True if the grant was created; otherwise, false.
*/
static bool p25SeedU2UGrant(p25::Control& control, uint32_t srcId, uint32_t dstId);
/**
* @brief Configures P25 unit-to-unit call-procedure policy for a test.
* @param control P25 control instance.
* @param availabilityCheck Flag indicating whether the availability check is enabled.
*/
static void p25ConfigureU2U(p25::Control& control, bool availabilityCheck);
/**
* @brief Registers a P25 unit for a call-procedure test.
* @param control P25 control instance.
* @param srcId Radio ID to register.
*/
static void p25RegisterUnit(p25::Control& control, uint32_t srcId);
/**
* @brief Injects an independently encoded raw TSBK into P25 control signaling.
* @param control P25 control instance.
* @param raw Raw 12-byte TSBK including CRC.
* @returns bool True if the TSBK was accepted; otherwise, false.
*/
static bool p25ProcessRawTSBK(p25::Control& control, const uint8_t* raw);
/**
* @brief Releases a P25 grant for a steering-policy test.
* @param control P25 control instance.

@ -15,6 +15,7 @@
#include "common/lookups/TalkgroupRulesLookup.h"
#include "common/network/NetRPC.h"
#include "common/network/Network.h"
#include "common/edac/CRC.h"
#include "common/p25/P25Defines.h"
#include "common/p25/data/LowSpeedData.h"
#include "common/p25/lc/LC.h"
@ -142,6 +143,42 @@ void buildP25RFFrame(const uint8_t* payload, uint8_t* frame)
::memcpy(frame + 2U, payload, p25::defines::P25_LDU_FRAME_LENGTH_BYTES);
}
/**
* @brief Builds an independently encoded UU VCH request TSBK.
* @param srcId The ID of the source.
* @param dstId The ID of the destination.
* @param raw The buffer to store the constructed TSBK.
*/
void buildUUVchRequest(uint32_t srcId, uint32_t dstId, uint8_t* raw)
{
::memset(raw, 0x00U, p25::defines::P25_TSBK_LENGTH_BYTES);
raw[0U] = 0x80U | p25::defines::TSBKO::IOSP_UU_VCH;
raw[1U] = p25::defines::MFG_STANDARD;
raw[2U] = 0x04U;
SET_UINT24(dstId, raw, 4U);
SET_UINT24(srcId, raw, 7U);
edac::CRC::addCCITT162(raw, p25::defines::P25_TSBK_LENGTH_BYTES);
}
/**
* @brief Builds an independently encoded UU ANS response ISP TSBK.
* @param responderId The ID of the responder.
* @param callerId The ID of the caller.
* @param response The response code.
* @param raw The buffer to store the constructed TSBK.
*/
void buildUUAnswerResponse(uint32_t responderId, uint32_t callerId, uint8_t response, uint8_t* raw)
{
::memset(raw, 0x00U, p25::defines::P25_TSBK_LENGTH_BYTES);
raw[0U] = 0x80U | p25::defines::TSBKO::IOSP_UU_ANS;
raw[1U] = p25::defines::MFG_STANDARD;
raw[2U] = 0x04U;
raw[3U] = response;
SET_UINT24(callerId, raw, 4U);
SET_UINT24(responderId, raw, 7U);
edac::CRC::addCCITT162(raw, p25::defines::P25_TSBK_LENGTH_BYTES);
}
} // namespace
// ---------------------------------------------------------------------------
@ -591,6 +628,103 @@ TEST_CASE("P25 enhanced conventional RF grant permits only the owning source to
REQUIRE(HostTestHooks::p25RFLastDstId(*harness.m_control) == 2001U);
}
TEST_CASE("P25 unit-to-unit grant admits either participant as first talker",
"[p25][host][control][u2u]")
{
P25HostHarness harness;
const uint32_t channelNo = 1U;
const uint32_t callingUnit = 5519008U;
const uint32_t calledUnit = 5519007U;
harness.m_chLookup.setRFChData(channelNo, lookups::VoiceChData(1U, channelNo, "", 0U, ""));
REQUIRE(harness.m_chLookup.initializeRFCh(channelNo));
REQUIRE(HostTestHooks::p25SeedU2UGrant(*harness.m_control, callingUnit, calledUnit));
// TIA-102.AABD permits either party to be the first talker after the
// availability-check procedure. Voice admission therefore must recognize
// the same private call when its direction is reversed.
CHECK(HostTestHooks::p25IsGranted(*harness.m_control, calledUnit));
CHECK(HostTestHooks::p25IsGranted(*harness.m_control, callingUnit));
}
TEST_CASE("P25 unit-to-unit availability-check procedure grants only after proceed",
"[p25][host][control][u2u][procedure]")
{
P25HostHarness harness;
const uint32_t channelNo = 1U;
const uint32_t caller = 5519008U;
const uint32_t called = 5519007U;
uint8_t raw[p25::defines::P25_TSBK_LENGTH_BYTES];
harness.m_chLookup.setRFChData(channelNo, lookups::VoiceChData(1U, channelNo, "", 0U, ""));
REQUIRE(harness.m_chLookup.initializeRFCh(channelNo));
HostTestHooks::p25ConfigureU2U(*harness.m_control, true);
HostTestHooks::p25RegisterUnit(*harness.m_control, caller);
HostTestHooks::p25RegisterUnit(*harness.m_control, called);
buildUUVchRequest(caller, called, raw);
REQUIRE(HostTestHooks::p25ProcessRawTSBK(*harness.m_control, raw));
CHECK_FALSE(HostTestHooks::p25IsGranted(*harness.m_control, caller));
CHECK_FALSE(HostTestHooks::p25IsGranted(*harness.m_control, called));
buildUUAnswerResponse(called, caller, p25::defines::ResponseCode::ANS_WAIT, raw);
REQUIRE(HostTestHooks::p25ProcessRawTSBK(*harness.m_control, raw));
CHECK_FALSE(HostTestHooks::p25IsGranted(*harness.m_control, caller));
CHECK_FALSE(HostTestHooks::p25IsGranted(*harness.m_control, called));
buildUUAnswerResponse(called, caller, p25::defines::ResponseCode::ANS_PROCEED, raw);
REQUIRE(HostTestHooks::p25ProcessRawTSBK(*harness.m_control, raw));
CHECK(HostTestHooks::p25IsGranted(*harness.m_control, caller));
}
TEST_CASE("P25 unit-to-unit rejects unsolicited and mismatched answer responses",
"[p25][host][control][u2u][procedure]")
{
P25HostHarness harness;
const uint32_t channelNo = 1U;
const uint32_t caller = 5519008U;
const uint32_t called = 5519007U;
const uint32_t unrelatedCaller = 5519006U;
uint8_t raw[p25::defines::P25_TSBK_LENGTH_BYTES];
harness.m_chLookup.setRFChData(channelNo, lookups::VoiceChData(1U, channelNo, "", 0U, ""));
REQUIRE(harness.m_chLookup.initializeRFCh(channelNo));
HostTestHooks::p25ConfigureU2U(*harness.m_control, true);
HostTestHooks::p25RegisterUnit(*harness.m_control, caller);
HostTestHooks::p25RegisterUnit(*harness.m_control, called);
HostTestHooks::p25RegisterUnit(*harness.m_control, unrelatedCaller);
buildUUAnswerResponse(called, caller, p25::defines::ResponseCode::ANS_PROCEED, raw);
REQUIRE(HostTestHooks::p25ProcessRawTSBK(*harness.m_control, raw));
CHECK_FALSE(HostTestHooks::p25IsGranted(*harness.m_control, caller));
REQUIRE(HostTestHooks::p25ReleaseGrant(*harness.m_control, caller));
buildUUVchRequest(caller, called, raw);
REQUIRE(HostTestHooks::p25ProcessRawTSBK(*harness.m_control, raw));
buildUUAnswerResponse(called, unrelatedCaller, p25::defines::ResponseCode::ANS_PROCEED, raw);
REQUIRE(HostTestHooks::p25ProcessRawTSBK(*harness.m_control, raw));
CHECK_FALSE(HostTestHooks::p25IsGranted(*harness.m_control, unrelatedCaller));
}
TEST_CASE("P25 unit-to-unit without availability check grants the request immediately",
"[p25][host][control][u2u][procedure]")
{
P25HostHarness harness;
const uint32_t channelNo = 1U;
const uint32_t caller = 5519008U;
const uint32_t called = 5519007U;
uint8_t raw[p25::defines::P25_TSBK_LENGTH_BYTES];
harness.m_chLookup.setRFChData(channelNo, lookups::VoiceChData(1U, channelNo, "", 0U, ""));
REQUIRE(harness.m_chLookup.initializeRFCh(channelNo));
HostTestHooks::p25ConfigureU2U(*harness.m_control, false);
HostTestHooks::p25RegisterUnit(*harness.m_control, caller);
buildUUVchRequest(caller, called, raw);
REQUIRE(HostTestHooks::p25ProcessRawTSBK(*harness.m_control, raw));
CHECK(HostTestHooks::p25IsGranted(*harness.m_control, called));
}
TEST_CASE("P25 host preserves encrypted network call parameters", "[p25][host][control][crypto]")
{
P25HostHarness harness;

@ -0,0 +1,69 @@
// 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.
*/
#include "host/Defines.h"
#include "common/edac/CRC.h"
#include "common/p25/P25Defines.h"
#include "common/p25/lc/tsbk/TSBKFactory.h"
using namespace p25;
using namespace p25::defines;
using namespace p25::lc;
using namespace p25::lc::tsbk;
#include <catch2/catch_test_macros.hpp>
TEST_CASE("TSBK unit-to-unit answer request and response layouts", "[p25][tsbk][u2u]")
{
const uint32_t callingUnit = 0x543210U;
const uint32_t calledUnit = 0x123456U;
SECTION("OSP answer request omits the ISP answer-response octet") {
IOSP_UU_ANS request;
request.setPriority(4U);
request.setSrcId(callingUnit);
request.setDstId(calledUnit);
uint8_t raw[P25_TSBK_LENGTH_BYTES];
request.encode(raw, true, true);
REQUIRE((raw[0U] & 0x3FU) == TSBKO::IOSP_UU_ANS);
REQUIRE(raw[1U] == MFG_STANDARD);
REQUIRE(raw[2U] == 0x04U); // Service options
REQUIRE(raw[3U] == 0x00U); // Reserved in the OSP request
REQUIRE(raw[4U] == 0x12U); // Target address
REQUIRE(raw[5U] == 0x34U);
REQUIRE(raw[6U] == 0x56U);
REQUIRE(raw[7U] == 0x54U); // Source address
REQUIRE(raw[8U] == 0x32U);
REQUIRE(raw[9U] == 0x10U);
}
SECTION("ISP answer response decodes responder as source and caller as target") {
uint8_t raw[P25_TSBK_LENGTH_BYTES] = { 0U };
raw[0U] = 0x80U | TSBKO::IOSP_UU_ANS;
raw[1U] = MFG_STANDARD;
raw[2U] = 0x04U; // Service options
raw[3U] = ResponseCode::ANS_PROCEED;
raw[4U] = 0x54U; // Target: original caller
raw[5U] = 0x32U;
raw[6U] = 0x10U;
raw[7U] = 0x12U; // Source: responding called unit
raw[8U] = 0x34U;
raw[9U] = 0x56U;
edac::CRC::addCCITT162(raw, P25_TSBK_LENGTH_BYTES);
std::unique_ptr<TSBK> tsbk = TSBKFactory::createTSBK(raw, true);
REQUIRE(tsbk != nullptr);
IOSP_UU_ANS* response = dynamic_cast<IOSP_UU_ANS*>(tsbk.get());
REQUIRE(response != nullptr);
REQUIRE(response->getResponse() == ResponseCode::ANS_PROCEED);
REQUIRE(response->getPriority() == 4U);
REQUIRE(response->getSrcId() == calledUnit);
REQUIRE(response->getDstId() == callingUnit);
}
}
Loading…
Cancel
Save

Powered by TurnKey Linux.