From 14b749fff1b2a6cfa49947f3d617d9258bf6aeb6 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 7 Jan 2025 15:27:34 -0500 Subject: [PATCH] correct issue where DFSI FSC control port wasn't living at the voice conveyance port + 1; refactor the proof of concept V24UDPport class to handle voice conveyance frames in their own thread (this is so the main clock doesn't get locked up); correct bad offsetting of data for V24UDPort causing weird overflow, underflow and buffer corruption; --- configs/config.example.yml | 2 + src/host/Host.Config.cpp | 5 +- .../modem/port/specialized/V24UDPPort.cpp | 173 ++++++++++++------ src/host/modem/port/specialized/V24UDPPort.h | 15 ++ 4 files changed, 139 insertions(+), 56 deletions(-) diff --git a/configs/config.example.yml b/configs/config.example.yml index de7b01fe..e366f611 100644 --- a/configs/config.example.yml +++ b/configs/config.example.yml @@ -590,6 +590,8 @@ system: jitter: 200 # Timer which will reset local/remote call flags if frames aren't received longer than this time in ms callTimeout: 200 + # Flag indicating when operating in V.24 UDP mode should the FSC protocol be used to negotiate connection. + useFSC: false # Sets received the signal offset from DC. rxDCOffset: 0 # Valid values between -128 and 128 diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp index 9ad5d41b..67a5a536 100644 --- a/src/host/Host.Config.cpp +++ b/src/host/Host.Config.cpp @@ -594,7 +594,10 @@ bool Host::createModem() if (modemMode == MODEM_MODE_DFSI) { yaml::Node networkConf = m_conf["network"]; uint32_t id = networkConf["id"].as(1000U); - modemPort = new port::specialized::V24UDPPort(id, g_remoteAddress, g_remotePort, g_remotePort, useFSCForUDP, debug); + modemPort = new port::specialized::V24UDPPort(id, g_remoteAddress, g_remotePort, 0U, useFSCForUDP, debug); + if (useFSCForUDP) { + modemPort = new port::specialized::V24UDPPort(id, g_remoteAddress, g_remotePort + 1U, g_remotePort, useFSCForUDP, debug); + } m_udpDSFIRemotePort = modemPort; } else { modemPort = new port::UDPPort(g_remoteAddress, g_remotePort); diff --git a/src/host/modem/port/specialized/V24UDPPort.cpp b/src/host/modem/port/specialized/V24UDPPort.cpp index 25f11586..fbfea833 100644 --- a/src/host/modem/port/specialized/V24UDPPort.cpp +++ b/src/host/modem/port/specialized/V24UDPPort.cpp @@ -38,6 +38,12 @@ const uint32_t BUFFER_LENGTH = 2000U; const char* V24_UDP_HARDWARE = "V.24 UDP Modem Controller"; const uint8_t V24_UDP_PROTOCOL_VERSION = 4U; +// --------------------------------------------------------------------------- +// Static Class Members +// --------------------------------------------------------------------------- + +std::mutex V24UDPPort::m_bufferMutex; + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -139,60 +145,7 @@ void V24UDPPort::clock(uint32_t ms) processCtrlNetwork(); } - // if we have a RTP voice socket - if (m_socket != nullptr) { - uint8_t data[BUFFER_LENGTH]; - ::memset(data, 0x00U, BUFFER_LENGTH); - - sockaddr_storage addr; - uint32_t addrLen; - int ret = m_socket->read(data, BUFFER_LENGTH, addr, addrLen); - if (ret != 0) { - // An error occurred on the socket - if (ret < 0) - return; - - // Add new data to the ring buffer - if (ret > 0) { - RTPHeader rtpHeader = RTPHeader(); - rtpHeader.decode(data); - - // ensure payload type is correct - if (rtpHeader.getPayloadType() != DFSI_RTP_PAYLOAD_TYPE) - { - LogError(LOG_MODEM, "Invalid RTP header received from network"); - return; - } - - // copy message - uint32_t messageLength = ret - RTP_HEADER_LENGTH_BYTES; - UInt8Array __message = std::make_unique(messageLength); - uint8_t* message = __message.get(); - ::memset(message, 0x00U, messageLength); - - ::memcpy(message, data + RTP_HEADER_LENGTH_BYTES, messageLength); - - if (udp::Socket::match(addr, m_addr)) { - UInt8Array __reply = std::make_unique(messageLength + 4U); - uint8_t* reply = __reply.get(); - - reply[0U] = DVM_SHORT_FRAME_START; - reply[1U] = messageLength & 0xFFU; - reply[2U] = CMD_P25_DATA; - - reply[3U] = 0x00U; - - ::memcpy(reply + 4U, message, messageLength); - - m_buffer.addData(reply, messageLength + 4U); - } - else { - std::string addrStr = udp::Socket::address(addr); - LogWarning(LOG_HOST, "SECURITY: Remote modem mode encountered invalid IP address; %s", addrStr.c_str()); - } - } - } - } + processVCNetwork(); } /* Resets the RTP packet sequence and stream ID. */ @@ -231,6 +184,8 @@ int V24UDPPort::read(uint8_t* buffer, uint32_t length) assert(buffer != nullptr); assert(length > 0U); + std::lock_guard lock(m_bufferMutex); + // Get required data from the ring buffer uint32_t avail = m_buffer.dataSize(); if (avail < length) @@ -264,7 +219,10 @@ int V24UDPPort::write(const uint8_t* buffer, uint32_t length) { if (m_socket != nullptr) { uint32_t messageLen = 0U; - uint8_t* message = generateMessage(buffer + 3U, length - 3U, m_streamId, m_peerId, m_pktSeq, &messageLen); + uint8_t* message = generateMessage(buffer + 4U, length - 4U, m_streamId, m_peerId, m_pktSeq, &messageLen); + + if (m_debug) + Utils::dump(1U, "!!! Tx Outgoing DFSI UDP", buffer + 4U, length - 4U); bool written = m_socket->write(message, messageLen, m_addr, m_addrLen); if (written) @@ -458,6 +416,107 @@ void* V24UDPPort::threadedCtrlNetworkRx(void* arg) return nullptr; } +/* Process voice conveyance frames from the network. */ + +void V24UDPPort::processVCNetwork() +{ + // if we have a RTP voice socket + if (m_socket != nullptr) { + uint8_t data[BUFFER_LENGTH]; + ::memset(data, 0x00U, BUFFER_LENGTH); + + sockaddr_storage addr; + uint32_t addrLen; + int ret = m_socket->read(data, BUFFER_LENGTH, addr, addrLen); + if (ret != 0) { + // An error occurred on the socket + if (ret < 0) + return; + + // Add new data to the ring buffer + if (ret > 0) { + if (m_debug) + Utils::dump("!!! Rx Incoming DFSI UDP", data, ret); + + V24PacketRequest* req = new V24PacketRequest(); + req->address = addr; + req->addrLen = addrLen; + + req->rtpHeader = RTPHeader(); + req->rtpHeader.decode(data); + + // ensure payload type is correct + if (req->rtpHeader.getPayloadType() != DFSI_RTP_PAYLOAD_TYPE) { + LogError(LOG_MODEM, "Invalid RTP header received from network"); + delete req; + return; + } + + // copy message + req->length = ret - RTP_HEADER_LENGTH_BYTES; + req->buffer = new uint8_t[req->length]; + ::memset(req->buffer, 0x00U, req->length); + + ::memcpy(req->buffer, data + RTP_HEADER_LENGTH_BYTES, req->length); + + if (!Thread::runAsThread(this, threadedVCNetworkRx, req)) { + delete[] req->buffer; + delete req; + return; + } + } + } + } +} + +/* Process a data frames from the network. */ + +void* V24UDPPort::threadedVCNetworkRx(void* arg) +{ + V24PacketRequest* req = (V24PacketRequest*)arg; + if (req != nullptr) { +#if defined(_WIN32) + ::CloseHandle(req->thread); +#else + ::pthread_detach(req->thread); +#endif // defined(_WIN32) + + V24UDPPort* network = static_cast(req->obj); + if (network == nullptr) { + delete req; + return nullptr; + } + + if (req->length > 0) { + if (udp::Socket::match(req->address, network->m_addr)) { + UInt8Array __reply = std::make_unique(req->length + 4U); + uint8_t* reply = __reply.get(); + + reply[0U] = DVM_SHORT_FRAME_START; + reply[1U] = (req->length + 4U) & 0xFFU; + reply[2U] = CMD_P25_DATA; + + reply[3U] = 0x00U; + + ::memcpy(reply + 4U, req->buffer, req->length); + + std::lock_guard lock(m_bufferMutex); + network->m_buffer.addData(reply, req->length + 4U); + } + else { + std::string addrStr = udp::Socket::address(req->address); + LogWarning(LOG_HOST, "SECURITY: Remote modem mode encountered invalid IP address; %s", addrStr.c_str()); + } + } + + if (req->buffer != nullptr) + delete[] req->buffer; + delete req; + } + + return nullptr; +} + /* Internal helper to setup the voice channel port. */ void V24UDPPort::createVCPort(uint16_t port) @@ -580,6 +639,7 @@ void V24UDPPort::getVersion() reply[1U] = count; + std::lock_guard lock(m_bufferMutex); m_buffer.addData(reply, count); } @@ -612,6 +672,7 @@ void V24UDPPort::getStatus() reply[11U] = 0U; + std::lock_guard lock(m_bufferMutex); m_buffer.addData(reply, 12U); } @@ -626,6 +687,7 @@ void V24UDPPort::writeAck(uint8_t type) reply[2U] = CMD_ACK; reply[3U] = type; + std::lock_guard lock(m_bufferMutex); m_buffer.addData(reply, 4U); } @@ -641,5 +703,6 @@ void V24UDPPort::writeNAK(uint8_t opcode, uint8_t err) reply[3U] = opcode; reply[4U] = err; + std::lock_guard lock(m_bufferMutex); m_buffer.addData(reply, 5U); } \ No newline at end of file diff --git a/src/host/modem/port/specialized/V24UDPPort.h b/src/host/modem/port/specialized/V24UDPPort.h index ba114168..28401c5a 100644 --- a/src/host/modem/port/specialized/V24UDPPort.h +++ b/src/host/modem/port/specialized/V24UDPPort.h @@ -31,6 +31,7 @@ #include #include +#include namespace modem { @@ -150,6 +151,8 @@ namespace modem bool m_debug; + static std::mutex m_bufferMutex; + /** * @brief Process FSC control frames from the network. */ @@ -162,6 +165,18 @@ namespace modem */ static void* threadedCtrlNetworkRx(void* arg); + /** + * @brief Process voice conveyance frames from the network. + */ + void processVCNetwork(); + + /** + * @brief Entry point to process a given network packet. + * @param arg Instance of the NetPacketRequest structure. + * @returns void* (Ignore) + */ + static void* threadedVCNetworkRx(void* arg); + /** * @brief Internal helper to setup the voice channel port. * @param port Port number.