implement in-call control mechanism (this allows the FNE to command a host to stop transmitting if a ACL check fails);

pull/86/head
Bryan Biedenkapp 1 year ago
parent 15656dcdbf
commit 59f85c5976

@ -87,6 +87,8 @@ master:
disallowExtAdjStsBcast: true
# Flag indicating whether or not a conventional site can override affiliation rules.
allowConvSiteAffOverride: true
# Flag indicating whether or not In-Call Control feedback is enabled.
enableInCallCtrl: true
# Flag indicating that traffic headers will be filtered by destination ID (i.e. valid RID or valid TGID).
filterHeaders: true

@ -63,6 +63,7 @@ namespace network
PONG = 0x75U, //! Pong
GRANT_REQ = 0x7AU, //! Grant Request
INCALL_CTRL = 0x7BU, //! In-Call Control
ACK = 0x7EU, //! Packet Acknowledge
NAK = 0x7FU, //! Packet Negative Acknowledge
@ -111,6 +112,19 @@ namespace network
};
};
/**
* @brief Network In-Call Control
* @ingroup network_core
*/
namespace NET_ICC {
enum ENUM : uint8_t {
NOP = 0xFFU, //! No Operation Sub-Function
BUSY_DENY = 0x00U, //! Busy Deny
REJECT_TRAFFIC = 0x01U, //! Reject Active Traffic
};
};
namespace frame
{
// ---------------------------------------------------------------------------

@ -124,6 +124,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions)
m_disallowAdjStsBcast = conf["disallowAdjStsBcast"].as<bool>(false);
m_disallowExtAdjStsBcast = conf["disallowExtAdjStsBcast"].as<bool>(true);
m_allowConvSiteAffOverride = conf["allowConvSiteAffOverride"].as<bool>(true);
m_enableInCallCtrl = conf["enableInCallCtrl"].as<bool>(true);
m_softConnLimit = conf["connectionLimit"].as<uint32_t>(MAX_HARD_CONN_CAP);
if (m_softConnLimit > MAX_HARD_CONN_CAP) {
@ -179,6 +180,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions)
LogInfo(" Dump Packet Data: %s", m_dumpDataPacket ? "yes" : "no");
LogInfo(" Disable P25 ADJ_STS_BCAST to external peers: %s", m_disallowExtAdjStsBcast ? "yes" : "no");
LogInfo(" Allow conventional sites to override affiliation and receive all traffic: %s", m_allowConvSiteAffOverride ? "yes" : "no");
LogInfo(" Enable In-Call Control: %s", m_enableInCallCtrl ? "yes" : "no");
LogInfo(" Restrict grant response by affiliation: %s", m_restrictGrantToAffOnly ? "yes" : "no");
LogInfo(" Traffic Headers Filtered by Destination ID: %s", m_filterHeaders ? "yes" : "no");
LogInfo(" Traffic Terminators Filtered by Destination ID: %s", m_filterTerminators ? "yes" : "no");
@ -2243,6 +2245,24 @@ void FNENetwork::writePeerList(uint32_t peerId)
return;
}
/* Helper to send a In-Call Control command to the specified peer. */
bool FNENetwork::writePeerICC(uint32_t peerId, NET_SUBFUNC::ENUM subFunc, NET_ICC::ENUM command, uint8_t slotNo)
{
assert(peerId > 0);
if (!m_enableInCallCtrl)
return false;
uint8_t buffer[DATA_PACKET_LENGTH];
::memset(buffer, 0x00U, DATA_PACKET_LENGTH);
__SET_UINT32(peerId, buffer, 6U); // Peer ID
buffer[10U] = (uint8_t)command; // In-Call Control Command
buffer[11U] = slotNo;
return writePeer(peerId, { NET_FUNC::INCALL_CTRL, subFunc }, buffer, 12U, RTP_END_OF_CALL_SEQ, false, true);
}
/* Helper to send a data message to the specified peer. */
bool FNENetwork::writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data,
@ -2379,7 +2399,7 @@ bool FNENetwork::writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REA
__SET_UINT16B((uint16_t)reason, buffer, 10U); // Reason
logPeerNAKReason(peerId, tag, reason);
return writePeer(peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, buffer, 10U, RTP_END_OF_CALL_SEQ, false, true);
return writePeer(peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, buffer, 12U, RTP_END_OF_CALL_SEQ, false, true);
}
/* Helper to send a NAK response to the specified peer. */

