correct P25 unit-to-unit routing across FNE sites; use the explicit network U2U flag for private-call validation, routing, and late-entry voice when a private LCO is unavailable; restrict network answer requests to the control site serving the target subscriber; document conventional interoperability requirements; add FNE and host regression tests;

pull/134/head
Bryan Biedenkapp 4 days ago
parent bcb8894154
commit 2ba4dbf97c

@ -340,6 +340,8 @@ protocols:
# Flag indicating whether or not the host will acknowledge message packets.
noMessageAck: true
# Flag indicating that unit-to-unit availiability checks should be performed for a private call.
# (Disable unitToUnitAvailCheck for trunked sites that must call conventional RIDs, as unit-to-unit availability
# checks are not applicable in such scenarios.)
unitToUnitAvailCheck: false
# Flag indicating telephone interconnect requests are forwarded to the network for a telephone
# interconnect gateway peer to service. (If disabled, telephone interconnect requests are denied.)

@ -586,8 +586,9 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
TrafficNetwork::MetricsLogging::incrementActiveCalls(m_network);
}
// is this a private call?
if (lco == LCO::PRIVATE) {
// The network U2U flag is authoritative across all voice
// data units, including late entry without a private LC.
if (!control.getGroup() && !isTelIntTraffic(control, duid, nullptr)) {
m_statusPVCall.lock(false);
m_statusPVCall[dstId].callStartTime = pktTime;
m_statusPVCall[dstId].srcId = srcId;
@ -687,7 +688,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
privateCallInProgress = true;
}
} else {
if (lco == LCO::PRIVATE) {
if (!control.getGroup() && !telInt) {
privateCallInProgress = true;
}
}
@ -754,7 +755,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
}
// is this a private call?
if ((lco == LCO::PRIVATE) && !neighbor) {
if (!control.getGroup() && !telInt && !neighbor) {
// is this a private call? if so only repeat to the peer that registered the unit
auto it = std::find_if(m_statusPVCall.begin(), m_statusPVCall.end(), [&](StatusMapPair& x) {
if (x.second.dstId == dstId) {
@ -1605,7 +1606,15 @@ bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, DUID::E duid,
if (g_promiscuousHub)
return true;
if (control.getLCO() == LCO::PRIVATE) {
// telephone interconnect calls are individual calls routed by their
// gateway policy rather than the U2U subscriber policy
if (duid != DUID::TSDU && isTelIntTraffic(control, duid, nullptr)) {
return true;
}
// use the explicit network individual-call flag; private voice LC is not
// guaranteed in every LDU, particularly during late entry
if (!control.getGroup()) {
if (m_network->m_disallowU2U)
return false;
if (!m_network->checkU2UDroppedPeer(peerId))
@ -1620,11 +1629,6 @@ bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, DUID::E duid,
return false;
}
// telephone interconnect calls are individual calls routed by isTelIntPeerPermitted()
if (duid != DUID::TSDU && isTelIntTraffic(control, duid, nullptr)) {
return true;
}
TrafficNetwork::PeerConnectionPtr connection = peerId > 0U ? m_network->findPeer(peerId) : nullptr;
if (connection != nullptr) {
@ -1868,6 +1872,8 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
}
m_rejectedCallStreams.unlock();
const bool telIntCall = (duid != DUID::TSDU) && isTelIntTraffic(control, duid, nullptr);
// validate private call in-progress
bool privateCallInProgress = false;
if ((control.getLCO() != LCO::PRIVATE) && !control.getGroup()) {
@ -1885,8 +1891,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
}
// is this a private or telephone interconnect call?
bool telIntCall = (duid != DUID::TSDU) && isTelIntTraffic(control, duid, nullptr);
if ((control.getLCO() == LCO::PRIVATE) || telIntCall || privateCallInProgress) {
if ((!control.getGroup() && !telIntCall) || telIntCall || privateCallInProgress) {
// is the destination ID a blacklisted ID?
lookups::RadioId rid = m_network->m_ridLookup->find(control.getDstId());
if (!rid.radioDefault()) {

@ -1050,12 +1050,16 @@ bool ControlSignaling::processNetwork(uint8_t* data, uint32_t len, lc::LC& contr
}
else {
VERBOSE_LOG_TSBK_NET(tsbk->toString(), srcId, dstId);
if (m_p25->m_affiliations->isUnitReg(dstId)) {
const uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) +
(tsbk->getEncrypted() ? 0x40U : 0x00U) +
(tsbk->getPriority() & 0x07U);
startU2UAvailabilityTransaction(srcId, dstId, serviceOptions, false, true);
// only the control-enabled site currently serving
// the target SU may place this request on RF
if (!m_p25->m_enableControl || !m_p25->m_affiliations->isUnitReg(dstId)) {
return true;
}
const uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) +
(tsbk->getEncrypted() ? 0x40U : 0x00U) +
(tsbk->getPriority() & 0x07U);
startU2UAvailabilityTransaction(srcId, dstId, serviceOptions, false, true);
}
}
break;

@ -570,6 +570,25 @@ TEST_CASE("FNE call handlers reject globally disabled unit-to-unit traffic", "[f
}
}
TEST_CASE("FNE P25 uses the explicit U2U flag for late-entry voice", "[fne][callhandler][routing][u2u]")
{
constexpr uint32_t PEER_ID = 1001U;
constexpr uint32_t SOURCE_ID = 5519007U;
constexpr uint32_t DESTINATION_ID = 5519001U;
FNEHarness harness;
FNETestHooks::addPeer(harness.traffic, PEER_ID, network::NET_STAT_RUNNING, true);
harness.radios.addEntry(SOURCE_ID, true, "test source");
harness.radios.addEntry(DESTINATION_ID, true, "test destination");
CHECK(FNETestHooks::validateP25IndividualCall(
harness.traffic, PEER_ID, SOURCE_ID, DESTINATION_ID));
FNETestHooks::disallowU2U(harness.traffic, true);
CHECK_FALSE(FNETestHooks::validateP25IndividualCall(
harness.traffic, PEER_ID, SOURCE_ID, DESTINATION_ID));
}
TEST_CASE("FNE call handlers validate source radios and group state", "[fne][callhandler][validation]")
{
constexpr uint32_t PEER_ID = 1001U;

@ -201,6 +201,7 @@ bool FNETestHooks::isPeerPermitted(TrafficNetwork& network, ParrotProtocol proto
p25::lc::LC control;
control.setDstId(destinationId);
control.setLCO(group ? p25::defines::LCO::GROUP : p25::defines::LCO::PRIVATE);
control.setGroup(group);
return network.m_tagP25->isPeerPermitted(
peerId, control, p25::defines::DUID::LDU1, STREAM_ID, fromUpstream);
}
@ -280,6 +281,24 @@ bool FNETestHooks::validateGroupCall(TrafficNetwork& network, ParrotProtocol pro
return false;
}
/* Validates P25 individual traffic whose current LCO is not private. */
bool FNETestHooks::validateP25IndividualCall(TrafficNetwork& network, uint32_t peerId,
uint32_t sourceId, uint32_t destinationId)
{
constexpr uint32_t STREAM_ID = 103U;
p25::lc::LC control;
control.setSrcId(sourceId);
control.setDstId(destinationId);
control.setLCO(p25::defines::LCO::GROUP);
control.setGroup(false);
return network.m_tagP25->validate(
peerId, control, p25::defines::DUID::LDU2, nullptr, 0U, STREAM_ID) &&
network.m_tagP25->isPeerPermitted(
peerId, control, p25::defines::DUID::LDU2, STREAM_ID, false);
}
/* Sets whether unit-to-unit traffic is globally disabled. */
void FNETestHooks::disallowU2U(TrafficNetwork& network, bool disabled)

@ -164,6 +164,16 @@ public:
*/
static bool validateGroupCall(network::TrafficNetwork& network, ParrotProtocol protocol,
uint32_t peerId, uint32_t sourceId, uint32_t talkgroupId, uint8_t slotNo = 1U);
/**
* @brief Validates P25 individual traffic whose current LCO is not private.
* @param network The TrafficNetwork instance.
* @param peerId Originating peer ID.
* @param sourceId Source subscriber ID.
* @param destinationId Target subscriber ID.
* @return True when the explicit individual-call flag is honored.
*/
static bool validateP25IndividualCall(network::TrafficNetwork& network, uint32_t peerId,
uint32_t sourceId, uint32_t destinationId);
/**
* @brief Sets whether unit-to-unit traffic is globally disabled.
* @param network The TrafficNetwork instance.

@ -1000,6 +1000,22 @@ TEST_CASE("P25 unit-to-unit target site correlates a network answer request with
CHECK(HostTestHooks::p25IsU2UGranted(*harness.m_control, caller, called));
}
TEST_CASE("P25 network answer request is ignored outside the target unit site",
"[p25][host][control][u2u][procedure][network]")
{
P25HostHarness harness;
const uint32_t caller = 5519008U;
const uint32_t called = 5519007U;
uint8_t raw[p25::defines::P25_TSBK_LENGTH_BYTES];
HostTestHooks::p25ConfigureU2U(*harness.m_control, true);
buildUUAnswerRequest(caller, called, raw);
REQUIRE(HostTestHooks::p25ProcessNetworkRawTSBK(*harness.m_control, raw));
CHECK(HostTestHooks::p25PendingU2UCount(*harness.m_control) == 0U);
CHECK_FALSE(HostTestHooks::p25IsU2UGranted(*harness.m_control, caller, called));
}
TEST_CASE("P25 host preserves encrypted network call parameters", "[p25][host][control][crypto]")
{
P25HostHarness harness;

Loading…
Cancel
Save

Powered by TurnKey Linux.