From 3599bb0f1e0b93d30d9c87d6800bc88067a39cf8 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Wed, 29 Jan 2020 18:58:52 +0000 Subject: [PATCH 1/6] First attempt at LLRP header and PDU All there apart from flags should always be 0xF0 rather than 0x70 (cherry picked from commit 23a00bbc5ce89055c08bda9c342449fcfbade2fd) --- include/ola/acn/ACNVectors.h | 3 + libs/acn/HeaderSet.h | 8 +- libs/acn/LLRPHeader.h | 69 ++++++++++++++++ libs/acn/LLRPInflator.cpp | 70 ++++++++++++++++ libs/acn/LLRPInflator.h | 58 ++++++++++++++ libs/acn/LLRPInflatorTest.cpp | 126 +++++++++++++++++++++++++++++ libs/acn/LLRPPDU.cpp | 117 +++++++++++++++++++++++++++ libs/acn/LLRPPDU.h | 62 ++++++++++++++ libs/acn/LLRPPDUTest.cpp | 147 ++++++++++++++++++++++++++++++++++ libs/acn/Makefile.mk | 14 ++++ 10 files changed, 673 insertions(+), 1 deletion(-) create mode 100644 libs/acn/LLRPHeader.h create mode 100644 libs/acn/LLRPInflator.cpp create mode 100644 libs/acn/LLRPInflator.h create mode 100644 libs/acn/LLRPInflatorTest.cpp create mode 100644 libs/acn/LLRPPDU.cpp create mode 100644 libs/acn/LLRPPDU.h create mode 100644 libs/acn/LLRPPDUTest.cpp diff --git a/include/ola/acn/ACNVectors.h b/include/ola/acn/ACNVectors.h index b45e1c0e76..9e8211ff13 100644 --- a/include/ola/acn/ACNVectors.h +++ b/include/ola/acn/ACNVectors.h @@ -47,6 +47,9 @@ enum RootVector { VECTOR_ROOT_E131 = 4, /**< E1.31 (sACN) */ VECTOR_ROOT_E133 = 5, /**< E1.33 (RDNNet) */ VECTOR_ROOT_NULL = 6, /**< NULL (empty) root */ + VECTOR_ROOT_BROKER = 9, /**< E1.33 (Broker) */ + VECTOR_ROOT_LLRP = 0x0A, /**< E1.33 (LLRP) */ + VECTOR_ROOT_EPT = 0x0B, /**< E1.33 (EPT) */ }; /** diff --git a/libs/acn/HeaderSet.h b/libs/acn/HeaderSet.h index c3e9a44f98..06a56c26aa 100644 --- a/libs/acn/HeaderSet.h +++ b/libs/acn/HeaderSet.h @@ -26,6 +26,7 @@ #include "libs/acn/DMPHeader.h" #include "libs/acn/E131Header.h" #include "libs/acn/E133Header.h" +#include "libs/acn/LLRPHeader.h" #include "libs/acn/RootHeader.h" #include "libs/acn/TransportHeader.h" @@ -56,13 +57,17 @@ class HeaderSet { const DMPHeader &GetDMPHeader() const { return m_dmp_header; } void SetDMPHeader(const DMPHeader &header) { m_dmp_header = header; } + const LLRPHeader &GetLLRPHeader() const { return m_llrp_header; } + void SetLLRPHeader(const LLRPHeader &header) { m_llrp_header = header; } + bool operator==(const HeaderSet &other) const { return ( m_transport_header == other.m_transport_header && m_root_header == other.m_root_header && m_e131_header == other.m_e131_header && m_e133_header == other.m_e133_header && - m_dmp_header == other.m_dmp_header); + m_dmp_header == other.m_dmp_header && + m_llrp_header == other.m_llrp_header); } private: @@ -71,6 +76,7 @@ class HeaderSet { E131Header m_e131_header; E133Header m_e133_header; DMPHeader m_dmp_header; + LLRPHeader m_llrp_header; }; } // namespace acn } // namespace ola diff --git a/libs/acn/LLRPHeader.h b/libs/acn/LLRPHeader.h new file mode 100644 index 0000000000..1d964268fd --- /dev/null +++ b/libs/acn/LLRPHeader.h @@ -0,0 +1,69 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * LLRPHeader.h + * The E1.33 LLRP Header + * Copyright (C) 2020 Peter Newman + */ + +#ifndef LIBS_ACN_LLRPHEADER_H_ +#define LIBS_ACN_LLRPHEADER_H_ + +#include +#include + +#include + +namespace ola { +namespace acn { + +/* + * Header for the LLRP layer + */ +class LLRPHeader { + public: + LLRPHeader() + : m_transaction_number(0) { + } + + LLRPHeader(const ola::acn::CID &destination_cid, + uint32_t transaction_number) + : m_destination_cid(destination_cid), + m_transaction_number(transaction_number) { + } + ~LLRPHeader() {} + + const ola::acn::CID DestinationCid() const { return m_destination_cid; } + uint32_t TransactionNumber() const { return m_transaction_number; } + + bool operator==(const LLRPHeader &other) const { + return m_destination_cid == other.m_destination_cid && + m_transaction_number == other.m_transaction_number; + } + + PACK( + struct llrp_pdu_header_s { + uint8_t destination_cid[CID::CID_LENGTH]; + uint32_t transaction_number; + }); + typedef struct llrp_pdu_header_s llrp_pdu_header; + + private: + ola::acn::CID m_destination_cid; + uint32_t m_transaction_number; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_LLRPHEADER_H_ diff --git a/libs/acn/LLRPInflator.cpp b/libs/acn/LLRPInflator.cpp new file mode 100644 index 0000000000..e629df8d50 --- /dev/null +++ b/libs/acn/LLRPInflator.cpp @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * LLRPInflator.cpp + * The Inflator for E1.33 LLRP + * Copyright (C) 2020 Peter Newman + */ + +#include "ola/Logging.h" +#include "ola/network/NetworkUtils.h" +#include "libs/acn/LLRPInflator.h" + +namespace ola { +namespace acn { + +using ola::network::NetworkToHost; + +/* + * Decode the E1.33 LLRP headers. If data is null we're expected to use the + * last header we got. + * @param headers the HeaderSet to add to + * @param data a pointer to the data + * @param length length of the data + * @returns true if successful, false otherwise + */ +bool LLRPInflator::DecodeHeader(HeaderSet *headers, + const uint8_t *data, + unsigned int length, + unsigned int *bytes_used) { + if (data) { + // the header bit was set, decode it + if (length >= sizeof(LLRPHeader::llrp_pdu_header)) { + LLRPHeader::llrp_pdu_header raw_header; + memcpy(&raw_header, data, sizeof(LLRPHeader::llrp_pdu_header)); + LLRPHeader header( + ola::acn::CID::FromData(raw_header.destination_cid), + NetworkToHost(raw_header.transaction_number)); + m_last_header = header; + m_last_header_valid = true; + headers->SetLLRPHeader(header); + *bytes_used = sizeof(LLRPHeader::llrp_pdu_header); + return true; + } + *bytes_used = 0; + return false; + } + + // use the last header if it exists + *bytes_used = 0; + if (!m_last_header_valid) { + OLA_WARN << "Missing E1.33 LLRP Header data"; + return false; + } + headers->SetLLRPHeader(m_last_header); + return true; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/LLRPInflator.h b/libs/acn/LLRPInflator.h new file mode 100644 index 0000000000..34a5aa023d --- /dev/null +++ b/libs/acn/LLRPInflator.h @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * LLRPInflator.h + * Interface for the LLRPInflator class. + * Copyright (C) 2020 Peter Newman + */ + +#ifndef LIBS_ACN_LLRPINFLATOR_H_ +#define LIBS_ACN_LLRPINFLATOR_H_ + +#include "ola/acn/ACNVectors.h" +#include "libs/acn/BaseInflator.h" +#include "libs/acn/LLRPHeader.h" + +namespace ola { +namespace acn { + +class LLRPInflator: public BaseInflator { + friend class LLRPInflatorTest; + + public: + LLRPInflator() + : BaseInflator(), + m_last_header_valid(false) { + } + ~LLRPInflator() {} + + uint32_t Id() const { return ola::acn::VECTOR_ROOT_LLRP; } + + protected: + bool DecodeHeader(HeaderSet *headers, + const uint8_t *data, + unsigned int len, + unsigned int *bytes_used); + + void ResetHeaderField() { + m_last_header_valid = false; + } + private: + LLRPHeader m_last_header; + bool m_last_header_valid; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_LLRPINFLATOR_H_ diff --git a/libs/acn/LLRPInflatorTest.cpp b/libs/acn/LLRPInflatorTest.cpp new file mode 100644 index 0000000000..b36c5cdc05 --- /dev/null +++ b/libs/acn/LLRPInflatorTest.cpp @@ -0,0 +1,126 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * LLRPInflatorTest.cpp + * Test fixture for the LLRPInflator class + * Copyright (C) 2020 Peter Newman + */ + +#include + +#include "ola/Logging.h" +#include "ola/network/NetworkUtils.h" +#include "libs/acn/HeaderSet.h" +#include "libs/acn/PDUTestCommon.h" +#include "libs/acn/LLRPInflator.h" +#include "libs/acn/LLRPPDU.h" +#include "ola/testing/TestUtils.h" + + +namespace ola { +namespace acn { + +using ola::network::HostToNetwork; + +class LLRPInflatorTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(LLRPInflatorTest); + CPPUNIT_TEST(testDecodeHeader); + CPPUNIT_TEST(testInflatePDU); + CPPUNIT_TEST_SUITE_END(); + + public: + void testDecodeHeader(); + void testInflatePDU(); + private: + static const uint8_t TEST_DATA[]; + static const uint8_t TEST_DATA2[]; +}; + +const uint8_t LLRPInflatorTest::TEST_DATA[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15}; +const uint8_t LLRPInflatorTest::TEST_DATA2[] = {10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, + 25}; +CPPUNIT_TEST_SUITE_REGISTRATION(LLRPInflatorTest); + + +/* + * Check that we can decode headers properly + */ +void LLRPInflatorTest::testDecodeHeader() { + LLRPHeader::llrp_pdu_header header; + memset(&header, 0, sizeof(header)); + LLRPInflator inflator; + HeaderSet header_set, header_set2; + unsigned int bytes_used; + const ola::acn::CID destination_cid = CID::FromData(TEST_DATA); + + destination_cid.Pack(header.destination_cid); + header.transaction_number = HostToNetwork(72650u); + + OLA_ASSERT(inflator.DecodeHeader(&header_set, + reinterpret_cast(&header), + sizeof(header), + &bytes_used)); + OLA_ASSERT_EQ((unsigned int) sizeof(header), bytes_used); + LLRPHeader decoded_header = header_set.GetLLRPHeader(); + OLA_ASSERT(destination_cid == decoded_header.DestinationCid()); + OLA_ASSERT_EQ((uint32_t) 72650, decoded_header.TransactionNumber()); + + // try an undersized header + OLA_ASSERT_FALSE(inflator.DecodeHeader( + &header_set, + reinterpret_cast(&header), + static_cast(sizeof(header) - 1), + &bytes_used)); + OLA_ASSERT_EQ((unsigned int) 0, bytes_used); + + // test inheriting the header from the prev call + OLA_ASSERT(inflator.DecodeHeader(&header_set2, NULL, 0, &bytes_used)); + OLA_ASSERT_EQ((unsigned int) 0, bytes_used); + decoded_header = header_set2.GetLLRPHeader(); + OLA_ASSERT(destination_cid == decoded_header.DestinationCid()); + OLA_ASSERT_EQ((uint32_t) 72650, decoded_header.TransactionNumber()); + + inflator.ResetHeaderField(); + OLA_ASSERT_FALSE(inflator.DecodeHeader(&header_set2, NULL, 0, &bytes_used)); + OLA_ASSERT_EQ((unsigned int) 0, bytes_used); +} + + +/* + * Check that we can inflate a LLRP PDU that contains other PDUs + */ +void LLRPInflatorTest::testInflatePDU() { + const ola::acn::CID destination_cid = CID::FromData(TEST_DATA2); + LLRPHeader header(destination_cid, 2370); + // TODO(Peter): pass a different type of msg here as well + LLRPPDU pdu(3, header, NULL); + OLA_ASSERT_EQ((unsigned int) 26, pdu.Size()); + + unsigned int size = pdu.Size(); + uint8_t *data = new uint8_t[size]; + unsigned int bytes_used = size; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ((unsigned int) size, bytes_used); + + LLRPInflator inflator; + HeaderSet header_set; + OLA_ASSERT(inflator.InflatePDUBlock(&header_set, data, size)); + OLA_ASSERT(header == header_set.GetLLRPHeader()); + delete[] data; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/LLRPPDU.cpp b/libs/acn/LLRPPDU.cpp new file mode 100644 index 0000000000..5d8eba531d --- /dev/null +++ b/libs/acn/LLRPPDU.cpp @@ -0,0 +1,117 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * LLRPPDU.cpp + * The LLRPPDU + * Copyright (C) 2020 Peter Newman + */ + + +#include "ola/Logging.h" +#include "ola/base/Array.h" +#include "ola/network/NetworkUtils.h" +#include "libs/acn/LLRPPDU.h" + +namespace ola { +namespace acn { + +using ola::io::OutputStream; +using ola::network::HostToNetwork; + +/* + * Size of the header portion. + */ +unsigned int LLRPPDU::HeaderSize() const { + return sizeof(LLRPHeader::llrp_pdu_header); +} + + +/* + * Size of the data portion + */ +unsigned int LLRPPDU::DataSize() const { + return m_pdu ? m_pdu->Size() : 0; +} + + +/* + * Pack the header portion. + */ +bool LLRPPDU::PackHeader(uint8_t *data, unsigned int *length) const { + unsigned int header_size = HeaderSize(); + + if (*length < header_size) { + OLA_WARN << "LLRPPDU::PackHeader: buffer too small, got " << *length + << " required " << header_size; + *length = 0; + return false; + } + + LLRPHeader::llrp_pdu_header header; + m_header.DestinationCid().Pack(header.destination_cid); + header.transaction_number = HostToNetwork(m_header.TransactionNumber()); + *length = sizeof(LLRPHeader::llrp_pdu_header); + memcpy(data, &header, *length); + return true; +} + + +/* + * Pack the data portion. + */ +bool LLRPPDU::PackData(uint8_t *data, unsigned int *length) const { + if (m_pdu) + return m_pdu->Pack(data, length); + *length = 0; + return true; +} + + +/* + * Pack the header into a buffer. + */ +void LLRPPDU::PackHeader(OutputStream *stream) const { + LLRPHeader::llrp_pdu_header header; + m_header.DestinationCid().Pack(header.destination_cid); + header.transaction_number = HostToNetwork(m_header.TransactionNumber()); + stream->Write(reinterpret_cast(&header), + sizeof(LLRPHeader::llrp_pdu_header)); +} + + +/* + * Pack the data into a buffer + */ +void LLRPPDU::PackData(OutputStream *stream) const { + if (m_pdu) + m_pdu->Write(stream); +} + + +void LLRPPDU::PrependPDU(ola::io::IOStack *stack, uint32_t vector, + const ola::acn::CID &destination_cid, uint32_t transaction_number) { + LLRPHeader::llrp_pdu_header header; + destination_cid.Pack(header.destination_cid); + header.transaction_number = HostToNetwork(transaction_number); + stack->Write(reinterpret_cast(&header), + sizeof(LLRPHeader::llrp_pdu_header)); + + vector = HostToNetwork(vector); + stack->Write(reinterpret_cast(&vector), sizeof(vector)); + // Flags for LLRP should always be 0xF0 + PrependFlagsAndLength(stack, 0xf0); +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/LLRPPDU.h b/libs/acn/LLRPPDU.h new file mode 100644 index 0000000000..76ccdc7d65 --- /dev/null +++ b/libs/acn/LLRPPDU.h @@ -0,0 +1,62 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * LLRPPDU.h + * Interface for the LLRPPDU class + * Copyright (C) 2020 Peter Newman + */ + +#ifndef LIBS_ACN_LLRPPDU_H_ +#define LIBS_ACN_LLRPPDU_H_ + +#include +#include + +#include "libs/acn/PDU.h" +#include "libs/acn/LLRPHeader.h" + +namespace ola { +namespace acn { + +class RDMPDU; + +class LLRPPDU: public PDU { + public: + LLRPPDU(unsigned int vector, + const LLRPHeader &header, + const PDU *pdu): + PDU(vector), + m_header(header), + m_pdu(pdu) {} + ~LLRPPDU() {} + + unsigned int HeaderSize() const; + unsigned int DataSize() const; + bool PackHeader(uint8_t *data, unsigned int *length) const; + bool PackData(uint8_t *data, unsigned int *length) const; + + void PackHeader(ola::io::OutputStream *stream) const; + void PackData(ola::io::OutputStream *stream) const; + + static void PrependPDU(ola::io::IOStack *stack, uint32_t vector, + const ola::acn::CID &destination_cid, uint32_t transaction_number); + + private: + LLRPHeader m_header; + const PDU *m_pdu; +}; +} // namespace acn +} // namespace ola +#endif // LIBS_ACN_LLRPPDU_H_ diff --git a/libs/acn/LLRPPDUTest.cpp b/libs/acn/LLRPPDUTest.cpp new file mode 100644 index 0000000000..b924d8a422 --- /dev/null +++ b/libs/acn/LLRPPDUTest.cpp @@ -0,0 +1,147 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * LLRPPDUTest.cpp + * Test fixture for the LLRPPDU class + * Copyright (C) 2020 Peter Newman + */ + +#include + +#include "ola/Logging.h" +#include "ola/io/IOQueue.h" +#include "ola/io/OutputStream.h" +#include "ola/network/NetworkUtils.h" +#include "ola/testing/TestUtils.h" +#include "libs/acn/LLRPPDU.h" +#include "libs/acn/PDUTestCommon.h" + + +namespace ola { +namespace acn { + +using ola::io::IOQueue; +using ola::io::OutputStream; +using ola::network::HostToNetwork; +using std::string; + +class LLRPPDUTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(LLRPPDUTest); + CPPUNIT_TEST(testSimpleLLRPPDU); + CPPUNIT_TEST(testSimpleLLRPPDUToOutputStream); + CPPUNIT_TEST_SUITE_END(); + + public: + void testSimpleLLRPPDU(); + void testSimpleLLRPPDUToOutputStream(); + + void setUp() { + ola::InitLogging(ola::OLA_LOG_DEBUG, ola::OLA_LOG_STDERR); + } + + private: + static const unsigned int TEST_VECTOR; + static const uint8_t TEST_DATA[]; +}; + +const uint8_t LLRPPDUTest::TEST_DATA[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15}; + +CPPUNIT_TEST_SUITE_REGISTRATION(LLRPPDUTest); + +const unsigned int LLRPPDUTest::TEST_VECTOR = 39; + + +/* + * Test that packing a LLRPPDU without data works. + */ +void LLRPPDUTest::testSimpleLLRPPDU() { + const CID destination_cid = CID::FromData(TEST_DATA); + LLRPHeader header(destination_cid, 101); + LLRPPDU pdu(TEST_VECTOR, header, NULL); + + OLA_ASSERT_EQ(20u, pdu.HeaderSize()); + OLA_ASSERT_EQ(0u, pdu.DataSize()); + OLA_ASSERT_EQ(26u, pdu.Size()); + + unsigned int size = pdu.Size(); + uint8_t *data = new uint8_t[size]; + unsigned int bytes_used = size; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(size, bytes_used); + + // spot check the data + OLA_ASSERT_EQ((uint8_t) 0x70, data[0]); + OLA_ASSERT_EQ((uint8_t) bytes_used, data[1]); + unsigned int actual_value; + memcpy(&actual_value, data + 2, sizeof(actual_value)); + OLA_ASSERT_EQ(HostToNetwork(TEST_VECTOR), actual_value); + + uint8_t buffer[CID::CID_LENGTH]; + destination_cid.Pack(buffer); + OLA_ASSERT_FALSE(memcmp(&data[6], buffer, CID::CID_LENGTH)); + // transaction number + OLA_ASSERT_EQ((uint8_t) 0, data[6 + CID::CID_LENGTH]); + OLA_ASSERT_EQ((uint8_t) 0, data[6 + CID::CID_LENGTH + 1]); + OLA_ASSERT_EQ((uint8_t) 0, data[6 + CID::CID_LENGTH + 2]); + OLA_ASSERT_EQ((uint8_t) 101, data[6 + CID::CID_LENGTH + 3]); + + // test undersized buffer + bytes_used = size - 1; + OLA_ASSERT_FALSE(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(0u, bytes_used); + + // test oversized buffer + bytes_used = size + 1; + OLA_ASSERT(pdu.Pack(data, &bytes_used)); + OLA_ASSERT_EQ(size, bytes_used); + delete[] data; +} + + +/* + * Test that writing to an output stream works. + */ +void LLRPPDUTest::testSimpleLLRPPDUToOutputStream() { + const ola::acn::CID destination_cid = CID::FromData(TEST_DATA); + LLRPHeader header(destination_cid, 101); + LLRPPDU pdu(TEST_VECTOR, header, NULL); + + OLA_ASSERT_EQ(20u, pdu.HeaderSize()); + OLA_ASSERT_EQ(0u, pdu.DataSize()); + OLA_ASSERT_EQ(26u, pdu.Size()); + + IOQueue output; + OutputStream stream(&output); + pdu.Write(&stream); + OLA_ASSERT_EQ(26u, output.Size()); + + uint8_t *pdu_data = new uint8_t[output.Size()]; + unsigned int pdu_size = output.Peek(pdu_data, output.Size()); + OLA_ASSERT_EQ(output.Size(), pdu_size); + + uint8_t EXPECTED[] = { + 0xf0, 0x1a, + 0, 0, 0, 39, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 0, 0, 0, 101, // transaction number + }; + OLA_ASSERT_DATA_EQUALS(EXPECTED, sizeof(EXPECTED), pdu_data, pdu_size); + output.Pop(output.Size()); + delete[] pdu_data; +} +} // namespace acn +} // namespace ola diff --git a/libs/acn/Makefile.mk b/libs/acn/Makefile.mk index 57bcf5276e..42d91ea17d 100644 --- a/libs/acn/Makefile.mk +++ b/libs/acn/Makefile.mk @@ -61,6 +61,11 @@ libs_acn_libolae131core_la_SOURCES = \ libs/acn/E133StatusPDU.cpp \ libs/acn/E133StatusPDU.h \ libs/acn/HeaderSet.h \ + libs/acn/LLRPHeader.h \ + libs/acn/LLRPInflator.cpp \ + libs/acn/LLRPInflator.h \ + libs/acn/LLRPPDU.cpp \ + libs/acn/LLRPPDU.h \ libs/acn/PDU.cpp \ libs/acn/PDU.h \ libs/acn/PDUTestCommon.h \ @@ -108,6 +113,7 @@ libs_acn_e131_loadtest_LDADD = libs/acn/libolae131core.la test_programs += \ libs/acn/E131Tester \ libs/acn/E133Tester \ + libs/acn/LLRPTester \ libs/acn/TransportTester libs_acn_E131Tester_SOURCES = \ @@ -140,6 +146,14 @@ libs_acn_E133Tester_LDADD = \ libs/acn/libolae131core.la \ $(COMMON_TESTING_LIBS) +libs_acn_LLRPTester_SOURCES = \ + libs/acn/LLRPInflatorTest.cpp \ + libs/acn/LLRPPDUTest.cpp +libs_acn_LLRPTester_CPPFLAGS = $(COMMON_TESTING_FLAGS) +libs_acn_LLRPTester_LDADD = \ + libs/acn/libolae131core.la \ + $(COMMON_TESTING_LIBS) + libs_acn_TransportTester_SOURCES = \ libs/acn/TCPTransportTest.cpp \ libs/acn/UDPTransportTest.cpp From bd89fea38e8547f2f71cc5f8f452acfbdabd2b30 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Mon, 3 Feb 2020 14:30:00 +0000 Subject: [PATCH 2/6] Update some E1.33 Vectors (cherry picked from commit 674ea511416bb3a8d955a1755d5251db6c53deeb) --- include/ola/acn/ACNVectors.h | 10 +++++++++- libs/acn/E133Inflator.h | 2 +- tools/e133/MessageBuilder.cpp | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/include/ola/acn/ACNVectors.h b/include/ola/acn/ACNVectors.h index 9e8211ff13..4e5a2bf520 100644 --- a/include/ola/acn/ACNVectors.h +++ b/include/ola/acn/ACNVectors.h @@ -45,7 +45,7 @@ namespace acn { enum RootVector { VECTOR_ROOT_E131_REV2 = 3, /**< Draft E1.31, used by some old gear. */ VECTOR_ROOT_E131 = 4, /**< E1.31 (sACN) */ - VECTOR_ROOT_E133 = 5, /**< E1.33 (RDNNet) */ + VECTOR_ROOT_RPT = 5, /**< E1.33 (RPT) */ VECTOR_ROOT_NULL = 6, /**< NULL (empty) root */ VECTOR_ROOT_BROKER = 9, /**< E1.33 (Broker) */ VECTOR_ROOT_LLRP = 0x0A, /**< E1.33 (LLRP) */ @@ -90,6 +90,14 @@ enum E133ControllerVector { VECTOR_CONTROLLER_EXPECT_MASTER = 5, /**< Expect master message */ }; +/** + * @brief Vectors used at the E1.33 LLRP layer. + */ +enum LLRPVector { + VECTOR_LLRP_PROBE_REQUEST = 1, /**< LLRP Probe Request */ + VECTOR_LLRP_PROBE_REPLY = 1, /**< LLRP Probe Reply */ + VECTOR_LLRP_RDM_CMD = 1, /**< LLRP RDM Command */ +}; /** * @} */ diff --git a/libs/acn/E133Inflator.h b/libs/acn/E133Inflator.h index ee7c968f66..d2f57daf66 100644 --- a/libs/acn/E133Inflator.h +++ b/libs/acn/E133Inflator.h @@ -38,7 +38,7 @@ class E133Inflator: public BaseInflator { } ~E133Inflator() {} - uint32_t Id() const { return ola::acn::VECTOR_ROOT_E133; } + uint32_t Id() const { return ola::acn::VECTOR_ROOT_RPT; } protected: bool DecodeHeader(HeaderSet *headers, diff --git a/tools/e133/MessageBuilder.cpp b/tools/e133/MessageBuilder.cpp index d2c6a5143a..a192b45bf0 100644 --- a/tools/e133/MessageBuilder.cpp +++ b/tools/e133/MessageBuilder.cpp @@ -109,7 +109,7 @@ void MessageBuilder::BuildTCPRootE133(IOStack *packet, uint16_t endpoint_id) { E133PDU::PrependPDU(packet, vector, m_source_name, sequence_number, endpoint_id); - RootPDU::PrependPDU(packet, ola::acn::VECTOR_ROOT_E133, m_cid); + RootPDU::PrependPDU(packet, ola::acn::VECTOR_ROOT_RPT, m_cid); PreamblePacker::AddTCPPreamble(packet); } @@ -123,7 +123,7 @@ void MessageBuilder::BuildUDPRootE133(IOStack *packet, uint16_t endpoint_id) { E133PDU::PrependPDU(packet, vector, m_source_name, sequence_number, endpoint_id); - RootPDU::PrependPDU(packet, ola::acn::VECTOR_ROOT_E133, m_cid); + RootPDU::PrependPDU(packet, ola::acn::VECTOR_ROOT_RPT, m_cid); PreamblePacker::AddUDPPreamble(packet); } } // namespace e133 From 8e7b75199ee0a2f70e01f3b96b350a901e5a1f72 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Tue, 4 Feb 2020 13:39:23 +0000 Subject: [PATCH 3/6] Tidy some Doxygen (cherry picked from commit f1cc481d6d900de8db5a64334fe7419b41bfffa4) --- include/ola/acn/ACNVectors.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/ola/acn/ACNVectors.h b/include/ola/acn/ACNVectors.h index 4e5a2bf520..fc08d266ff 100644 --- a/include/ola/acn/ACNVectors.h +++ b/include/ola/acn/ACNVectors.h @@ -98,6 +98,7 @@ enum LLRPVector { VECTOR_LLRP_PROBE_REPLY = 1, /**< LLRP Probe Reply */ VECTOR_LLRP_RDM_CMD = 1, /**< LLRP RDM Command */ }; + /** * @} */ From d9ef9fda716d0965db6e3dd865b975ba6cd198b6 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Tue, 4 Feb 2020 16:46:09 +0000 Subject: [PATCH 4/6] Fix the lint issues (cherry picked from commit 2d0ed43d1d351c696a8c5122c6c469d79bd2ee9f) --- libs/acn/LLRPPDU.cpp | 6 ++++-- libs/acn/LLRPPDUTest.cpp | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/acn/LLRPPDU.cpp b/libs/acn/LLRPPDU.cpp index 5d8eba531d..e13fcce225 100644 --- a/libs/acn/LLRPPDU.cpp +++ b/libs/acn/LLRPPDU.cpp @@ -100,8 +100,10 @@ void LLRPPDU::PackData(OutputStream *stream) const { } -void LLRPPDU::PrependPDU(ola::io::IOStack *stack, uint32_t vector, - const ola::acn::CID &destination_cid, uint32_t transaction_number) { +void LLRPPDU::PrependPDU(ola::io::IOStack *stack, + uint32_t vector, + const ola::acn::CID &destination_cid, + uint32_t transaction_number) { LLRPHeader::llrp_pdu_header header; destination_cid.Pack(header.destination_cid); header.transaction_number = HostToNetwork(transaction_number); diff --git a/libs/acn/LLRPPDUTest.cpp b/libs/acn/LLRPPDUTest.cpp index b924d8a422..af85b283e5 100644 --- a/libs/acn/LLRPPDUTest.cpp +++ b/libs/acn/LLRPPDUTest.cpp @@ -35,7 +35,6 @@ namespace acn { using ola::io::IOQueue; using ola::io::OutputStream; using ola::network::HostToNetwork; -using std::string; class LLRPPDUTest: public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(LLRPPDUTest); From ee0a5ab90f3dd6b3128a578dafd35072e4a73725 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Tue, 4 Feb 2020 15:35:15 +0000 Subject: [PATCH 5/6] Add LLRP Probe Request PDU, fix the length flags bug (cherry picked from commit e9dd3742f9310c0a72bccacfc45cfb9bacf115aa) --- include/ola/acn/ACNFlags.h | 70 +++++++++++++++++++++++++++++++++++ include/ola/acn/Makefile.mk | 1 + libs/acn/BaseInflator.cpp | 4 +- libs/acn/BaseInflator.h | 7 +++- libs/acn/BaseInflatorTest.cpp | 18 ++++----- libs/acn/LLRPInflatorTest.cpp | 2 +- libs/acn/LLRPPDU.cpp | 3 +- libs/acn/LLRPPDU.h | 8 ++-- libs/acn/LLRPPDUTest.cpp | 25 +++++++------ libs/acn/PDU.cpp | 32 ++++++++++++---- libs/acn/PDU.h | 37 +++++++++++++----- 11 files changed, 159 insertions(+), 48 deletions(-) create mode 100644 include/ola/acn/ACNFlags.h diff --git a/include/ola/acn/ACNFlags.h b/include/ola/acn/ACNFlags.h new file mode 100644 index 0000000000..a055a429b0 --- /dev/null +++ b/include/ola/acn/ACNFlags.h @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * ACNFlags.h + * Flags used in ACN PDUs + * Copyright (C) 2020 Peter Newman + */ + +#ifndef INCLUDE_OLA_ACN_ACNFLAGS_H_ +#define INCLUDE_OLA_ACN_ACNFLAGS_H_ + +/** + * @addtogroup acn + * @{ + * @file ACNFlags.h + * @brief ACN flag values. + * @} + */ + +#include + +namespace ola { +namespace acn { + +/** + * @addtogroup acn + * @{ + */ + +// masks for the flag fields +/** + * @brief This indicates a 20 bit length field (default is 12 bits) + */ +static const uint8_t LFLAG_MASK = 0x80; + +/** + * @brief This indicates a vector is present + */ +static const uint8_t VFLAG_MASK = 0x40; + +/** + * @brief This indicates a header field is present + */ +static const uint8_t HFLAG_MASK = 0x20; + +/** + * @brief This indicates a data field is present + */ +static const uint8_t DFLAG_MASK = 0x10; + +/** + * @} + */ + +} // namespace acn +} // namespace ola + +#endif // INCLUDE_OLA_ACN_ACNFLAGS_H_ diff --git a/include/ola/acn/Makefile.mk b/include/ola/acn/Makefile.mk index 56bc647eb5..afd628d812 100644 --- a/include/ola/acn/Makefile.mk +++ b/include/ola/acn/Makefile.mk @@ -3,6 +3,7 @@ olaacninclude_HEADERS = if INSTALL_ACN olaacninclude_HEADERS += \ + include/ola/acn/ACNFlags.h \ include/ola/acn/ACNPort.h \ include/ola/acn/ACNVectors.h \ include/ola/acn/CID.h diff --git a/libs/acn/BaseInflator.cpp b/libs/acn/BaseInflator.cpp index e3917967ad..a41dca370c 100644 --- a/libs/acn/BaseInflator.cpp +++ b/libs/acn/BaseInflator.cpp @@ -163,7 +163,7 @@ bool BaseInflator::DecodeLength(const uint8_t *data, bool BaseInflator::DecodeVector(uint8_t flags, const uint8_t *data, unsigned int length, uint32_t *vector, unsigned int *bytes_used) { - if (flags & PDU::VFLAG_MASK) { + if (flags & ola::acn::VFLAG_MASK) { if ((unsigned int) m_vector_size > length) { *vector = 0; *bytes_used = 0; @@ -223,7 +223,7 @@ bool BaseInflator::InflatePDU(HeaderSet *headers, return false; } - if (flags & PDU::HFLAG_MASK) { + if (flags & ola::acn::HFLAG_MASK) { result = DecodeHeader(headers, data + data_offset, pdu_len - data_offset, &header_bytes_used); diff --git a/libs/acn/BaseInflator.h b/libs/acn/BaseInflator.h index 384351bc28..07b7c6b88b 100644 --- a/libs/acn/BaseInflator.h +++ b/libs/acn/BaseInflator.h @@ -87,8 +87,11 @@ class BaseInflator : public InflatorInterface { unsigned int len); // masks for the flag fields - // This indicates a 20 bit length field (default is 12 bits) - static const uint8_t LFLAG_MASK = 0x80; + /** + * @brief This indicates a 20 bit length field (default is 12 bits) + * @deprecated Use ola::acn::LFLAG_MASK instead (4 Feb 2020). + */ + static const uint8_t LFLAG_MASK = ola::acn::LFLAG_MASK; // This masks the first 4 bits of the length field static const uint8_t LENGTH_MASK = 0x0F; diff --git a/libs/acn/BaseInflatorTest.cpp b/libs/acn/BaseInflatorTest.cpp index 17e86121b8..a4e0a0dd81 100644 --- a/libs/acn/BaseInflatorTest.cpp +++ b/libs/acn/BaseInflatorTest.cpp @@ -204,7 +204,7 @@ void BaseInflatorTest::testDecodeVector() { uint8_t data[] = {1, 2, 3, 4, 5, 6}; // the test data unsigned int vector = 1; unsigned int bytes_used = 0; - uint8_t flags = PDU::VFLAG_MASK; + uint8_t flags = VFLAG_MASK; OLA_ASSERT_FALSE(inflator.DecodeVector(flags, data, 0, &vector, &bytes_used)); OLA_ASSERT_EQ((unsigned int) 0, vector); @@ -235,7 +235,7 @@ void BaseInflatorTest::testDecodeVector() { } // now try with a vector size of 2 - flags = PDU::VFLAG_MASK; + flags = VFLAG_MASK; TestInflator inflator2(0, PDU::TWO_BYTES); for (unsigned int i = 0; i < 2; i++) { OLA_ASSERT_FALSE( @@ -270,7 +270,7 @@ void BaseInflatorTest::testDecodeVector() { } // now try with a vector size of 4 - flags = PDU::VFLAG_MASK; + flags = VFLAG_MASK; TestInflator inflator4(0, PDU::FOUR_BYTES); for (unsigned int i = 0; i < 4; i++) { OLA_ASSERT_FALSE( @@ -297,7 +297,7 @@ void BaseInflatorTest::testDecodeVector() { void BaseInflatorTest::testInflatePDU() { TestInflator inflator; // test with a vector size of 2 HeaderSet header_set; - uint8_t flags = PDU::VFLAG_MASK; + uint8_t flags = VFLAG_MASK; unsigned int data_size = static_cast(PDU::TWO_BYTES + sizeof(PDU_DATA)); uint8_t *data = new uint8_t[data_size]; @@ -324,7 +324,7 @@ void BaseInflatorTest::testInflatePDUBlock() { length_size + PDU::TWO_BYTES + sizeof(PDU_DATA)); uint8_t *data = new uint8_t[data_size]; // setup the vector - data[0] = PDU::VFLAG_MASK; + data[0] = VFLAG_MASK; data[1] = static_cast(data_size); data[2] = 0x01; data[3] = 0x21; @@ -337,14 +337,14 @@ void BaseInflatorTest::testInflatePDUBlock() { // inflate a multi-pdu block data = new uint8_t[2 * data_size]; - data[0] = PDU::VFLAG_MASK; + data[0] = VFLAG_MASK; data[1] = static_cast(data_size); data[2] = 0x01; data[3] = 0x21; memcpy(data + length_size + PDU::TWO_BYTES, PDU_DATA, sizeof(PDU_DATA)); - data[data_size] = PDU::VFLAG_MASK; + data[data_size] = VFLAG_MASK; data[data_size + 1] = static_cast(data_size); data[data_size + 2] = 0x01; data[data_size + 3] = 0x21; @@ -362,11 +362,11 @@ void BaseInflatorTest::testInflatePDUBlock() { unsigned int pdu_size = data_size + length_size + PDU::TWO_BYTES; data = new uint8_t[pdu_size]; - data[0] = PDU::VFLAG_MASK; + data[0] = VFLAG_MASK; data[1] = static_cast(pdu_size); data[2] = 0x01; data[3] = 0x21; - data[4] = PDU::VFLAG_MASK; + data[4] = VFLAG_MASK; data[5] = static_cast(data_size); data[6] = 0x01; data[7] = 0x21; diff --git a/libs/acn/LLRPInflatorTest.cpp b/libs/acn/LLRPInflatorTest.cpp index b36c5cdc05..16bcd96431 100644 --- a/libs/acn/LLRPInflatorTest.cpp +++ b/libs/acn/LLRPInflatorTest.cpp @@ -108,7 +108,7 @@ void LLRPInflatorTest::testInflatePDU() { LLRPHeader header(destination_cid, 2370); // TODO(Peter): pass a different type of msg here as well LLRPPDU pdu(3, header, NULL); - OLA_ASSERT_EQ((unsigned int) 26, pdu.Size()); + OLA_ASSERT_EQ((unsigned int) 27, pdu.Size()); unsigned int size = pdu.Size(); uint8_t *data = new uint8_t[size]; diff --git a/libs/acn/LLRPPDU.cpp b/libs/acn/LLRPPDU.cpp index e13fcce225..4fb3b3644f 100644 --- a/libs/acn/LLRPPDU.cpp +++ b/libs/acn/LLRPPDU.cpp @@ -112,8 +112,7 @@ void LLRPPDU::PrependPDU(ola::io::IOStack *stack, vector = HostToNetwork(vector); stack->Write(reinterpret_cast(&vector), sizeof(vector)); - // Flags for LLRP should always be 0xF0 - PrependFlagsAndLength(stack, 0xf0); + PrependFlagsAndLength(stack, VFLAG_MASK | HFLAG_MASK | DFLAG_MASK, true); } } // namespace acn } // namespace ola diff --git a/libs/acn/LLRPPDU.h b/libs/acn/LLRPPDU.h index 76ccdc7d65..a2636c1a9c 100644 --- a/libs/acn/LLRPPDU.h +++ b/libs/acn/LLRPPDU.h @@ -37,7 +37,7 @@ class LLRPPDU: public PDU { LLRPPDU(unsigned int vector, const LLRPHeader &header, const PDU *pdu): - PDU(vector), + PDU(vector, FOUR_BYTES, true), m_header(header), m_pdu(pdu) {} ~LLRPPDU() {} @@ -50,8 +50,10 @@ class LLRPPDU: public PDU { void PackHeader(ola::io::OutputStream *stream) const; void PackData(ola::io::OutputStream *stream) const; - static void PrependPDU(ola::io::IOStack *stack, uint32_t vector, - const ola::acn::CID &destination_cid, uint32_t transaction_number); + static void PrependPDU(ola::io::IOStack *stack, + uint32_t vector, + const ola::acn::CID &destination_cid, + uint32_t transaction_number); private: LLRPHeader m_header; diff --git a/libs/acn/LLRPPDUTest.cpp b/libs/acn/LLRPPDUTest.cpp index af85b283e5..01c30db22a 100644 --- a/libs/acn/LLRPPDUTest.cpp +++ b/libs/acn/LLRPPDUTest.cpp @@ -73,7 +73,7 @@ void LLRPPDUTest::testSimpleLLRPPDU() { OLA_ASSERT_EQ(20u, pdu.HeaderSize()); OLA_ASSERT_EQ(0u, pdu.DataSize()); - OLA_ASSERT_EQ(26u, pdu.Size()); + OLA_ASSERT_EQ(27u, pdu.Size()); unsigned int size = pdu.Size(); uint8_t *data = new uint8_t[size]; @@ -82,20 +82,21 @@ void LLRPPDUTest::testSimpleLLRPPDU() { OLA_ASSERT_EQ(size, bytes_used); // spot check the data - OLA_ASSERT_EQ((uint8_t) 0x70, data[0]); - OLA_ASSERT_EQ((uint8_t) bytes_used, data[1]); + OLA_ASSERT_EQ((uint8_t) 0xf0, data[0]); + // bytes_used is technically data[1] and data[2] if > 255 + OLA_ASSERT_EQ((uint8_t) bytes_used, data[2]); unsigned int actual_value; - memcpy(&actual_value, data + 2, sizeof(actual_value)); + memcpy(&actual_value, data + 3, sizeof(actual_value)); OLA_ASSERT_EQ(HostToNetwork(TEST_VECTOR), actual_value); uint8_t buffer[CID::CID_LENGTH]; destination_cid.Pack(buffer); - OLA_ASSERT_FALSE(memcmp(&data[6], buffer, CID::CID_LENGTH)); + OLA_ASSERT_DATA_EQUALS(&data[7], CID::CID_LENGTH, buffer, sizeof(buffer)); // transaction number - OLA_ASSERT_EQ((uint8_t) 0, data[6 + CID::CID_LENGTH]); - OLA_ASSERT_EQ((uint8_t) 0, data[6 + CID::CID_LENGTH + 1]); - OLA_ASSERT_EQ((uint8_t) 0, data[6 + CID::CID_LENGTH + 2]); - OLA_ASSERT_EQ((uint8_t) 101, data[6 + CID::CID_LENGTH + 3]); + OLA_ASSERT_EQ((uint8_t) 0, data[7 + CID::CID_LENGTH]); + OLA_ASSERT_EQ((uint8_t) 0, data[7 + CID::CID_LENGTH + 1]); + OLA_ASSERT_EQ((uint8_t) 0, data[7 + CID::CID_LENGTH + 2]); + OLA_ASSERT_EQ((uint8_t) 101, data[7 + CID::CID_LENGTH + 3]); // test undersized buffer bytes_used = size - 1; @@ -120,19 +121,19 @@ void LLRPPDUTest::testSimpleLLRPPDUToOutputStream() { OLA_ASSERT_EQ(20u, pdu.HeaderSize()); OLA_ASSERT_EQ(0u, pdu.DataSize()); - OLA_ASSERT_EQ(26u, pdu.Size()); + OLA_ASSERT_EQ(27u, pdu.Size()); IOQueue output; OutputStream stream(&output); pdu.Write(&stream); - OLA_ASSERT_EQ(26u, output.Size()); + OLA_ASSERT_EQ(27u, output.Size()); uint8_t *pdu_data = new uint8_t[output.Size()]; unsigned int pdu_size = output.Peek(pdu_data, output.Size()); OLA_ASSERT_EQ(output.Size(), pdu_size); uint8_t EXPECTED[] = { - 0xf0, 0x1a, + 0xf0, 0x00, 0x1b, 0, 0, 0, 39, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, diff --git a/libs/acn/PDU.cpp b/libs/acn/PDU.cpp index c9166db6cb..0f256eca13 100644 --- a/libs/acn/PDU.cpp +++ b/libs/acn/PDU.cpp @@ -35,8 +35,9 @@ using ola::network::HostToNetwork; unsigned int PDU::Size() const { unsigned int length = m_vector_size + HeaderSize() + DataSize(); - if (length > TWOB_LENGTH_LIMIT - 2) + if ((length > TWOB_LENGTH_LIMIT - 2) || m_force_length_flag) { length += 1; + } length += 2; return length; } @@ -59,7 +60,7 @@ bool PDU::Pack(uint8_t *buffer, unsigned int *length) const { return false; } - if (size <= TWOB_LENGTH_LIMIT) { + if (size <= TWOB_LENGTH_LIMIT && !m_force_length_flag) { buffer[0] = (uint8_t) ((size & 0x0f00) >> 8); buffer[1] = (uint8_t) (size & 0xff); } else { @@ -69,6 +70,11 @@ bool PDU::Pack(uint8_t *buffer, unsigned int *length) const { offset += 1; } + if (m_force_length_flag) { + // TODO(Peter): Should this happen regardless of the force when we're + // writing 20 bits of length? + buffer[0] |= LFLAG_MASK; + } buffer[0] |= VFLAG_MASK; buffer[0] |= HFLAG_MASK; buffer[0] |= DFLAG_MASK; @@ -117,13 +123,18 @@ bool PDU::Pack(uint8_t *buffer, unsigned int *length) const { void PDU::Write(OutputStream *stream) const { unsigned int size = Size(); - if (size <= TWOB_LENGTH_LIMIT) { + if (size <= TWOB_LENGTH_LIMIT && !m_force_length_flag) { uint16_t flags_and_length = static_cast(size); flags_and_length |= (VFLAG_MASK | HFLAG_MASK | DFLAG_MASK) << 8u; *stream << HostToNetwork(flags_and_length); } else { uint8_t vhl_flags = static_cast((size & 0x0f0000) >> 16); vhl_flags |= VFLAG_MASK | HFLAG_MASK | DFLAG_MASK; + if (m_force_length_flag) { + // TODO(Peter): Should this happen regardless of the force as we're + // writing 20 bits of length? + vhl_flags |= LFLAG_MASK; + } *stream << vhl_flags; *stream << (uint8_t) ((size & 0xff00) >> 8); *stream << (uint8_t) (size & 0xff); @@ -150,8 +161,9 @@ void PDU::Write(OutputStream *stream) const { * Prepend the flags and length to an OutputBufferInterface. */ void PDU::PrependFlagsAndLength(ola::io::OutputBufferInterface *output, - uint8_t flags) { - PrependFlagsAndLength(output, output->Size(), flags); + uint8_t flags, + bool force_length_flag) { + PrependFlagsAndLength(output, output->Size(), flags, force_length_flag); } @@ -160,8 +172,9 @@ void PDU::PrependFlagsAndLength(ola::io::OutputBufferInterface *output, */ void PDU::PrependFlagsAndLength(ola::io::OutputBufferInterface *output, unsigned int size, - uint8_t flags) { - if (size + 2 <= TWOB_LENGTH_LIMIT) { + uint8_t flags, + bool force_length_flag) { + if (size + 2 <= TWOB_LENGTH_LIMIT && !force_length_flag) { size += 2; uint16_t flags_and_length = static_cast(size); flags_and_length |= static_cast(flags << 8u); @@ -173,6 +186,11 @@ void PDU::PrependFlagsAndLength(ola::io::OutputBufferInterface *output, uint8_t flags_and_length[3]; flags_and_length[0] = static_cast((size & 0x0f0000) >> 16); flags_and_length[0] |= flags; + if (force_length_flag) { + // TODO(Peter): Should this happen regardless of the force as we're + // writing 20 bits of length? + flags_and_length[0] |= LFLAG_MASK; + } flags_and_length[1] = static_cast((size & 0xff00) >> 8); flags_and_length[2] = static_cast(size & 0xff); output->Write(flags_and_length, sizeof(flags_and_length)); diff --git a/libs/acn/PDU.h b/libs/acn/PDU.h index 64c1e82adb..2be285dead 100644 --- a/libs/acn/PDU.h +++ b/libs/acn/PDU.h @@ -22,6 +22,7 @@ #define LIBS_ACN_PDU_H_ #include +#include #include #include #include @@ -41,9 +42,12 @@ class PDU { FOUR_BYTES = 4, } vector_size; - explicit PDU(unsigned int vector, vector_size size = FOUR_BYTES): + explicit PDU(unsigned int vector, + vector_size size = FOUR_BYTES, + bool force_length_flag = false): m_vector(vector), - m_vector_size(size) {} + m_vector_size(size), + m_force_length_flag(force_length_flag) {} virtual ~PDU() {} // Returns the size of this PDU @@ -72,23 +76,36 @@ class PDU { static void PrependFlagsAndLength( ola::io::OutputBufferInterface *output, - uint8_t flags = VFLAG_MASK | HFLAG_MASK | DFLAG_MASK); + uint8_t flags = VFLAG_MASK | HFLAG_MASK | DFLAG_MASK, + bool force_length_flag = false); static void PrependFlagsAndLength( ola::io::OutputBufferInterface *output, unsigned int length, - uint8_t flags); + uint8_t flags, + bool force_length_flag = false); + + /** + * @brief This indicates a vector is present. + * @deprecated Use ola::acn::VFLAG_MASK instead (4 Feb 2020). + */ + static const uint8_t VFLAG_MASK = ola::acn::VFLAG_MASK; + /** + * @brief This indicates a header field is present. + * @deprecated Use ola::acn::HFLAG_MASK instead (4 Feb 2020). + */ + static const uint8_t HFLAG_MASK = ola::acn::HFLAG_MASK; + /** + * @brief This indicates a data field is present. + * @deprecated Use ola::acn::DFLAG_MASK instead (4 Feb 2020). + */ + static const uint8_t DFLAG_MASK = ola::acn::DFLAG_MASK; - // This indicates a vector is present - static const uint8_t VFLAG_MASK = 0x40; - // This indicates a header field is present - static const uint8_t HFLAG_MASK = 0x20; - // This indicates a data field is present - static const uint8_t DFLAG_MASK = 0x10; private: unsigned int m_vector; unsigned int m_vector_size; + bool m_force_length_flag; // The max PDU length that can be represented with the 2 byte format for // the length field. From cb1d8f769dc61a645ea231886a6a1ef760af43bb Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Sat, 16 Mar 2024 12:38:09 +0000 Subject: [PATCH 6/6] Improve clarity with more braces --- libs/acn/LLRPHeader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/acn/LLRPHeader.h b/libs/acn/LLRPHeader.h index 1d964268fd..c0761cd5c0 100644 --- a/libs/acn/LLRPHeader.h +++ b/libs/acn/LLRPHeader.h @@ -49,8 +49,8 @@ class LLRPHeader { uint32_t TransactionNumber() const { return m_transaction_number; } bool operator==(const LLRPHeader &other) const { - return m_destination_cid == other.m_destination_cid && - m_transaction_number == other.m_transaction_number; + return ((m_destination_cid == other.m_destination_cid) && + (m_transaction_number == other.m_transaction_number)); } PACK(