@ -457,6 +457,7 @@ namespace network
bool m_disallowExtAdjStsBcast;
bool m_allowConvSiteAffOverride;
bool m_restrictGrantToAffOnly;
bool m_enableInCallCtrl;
bool m_filterHeaders;
bool m_filterTerminators;
@ -567,6 +568,16 @@ namespace network
*/
void writePeerList(uint32_t peerId);
/**
* @brief Helper to send a In-Call Control command to the specified peer.
* @param peerId Peer ID.
* @param subFunc Network Sub-Function.
* @param command In-Call Control Command.
* @param slotNo DMR slot.
*/
bool writePeerICC(uint32_t peerId, NET_SUBFUNC::ENUM subFunc = NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR,
NET_ICC::ENUM command = NET_ICC::NOP, uint8_t slotNo = 0U);
/**
* @brief Helper to send a data message to the specified peer.
* @param peerId Peer ID.

@ -738,6 +738,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
.request(m_network->m_influxServer);
}
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getSlotNo());
return false;
}
}
@ -789,6 +790,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
.request(m_network->m_influxServer);
}
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getSlotNo());
return false;
}
@ -808,6 +810,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
.request(m_network->m_influxServer);
}
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getSlotNo());
return false;
}
@ -826,6 +829,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
.request(m_network->m_influxServer);
}
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getSlotNo());
return false;
}
}

@ -552,6 +552,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
.request(m_network->m_influxServer);
}
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC);
return false;
}
}
@ -603,6 +604,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
.request(m_network->m_influxServer);
}
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC);
return false;
}
@ -620,6 +622,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
.request(m_network->m_influxServer);
}
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC);
return false;
}

@ -1034,6 +1034,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
.request(m_network->m_influxServer);
}
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC);
return false;
}
}
@ -1113,6 +1114,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
.request(m_network->m_influxServer);
}
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC);
return false;
}
@ -1130,6 +1132,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
.request(m_network->m_influxServer);
}
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC);
return false;
}

@ -176,6 +176,11 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa
m_slot1->m_ignoreAffiliationCheck = ignoreAffiliationCheck;
m_slot2->m_ignoreAffiliationCheck = ignoreAffiliationCheck;
// set the In-Call Control function callback
if (m_network != nullptr) {
m_network->setDMRICCCallback([=](network::NET_ICC::ENUM command, uint8_t slotNo) { processInCallCtrl(command, slotNo); });
}
if (printOptions) {
if (enableTSCC) {
LogInfo(" TSCC Slot: %u", m_tsccSlotNo);
@ -737,3 +742,18 @@ void Control::processNetwork()
break;
}
}
/* Helper to process an In-Call Control message. */
void Control::processInCallCtrl(network::NET_ICC::ENUM command, uint8_t slotNo)
{
switch (slotNo) {
case 1U:
return m_slot1->processInCallCtrl(command);
case 2U:
return m_slot2->processInCallCtrl(command);
default:
LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", slotNo);
break;
}
}

@ -31,6 +31,7 @@
#include "common/lookups/IdenTableLookup.h"
#include "common/lookups/RadioIdLookup.h"
#include "common/lookups/TalkgroupRulesLookup.h"
#include "common/network/RTPFNEHeader.h"
#include "common/yaml/Yaml.h"
#include "dmr/lookups/DMRAffiliationLookup.h"
#include "dmr/Slot.h"
@ -330,6 +331,12 @@ namespace dmr
* @brief Process a data frames from the network.
*/
void processNetwork();
/**
* @brief Helper to process an In-Call Control message.
* @param command In-Call Control Command.
* @param slotNo DMR slot.
*/
void processInCallCtrl(network::NET_ICC::ENUM command, uint8_t slotNo);
};
} // namespace dmr

