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. */
FSCACK::FSCACK() : FSCMessage(),
responseData(nullptr),
m_ackMessageId(FSCMessageType::FSC_INVALID),
m_ackVersion(1U),
m_ackCorrelationTag(0U),

@ -602,10 +602,10 @@ bool Host::createModem()
yaml::Node networkConf = m_conf["network"];
uint32_t id = networkConf["id"].as<uint32_t>(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) {

@ -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");

@ -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;

@ -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<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.
*/
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

@ -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<uint8_t[]>(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. */

@ -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<uint8_t> 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.
*/

Loading…
Cancel
Save

Powered by TurnKey Linux.