From 4f5051e8b6458f987139b8d3f76ac417bf8196c8 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 5 Dec 2025 17:10:24 -0500 Subject: [PATCH] better implement V(R) V(S) checking; --- .../callhandler/packetdata/P25PacketData.cpp | 49 +++++++++++++++++-- .../callhandler/packetdata/P25PacketData.h | 3 +- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index d11871ad..5e1a77e5 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -64,6 +64,7 @@ P25PacketData::P25PacketData(FNENetwork* network, TagP25Data* tag, bool debug) : m_arpTable(), m_readyForNextPkt(), m_suSendSeq(), + m_suRecvSeq(), m_debug(debug) { assert(network != nullptr); @@ -764,6 +765,49 @@ void P25PacketData::dispatch(uint32_t peerId) } } + // sequence validation - check N(S) against V(R) + uint8_t receivedNs = status->assembler.dataHeader.getNs(); + bool synchronize = status->assembler.dataHeader.getSynchronize(); + + // Initialize V(R) if first packet from this LLId + auto recvSeqIt = m_suRecvSeq.find(srcLlId); + if (recvSeqIt == m_suRecvSeq.end()) { + m_suRecvSeq[srcLlId] = 0U; + } + + uint8_t expectedNs = m_suRecvSeq[srcLlId]; + bool sequenceValid = false; + + // handle synchronize flag - resets receive window per TIA-102 + if (synchronize) { + //LogDebugEx(LOG_P25, "P25PacketData::dispatch()", "synchronize flag set by llId %u, resetting V(R) from %u to %u", + // srcLlId, expectedNs, (receivedNs + 1) % 8); + m_suRecvSeq[srcLlId] = (receivedNs + 1) % 8; + sequenceValid = true; + } + else if (receivedNs == expectedNs || receivedNs == (expectedNs + 1) % 8) { + // accept if N(S) == V(R) or V(R)+1 (allows one-ahead windowing) + m_suRecvSeq[srcLlId] = (receivedNs + 1) % 8; + sequenceValid = true; + //LogDebugEx(LOG_P25, "P25PacketData::dispatch()", "sequence valid, llId %u, N(S) = %u, V(R) now = %u", + // srcLlId, receivedNs, m_suRecvSeq[srcLlId]); + } + else { + // out of sequence - send NACK_OUT_OF_SEQ + LogWarning(LOG_P25, P25_PDU_STR ", NACK_OUT_OF_SEQ, llId %u, expected N(S) %u or %u, received N(S) = %u", + srcLlId, expectedNs, (expectedNs + 1) % 8, receivedNs); + if (status->assembler.getExtendedAddress()) { + write_PDU_Ack_Response(PDUAckClass::NACK, PDUAckType::NACK_OUT_OF_SEQ, expectedNs, srcLlId, true, dstLlId); + } else { + write_PDU_Ack_Response(PDUAckClass::NACK, PDUAckType::NACK_OUT_OF_SEQ, expectedNs, srcLlId, false); + } + break; // don't process out-of-sequence packet + } + + if (!sequenceValid) { + break; // should never reach here due to logic above, but safety check + } + // transmit packet to IP network LogInfoEx(LOG_P25, "PDU -> VTUN, IP Data, srcIp = %s (%u), dstIp = %s (%u), pktLen = %u, proto = %02X%s", srcIp, srcLlId, dstIp, dstLlId, pktLen, proto, (proto == 0x01) ? " (ICMP)" : ""); @@ -782,11 +826,10 @@ void P25PacketData::dispatch(uint32_t peerId) //LogDebugEx(LOG_P25, "P25PacketData::dispatch()", "marking llId %u ready for next packet (proto = %02X)", srcLlId, proto); if (status->assembler.getExtendedAddress()) { m_readyForNextPkt[srcLlId] = true; - write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->assembler.dataHeader.getNs(), srcLlId, - true, dstLlId); + write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, receivedNs, srcLlId, true, dstLlId); } else { m_readyForNextPkt[srcLlId] = true; - write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, status->assembler.dataHeader.getNs(), srcLlId, false); + write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, receivedNs, srcLlId, false); } } #endif // !defined(_WIN32) diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index 15737383..aa1514a4 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -199,7 +199,8 @@ namespace network std::unordered_map m_arpTable; typedef std::pair ReadyForNextPktPair; std::unordered_map m_readyForNextPkt; - std::unordered_map m_suSendSeq; + std::unordered_map m_suSendSeq; // V(S) send state variable per LLId + std::unordered_map m_suRecvSeq; // V(R) receive state variable per LLId bool m_debug;