@ -453,6 +453,27 @@ void Slot::processNetwork(const data::NetData& dmrData)
}
}
/* Helper to process an In-Call Control message. */
void Slot::processInCallCtrl(network::NET_ICC::ENUM command)
{
switch (command) {
case network::NET_ICC::REJECT_TRAFFIC:
{
processFrameLoss();
m_rfLastDstId = 0U;
m_rfLastSrcId = 0U;
m_rfTGHang.stop();
m_rfState = RS_RF_REJECTED;
}
break;
default:
break;
}
}
/* Updates the DMR slot processor. */
void Slot::clock()

@ -157,6 +157,14 @@ namespace dmr
void processNetwork(const data::NetData& data);
/** @} */
/** @name In-Call Control */
/**
* @brief Helper to process an In-Call Control message.
* @param command In-Call Control Command.
*/
void processInCallCtrl(network::NET_ICC::ENUM command);
/** @} */
/** @name Data Clocking */
/**
* @brief Updates the processor.

@ -71,7 +71,10 @@ Network::Network(const std::string& address, uint16_t port, uint16_t localPort,
m_restApiPort(0),
m_conventional(false),
m_remotePeerId(0U),
m_promiscuousPeer(false)
m_promiscuousPeer(false),
m_dmrInCallCallback(nullptr),
m_p25InCallCallback(nullptr),
m_nxdnInCallCallback(nullptr)
{
assert(!address.empty());
assert(port > 0U);
@ -486,6 +489,42 @@ void Network::clock(uint32_t ms)
}
break;
case NET_FUNC::INCALL_CTRL:
{
if (fneHeader.getSubFunction() == NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR) { // DMR In-Call Control
if (m_enabled && m_dmrEnabled) {
NET_ICC::ENUM command = (NET_ICC::ENUM)buffer[10U];
uint8_t slot = buffer[11U];
if (m_dmrInCallCallback != nullptr) {
m_dmrInCallCallback(command, slot);
}
}
}
else if (fneHeader.getSubFunction() == NET_SUBFUNC::PROTOCOL_SUBFUNC_P25) { // P25 In-Call Control
if (m_enabled && m_p25Enabled) {
NET_ICC::ENUM command = (NET_ICC::ENUM)buffer[10U];
if (m_p25InCallCallback != nullptr) {
m_p25InCallCallback(command);
}
}
}
else if (fneHeader.getSubFunction() == NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN) { // NXDN In-Call Control
if (m_enabled && m_nxdnEnabled) {
NET_ICC::ENUM command = (NET_ICC::ENUM)buffer[10U];
if (m_nxdnInCallCallback != nullptr) {
m_nxdnInCallCallback(command);
}
}
}
else {
Utils::dump("unknown protocol opcode from the master", buffer.get(), length);
}
}
break;
case NET_FUNC::NAK: // Master Negative Ack
{
// DVM 3.6 adds support to respond with a NAK reason, as such we just check if the NAK response is greater

@ -32,6 +32,7 @@
#include <string>
#include <cstdint>
#include <functional>
namespace network
{
@ -152,6 +153,22 @@ namespace network
*/
void enable(bool enabled);
/**
* @brief Helper to set the DMR In-Call Control callback.
* @param callback
*/
void setDMRICCCallback(std::function<void(NET_ICC::ENUM, uint8_t)>&& callback) { m_dmrInCallCallback = callback; }
/**
* @brief Helper to set the P25 In-Call Control callback.
* @param callback
*/
void setP25ICCCallback(std::function<void(NET_ICC::ENUM)>&& callback) { m_p25InCallCallback = callback; }
/**
* @brief Helper to set the NXDN In-Call Control callback.
* @param callback
*/
void setNXDNICCCallback(std::function<void(NET_ICC::ENUM)>&& callback) { m_nxdnInCallCallback = callback; }
public:
/**
* @brief Last received RTP sequence number.
@ -213,6 +230,10 @@ namespace network
bool m_promiscuousPeer;
std::function<void(NET_ICC::ENUM command, uint8_t slotNo)> m_dmrInCallCallback;
std::function<void(NET_ICC::ENUM command)> m_p25InCallCallback;
std::function<void(NET_ICC::ENUM command)> m_nxdnInCallCallback;
/**
* @brief User overrideable handler that allows user code to process network packets not handled by this class.
* @param peerId Peer ID.

@ -296,6 +296,11 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
}
}
// set the In-Call Control function callback
if (m_network != nullptr) {
m_network->setNXDNICCCallback([=](network::NET_ICC::ENUM command) { processInCallCtrl(command); });
}
if (printOptions) {
LogInfo(" Silence Threshold: %u (%.1f%%)", m_voice->m_silenceThreshold, float(m_voice->m_silenceThreshold) / 12.33F);
LogInfo(" Frame Loss Threshold: %u", m_frameLossThreshold);
@ -1032,6 +1037,27 @@ void Control::processFrameLoss()
m_rfLC.reset();
}
/* Helper to process an In-Call Control message. */
void Control::processInCallCtrl(network::NET_ICC::ENUM command)
{
switch (command) {
case network::NET_ICC::REJECT_TRAFFIC:
{
processFrameLoss();
m_rfLastDstId = 0U;
m_rfLastSrcId = 0U;
m_rfTGHang.stop();
m_rfState = RS_RF_REJECTED;
}
break;
default:
break;
}
}
/* Helper to send a REST API request to the CC to release a channel grant at the end of a call. */
void Control::notifyCC_ReleaseGrant(uint32_t dstId)

