From 0f7eabff82df336ef591ae1aeb1b2a20672341c8 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 8 Apr 2024 22:05:17 -0400 Subject: [PATCH] implement support for peers that identify themselves as "conventional" to ignore affiliated talkgroup rules and be able to receive all traffic if the FNE is configured to allow promiscuous operation; implement extremely preliminary support to allow a CC to claim a VC peer, allowing for appropriate grouping of peers for trunked sites; --- configs/fne-config.example.yml | 2 ++ src/common/network/BaseNetwork.cpp | 24 +++++++++++++ src/common/network/BaseNetwork.h | 3 ++ src/fne/network/FNENetwork.cpp | 43 ++++++++++++++++++++++ src/fne/network/FNENetwork.h | 9 +++++ src/fne/network/fne/TagDMRData.cpp | 25 +++++++++++-- src/fne/network/fne/TagNXDNData.cpp | 25 +++++++++++-- src/fne/network/fne/TagP25Data.cpp | 25 +++++++++++-- src/host/Host.Config.cpp | 23 +++++++----- src/host/Host.cpp | 55 ++++++++++++++++++++++++++--- src/host/Host.h | 1 + src/host/network/Network.cpp | 2 ++ src/host/network/Network.h | 4 +++ src/host/network/RESTAPI.cpp | 51 ++++++++++++++++++++++++++ src/host/network/RESTAPI.h | 2 ++ src/host/network/RESTDefines.h | 1 + 16 files changed, 277 insertions(+), 18 deletions(-) diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 7e3afa43..19a9cfaa 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -75,6 +75,8 @@ master: disallowAdjStsBcast: false # Flag indicating whether or not a P25 ADJ_STS_BCAST will pass to connected external peers. disallowExtAdjStsBcast: true + # Flag indicating whether or not a conventional site can override affiliation rules. + allowConvSiteAffOverride: true # Flag indicating whether or not InfluxDB logging and metrics recording is enabled. enableInflux: false diff --git a/src/common/network/BaseNetwork.cpp b/src/common/network/BaseNetwork.cpp index 9afb312c..6000d2f8 100644 --- a/src/common/network/BaseNetwork.cpp +++ b/src/common/network/BaseNetwork.cpp @@ -250,6 +250,30 @@ bool BaseNetwork::announceAffiliationUpdate(const std::unordered_map +/// Writes a complete update of the peer's voice channel list to the network. +/// +/// +bool BaseNetwork::announceSiteVCs(const std::vector peers) +{ + if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING) + return false; + + uint8_t buffer[4U + (peers.size() * 4U)]; + ::memset(buffer, 0x00U, 4U + (peers.size() * 4U)); + + __SET_UINT32(peers.size(), buffer, 0U); + + // write peer IDs to active TGID payload + uint32_t offs = 4U; + for (auto it : peers) { + __SET_UINT32(it, buffer, offs); + offs += 4U; + } + + return writeMaster({ NET_FUNC_ANNOUNCE, NET_ANNC_SUBFUNC_SITE_VC }, buffer, 4U + (peers.size() * 4U), 0U, 0U); +} + /// /// Resets the DMR ring buffer for the given slot. /// diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index a88f87a6..dcb3e443 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -109,6 +109,7 @@ namespace network const uint8_t NET_ANNC_SUBFUNC_UNIT_REG = 0x01U; // Announce Unit Registration const uint8_t NET_ANNC_SUBFUNC_UNIT_DEREG = 0x02U; // Announce Unit Deregistration const uint8_t NET_ANNC_SUBFUNC_AFFILS = 0x90U; // Update All Affiliations + const uint8_t NET_ANNC_SUBFUNC_SITE_VC = 0x9AU; // Announce Site VCs // --------------------------------------------------------------------------- // Network Peer Connection Status @@ -183,6 +184,8 @@ namespace network virtual bool announceUnitDeregistration(uint32_t srcId); /// Writes a complete update of the peer affiliation list to the network. virtual bool announceAffiliationUpdate(const std::unordered_map affs); + /// Writes a complete update of the peer's voice channel list to the network. + virtual bool announceSiteVCs(const std::vector peers); /// Updates the timer by the passed number of milliseconds. virtual void clock(uint32_t ms) = 0; diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 3735f3b0..2e4b5ea9 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -95,6 +95,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port, m_callInProgress(false), m_disallowAdjStsBcast(false), m_disallowExtAdjStsBcast(true), + m_allowConvSiteAffOverride(false), m_enableInfluxDB(false), m_influxServerAddress("127.0.0.1"), m_influxServerPort(8086U), @@ -134,6 +135,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) { m_disallowAdjStsBcast = conf["disallowAdjStsBcast"].as(false); m_disallowExtAdjStsBcast = conf["disallowExtAdjStsBcast"].as(true); + m_allowConvSiteAffOverride = conf["allowConvSiteAffOverride"].as(true); m_softConnLimit = conf["connectionLimit"].as(MAX_HARD_CONN_CAP); if (m_softConnLimit > MAX_HARD_CONN_CAP) { @@ -164,6 +166,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) LogWarning(LOG_NET, "NOTICE: All P25 ADJ_STS_BCAST messages will be blocked and dropped!"); } 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(" InfluxDB Reporting Enabled: %s", m_enableInfluxDB ? "yes" : "no"); if (m_enableInfluxDB) { LogInfo(" InfluxDB Address: %s", m_influxServerAddress.c_str()); @@ -682,6 +685,17 @@ void* FNENetwork::threadedNetworkRx(void* arg) if (peerConfig["externalPeer"].is()) { bool external = peerConfig["externalPeer"].get(); connection->isExternalPeer(external); + if (external) + LogInfoEx(LOG_NET, "PEER %u reports external peer", peerId); + } + + if (peerConfig["conventionalPeer"].is()) { + if (network->m_allowConvSiteAffOverride) { + bool convPeer = peerConfig["conventionalPeer"].get(); + connection->isConventionalPeer(convPeer); + if (convPeer) + LogInfoEx(LOG_NET, "PEER %u reports conventional peer", peerId); + } } if (peerConfig["software"].is()) { @@ -1017,6 +1031,35 @@ void* FNENetwork::threadedNetworkRx(void* arg) } } } + else if (req->fneHeader.getSubFunction() == NET_ANNC_SUBFUNC_SITE_VC) { // Announce Site VCs + if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) { + FNEPeerConnection* connection = network->m_peers[peerId]; + if (connection != nullptr) { + std::string ip = udp::Socket::address(req->address); + + // validate peer (simple validation really) + if (connection->connected() && connection->address() == ip) { + // update peer association + uint32_t len = __GET_UINT32(req->buffer, 0U); + uint32_t offs = 4U; + for (uint32_t i = 0; i < len; i++) { + uint32_t vcPeerId = __GET_UINT32(req->buffer, offs); + if (vcPeerId > 0 && (network->m_peers.find(vcPeerId) != network->m_peers.end())) { + FNEPeerConnection* vcConnection = network->m_peers[vcPeerId]; + if (vcConnection != nullptr) { + vcConnection->ccPeerId(peerId); + } + } + offs += 4U; + } + LogMessage(LOG_NET, "PEER %u announced %u VCs", peerId, len); + } + else { + network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); + } + } + } + } else { network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_ILLEGAL_PACKET); Utils::dump("unknown announcement opcode from the peer", req->buffer, req->length); diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 5e06b91c..20d8c76b 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -82,6 +82,7 @@ namespace network /// Initializes a new instance of the FNEPeerConnection class. FNEPeerConnection() : m_id(0U), + m_ccPeerId(0U), m_currStreamId(0U), m_socketStorage(), m_sockStorageLen(0U), @@ -106,6 +107,7 @@ namespace network /// FNEPeerConnection(uint32_t id, sockaddr_storage& socketStorage, uint32_t sockStorageLen) : m_id(id), + m_ccPeerId(0U), m_currStreamId(0U), m_socketStorage(socketStorage), m_sockStorageLen(sockStorageLen), @@ -132,6 +134,9 @@ namespace network /// Peer ID. __PROPERTY_PLAIN(uint32_t, id); + /// Control Channel Peer ID. + __PROPERTY_PLAIN(uint32_t, ccPeerId); + /// Current Stream ID. __PROPERTY_PLAIN(uint32_t, currStreamId); @@ -163,6 +168,9 @@ namespace network /// Flag indicating this connection is from an external peer. __PROPERTY_PLAIN(bool, isExternalPeer); + /// Flag indicating this connection is from an conventional peer. + /// This flag is specifically used to determine whether affiliation based checking is performed. + __PROPERTY_PLAIN(bool, isConventionalPeer); /// JSON objecting containing peer configuration information. __PROPERTY_PLAIN(json::object, config); @@ -293,6 +301,7 @@ namespace network bool m_disallowAdjStsBcast; bool m_disallowExtAdjStsBcast; + bool m_allowConvSiteAffOverride; bool m_enableInfluxDB; std::string m_influxServerAddress; diff --git a/src/fne/network/fne/TagDMRData.cpp b/src/fne/network/fne/TagDMRData.cpp index 26bf549a..28631c30 100644 --- a/src/fne/network/fne/TagDMRData.cpp +++ b/src/fne/network/fne/TagDMRData.cpp @@ -611,13 +611,34 @@ bool TagDMRData::isPeerPermitted(uint32_t peerId, data::Data& data, uint32_t str } } + FNEPeerConnection* connection = nullptr; + if (peerId > 0 && (m_network->m_peers.find(peerId) != m_network->m_peers.end())) { + connection = m_network->m_peers[peerId]; + } + + // is this peer a conventional peer? + if (m_network->m_allowConvSiteAffOverride) { + if (connection != nullptr) { + if (connection->isConventionalPeer()) { + external = true; // we'll just set the external flag to disable the affiliation check + // for conventional peers + } + } + } + // is this a TG that requires affiliations to repeat? // NOTE: external peers *always* repeat traffic regardless of affiliation if (tg.config().affiliated() && !external) { + uint32_t lookupPeerId = peerId; + if (connection != nullptr) { + if (connection->ccPeerId() > 0U) + lookupPeerId = connection->ccPeerId(); + } + // check the affiliations for this peer to see if we can repeat traffic - lookups::AffiliationLookup* aff = m_network->m_peerAffiliations[peerId]; + lookups::AffiliationLookup* aff = m_network->m_peerAffiliations[lookupPeerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u has an invalid affiliations lookup? This shouldn't happen BUGBUG.", peerId); + LogError(LOG_NET, "PEER %u has an invalid affiliations lookup? This shouldn't happen BUGBUG.", lookupPeerId); return false; // this will cause no traffic to pass for this peer now...I'm not sure this is good behavior } else { diff --git a/src/fne/network/fne/TagNXDNData.cpp b/src/fne/network/fne/TagNXDNData.cpp index fd34e4a5..61a92f1c 100644 --- a/src/fne/network/fne/TagNXDNData.cpp +++ b/src/fne/network/fne/TagNXDNData.cpp @@ -423,13 +423,34 @@ bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t message } } + FNEPeerConnection* connection = nullptr; + if (peerId > 0 && (m_network->m_peers.find(peerId) != m_network->m_peers.end())) { + connection = m_network->m_peers[peerId]; + } + + // is this peer a conventional peer? + if (m_network->m_allowConvSiteAffOverride) { + if (connection != nullptr) { + if (connection->isConventionalPeer()) { + external = true; // we'll just set the external flag to disable the affiliation check + // for conventional peers + } + } + } + // is this a TG that requires affiliations to repeat? // NOTE: external peers *always* repeat traffic regardless of affiliation if (tg.config().affiliated() && !external) { + uint32_t lookupPeerId = peerId; + if (connection != nullptr) { + if (connection->ccPeerId() > 0U) + lookupPeerId = connection->ccPeerId(); + } + // check the affiliations for this peer to see if we can repeat traffic - lookups::AffiliationLookup* aff = m_network->m_peerAffiliations[peerId]; + lookups::AffiliationLookup* aff = m_network->m_peerAffiliations[lookupPeerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u has an invalid affiliations lookup? This shouldn't happen BUGBUG.", peerId); + LogError(LOG_NET, "PEER %u has an invalid affiliations lookup? This shouldn't happen BUGBUG.", lookupPeerId); return false; // this will cause no traffic to pass for this peer now...I'm not sure this is good behavior } else { diff --git a/src/fne/network/fne/TagP25Data.cpp b/src/fne/network/fne/TagP25Data.cpp index 8b3f9e65..eaa5a736 100644 --- a/src/fne/network/fne/TagP25Data.cpp +++ b/src/fne/network/fne/TagP25Data.cpp @@ -773,13 +773,34 @@ bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, uint8_t duid, } } + FNEPeerConnection* connection = nullptr; + if (peerId > 0 && (m_network->m_peers.find(peerId) != m_network->m_peers.end())) { + connection = m_network->m_peers[peerId]; + } + + // is this peer a conventional peer? + if (m_network->m_allowConvSiteAffOverride) { + if (connection != nullptr) { + if (connection->isConventionalPeer()) { + external = true; // we'll just set the external flag to disable the affiliation check + // for conventional peers + } + } + } + // is this a TG that requires affiliations to repeat? // NOTE: external peers *always* repeat traffic regardless of affiliation if (tg.config().affiliated() && !external) { + uint32_t lookupPeerId = peerId; + if (connection != nullptr) { + if (connection->ccPeerId() > 0U) + lookupPeerId = connection->ccPeerId(); + } + // check the affiliations for this peer to see if we can repeat traffic - lookups::AffiliationLookup* aff = m_network->m_peerAffiliations[peerId]; + lookups::AffiliationLookup* aff = m_network->m_peerAffiliations[lookupPeerId]; if (aff == nullptr) { - LogError(LOG_NET, "PEER %u has an invalid affiliations lookup? This shouldn't happen BUGBUG.", peerId); + LogError(LOG_NET, "PEER %u has an invalid affiliations lookup? This shouldn't happen BUGBUG.", lookupPeerId); return false; // this will cause no traffic to pass for this peer now...I'm not sure this is good behavior } else { diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp index c3eb6c54..454a0fdb 100644 --- a/src/host/Host.Config.cpp +++ b/src/host/Host.Config.cpp @@ -734,6 +734,11 @@ bool Host::createNetwork() restApiEnableSSL = false; } + yaml::Node protocolConf = m_conf["protocols"]; + bool dmrCtrlChannel = protocolConf["dmr"]["control"]["dedicated"].as(false); + bool p25CtrlChannel = protocolConf["p25"]["control"]["dedicated"].as(false); + bool nxdnCtrlChannel = protocolConf["nxdn"]["control"]["dedicated"].as(false); + IdenTable entry = m_idenTable->find(m_channelId); LogInfo("Network Parameters"); @@ -776,21 +781,23 @@ bool Host::createNetwork() // initialize networking if (netEnable) { - m_network = new Network( - address, port, local, - id, password, m_duplex, - debug, m_dmrEnabled, m_p25Enabled, - m_nxdnEnabled, slot1, slot2, - allowActivityTransfer, allowDiagnosticTransfer, updateLookup, - saveLookup - ); + m_network = new Network(address, port, local, id, password, m_duplex, debug, m_dmrEnabled, m_p25Enabled, m_nxdnEnabled, slot1, slot2, + allowActivityTransfer, allowDiagnosticTransfer, updateLookup, saveLookup); m_network->setLookups(m_ridLookup, m_tidLookup); m_network->setMetadata(m_identity, m_rxFrequency, m_txFrequency, entry.txOffsetMhz(), entry.chBandwidthKhz(), m_channelId, m_channelNo, m_power, m_latitude, m_longitude, m_height, m_location); + if (restApiEnable) { m_network->setRESTAPIData(restApiPassword, restApiPort); } + + if (!dmrCtrlChannel && !p25CtrlChannel && !nxdnCtrlChannel) { + if (m_controlChData.address().empty() && m_controlChData.port() == 0) { + m_network->setConventional(true); + } + } + if (encrypted) { m_network->setPresharedKey(presharedKey); } diff --git a/src/host/Host.cpp b/src/host/Host.cpp index 2ceb5fc1..a1925604 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -20,6 +20,7 @@ #include "common/Thread.h" #include "common/ThreadFunc.h" #include "common/Utils.h" +#include "remote/RESTClient.h" #include "host/Host.h" #include "ActivityLog.h" #include "HostMain.h" @@ -87,6 +88,7 @@ Host::Host(const std::string& confFile) : m_channelNo(0U), m_voiceChNo(), m_voiceChData(), + m_voiceChPeerId(), m_controlChData(), m_idenTable(nullptr), m_ridLookup(nullptr), @@ -895,6 +897,10 @@ int Host::run() nxdnFrameWriteThread.run(); nxdnFrameWriteThread.setName("nxdn:frame-w"); + Timer ccRegisterTimer(1000U, 120U); + ccRegisterTimer.start(); + bool hasInitialRegistered = false; + // main execution loop while (!killed) { if (m_modem->hasLockout() && m_state != HOST_STATE_LOCKOUT) @@ -1043,6 +1049,47 @@ int Host::run() if (nxdn != nullptr) nxdn->clock(ms); + ccRegisterTimer.clock(ms); + + // VC -> CC presence registration + if (!m_controlChData.address().empty() && m_controlChData.port() != 0 && m_network != nullptr) { + if ((ccRegisterTimer.isRunning() && ccRegisterTimer.hasExpired()) || !hasInitialRegistered) { + LogMessage(LOG_HOST, "CC %s:%u, notifying CC of VC registration, peerId = %u", m_controlChData.address().c_str(), m_controlChData.port(), m_network->getPeerId()); + hasInitialRegistered = true; + + // callback REST API to release the granted TG on the specified control channel + json::object req = json::object(); + req["channelNo"].set(m_channelNo); + uint32_t peerId = m_network->getPeerId(); + req["peerId"].set(peerId); + + int ret = RESTClient::send(m_controlChData.address(), m_controlChData.port(), m_controlChData.password(), + HTTP_PUT, PUT_REGISTER_CC_VC, req, m_controlChData.ssl(), REST_QUICK_WAIT, false); + if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + ::LogError(LOG_HOST, "failed to notify the CC %s:%u of VC registration", m_controlChData.address().c_str(), m_controlChData.port()); + } + + ccRegisterTimer.start(); + } + } + + // CC -> FNE registered VC announcement + if (m_dmrCtrlChannel || m_p25CtrlChannel || m_nxdnCtrlChannel) { + if (ccRegisterTimer.isRunning() && ccRegisterTimer.hasExpired()) { + if (m_network != nullptr && m_voiceChPeerId.size() > 0) { + LogMessage(LOG_HOST, "notifying FNE of VC registrations, peerId = %u", m_network->getPeerId()); + + std::vector peers; + for (auto it : m_voiceChPeerId) { + peers.push_back(it.second); + } + + m_network->announceSiteVCs(peers); + } + ccRegisterTimer.start(); + } + } + // ------------------------------------------------------ // -- Timer Clocking -- // ------------------------------------------------------ @@ -1060,7 +1107,7 @@ int Host::run() m_nxdnBcastDurationTimer.stop(); } - LogDebug(LOG_HOST, "CW, start transmitting"); + LogMessage(LOG_HOST, "CW, start transmitting"); m_isTxCW = true; std::lock_guard lock(clockingMutex); @@ -1081,7 +1128,7 @@ int Host::run() m_modem->clock(ms); if (!first && !m_modem->hasTX()) { - LogDebug(LOG_HOST, "CW, finished transmitting"); + LogMessage(LOG_HOST, "CW, finished transmitting"); break; } @@ -1131,10 +1178,10 @@ int Host::run() g_fireDMRBeacon = false; if (m_dmrTSCCData) { - LogDebug(LOG_HOST, "DMR, start CC broadcast"); + LogMessage(LOG_HOST, "DMR, start CC broadcast"); } else { - LogDebug(LOG_HOST, "DMR, roaming beacon burst"); + LogMessage(LOG_HOST, "DMR, roaming beacon burst"); } dmrBeaconIntervalTimer.start(); m_dmrBeaconDurationTimer.start(); diff --git a/src/host/Host.h b/src/host/Host.h index 52094f90..d6033f2d 100644 --- a/src/host/Host.h +++ b/src/host/Host.h @@ -109,6 +109,7 @@ private: std::vector m_voiceChNo; std::unordered_map m_voiceChData; + std::unordered_map m_voiceChPeerId; lookups::VoiceChData m_controlChData; lookups::IdenTableLookup* m_idenTable; diff --git a/src/host/network/Network.cpp b/src/host/network/Network.cpp index f2228a05..4ceb2064 100644 --- a/src/host/network/Network.cpp +++ b/src/host/network/Network.cpp @@ -82,6 +82,7 @@ Network::Network(const std::string& address, uint16_t port, uint16_t localPort, m_location(), m_restApiPassword(), m_restApiPort(0), + m_conventional(false), m_remotePeerId(0U) { assert(!address.empty()); @@ -811,6 +812,7 @@ bool Network::writeConfig() rcon["port"].set(m_restApiPort); // REST API Port config["rcon"].set(rcon); + config["conventionalPeer"].set(m_conventional); // Conventional Peer Marker config["software"].set(std::string(software)); // Software ID json::value v = json::value(config); diff --git a/src/host/network/Network.h b/src/host/network/Network.h index b22e14ef..130ea8ad 100644 --- a/src/host/network/Network.h +++ b/src/host/network/Network.h @@ -52,6 +52,8 @@ namespace network uint8_t channelId, uint32_t channelNo, uint32_t power, float latitude, float longitude, int height, const std::string& location); /// Sets REST API configuration settings from the modem. void setRESTAPIData(const std::string& password, uint16_t port); + /// Sets a flag indicating whether the conventional option is sent to the FNE. + void setConventional(bool conv) { m_conventional = conv; } /// Sets endpoint preshared encryption key. void setPresharedKey(const uint8_t* presharedKey); @@ -120,6 +122,8 @@ namespace network std::string m_restApiPassword; uint16_t m_restApiPort; + bool m_conventional; + uint32_t m_remotePeerId; /// Writes login request to the network. diff --git a/src/host/network/RESTAPI.cpp b/src/host/network/RESTAPI.cpp index be488e1e..4085eed1 100644 --- a/src/host/network/RESTAPI.cpp +++ b/src/host/network/RESTAPI.cpp @@ -305,6 +305,7 @@ void RESTAPI::initializeEndpoints() m_dispatcher.match(GET_RELEASE_GRNTS).get(REST_API_BIND(RESTAPI::restAPI_GetReleaseGrants, this)); m_dispatcher.match(GET_RELEASE_AFFS).get(REST_API_BIND(RESTAPI::restAPI_GetReleaseAffs, this)); + m_dispatcher.match(PUT_REGISTER_CC_VC).put(REST_API_BIND(RESTAPI::restAPI_PutRegisterCCVC, this)); m_dispatcher.match(PUT_RELEASE_TG).put(REST_API_BIND(RESTAPI::restAPI_PutReleaseGrant, this)); m_dispatcher.match(PUT_TOUCH_TG).put(REST_API_BIND(RESTAPI::restAPI_PutTouchGrant, this)); @@ -1146,6 +1147,56 @@ void RESTAPI::restAPI_GetReleaseAffs(const HTTPPayload& request, HTTPPayload& re } } +/// +/// +/// +/// +/// +/// +void RESTAPI::restAPI_PutRegisterCCVC(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +{ + if (!validateAuth(request, reply)) { + return; + } + + json::object req = json::object(); + if (!parseRequestBody(request, reply, req)) { + return; + } + + errorPayload(reply, "OK", HTTPPayload::OK); + + if (!m_host->m_dmrTSCCData && !m_host->m_p25CCData && !m_host->m_nxdnCCData) { + errorPayload(reply, "Host is not a control channel, cannot register voice channel"); + return; + } + + // validate channelNo is a string within the JSON blob + if (!req["channelNo"].is()) { + errorPayload(reply, "channelNo was not a valid integer"); + return; + } + + uint32_t channelNo = req["channelNo"].get(); + + // validate channelNo is a string within the JSON blob + if (!req["peerId"].is()) { + errorPayload(reply, "peerId was not a valid integer"); + return; + } + + uint32_t peerId = req["peerId"].get(); + + // LogDebug(LOG_REST, "restAPI_PutRegisterCCVC(): callback, channelNo = %u, peerId = %u", channelNo, peerId); + + if (m_host->m_voiceChData.find(channelNo) != m_host->m_voiceChData.end()) { + ::lookups::VoiceChData voiceCh = m_host->m_voiceChData[channelNo]; + + m_host->m_voiceChPeerId[channelNo] = peerId; + LogMessage(LOG_REST, "VC %s:%u, registration notice, peerId = %u, chId = %u, chNo = %u", voiceCh.address().c_str(), voiceCh.port(), peerId, voiceCh.chId(), channelNo); + } +} + /// /// /// diff --git a/src/host/network/RESTAPI.h b/src/host/network/RESTAPI.h index 0b946694..1a127b00 100644 --- a/src/host/network/RESTAPI.h +++ b/src/host/network/RESTAPI.h @@ -125,6 +125,8 @@ private: /// void restAPI_GetReleaseAffs(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /// + void restAPI_PutRegisterCCVC(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); /// void restAPI_PutReleaseGrant(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); /// diff --git a/src/host/network/RESTDefines.h b/src/host/network/RESTDefines.h index 76300ca7..632a3a61 100644 --- a/src/host/network/RESTDefines.h +++ b/src/host/network/RESTDefines.h @@ -51,6 +51,7 @@ #define GET_RELEASE_GRNTS "/release-grants" #define GET_RELEASE_AFFS "/release-affs" +#define PUT_REGISTER_CC_VC "/register-cc-vc" #define PUT_RELEASE_TG "/release-tg-grant" #define PUT_TOUCH_TG "/touch-tg-grant"