= Fix Bridge vocoder integration, framing, and buffer bounds

- Fix half-rate encoder input overread
- Bound NXDN FACCH1 and UDCH puncture-table access
- Fix P25 receive buffer bounds
- Correct NXDN transmit buffer strides and SACCH fragment assembly
- Fix DMR sync, burst sequencing, call termination, and buffer cleanup
- Add focused regression tests and correct static vocoder linkage
- Rename packet-state enum value to avoid a Windows macro collision
pull/134/head
Dev_Ranger 6 days ago
parent 54cee44381
commit 3ba67a1506

@ -292,7 +292,7 @@ if (ENABLE_TESTS)
list(FILTER dvmfnetest_SRC EXCLUDE REGEX "/HostFNE\\.cpp$")
add_executable(dvmtests ${common_INCLUDE} ${dvmhost_SRC} ${dvmfnetest_SRC} ${dvmtests_SRC} src/remote/RESTClient.cpp)
target_compile_definitions(dvmtests PUBLIC -DCATCH2_TEST_COMPILATION)
target_link_libraries(dvmtests PRIVATE Catch2::Catch2WithMain common ${OPENSSL_LIBRARIES} asio::asio Threads::Threads util)
target_link_libraries(dvmtests PRIVATE Catch2::Catch2WithMain common vocoder ${OPENSSL_LIBRARIES} asio::asio Threads::Threads util)
target_include_directories(dvmtests PRIVATE ${OPENSSL_INCLUDE_DIR} src src/host src/fne tests)
if (SHARED_LIBS AND NOT COMPILE_WIN32)
set_target_properties(dvmtests PROPERTIES

@ -43,6 +43,7 @@ if (SHARED_LIBS)
target_link_libraries(vocoder PRIVATE common)
else ()
target_compile_definitions(vocoder PUBLIC VOCODER_STATIC)
target_link_libraries(vocoder PRIVATE common)
endif (SHARED_LIBS)
target_include_directories(vocoder PRIVATE src src/vocoder)

@ -17,6 +17,7 @@
#include "common/dmr/data/NetData.h"
#include "common/dmr/lc/FullLC.h"
#include "common/dmr/SlotType.h"
#include "common/dmr/Sync.h"
#include "common/Log.h"
#include "common/Utils.h"
#include "bridge/ActivityLog.h"
@ -348,12 +349,13 @@ void HostBridge::encodeDMRAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_
srcId = m_srcId;
uint8_t* data = nullptr;
m_dmrN = (uint8_t)(m_dmrSeqNo % 6);
if (m_ambeCount == AMBE_PER_SLOT) {
// is this the intitial sequence?
if (m_dmrSeqNo == 0) {
// send DMR voice header
data = new uint8_t[DMR_FRAME_LENGTH_BYTES];
::memset(data, 0x00U, DMR_FRAME_LENGTH_BYTES);
Sync::addDMRDataSync(data, true);
// generate DMR LC
lc::LC dmrLC = lc::LC();
@ -408,6 +410,7 @@ void HostBridge::encodeDMRAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_
// send DMR voice
data = new uint8_t[DMR_FRAME_LENGTH_BYTES];
::memset(data, 0x00U, DMR_FRAME_LENGTH_BYTES);
::memcpy(data, m_ambeBuffer, 13U);
data[13U] = (uint8_t)(m_ambeBuffer[13U] & 0xF0);
@ -415,8 +418,10 @@ void HostBridge::encodeDMRAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_
::memcpy(data + 20U, m_ambeBuffer + 14U, 13U);
DataType::E dataType = DataType::VOICE_SYNC;
if (m_dmrN == 0)
if (m_dmrN == 0) {
dataType = DataType::VOICE_SYNC;
Sync::addDMRAudioSync(data, true);
}
else {
dataType = DataType::VOICE;
@ -446,9 +451,11 @@ void HostBridge::encodeDMRAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_
dmrData.setData(data);
m_network->writeDMR(dmrData, false);
delete[] data;
m_txStreamId = m_network->getDMRStreamId(m_slot);
m_dmrSeqNo++;
m_dmrN = (m_dmrN + 1U) % 6U;
::memset(m_ambeBuffer, 0x00U, 27U);
m_ambeCount = 0U;
}

@ -198,6 +198,7 @@ void HostBridge::processNXDNNetwork(uint8_t* buffer, uint32_t length)
m_nxdnCrypto->resetKeystream();
m_rxNXDNSACCHLC = lc::RTCH();
::memset(m_rxNXDNSACCHData, 0x00U, sizeof(m_rxNXDNSACCHData));
m_rxNXDNSACCHMask = 0U;
m_rxNXDNPendingMI = false;
@ -230,13 +231,17 @@ void HostBridge::processNXDNNetwork(uint8_t* buffer, uint32_t length)
uint8_t quarter = 3U - (uint8_t)voiceSACCH.getStructure();
if (quarter == 0U) {
m_rxNXDNSACCHLC = lc::RTCH();
::memset(m_rxNXDNSACCHData, 0x00U, sizeof(m_rxNXDNSACCHData));
m_rxNXDNSACCHMask = 0U;
}
m_rxNXDNSACCHLC.decode(fragment, 18U, quarter * 18U);
// RTCH::decode's offset addresses its input; assemble the fragments first.
for (uint32_t bit = 0U; bit < 18U; bit++)
WRITE_BIT(m_rxNXDNSACCHData, quarter * 18U + bit, READ_BIT(fragment, bit));
m_rxNXDNSACCHMask |= (uint8_t)(1U << quarter);
if (quarter == 3U && m_rxNXDNSACCHMask == 0x0FU) {
m_rxNXDNSACCHLC.decode(m_rxNXDNSACCHData, 72U);
uint8_t sacchType = m_rxNXDNSACCHLC.getMessageType();
if (sacchType == MessageType::RTCH_VCALL) {
m_rxNXDNLC = m_rxNXDNSACCHLC;
@ -559,8 +564,8 @@ void HostBridge::encodeNXDNAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32
m_nxdnN = 0U;
}
uint8_t* packedAMBE = m_nxdnAMBE + (m_nxdnN * dmr::defines::DMR_AMBE_LENGTH_BYTES);
::memset(packedAMBE, 0x00U, dmr::defines::DMR_AMBE_LENGTH_BYTES);
uint8_t* packedAMBE = m_nxdnAMBE + (m_nxdnN * dmr::defines::RAW_AMBE_LENGTH_BYTES);
::memset(packedAMBE, 0x00U, dmr::defines::RAW_AMBE_LENGTH_BYTES);
for (uint32_t b = 0U; b < 49U; b++)
WRITE_BIT(packedAMBE, b, rawBits[b] != 0U);
m_nxdnN++;
@ -579,7 +584,7 @@ void HostBridge::encodeNXDNAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32
::memset(packedBits, 0x00U, 13U);
for (uint8_t half = 0U; half < 2U; half++) {
uint8_t* ambe = m_nxdnAMBE + ((pair * 2U + half) * dmr::defines::DMR_AMBE_LENGTH_BYTES);
uint8_t* ambe = m_nxdnAMBE + ((pair * 2U + half) * dmr::defines::RAW_AMBE_LENGTH_BYTES);
for (uint32_t b = 0U; b < 49U; b++) {
WRITE_BIT(packedBits, (half * 49U) + b, READ_BIT(ambe, b));
}

@ -64,6 +64,9 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length)
return;
}
if (length < 24U)
return;
bool grantDemand = (buffer[14U] & network::NET_CTRL_GRANT_DEMAND) == network::NET_CTRL_GRANT_DEMAND;
bool grantDenial = (buffer[14U] & network::NET_CTRL_GRANT_DENIAL) == network::NET_CTRL_GRANT_DENIAL;
bool unitToUnit = (buffer[14U] & network::NET_CTRL_U2U) == network::NET_CTRL_U2U;
@ -75,26 +78,14 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length)
if (duid == DUID::HDU || duid == DUID::TSDU || duid == DUID::PDU)
return;
// process raw P25 data bytes
UInt8Array data;
uint8_t frameLength = buffer[23U];
if (duid == DUID::PDU) {
frameLength = length;
data = std::unique_ptr<uint8_t[]>(new uint8_t[length]);
::memset(data.get(), 0x00U, length);
::memcpy(data.get(), buffer, length);
}
else {
if (frameLength <= 24) {
data = std::unique_ptr<uint8_t[]>(new uint8_t[frameLength]);
::memset(data.get(), 0x00U, frameLength);
}
else {
data = std::unique_ptr<uint8_t[]>(new uint8_t[frameLength]);
::memset(data.get(), 0x00U, frameLength);
::memcpy(data.get(), buffer + 24U, frameLength);
}
}
// LDU counts include the 24-byte network header; terminators need no voice payload.
uint32_t frameLength = std::min<uint32_t>(buffer[23U], length);
frameLength = frameLength > 24U ? frameLength - 24U : 0U;
if ((duid == DUID::LDU1 || duid == DUID::LDU2) && frameLength < 91U)
return;
UInt8Array data = std::make_unique<uint8_t[]>(frameLength);
if (frameLength > 0U)
::memcpy(data.get(), buffer + 24U, frameLength);
// handle LDU, TDU or TSDU frame
uint8_t lco = buffer[4U];
@ -159,7 +150,7 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length)
m_callAlgoId = ALGO_UNENCRYPT;
// if this is the beginning of a call and we have a valid HDU frame, extract the algo ID
uint8_t frameType = buffer[180U];
uint8_t frameType = length >= (184U + MI_LENGTH_BYTES) ? buffer[180U] : FrameType::DATA_UNIT;
if (frameType == FrameType::HDU_VALID) {
m_callAlgoId = buffer[181U];
if (m_callAlgoId != ALGO_UNENCRYPT) {

@ -15,6 +15,7 @@
#include "common/network/RTPHeader.h"
#include "common/network/udp/Socket.h"
#include "common/dmr/DMRDefines.h"
#include "common/dmr/Sync.h"
#include "common/dmr/data/EMB.h"
#include "common/nxdn/NXDNDefines.h"
#include "common/nxdn/NXDNUtils.h"
@ -244,6 +245,7 @@ HostBridge::HostBridge(const std::string& confFile) :
m_p25N(0U),
m_rxNXDNLC(),
m_rxNXDNSACCHLC(),
m_rxNXDNSACCHData{},
m_rxNXDNSACCHMask(0U),
m_rxNXDNPendingMI(false),
m_rxNXDNNextMI(),
@ -2877,10 +2879,9 @@ void HostBridge::padSilenceAudio(uint32_t srcId, uint32_t dstId)
using namespace dmr::defines;
using namespace dmr::data;
m_dmrN = (uint8_t)(m_dmrSeqNo % 6);
// send DMR voice
uint8_t data[DMR_FRAME_LENGTH_BYTES];
::memset(data, 0x00U, DMR_FRAME_LENGTH_BYTES);
m_ambeCount = 0U;
@ -2897,8 +2898,10 @@ void HostBridge::padSilenceAudio(uint32_t srcId, uint32_t dstId)
::memcpy(data + 20U, m_ambeBuffer + 14U, 13U);
DataType::E dataType = DataType::VOICE_SYNC;
if (m_dmrN == 0)
if (m_dmrN == 0) {
dataType = DataType::VOICE_SYNC;
Sync::addDMRAudioSync(data, true);
}
else {
dataType = DataType::VOICE;
@ -2931,6 +2934,7 @@ void HostBridge::padSilenceAudio(uint32_t srcId, uint32_t dstId)
m_txStreamId = m_network->getDMRStreamId(m_slot);
m_dmrSeqNo++;
m_dmrN = (m_dmrN + 1U) % 6U;
}
break;
case TX_MODE_P25:

@ -266,6 +266,7 @@ private:
nxdn::lc::RTCH m_rxNXDNLC;
nxdn::lc::RTCH m_rxNXDNSACCHLC;
uint8_t m_rxNXDNSACCHData[9U];
uint8_t m_rxNXDNSACCHMask;
bool m_rxNXDNPendingMI;
uint8_t m_rxNXDNNextMI[nxdn::defines::MI_LENGTH_BYTES];

@ -11,6 +11,7 @@
#include "common/dmr/data/EMB.h"
#include "common/dmr/lc/FullLC.h"
#include "common/dmr/SlotType.h"
#include "common/dmr/Sync.h"
#include "common/p25/dfsi/DFSIDefines.h"
#include "common/p25/dfsi/LC.h"
#include "common/json/json.h"
@ -89,36 +90,27 @@ void PeerNetwork::writeDMRTerminator(dmr::data::NetData& data, uint32_t* seqNo,
using namespace dmr;
using namespace dmr::defines;
uint8_t n = (uint8_t)((*seqNo - 3U) % 6U);
uint32_t fill = 6U - n;
uint8_t* buffer = nullptr;
if (n > 0U) {
for (uint32_t i = 0U; i < fill; i++) {
// generate DMR AMBE data
buffer = new uint8_t[DMR_FRAME_LENGTH_BYTES];
::memcpy(buffer, SILENCE_DATA, DMR_FRAME_LENGTH_BYTES);
uint8_t lcss = embeddedData.getData(buffer, n);
// generated embedded signalling
data::EMB emb = data::EMB();
emb.setColorCode(0U);
emb.setLCSS(lcss);
emb.encode(buffer);
// generate DMR network frame
data.setData(buffer);
writeDMR(data);
seqNo++;
dmrN++;
delete[] buffer;
}
// Complete the current six-burst superframe before sending the terminator.
uint8_t buffer[DMR_FRAME_LENGTH_BYTES];
while (*dmrN != 0U) {
::memcpy(buffer, SILENCE_DATA, DMR_FRAME_LENGTH_BYTES);
uint8_t lcss = embeddedData.getData(buffer, *dmrN);
data::EMB emb = data::EMB();
emb.setColorCode(0U);
emb.setLCSS(lcss);
emb.encode(buffer);
data.setDataType(DataType::VOICE);
data.setN(*dmrN);
data.setSeqNo(*seqNo);
data.setData(buffer);
writeDMR(data);
(*seqNo)++;
*dmrN = (*dmrN + 1U) % 6U;
}
buffer = new uint8_t[DMR_FRAME_LENGTH_BYTES];
::memset(buffer, 0x00U, DMR_FRAME_LENGTH_BYTES);
Sync::addDMRDataSync(buffer, true);
// generate DMR LC
lc::LC dmrLC = lc::LC();
@ -134,13 +126,14 @@ void PeerNetwork::writeDMRTerminator(dmr::data::NetData& data, uint32_t* seqNo,
lc::FullLC fullLC = lc::FullLC();
fullLC.encode(dmrLC, buffer, DataType::TERMINATOR_WITH_LC);
// generate DMR network frame
data.setDataType(DataType::TERMINATOR_WITH_LC);
data.setN(0U);
data.setSeqNo(*seqNo);
data.setData(buffer);
writeDMR(data);
seqNo = 0;
dmrN = 0;
*seqNo = 0U;
*dmrN = 0U;
}
// ---------------------------------------------------------------------------

@ -106,7 +106,7 @@ bool FACCH1::decode(const uint8_t* data, uint32_t offset)
uint8_t puncture[210U];
uint32_t n = 0U, index = 0U;
for (uint32_t i = 0U; i < NXDN_FACCH1_FEC_LENGTH_BITS; i++) {
if (n == PUNCTURE_LIST[index]) {
if (index < (sizeof(PUNCTURE_LIST) / sizeof(PUNCTURE_LIST[0U])) && n == PUNCTURE_LIST[index]) {
puncture[n++] = 1U;
index++;
}
@ -179,7 +179,7 @@ void FACCH1::encode(uint8_t* data, uint32_t offset) const
uint32_t n = 0U, index = 0U;
for (uint32_t i = 0U; i < NXDN_FACCH1_FEC_CONV_LENGTH_BITS; i++) {
if (i != PUNCTURE_LIST[index]) {
if (index >= (sizeof(PUNCTURE_LIST) / sizeof(PUNCTURE_LIST[0U])) || i != PUNCTURE_LIST[index]) {
bool b = READ_BIT(convolution, i);
WRITE_BIT(puncture, n, b);
n++;

@ -130,7 +130,7 @@ bool UDCH::decode(const uint8_t* data)
uint8_t puncture[420U];
uint32_t n = 0U, index = 0U;
for (uint32_t i = 0U; i < NXDN_UDCH_FEC_LENGTH_BITS; i++) {
if (n == PUNCTURE_LIST[index]) {
if (index < (sizeof(PUNCTURE_LIST) / sizeof(PUNCTURE_LIST[0U])) && n == PUNCTURE_LIST[index]) {
puncture[n++] = 1U;
index++;
}
@ -207,7 +207,7 @@ void UDCH::encode(uint8_t* data) const
uint32_t n = 0U, index = 0U;
for (uint32_t i = 0U; i < NXDN_UDCH_FEC_CONV_LENGTH_BITS; i++) {
if (i != PUNCTURE_LIST[index]) {
if (index >= (sizeof(PUNCTURE_LIST) / sizeof(PUNCTURE_LIST[0U])) || i != PUNCTURE_LIST[index]) {
bool b = READ_BIT(convolution, i);
WRITE_BIT(puncture, n, b);
n++;

@ -810,7 +810,7 @@ void P25PacketData::dispatch(uint32_t peerId)
ReceiveSequenceResult sequenceResult =
m_dataLinkManager.acceptReceiveSequence(srcLlId, receivedNs, synchronize,
packetFingerprint, expectedNs);
if (sequenceResult == ReceiveSequenceResult::DUPLICATE) {
if (sequenceResult == ReceiveSequenceResult::DUPLICATE_FRAME) {
// re-ACK a duplicate, but never deliver the IP datagram twice
if (status->assembler.getExtendedAddress()) {
write_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, receivedNs,

@ -103,7 +103,7 @@ ReceiveSequenceResult DataLinkManager::acceptReceiveSequence(uint32_t llId, uint
}
if (sequence == state.receiveSequence && packetFingerprint == state.receiveFingerprint)
return ReceiveSequenceResult::DUPLICATE;
return ReceiveSequenceResult::DUPLICATE_FRAME;
// BAED permits gaps: record the newly received N(S), allowing the upper
// layer to observe that intervening logical packets were lost

@ -71,7 +71,7 @@ namespace network
*/
enum class ReceiveSequenceResult : uint8_t {
ACCEPTED, //!< Accepted receive sequence result
DUPLICATE, //!< Duplicate receive sequence result
DUPLICATE_FRAME, //!< Duplicate receive sequence result
OUT_OF_SEQUENCE //!< Out of sequence receive sequence result
};
/* @} */

@ -604,10 +604,9 @@ void MBEEncoder::encodeBits(uint8_t* bits, uint8_t* codeword)
uint8_t rawAmbe[9U];
::memset(rawAmbe, 0x00U, 9U);
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 8; ++j) {
rawAmbe[i] |= (bits[(i * 8) + j] << (7 - j));
}
// Only 49 input entries are provided; the remaining packed bits stay zero.
for (uint32_t i = 0U; i < 49U; i++) {
WRITE_BIT(rawAmbe, i, (bits[i] & 1U) != 0U);
}
// build DMR AMBE bytes

@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "vocoder/MBEEncoder.h"
#include "vocoder/MBEDecoder.h"
#include <catch2/catch_test_macros.hpp>
#include <cstring>
#include <memory>
TEST_CASE("Half-rate FEC consumes exactly 49 source bits", "[dmr][vocoder][bounds]") {
vocoder::MBEEncoder encoder(vocoder::ENCODE_DMR_AMBE);
vocoder::MBEDecoder decoder(vocoder::DECODE_DMR_AMBE);
// Independent vectors from Console's existing native FEC runtime, with a zero-padded input.
const uint8_t known[2U][9U] = {
{0x22U, 0x00U, 0x02U, 0x04U, 0x02U, 0x20U, 0x40U, 0x44U, 0x00U},
{0xDDU, 0xDDU, 0xFFU, 0xFFU, 0xDFU, 0xBFU, 0xBBU, 0xFFU, 0xBBU}
};
for (uint32_t pattern = 0U; pattern < 51U; pattern++) {
// An exact heap allocation makes a one-past-end read visible to ASAN.
auto bits = std::make_unique<uint8_t[]>(49U);
for (uint32_t i = 0U; i < 49U; i++)
bits[i] = pattern == 1U || (pattern >= 2U && i == pattern - 2U) ? 1U : 0U;
uint8_t output[11U] = {};
output[0U] = output[10U] = 0xA5U;
encoder.encodeBits(bits.get(), output + 1U);
REQUIRE(output[0U] == 0xA5U);
REQUIRE(output[10U] == 0xA5U);
if (pattern < 2U)
REQUIRE(::memcmp(output + 1U, known[pattern], 9U) == 0);
char decoded[49U] = {};
REQUIRE(decoder.decodeBits(output + 1U, decoded) == 0);
for (uint32_t i = 0U; i < 49U; i++)
REQUIRE(decoded[i] == bits[i]);
// Data beyond the API's 49 source entries must not change the last valid bit.
uint8_t padded[72U];
::memset(padded, 0xFFU, sizeof(padded));
::memcpy(padded, bits.get(), 49U);
uint8_t repeated[9U];
encoder.encodeBits(padded, repeated);
REQUIRE(::memcmp(output + 1U, repeated, 9U) == 0);
}
}

@ -189,3 +189,29 @@ TEST_CASE("FACCH1 golden test for voice call header", "[nxdn][facch1][golden]")
decoded.getData(dataOut);
REQUIRE(::memcmp(dataIn, dataOut, 10U) == 0);
}
TEST_CASE("FACCH1 matches independently encoded Console header", "[nxdn][facch1][bounds]") {
// Clear group VCALL, source 1234, destination 5678; Console's managed channel coder.
const uint8_t raw[10U] = {0x01U, 0x00U, 0x20U, 0x04U, 0xD2U, 0x16U, 0x2EU, 0x00U, 0x00U, 0x00U};
const uint8_t encoded[18U] = {
0x48U, 0xB4U, 0x05U, 0x01U, 0x00U, 0x08U, 0x00U, 0x30U, 0x38U,
0x08U, 0x80U, 0xA6U, 0x5CU, 0x02U, 0x02U, 0xCAU, 0x23U, 0xE1U
};
FACCH1 tx;
tx.setData(raw);
for (uint32_t offset : {96U, 240U}) {
uint8_t frame[NXDN_FRAME_LENGTH_BYTES];
::memset(frame, 0xA5U, sizeof(frame));
tx.encode(frame, offset);
REQUIRE(::memcmp(frame + offset / 8U, encoded, sizeof(encoded)) == 0);
for (uint32_t i = 0U; i < sizeof(frame); i++) {
if (i < offset / 8U || i >= offset / 8U + sizeof(encoded))
REQUIRE(frame[i] == 0xA5U);
}
FACCH1 rx;
REQUIRE(rx.decode(frame, offset));
uint8_t decoded[10U];
rx.getData(decoded);
REQUIRE(::memcmp(decoded, raw, sizeof(raw)) == 0);
}
}

@ -39,7 +39,7 @@ TEST_CASE("SACCH encodes and decodes idle pattern", "[nxdn][sacch]") {
REQUIRE(decoded.getStructure() == ChStructure::SR_SINGLE);
// Verify data matches
uint8_t decodedData[3U];
uint8_t decodedData[3U] = {};
decoded.getData(decodedData);
REQUIRE(::memcmp(decodedData, SACCH_IDLE, 3U) == 0);
}
@ -157,7 +157,7 @@ TEST_CASE("SACCH handles multi-part structures", "[nxdn][sacch]") {
REQUIRE(decoded.getStructure() == structure);
REQUIRE(decoded.getRAN() == 7U);
uint8_t decodedData[3U];
uint8_t decodedData[3U] = {};
decoded.getData(decodedData);
Utils::dump(2U, "decodedData", decodedData, 3U);
REQUIRE(::memcmp(decodedData, testData, 3U) == 0);

@ -162,7 +162,7 @@ TEST_CASE("P25 LLC state is isolated by LLID", "[p25][packet-data][llc]")
uint8_t expected = 0U;
CHECK(links.acceptReceiveSequence(1001U, 0U, true, 11U, expected) == ReceiveSequenceResult::ACCEPTED);
CHECK(links.acceptReceiveSequence(1001U, 0U, false, 11U, expected) == ReceiveSequenceResult::DUPLICATE);
CHECK(links.acceptReceiveSequence(1001U, 0U, false, 11U, expected) == ReceiveSequenceResult::DUPLICATE_FRAME);
CHECK(links.acceptReceiveSequence(1001U, 1U, false, 12U, expected) == ReceiveSequenceResult::ACCEPTED);
CHECK(links.acceptReceiveSequence(1001U, 7U, false, 13U, expected) == ReceiveSequenceResult::ACCEPTED);
CHECK(expected == 2U);
@ -181,7 +181,7 @@ TEST_CASE("P25 LLC receive sequence wraps and suppresses duplicates", "[p25][pac
REQUIRE(links.acceptReceiveSequence(77U, 7U, false, 21U, expected) == ReceiveSequenceResult::ACCEPTED);
CHECK(links.acceptReceiveSequence(77U, 0U, false, 22U, expected) == ReceiveSequenceResult::ACCEPTED);
CHECK(links.acceptReceiveSequence(77U, 0U, false, 22U, expected) == ReceiveSequenceResult::DUPLICATE);
CHECK(links.acceptReceiveSequence(77U, 0U, false, 22U, expected) == ReceiveSequenceResult::DUPLICATE_FRAME);
CHECK(links.acceptReceiveSequence(77U, 2U, false, 23U, expected) == ReceiveSequenceResult::ACCEPTED);
CHECK(expected == 1U);

Loading…
Cancel
Save

Powered by TurnKey Linux.