@ -31,6 +31,7 @@
#include "common/lookups/RadioIdLookup.h"
#include "common/lookups/TalkgroupRulesLookup.h"
#include "common/lookups/AffiliationLookup.h"
#include "common/network/RTPFNEHeader.h"
#include "common/RingBuffer.h"
#include "common/StopWatch.h"
#include "common/Timer.h"
@ -361,6 +362,11 @@ namespace nxdn
* @brief Helper to process loss of frame stream from modem.
*/
void processFrameLoss();
/**
* @brief Helper to process an In-Call Control message.
* @param command In-Call Control Command.
*/
void processInCallCtrl(network::NET_ICC::ENUM command);
/**
* @brief Helper to send a REST API request to the CC to release a channel grant at the end of a call.

@ -439,6 +439,11 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
}
});
// set the In-Call Control function callback
if (m_network != nullptr) {
m_network->setP25ICCCallback([=](network::NET_ICC::ENUM command) { processInCallCtrl(command); });
}
if (printOptions) {
LogInfo(" Silence Threshold: %u (%.1f%%)", m_voice->m_silenceThreshold, float(m_voice->m_silenceThreshold) / 12.33F);
LogInfo(" Frame Loss Threshold: %u", m_frameLossThreshold);
@ -1534,6 +1539,27 @@ void Control::processFrameLoss()
}
}
/* Helper to process an In-Call Control message. */
void Control::processInCallCtrl(network::NET_ICC::ENUM command)
{
switch (command) {
case network::NET_ICC::REJECT_TRAFFIC:
{
processFrameLoss();
m_rfLastDstId = 0U;
m_rfLastSrcId = 0U;
m_rfTGHang.stop();
m_rfState = RS_RF_REJECTED;
}
break;
default:
break;
}
}
/* Helper to send a REST API request to the CC to release a channel grant at the end of a call. */
void Control::notifyCC_ReleaseGrant(uint32_t dstId)

@ -31,6 +31,7 @@
#include "common/lookups/IdenTableLookup.h"
#include "common/lookups/RadioIdLookup.h"
#include "common/lookups/TalkgroupRulesLookup.h"
#include "common/network/RTPFNEHeader.h"
#include "common/p25/SiteData.h"
#include "common/RingBuffer.h"
#include "common/StopWatch.h"
@ -397,6 +398,11 @@ namespace p25
* @brief Helper to process loss of frame stream from modem.
*/
void processFrameLoss();
/**
* @brief Helper to process an In-Call Control message.
* @param command In-Call Control Command.
*/
void processInCallCtrl(network::NET_ICC::ENUM command);
/**
* @brief Helper to send a REST API request to the CC to release a channel grant at the end of a call.

Loading…
Cancel
Save

Powered by TurnKey Linux.