diff --git a/src/common/p25/dfsi/frames/fsc/FSCACK.cpp b/src/common/p25/dfsi/frames/fsc/FSCACK.cpp index 6d5d025a..3661ab68 100644 --- a/src/common/p25/dfsi/frames/fsc/FSCACK.cpp +++ b/src/common/p25/dfsi/frames/fsc/FSCACK.cpp @@ -26,6 +26,7 @@ using namespace p25::dfsi::frames::fsc; /* Initializes a instance of the FSCACK class. */ FSCACK::FSCACK() : FSCMessage(), + responseData(nullptr), m_ackMessageId(FSCMessageType::FSC_INVALID), m_ackVersion(1U), m_ackCorrelationTag(0U), diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp index b8f8c258..a69964d8 100644 --- a/src/host/Host.Config.cpp +++ b/src/host/Host.Config.cpp @@ -602,10 +602,10 @@ bool Host::createModem() yaml::Node networkConf = m_conf["network"]; uint32_t id = networkConf["id"].as(1000U); if (useFSCForUDP) { - modemPort = new port::specialized::V24UDPPort(id, g_remoteAddress, g_remotePort + 1U, g_remotePort, true, fscInitiator, debug); + modemPort = new port::specialized::V24UDPPort(id, g_remoteAddress, g_remotePort + 1U, g_remotePort, g_remoteLocalPort, true, fscInitiator, debug); ((modem::port::specialized::V24UDPPort*)modemPort)->setHeartbeatInterval(fscHeartbeat); } else { - modemPort = new port::specialized::V24UDPPort(id, g_remoteAddress, g_remotePort, 0U, false, false, debug); + modemPort = new port::specialized::V24UDPPort(id, g_remoteAddress, g_remotePort, 0U, 0U, false, false, debug); } m_udpDFSIRemotePort = modemPort; } else { @@ -617,6 +617,8 @@ bool Host::createModem() LogInfo(" UDP Mode: %s", m_modemRemote ? "master" : "peer"); LogInfo(" UDP Address: %s", g_remoteAddress.c_str()); LogInfo(" UDP Port: %u", g_remotePort); + if (g_remoteLocalPort > 0U) + LogInfo(" Local Listening UDP Port: %u", g_remoteLocalPort); } if (!m_modemRemote) { diff --git a/src/host/HostMain.cpp b/src/host/HostMain.cpp index d3945669..608d5bf0 100644 --- a/src/host/HostMain.cpp +++ b/src/host/HostMain.cpp @@ -53,6 +53,7 @@ bool g_killed = false; bool g_remoteModemMode = false; std::string g_remoteAddress = std::string("127.0.0.1"); uint16_t g_remotePort = REMOTE_MODEM_PORT; +uint16_t g_remoteLocalPort = 0U; bool g_fireDMRBeacon = false; bool g_fireP25Control = false; @@ -140,6 +141,7 @@ void usage(const char* message, const char* arg) " --remote remote modem mode\n" " -a remote modem command address\n" " -p remote modem command port\n" + " -P remote modem command port (local listening port)\n" "\n" " -- stop handling options\n", g_progExe.c_str()); @@ -218,6 +220,16 @@ int checkArgs(int argc, char* argv[]) p += 2; } + else if (IS("-P")) { + if ((argc - 1) <= 0) + usage("error: %s", "must specify the port to connect to"); + g_remoteLocalPort = (uint16_t)::atoi(argv[++i]); + + if (g_remoteLocalPort == 0) + usage("error: %s", "remote port number cannot be blank or 0!"); + + p += 2; + } else if (IS("-v")) { ::fprintf(stdout, __PROG_NAME__ " %s (built %s)\r\n", __VER__, __BUILD__); ::fprintf(stdout, "Copyright (c) 2017-2025 Bryan Biedenkapp, N2PLL and DVMProject (https://github.com/dvmproject) Authors.\n"); diff --git a/src/host/HostMain.h b/src/host/HostMain.h index 4755ea34..ca9b9790 100644 --- a/src/host/HostMain.h +++ b/src/host/HostMain.h @@ -49,6 +49,8 @@ extern bool g_remoteModemMode; extern std::string g_remoteAddress; /** @brief (Global) Remote Modem Port. */ extern uint16_t g_remotePort; +/** @brief (Global) Local Remote Modem Port (Listening Port). */ +extern uint16_t g_remoteLocalPort; /** @brief (Global) Fire DMR beacon flag. */ extern bool g_fireDMRBeacon; diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index 88d3b62d..2818c8f9 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -1155,11 +1155,8 @@ void ModemV24::convertToAirTIA(const uint8_t *data, uint32_t length) dataOffs += StartOfStream::LENGTH; - // only ack the first start of stream block - if (blockCnt == 1U) { - // ack start of stream - ackStartOfStreamTIA(); - } + // ack start of stream + ackStartOfStreamTIA(); } break; case BlockType::END_OF_STREAM: @@ -1945,7 +1942,7 @@ void ModemV24::ackStartOfStreamTIA() if (m_trace) Utils::dump(1U, "ModemV24::ackStartOfStreamTIA() Ack StartOfStream", buffer, length); - queueP25Frame(buffer, length, STT_NON_IMBE); + writeImmediate(buffer, length); } /* Internal helper to convert from TIA-102 air interface to V.24/DFSI. */ @@ -2573,3 +2570,27 @@ void ModemV24::convertFromAirTIA(uint8_t* data, uint32_t length) } } } + +/* Writes raw data to the air interface modem. */ + +int ModemV24::writeImmediate(const uint8_t* data, uint32_t length) +{ + assert(data != nullptr); + + // add the DVM start byte, length byte, CMD byte, and padding 0 + uint8_t header[4U]; + header[0U] = DVM_SHORT_FRAME_START; + header[1U] = length & 0xFFU; + header[2U] = CMD_P25_DATA; + header[3U] = 0x00U; + + // get the actual data + UInt8Array __buffer = std::make_unique(length + 4U); + uint8_t* buffer = __buffer.get(); + + ::memset(buffer, 0x00U, length + 4U); + ::memcpy(buffer, header, 4U); + ::memcpy(buffer + 4U, data, length); + + return m_port->write(data, length); +} diff --git a/src/host/modem/ModemV24.h b/src/host/modem/ModemV24.h index f4069f91..04f93aab 100644 --- a/src/host/modem/ModemV24.h +++ b/src/host/modem/ModemV24.h @@ -408,6 +408,14 @@ namespace modem * @param length Length of buffer. */ void convertFromAirTIA(uint8_t* data, uint32_t length); + + /** + * @brief Writes raw data to the air interface modem (with no jitter delay). + * @param data Data to write to modem. + * @param length Length of data to write. + * @returns int Actual length of data written. + */ + int writeImmediate(const uint8_t* data, uint32_t length); }; } // namespace modem diff --git a/src/host/modem/port/specialized/V24UDPPort.cpp b/src/host/modem/port/specialized/V24UDPPort.cpp index f59ac3c2..1c85194c 100644 --- a/src/host/modem/port/specialized/V24UDPPort.cpp +++ b/src/host/modem/port/specialized/V24UDPPort.cpp @@ -50,7 +50,8 @@ std::mutex V24UDPPort::m_bufferMutex; /* Initializes a new instance of the V24UDPPort class. */ -V24UDPPort::V24UDPPort(uint32_t peerId, const std::string& address, uint16_t modemPort, uint16_t controlPort, bool useFSC, bool fscInitiator, bool debug) : +V24UDPPort::V24UDPPort(uint32_t peerId, const std::string& address, uint16_t modemPort, uint16_t controlPort, + uint16_t controlLocalPort, bool useFSC, bool fscInitiator, bool debug) : m_socket(nullptr), m_localPort(modemPort), m_controlSocket(nullptr), @@ -60,8 +61,11 @@ V24UDPPort::V24UDPPort(uint32_t peerId, const std::string& address, uint16_t mod m_controlAddr(), m_addrLen(0U), m_ctrlAddrLen(0U), - m_remoteAddr(), - m_remoteAddrLen(0U), + m_ctrlLocalPort(controlLocalPort), + m_remoteCtrlAddr(), + m_remoteCtrlAddrLen(0U), + m_remoteRTPAddr(), + m_remoteRTPAddrLen(0U), m_buffer(2000U, "UDP Port Ring Buffer"), m_fscInitiator(fscInitiator), m_timeoutTimer(1000U, 45U), @@ -83,15 +87,18 @@ V24UDPPort::V24UDPPort(uint32_t peerId, const std::string& address, uint16_t mod assert(modemPort > 0U); if (controlPort > 0U && useFSC) { - m_controlSocket = new Socket(controlPort); + if (controlLocalPort == 0U) + controlLocalPort = controlPort; + + m_controlSocket = new Socket(controlLocalPort); m_ctrlFrameQueue = new RawFrameQueue(m_controlSocket, m_debug); if (udp::Socket::lookup(address, controlPort, m_controlAddr, m_ctrlAddrLen) != 0) m_ctrlAddrLen = 0U; if (m_ctrlAddrLen > 0U) { - m_remoteAddr = m_controlAddr; - m_remoteAddrLen = m_remoteAddrLen; + m_remoteCtrlAddr = m_controlAddr; + m_remoteCtrlAddrLen = m_remoteCtrlAddrLen; std::string ctrlAddrStr = udp::Socket::address(m_controlAddr); LogWarning(LOG_HOST, "SECURITY: Remote modem expects V.24 control channel IP address; %s for remote modem control", ctrlAddrStr.c_str()); @@ -272,7 +279,7 @@ int V24UDPPort::write(const uint8_t* buffer, uint32_t length) 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); + bool written = m_socket->write(message, messageLen, m_remoteRTPAddr, m_remoteRTPAddrLen); if (written) return length; } else { @@ -408,16 +415,24 @@ void* V24UDPPort::threadedCtrlNetworkRx(void* arg) network->m_socket = nullptr; } - network->m_localPort = vcBasePort; - network->createVCPort(vcBasePort); + uint16_t remoteCtrlPort = Socket::port(req->address); + network->m_remoteCtrlAddr = req->address; + network->m_remoteCtrlAddrLen = req->addrLen; + + // setup local RTP VC port (where we receive traffic) + network->createVCPort(network->m_localPort); network->m_socket->open(network->m_addr); + // setup remote RTP VC port (where we send traffic to) + std::string remoteAddress = Socket::address(req->address); + network->createRemoteVCPort(remoteAddress, vcBasePort); + network->m_fscState = CS_CONNECTED; network->m_reqConnectionTimer.stop(); network->m_heartbeatTimer.start(); network->m_timeoutTimer.start(); - LogMessage(LOG_MODEM, "V.24 UDP, Established DFSI FSC Connection, vcBasePort = %u", vcBasePort); + LogMessage(LOG_MODEM, "V.24 UDP, Established DFSI FSC Connection, ctrlRemotePort = %u, vcLocalPort = %u, vcRemotePort = %u", remoteCtrlPort, network->m_localPort, vcBasePort); } break; @@ -449,6 +464,10 @@ void* V24UDPPort::threadedCtrlNetworkRx(void* arg) LogError(LOG_MODEM, "V.24 UDP, unknown ACK opcode, ackMessageId = $%02X", ackMessage->getAckMessageId()); break; } + + if (ackMessage->getResponseLength() > 0U && ackMessage->responseData != nullptr) { + delete[] ackMessage->responseData; // FSCACK doesn't clean itself up... + } } break; @@ -478,21 +497,28 @@ void* V24UDPPort::threadedCtrlNetworkRx(void* arg) network->m_socket = nullptr; } - uint16_t vcPort = connMessage->getVCBasePort(); + uint16_t vcBasePort = connMessage->getVCBasePort(); network->m_heartbeatInterval = connMessage->getHostHeartbeatPeriod(); if (network->m_heartbeatInterval > 30U) network->m_heartbeatInterval = 30U; - network->m_localPort = vcPort; + uint16_t remoteCtrlPort = Socket::port(req->address); + network->m_remoteCtrlAddr = req->address; + network->m_remoteCtrlAddrLen = req->addrLen; + + LogMessage(LOG_MODEM, "V.24 UDP, Incoming DFSI FSC Connection, ctrlRemotePort = %u, vcLocalPort = %u, vcRemotePort = %u, hostHBInterval = %u", remoteCtrlPort, network->m_localPort, vcBasePort, connMessage->getHostHeartbeatPeriod()); + + // setup local RTP VC port (where we receive traffic) network->createVCPort(network->m_localPort); network->m_socket->open(network->m_addr); + // setup remote RTP VC port (where we send traffic to) + std::string remoteAddress = Socket::address(req->address); + network->createRemoteVCPort(remoteAddress, vcBasePort); + network->m_fscState = CS_CONNECTED; network->m_reqConnectionTimer.stop(); - network->m_remoteAddr = req->address; - network->m_remoteAddrLen = req->addrLen; - if (connMessage->getHostHeartbeatPeriod() > 30U) LogWarning(LOG_MODEM, "V.24 UDP, DFSI FSC Connection, requested heartbeat of %u, reduce to 30 seconds or less", connMessage->getHostHeartbeatPeriod()); @@ -500,8 +526,6 @@ void* V24UDPPort::threadedCtrlNetworkRx(void* arg) network->m_heartbeatTimer.start(); network->m_timeoutTimer.start(); - LogMessage(LOG_MODEM, "V.24 UDP, Incoming DFSI FSC Connection, vcBasePort = %u, hostHBInterval = %u", network->m_localPort, connMessage->getHostHeartbeatPeriod()); - // construct connect ACK response data uint8_t respData[3U]; ::memset(respData, 0x00U, 3U); @@ -517,7 +541,8 @@ void* V24UDPPort::threadedCtrlNetworkRx(void* arg) ::memset(buffer, 0x00U, FSCACK::LENGTH + 3U); ackResp.encode(buffer); - network->m_ctrlFrameQueue->write(buffer, FSCACK::LENGTH + 3U, req->address, req->addrLen); + if (network->m_ctrlFrameQueue->write(buffer, FSCACK::LENGTH + 3U, req->address, req->addrLen)) + LogMessage(LOG_MODEM, "V.24 UDP, Established DFSI FSC Connection, ctrlRemotePort = %u, vcLocalPort = %u, vcRemotePort = %u", remoteCtrlPort, network->m_localPort, vcBasePort); } break; @@ -676,7 +701,7 @@ void* V24UDPPort::threadedVCNetworkRx(void* arg) } if (req->length > 0) { - if (udp::Socket::match(req->address, network->m_addr)) { + if (udp::Socket::match(req->address, network->m_remoteRTPAddr)) { UInt8Array __reply = std::make_unique(req->length + 4U); uint8_t* reply = __reply.get(); @@ -705,7 +730,7 @@ void* V24UDPPort::threadedVCNetworkRx(void* arg) return nullptr; } -/* Internal helper to setup the voice channel port. */ +/* Internal helper to setup the local voice channel port. */ void V24UDPPort::createVCPort(uint16_t port) { @@ -716,7 +741,20 @@ void V24UDPPort::createVCPort(uint16_t port) if (m_addrLen > 0U) { std::string addrStr = udp::Socket::address(m_addr); - LogWarning(LOG_HOST, "SECURITY: Remote modem expects V.24 voice channel IP address; %s for remote modem control", addrStr.c_str()); + LogWarning(LOG_HOST, "SECURITY: Remote modem expects V.24 voice channel IP address; %s:%u for Rx traffic", addrStr.c_str(), port); + } +} + +/* Internal helper to setup the remote voice channel port. */ + +void V24UDPPort::createRemoteVCPort(std::string address, uint16_t port) +{ + if (udp::Socket::lookup(address, port, m_remoteRTPAddr, m_remoteRTPAddrLen) != 0) + m_remoteRTPAddrLen = 0U; + + if (m_remoteRTPAddrLen > 0U) { + std::string addrStr = udp::Socket::address(m_remoteRTPAddr); + LogWarning(LOG_HOST, "SECURITY: Remote modem expects V.24 voice channel IP address; %s:%u for Tx traffic", addrStr.c_str(), port); } } @@ -752,7 +790,7 @@ void V24UDPPort::writeHeartbeat() FSCHeartbeat hb = FSCHeartbeat(); hb.encode(buffer); - m_ctrlFrameQueue->write(buffer, FSCHeartbeat::LENGTH, m_remoteAddr, m_remoteAddrLen); + m_ctrlFrameQueue->write(buffer, FSCHeartbeat::LENGTH, m_remoteCtrlAddr, m_remoteCtrlAddrLen); } /* Generate RTP message. */ diff --git a/src/host/modem/port/specialized/V24UDPPort.h b/src/host/modem/port/specialized/V24UDPPort.h index 96d833d2..0329f54a 100644 --- a/src/host/modem/port/specialized/V24UDPPort.h +++ b/src/host/modem/port/specialized/V24UDPPort.h @@ -71,11 +71,13 @@ namespace modem * @param address Hostname/IP address to connect to. * @param modemPort Port number. * @param controlPort Control Port number. + * @param controlLocalPort Local listening control port number. * @param useFSC Flag indicating whether or not FSC handshakes are used to setup communications. * @param fscInitiator Flag indicating whether or not the FSC handshake should be initiated when the port is opened. * @param debug Flag indicating whether network debug is enabled. */ - V24UDPPort(uint32_t peerId, const std::string& modemAddress, uint16_t modemPort, uint16_t controlPort = 0U, bool useFSC = false, bool fscInitiator = false, bool debug = false); + V24UDPPort(uint32_t peerId, const std::string& modemAddress, uint16_t modemPort, uint16_t controlPort = 0U, + uint16_t controlLocalPort = 0U, bool useFSC = false, bool fscInitiator = false, bool debug = false); /** * @brief Finalizes a instance of the V24UDPPort class. */ @@ -145,8 +147,12 @@ namespace modem uint32_t m_addrLen; uint32_t m_ctrlAddrLen; - sockaddr_storage m_remoteAddr; - uint32_t m_remoteAddrLen; + uint16_t m_ctrlLocalPort; + + sockaddr_storage m_remoteCtrlAddr; + uint32_t m_remoteCtrlAddrLen; + sockaddr_storage m_remoteRTPAddr; + uint32_t m_remoteRTPAddrLen; RingBuffer m_buffer; @@ -205,10 +211,16 @@ namespace modem static void* threadedVCNetworkRx(void* arg); /** - * @brief Internal helper to setup the voice channel port. + * @brief Internal helper to setup the local voice channel port. * @param port Port number. */ void createVCPort(uint16_t port); + /** + * @brief Internal helper to setup the remote voice channel port. + * @param address IP Address or Hostname. + * @param port Port number. + */ + void createRemoteVCPort(std::string address, uint16_t port); /** * @brief Internal helper to write a FSC connect packet. */