implement enhancement/alterations to allow back-to-back configuration of dvmhost in TIA-102 DFSI mode (with FSC enabled); correct minor nullref handling in FSCACK; add writeImmediate() to ModemV24 to support TIA-102's need to immediately ack a Start of Stream block;

pull/84/head
Bryan Biedenkapp 11 months ago
parent c3b31d241e
commit d7b887a32d

@ -26,6 +26,7 @@ using namespace p25::dfsi::frames::fsc;
/* Initializes a instance of the FSCACK class. */ /* Initializes a instance of the FSCACK class. */
FSCACK::FSCACK() : FSCMessage(), FSCACK::FSCACK() : FSCMessage(),
responseData(nullptr),
m_ackMessageId(FSCMessageType::FSC_INVALID), m_ackMessageId(FSCMessageType::FSC_INVALID),
m_ackVersion(1U), m_ackVersion(1U),
m_ackCorrelationTag(0U), m_ackCorrelationTag(0U),

@ -602,10 +602,10 @@ bool Host::createModem()
yaml::Node networkConf = m_conf["network"]; yaml::Node networkConf = m_conf["network"];
uint32_t id = networkConf["id"].as<uint32_t>(1000U); uint32_t id = networkConf["id"].as<uint32_t>(1000U);
if (useFSCForUDP) { 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); ((modem::port::specialized::V24UDPPort*)modemPort)->setHeartbeatInterval(fscHeartbeat);
} else { } 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; m_udpDFSIRemotePort = modemPort;
} else { } else {
@ -617,6 +617,8 @@ bool Host::createModem()
LogInfo(" UDP Mode: %s", m_modemRemote ? "master" : "peer"); LogInfo(" UDP Mode: %s", m_modemRemote ? "master" : "peer");
LogInfo(" UDP Address: %s", g_remoteAddress.c_str()); LogInfo(" UDP Address: %s", g_remoteAddress.c_str());
LogInfo(" UDP Port: %u", g_remotePort); LogInfo(" UDP Port: %u", g_remotePort);
if (g_remoteLocalPort > 0U)
LogInfo(" Local Listening UDP Port: %u", g_remoteLocalPort);
} }
if (!m_modemRemote) { if (!m_modemRemote) {

@ -53,6 +53,7 @@ bool g_killed = false;
bool g_remoteModemMode = false; bool g_remoteModemMode = false;
std::string g_remoteAddress = std::string("127.0.0.1"); std::string g_remoteAddress = std::string("127.0.0.1");
uint16_t g_remotePort = REMOTE_MODEM_PORT; uint16_t g_remotePort = REMOTE_MODEM_PORT;
uint16_t g_remoteLocalPort = 0U;
bool g_fireDMRBeacon = false; bool g_fireDMRBeacon = false;
bool g_fireP25Control = false; bool g_fireP25Control = false;
@ -140,6 +141,7 @@ void usage(const char* message, const char* arg)
" --remote remote modem mode\n" " --remote remote modem mode\n"
" -a remote modem command address\n" " -a remote modem command address\n"
" -p remote modem command port\n" " -p remote modem command port\n"
" -P remote modem command port (local listening port)\n"
"\n" "\n"
" -- stop handling options\n", " -- stop handling options\n",
g_progExe.c_str()); g_progExe.c_str());
@ -218,6 +220,16 @@ int checkArgs(int argc, char* argv[])
p += 2; 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")) { else if (IS("-v")) {
::fprintf(stdout, __PROG_NAME__ " %s (built %s)\r\n", __VER__, __BUILD__); ::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"); ::fprintf(stdout, "Copyright (c) 2017-2025 Bryan Biedenkapp, N2PLL and DVMProject (https://github.com/dvmproject) Authors.\n");

@ -49,6 +49,8 @@ extern bool g_remoteModemMode;
extern std::string g_remoteAddress; extern std::string g_remoteAddress;
/** @brief (Global) Remote Modem Port. */ /** @brief (Global) Remote Modem Port. */
extern uint16_t g_remotePort; extern uint16_t g_remotePort;
/** @brief (Global) Local Remote Modem Port (Listening Port). */
extern uint16_t g_remoteLocalPort;
/** @brief (Global) Fire DMR beacon flag. */ /** @brief (Global) Fire DMR beacon flag. */
extern bool g_fireDMRBeacon; extern bool g_fireDMRBeacon;

@ -1155,12 +1155,9 @@ void ModemV24::convertToAirTIA(const uint8_t *data, uint32_t length)
dataOffs += StartOfStream::LENGTH; dataOffs += StartOfStream::LENGTH;
// only ack the first start of stream block
if (blockCnt == 1U) {
// ack start of stream // ack start of stream
ackStartOfStreamTIA(); ackStartOfStreamTIA();
} }
}
break; break;
case BlockType::END_OF_STREAM: case BlockType::END_OF_STREAM:
{ {
@ -1945,7 +1942,7 @@ void ModemV24::ackStartOfStreamTIA()
if (m_trace) if (m_trace)
Utils::dump(1U, "ModemV24::ackStartOfStreamTIA() Ack StartOfStream", buffer, length); 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. */ /* 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<uint8_t[]>(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);
}

@ -408,6 +408,14 @@ namespace modem
* @param length Length of buffer. * @param length Length of buffer.
*/ */
void convertFromAirTIA(uint8_t* data, uint32_t length); 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 } // namespace modem

@ -50,7 +50,8 @@ std::mutex V24UDPPort::m_bufferMutex;
/* Initializes a new instance of the V24UDPPort class. */ /* 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_socket(nullptr),
m_localPort(modemPort), m_localPort(modemPort),
m_controlSocket(nullptr), m_controlSocket(nullptr),
@ -60,8 +61,11 @@ V24UDPPort::V24UDPPort(uint32_t peerId, const std::string& address, uint16_t mod
m_controlAddr(), m_controlAddr(),
m_addrLen(0U), m_addrLen(0U),
m_ctrlAddrLen(0U), m_ctrlAddrLen(0U),
m_remoteAddr(), m_ctrlLocalPort(controlLocalPort),
m_remoteAddrLen(0U), m_remoteCtrlAddr(),
m_remoteCtrlAddrLen(0U),
m_remoteRTPAddr(),
m_remoteRTPAddrLen(0U),
m_buffer(2000U, "UDP Port Ring Buffer"), m_buffer(2000U, "UDP Port Ring Buffer"),
m_fscInitiator(fscInitiator), m_fscInitiator(fscInitiator),
m_timeoutTimer(1000U, 45U), m_timeoutTimer(1000U, 45U),
@ -83,15 +87,18 @@ V24UDPPort::V24UDPPort(uint32_t peerId, const std::string& address, uint16_t mod
assert(modemPort > 0U); assert(modemPort > 0U);
if (controlPort > 0U && useFSC) { 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); m_ctrlFrameQueue = new RawFrameQueue(m_controlSocket, m_debug);
if (udp::Socket::lookup(address, controlPort, m_controlAddr, m_ctrlAddrLen) != 0) if (udp::Socket::lookup(address, controlPort, m_controlAddr, m_ctrlAddrLen) != 0)
m_ctrlAddrLen = 0U; m_ctrlAddrLen = 0U;
if (m_ctrlAddrLen > 0U) { if (m_ctrlAddrLen > 0U) {
m_remoteAddr = m_controlAddr; m_remoteCtrlAddr = m_controlAddr;
m_remoteAddrLen = m_remoteAddrLen; m_remoteCtrlAddrLen = m_remoteCtrlAddrLen;
std::string ctrlAddrStr = udp::Socket::address(m_controlAddr); 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()); 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) if (m_debug)
Utils::dump(1U, "!!! Tx Outgoing DFSI UDP", buffer + 4U, length - 4U); 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) if (written)
return length; return length;
} else { } else {
@ -408,16 +415,24 @@ void* V24UDPPort::threadedCtrlNetworkRx(void* arg)
network->m_socket = nullptr; network->m_socket = nullptr;
} }
network->m_localPort = vcBasePort; uint16_t remoteCtrlPort = Socket::port(req->address);
network->createVCPort(vcBasePort); 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); 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_fscState = CS_CONNECTED;
network->m_reqConnectionTimer.stop(); network->m_reqConnectionTimer.stop();
network->m_heartbeatTimer.start(); network->m_heartbeatTimer.start();
network->m_timeoutTimer.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; break;
@ -449,6 +464,10 @@ void* V24UDPPort::threadedCtrlNetworkRx(void* arg)
LogError(LOG_MODEM, "V.24 UDP, unknown ACK opcode, ackMessageId = $%02X", ackMessage->getAckMessageId()); LogError(LOG_MODEM, "V.24 UDP, unknown ACK opcode, ackMessageId = $%02X", ackMessage->getAckMessageId());
break; break;
} }
if (ackMessage->getResponseLength() > 0U && ackMessage->responseData != nullptr) {
delete[] ackMessage->responseData; // FSCACK doesn't clean itself up...
}
} }
break; break;
@ -478,21 +497,28 @@ void* V24UDPPort::threadedCtrlNetworkRx(void* arg)
network->m_socket = nullptr; network->m_socket = nullptr;
} }
uint16_t vcPort = connMessage->getVCBasePort(); uint16_t vcBasePort = connMessage->getVCBasePort();
network->m_heartbeatInterval = connMessage->getHostHeartbeatPeriod(); network->m_heartbeatInterval = connMessage->getHostHeartbeatPeriod();
if (network->m_heartbeatInterval > 30U) if (network->m_heartbeatInterval > 30U)
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->createVCPort(network->m_localPort);
network->m_socket->open(network->m_addr); 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_fscState = CS_CONNECTED;
network->m_reqConnectionTimer.stop(); network->m_reqConnectionTimer.stop();
network->m_remoteAddr = req->address;
network->m_remoteAddrLen = req->addrLen;
if (connMessage->getHostHeartbeatPeriod() > 30U) 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()); 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_heartbeatTimer.start();
network->m_timeoutTimer.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 // construct connect ACK response data
uint8_t respData[3U]; uint8_t respData[3U];
::memset(respData, 0x00U, 3U); ::memset(respData, 0x00U, 3U);
@ -517,7 +541,8 @@ void* V24UDPPort::threadedCtrlNetworkRx(void* arg)
::memset(buffer, 0x00U, FSCACK::LENGTH + 3U); ::memset(buffer, 0x00U, FSCACK::LENGTH + 3U);
ackResp.encode(buffer); 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; break;
@ -676,7 +701,7 @@ void* V24UDPPort::threadedVCNetworkRx(void* arg)
} }
if (req->length > 0) { 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<uint8_t[]>(req->length + 4U); UInt8Array __reply = std::make_unique<uint8_t[]>(req->length + 4U);
uint8_t* reply = __reply.get(); uint8_t* reply = __reply.get();
@ -705,7 +730,7 @@ void* V24UDPPort::threadedVCNetworkRx(void* arg)
return nullptr; 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) void V24UDPPort::createVCPort(uint16_t port)
{ {
@ -716,7 +741,20 @@ void V24UDPPort::createVCPort(uint16_t port)
if (m_addrLen > 0U) { if (m_addrLen > 0U) {
std::string addrStr = udp::Socket::address(m_addr); 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(); FSCHeartbeat hb = FSCHeartbeat();
hb.encode(buffer); 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. */ /* Generate RTP message. */

@ -71,11 +71,13 @@ namespace modem
* @param address Hostname/IP address to connect to. * @param address Hostname/IP address to connect to.
* @param modemPort Port number. * @param modemPort Port number.
* @param controlPort Control 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 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 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. * @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. * @brief Finalizes a instance of the V24UDPPort class.
*/ */
@ -145,8 +147,12 @@ namespace modem
uint32_t m_addrLen; uint32_t m_addrLen;
uint32_t m_ctrlAddrLen; uint32_t m_ctrlAddrLen;
sockaddr_storage m_remoteAddr; uint16_t m_ctrlLocalPort;
uint32_t m_remoteAddrLen;
sockaddr_storage m_remoteCtrlAddr;
uint32_t m_remoteCtrlAddrLen;
sockaddr_storage m_remoteRTPAddr;
uint32_t m_remoteRTPAddrLen;
RingBuffer<uint8_t> m_buffer; RingBuffer<uint8_t> m_buffer;
@ -205,10 +211,16 @@ namespace modem
static void* threadedVCNetworkRx(void* arg); 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. * @param port Port number.
*/ */
void createVCPort(uint16_t port); 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. * @brief Internal helper to write a FSC connect packet.
*/ */

Loading…
Cancel
Save

Powered by TurnKey Linux.