From 1380530f09516c64577e1fe5ab5b474485c645ae Mon Sep 17 00:00:00 2001 From: Trevor Holbrook Date: Mon, 9 Nov 2020 18:58:56 +0000 Subject: [PATCH] First commit for BDX message utilities - establish directory for BDX - add BDXMessageUtils files - add unit tests for BDXMessageUtils - edit BUILD.gn files to build BDX directory and tests --- src/BUILD.gn | 1 + src/transport/BUILD.gn | 1 + src/transport/bdx/BDXMessageUtils.cpp | 463 ++++++++++++++++++ src/transport/bdx/BDXMessageUtils.h | 204 ++++++++ src/transport/bdx/BUILD.gn | 32 ++ src/transport/bdx/tests/BUILD.gn | 41 ++ src/transport/bdx/tests/TestBDX.h | 6 + .../bdx/tests/TestBDXMessageUtils.cpp | 145 ++++++ .../bdx/tests/TestBDXMessageUtilsDriver.cpp | 10 + 9 files changed, 903 insertions(+) create mode 100644 src/transport/bdx/BDXMessageUtils.cpp create mode 100644 src/transport/bdx/BDXMessageUtils.h create mode 100644 src/transport/bdx/BUILD.gn create mode 100644 src/transport/bdx/tests/BUILD.gn create mode 100644 src/transport/bdx/tests/TestBDX.h create mode 100644 src/transport/bdx/tests/TestBDXMessageUtils.cpp create mode 100644 src/transport/bdx/tests/TestBDXMessageUtilsDriver.cpp diff --git a/src/BUILD.gn b/src/BUILD.gn index 9d78b3f76ae749..adf166db220fb5 100644 --- a/src/BUILD.gn +++ b/src/BUILD.gn @@ -54,6 +54,7 @@ if (chip_build_tests) { "${chip_root}/src/protocols/bdx/tests", "${chip_root}/src/setup_payload/tests", "${chip_root}/src/system/tests", + "${chip_root}/src/transport/bdx/tests", "${chip_root}/src/transport/raw/tests", "${chip_root}/src/transport/retransmit/tests", "${chip_root}/src/transport/tests", diff --git a/src/transport/BUILD.gn b/src/transport/BUILD.gn index e0f63841861661..a281cece5fbd61 100644 --- a/src/transport/BUILD.gn +++ b/src/transport/BUILD.gn @@ -59,6 +59,7 @@ static_library("transport") { "${chip_root}/src/lib/mdns", "${chip_root}/src/lib/support", "${chip_root}/src/platform", + "${chip_root}/src/transport/bdx", "${chip_root}/src/transport/raw", ] } diff --git a/src/transport/bdx/BDXMessageUtils.cpp b/src/transport/bdx/BDXMessageUtils.cpp new file mode 100644 index 00000000000000..548e8e00ed6984 --- /dev/null +++ b/src/transport/bdx/BDXMessageUtils.cpp @@ -0,0 +1,463 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * Implements utility methods for working with some complex BDX messages. + */ + +#include + +#include +#include +#include + +#define VERSION_MASK 0x0F +#define SENDER_DRIVE_MASK 0x10 +#define RECEIVER_DRIVE_MASK 0x20 +#define ASYNC_MASK 0x40 +#define CONTROL_MODE_MASK 0xF0 + +#define DEFLEN_MASK 0x01 +#define START_OFFSET_MASK 0x02 +#define WIDERANGE_MASK 0x10 + +using namespace chip; +using namespace chip::BDX; +using namespace chip::Encoding::LittleEndian; + +CHIP_ERROR TransferInit::Pack(System::PacketBuffer * aBuffer) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + uint8_t rangeCtl = 0, ptcByte = 0; + + // BufBound has to be initialized with aBuffer so VerifyOrExit can't be used here. + if (aBuffer == NULL) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + BufBound bbuf(aBuffer->Start(), aBuffer->AvailableDataLength()); + + ptcByte |= mSupportedVersions & VERSION_MASK; + if (mSupportsSenderDrive) + ptcByte |= SENDER_DRIVE_MASK; + if (mSupportsReceiverDrive) + ptcByte |= RECEIVER_DRIVE_MASK; + if (mSupportsAsync) + ptcByte |= ASYNC_MASK; + + bbuf.Put(ptcByte); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + if (mDefLen) + rangeCtl |= DEFLEN_MASK; + if (mStartOffset) + rangeCtl |= START_OFFSET_MASK; + if (mWideRange) + rangeCtl |= WIDERANGE_MASK; + + bbuf.Put(rangeCtl); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + bbuf.PutLE16(mMaxBlockSize); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + if (mStartOffset > 0) + { + if (mWideRange) + { + bbuf.PutLE64(mStartOffset); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + } + else + { + bbuf.PutLE32((uint32_t) mStartOffset); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + } + } + + if (mDefLen) + { + if (mWideRange) + { + bbuf.PutLE64(mMaxLength); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + } + else + { + bbuf.PutLE32((uint32_t) mMaxLength); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + } + } + + VerifyOrExit(mFileDesignator && mFileDesLength > 0, err = CHIP_ERROR_INVALID_ARGUMENT); + bbuf.PutLE16(mFileDesLength); + bbuf.Put(mFileDesignator, (size_t) mFileDesLength); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + // Metadata is optional + if (mMetadata && mMetadataLength > 0) + { + bbuf.Put(mMetadata, (size_t) mMetadataLength); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + } + + aBuffer->SetDataLength(static_cast(bbuf.Written())); + +exit: + return err; +} + +CHIP_ERROR TransferInit::Parse(System::PacketBuffer * aBuffer, TransferInit & aParsedMessage) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + uint8_t ptcByte; + uint8_t rangeCtl; + bool hasStartOffset = false; + uint32_t tmpUint32Value = 0; + uint8_t * bufStart; + + // BufBound has to be initialized with aBuffer so VerifyOrExit can't be used here. + if (aBuffer == NULL) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + bufStart = aBuffer->Start(); + Reader bufReader(bufStart, aBuffer->DataLength()); + + err = bufReader.Read8(&ptcByte); + SuccessOrExit(err); + + aParsedMessage.mSupportedVersions = ptcByte & VERSION_MASK; + aParsedMessage.mSupportsSenderDrive = ((ptcByte & SENDER_DRIVE_MASK) != 0); + aParsedMessage.mSupportsReceiverDrive = ((ptcByte & RECEIVER_DRIVE_MASK) != 0); + aParsedMessage.mSupportsAsync = ((ptcByte & ASYNC_MASK) != 0); + + err = bufReader.Read8(&rangeCtl); + SuccessOrExit(err); + + aParsedMessage.mDefLen = (rangeCtl & DEFLEN_MASK) != 0; + hasStartOffset = (rangeCtl & START_OFFSET_MASK) != 0; + aParsedMessage.mWideRange = (rangeCtl & WIDERANGE_MASK) != 0; + + err = bufReader.Read16(&aParsedMessage.mMaxBlockSize); + SuccessOrExit(err); + + if (hasStartOffset) + { + if (aParsedMessage.mWideRange) + { + err = bufReader.Read64(&aParsedMessage.mStartOffset); + } + else + { + err = bufReader.Read32(&tmpUint32Value); + aParsedMessage.mStartOffset = tmpUint32Value; + } + + SuccessOrExit(err); + } + + if (aParsedMessage.mDefLen) + { + if (aParsedMessage.mWideRange) + { + err = bufReader.Read64(&aParsedMessage.mMaxLength); + } + else + { + err = bufReader.Read32(&tmpUint32Value); + aParsedMessage.mMaxLength = tmpUint32Value; + } + + SuccessOrExit(err); + } + + err = bufReader.Read16(&aParsedMessage.mFileDesLength); + SuccessOrExit(err); + + VerifyOrExit(bufReader.OctetsRead() + aParsedMessage.mFileDesLength <= aBuffer->DataLength(), + err = CHIP_ERROR_MESSAGE_INCOMPLETE); + aParsedMessage.mFileDesignator = &bufStart[bufReader.OctetsRead()]; + + // Rest of message is metadata (could be empty) + if (bufReader.OctetsRead() + aParsedMessage.mFileDesLength < aBuffer->DataLength()) + { + uint16_t metadataStartIndex = (uint16_t)(bufReader.OctetsRead() + aParsedMessage.mFileDesLength); + aParsedMessage.mMetadata = &bufStart[metadataStartIndex]; + aParsedMessage.mMetadataLength = (uint16_t)(aBuffer->DataLength() - metadataStartIndex); + } + +exit: + return err; +} + +bool TransferInit::operator==(const TransferInit & another) const +{ + bool fileDesMatches = memcmp(mFileDesignator, another.mFileDesignator, mFileDesLength) == 0; + bool metadataMatches = memcmp(mMetadata, another.mMetadata, mMetadataLength) == 0; + + return (mSupportedVersions == another.mSupportedVersions && mSupportsSenderDrive == another.mSupportsSenderDrive && + mSupportsReceiverDrive == another.mSupportsReceiverDrive && mSupportsAsync == another.mSupportsAsync && + mWideRange == another.mWideRange && mStartOffset == another.mStartOffset && mDefLen == another.mDefLen && + mMaxLength == another.mMaxLength && mMaxBlockSize == another.mMaxBlockSize && fileDesMatches && metadataMatches); +} + +CHIP_ERROR SendAccept::Pack(System::PacketBuffer * aBuffer) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + uint8_t tcByte = 0; + + // BufBound has to be initialized with aBuffer so VerifyOrExit can't be used here. + if (aBuffer == NULL) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + BufBound bbuf(aBuffer->Start(), aBuffer->AvailableDataLength()); + + tcByte |= mVersion & VERSION_MASK; + tcByte |= mControlMode; + bbuf.Put(tcByte); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + bbuf.PutLE16(mMaxBlockSize); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + bbuf.Put(mMetadata, (size_t) mMetadataLength); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + aBuffer->SetDataLength(static_cast(bbuf.Written())); + +exit: + return err; +} + +CHIP_ERROR SendAccept::Parse(System::PacketBuffer * aBuffer, SendAccept & aParsedMessage) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + uint8_t tcByte = 0; + uint8_t tmpControlMode = 0; + uint8_t * bufStart = NULL; + + // BufBound has to be initialized with aBuffer so VerifyOrExit can't be used here. + if (aBuffer == NULL) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + bufStart = aBuffer->Start(); + Reader bufReader(bufStart, aBuffer->DataLength()); + + err = bufReader.Read8(&tcByte); + SuccessOrExit(err); + + aParsedMessage.mVersion = tcByte & VERSION_MASK; + tmpControlMode = tcByte & CONTROL_MODE_MASK; + + // Determine transfer control mode. Only one mode should be selected. + // TODO: should this verification happen here? + VerifyOrExit(tmpControlMode == kSenderDrive || tmpControlMode == kReceiverDrive || tmpControlMode == kAsync, + err = CHIP_ERROR_MESSAGE_INCOMPLETE); + aParsedMessage.mControlMode = static_cast(tmpControlMode); + + err = bufReader.Read16(&aParsedMessage.mMaxBlockSize); + SuccessOrExit(err); + + // Rest of message is metadata (could be empty) + if (bufReader.OctetsRead() < aBuffer->DataLength()) + { + aParsedMessage.mMetadata = &bufStart[bufReader.OctetsRead()]; + aParsedMessage.mMetadataLength = (uint16_t)(aBuffer->DataLength() - bufReader.OctetsRead()); + } + +exit: + return err; +} + +bool SendAccept::operator==(const SendAccept & another) const +{ + bool metadataMatches = memcmp(mMetadata, another.mMetadata, mMetadataLength) == 0; + + return (mVersion == another.mVersion && mControlMode == another.mControlMode && mMaxBlockSize == another.mMaxBlockSize && + metadataMatches); +} + +CHIP_ERROR ReceiveAccept::Pack(System::PacketBuffer * aBuffer) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + uint8_t tcByte = 0; + uint8_t rangeCtl = 0; + + // BufBound has to be initialized with aBuffer so VerifyOrExit can't be used here. + if (aBuffer == NULL) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + BufBound bbuf(aBuffer->Start(), aBuffer->AvailableDataLength()); + + tcByte |= mVersion & VERSION_MASK; + tcByte |= mControlMode; + bbuf.Put(tcByte); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + if (mDefLen) + rangeCtl |= DEFLEN_MASK; + if (mStartOffset) + rangeCtl |= START_OFFSET_MASK; + if (mWideRange) + rangeCtl |= WIDERANGE_MASK; + + bbuf.Put(rangeCtl); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + bbuf.PutLE16(mMaxBlockSize); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + if (mStartOffset > 0) + { + if (mWideRange) + { + bbuf.PutLE64(mStartOffset); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + } + else + { + bbuf.PutLE32((uint32_t) mStartOffset); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + } + } + + if (mDefLen) + { + if (mWideRange) + { + bbuf.PutLE64(mLength); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + } + else + { + bbuf.PutLE32((uint32_t) mLength); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + } + } + + bbuf.Put(mMetadata, (size_t) mMetadataLength); + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + aBuffer->SetDataLength(static_cast(bbuf.Written())); + +exit: + return err; +} + +CHIP_ERROR ReceiveAccept::Parse(System::PacketBuffer * aBuffer, ReceiveAccept & aParsedMessage) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + bool hasStartOffset = false; + uint8_t tcByte = 0; + uint8_t rangeCtl = 0; + uint8_t tmpControlMode = 0; + uint8_t * bufStart = NULL; + uint32_t tmpUint32Value = 0; + + // BufBound has to be initialized with aBuffer so VerifyOrExit can't be used here. + if (aBuffer == NULL) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + bufStart = aBuffer->Start(); + Reader bufReader(bufStart, aBuffer->DataLength()); + + err = bufReader.Read8(&tcByte); + SuccessOrExit(err); + + aParsedMessage.mVersion = tcByte & VERSION_MASK; + tmpControlMode = tcByte & CONTROL_MODE_MASK; + + // Determine transfer control mode. Only one mode should be selected. + // TODO: should this verification happen here + VerifyOrExit(tmpControlMode == kSenderDrive || tmpControlMode == kReceiverDrive || tmpControlMode == kAsync, + err = CHIP_ERROR_MESSAGE_INCOMPLETE); + aParsedMessage.mControlMode = static_cast(tmpControlMode); + + err = bufReader.Read8(&rangeCtl); + SuccessOrExit(err); + + aParsedMessage.mDefLen = (rangeCtl & DEFLEN_MASK) != 0; + hasStartOffset = (rangeCtl & START_OFFSET_MASK) != 0; + aParsedMessage.mWideRange = (rangeCtl & WIDERANGE_MASK) != 0; + + err = bufReader.Read16(&aParsedMessage.mMaxBlockSize); + SuccessOrExit(err); + + if (hasStartOffset) + { + if (aParsedMessage.mWideRange) + { + err = bufReader.Read64(&aParsedMessage.mStartOffset); + } + else + { + err = bufReader.Read32(&tmpUint32Value); + aParsedMessage.mStartOffset = tmpUint32Value; + } + + SuccessOrExit(err); + } + + if (aParsedMessage.mDefLen) + { + if (aParsedMessage.mWideRange) + { + err = bufReader.Read64(&aParsedMessage.mLength); + } + else + { + err = bufReader.Read32(&tmpUint32Value); + aParsedMessage.mLength = tmpUint32Value; + } + + SuccessOrExit(err); + } + + // Rest of message is metadata (could be empty) + if (bufReader.OctetsRead() < aBuffer->DataLength()) + { + aParsedMessage.mMetadata = &bufStart[bufReader.OctetsRead()]; + aParsedMessage.mMetadataLength = (uint16_t)(aBuffer->DataLength() - bufReader.OctetsRead()); + } + + printf("read metadata\n"); + +exit: + return err; +} + +bool ReceiveAccept::operator==(const ReceiveAccept & another) const +{ + bool metadataMatches = memcmp(mMetadata, another.mMetadata, mMetadataLength) == 0; + + return (mVersion == another.mVersion && mControlMode == another.mControlMode && mWideRange == another.mWideRange && + mStartOffset == another.mStartOffset && mDefLen == another.mDefLen && mMaxBlockSize == another.mMaxBlockSize && + mLength == another.mLength && metadataMatches); +} diff --git a/src/transport/bdx/BDXMessageUtils.h b/src/transport/bdx/BDXMessageUtils.h new file mode 100644 index 00000000000000..226434c1956924 --- /dev/null +++ b/src/transport/bdx/BDXMessageUtils.h @@ -0,0 +1,204 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines structures and utility methods for working with BDX + * messages. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace BDX { + +enum ControlMode : uint8_t +{ + kNotSpecified = 0x00, + kSenderDrive = 0x10, + kReceiverDrive = 0x20, + kAsync = 0x40, +}; + +/* + * A structure for representing a SendInit or ReceiveInit message (both contain + * identical parameters). + */ +struct TransferInit +{ +public: + /** + * @brief + * Pack (write) the message into a PacketBuffer. + * + * @param[out] aBuffer a PacketBuffer to use to write the message + * + * @return CHIP_ERROR Any error that occurs when trying to write to the PacketBuffer + */ + CHIP_ERROR Pack(System::PacketBuffer * aBuffer); + + /** + * @brief + * Parse data from an PacketBuffer into a struct instance + * + * @param[in] aBuffer Pointer to a PacketBuffer containing the data + * @param[out] aParsedMessage Reference to a struct instance where results will be stored + * + * @return CHIP_ERROR Any error that occurs when trying to read the message + */ + static CHIP_ERROR Parse(System::PacketBuffer * aBuffer, TransferInit & aParsedMessage); + + /** + * @brief + * Equality check method. + */ + bool operator==(const TransferInit &) const; + + // Proposed Transfer Control (required) + bool mSupportsAsync; + bool mSupportsReceiverDrive; + bool mSupportsSenderDrive; + uint8_t mSupportedVersions; + + // Range Control (required) + bool mWideRange; ///< Set true to indicate mStartOffset and mDefLen are 64-bit, else 32-bit. + uint64_t mStartOffset; ///< Proposed start offset of data. 0 for no offset. + bool mDefLen; ///< True if transfer has a definite length. + + uint64_t mMaxLength; ///< Proposed max length of data in transfer, 0 for indefinite (required if mDefLen is set) + uint16_t mMaxBlockSize; ///< Proposed max block size to use in transfer (required) + + // File designator (required) + uint8_t * mFileDesignator; + uint16_t mFileDesLength; ///< Length of file designator string (not including null-terminator) + + // Additional metadata (optional, TLV format) + uint8_t * mMetadata; + uint16_t mMetadataLength; +}; + +struct SendInit : public TransferInit +{ +}; + +struct ReceiveInit : public TransferInit +{ +}; + +/* + * A structure for representing a SendAccept message. + */ +struct SendAccept +{ +public: + /** + * @brief + * Pack (write) the message into a PacketBuffer. + * + * @param[out] aBuffer a PacketBuffer to use to write the message + * + * @return CHIP_ERROR Any error that occurs when trying to write to the PacketBuffer + */ + CHIP_ERROR Pack(System::PacketBuffer * aBuffer); + + /** + * @brief + * Parse data from an PacketBuffer into a struct instance + * + * @param[in] aBuffer Pointer to a PacketBuffer containing the data + * @param[out] aParsedMessage Reference to a struct instance where results will be stored + * + * @return CHIP_ERROR Any error that occurs when trying to read the message + */ + static CHIP_ERROR Parse(System::PacketBuffer * aBuffer, SendAccept & aResponse); + + /** + * @brief + * Equality check method. + */ + bool operator==(const SendAccept &) const; + + // All required + uint8_t mVersion; ///< The agreed upon version for the transfer. + ControlMode mControlMode; ///< Agreed upon transfer control method + uint16_t mMaxBlockSize; ///< Chosen max block size to use in transfer + + // Additional metadata (optional, TLV format) + uint8_t * mMetadata; + uint16_t mMetadataLength; +}; + +/** + * @class ReceiveAccept + * + * @brief + * The ReceiveAccept message is used to accept a proposed exchange when the + * receiver is the initiator. + */ +struct ReceiveAccept +{ +public: + /** + * @brief + * Pack (write) the message into a PacketBuffer. + * + * @param[out] aBuffer a PacketBuffer to use to write the message + * + * @return CHIP_ERROR Any error that occurs when trying to write to the PacketBuffer + */ + CHIP_ERROR Pack(System::PacketBuffer * aBuffer); + + /** + * @brief + * Parse data from an PacketBuffer into a struct instance + * + * @param[in] aBuffer Pointer to a PacketBuffer containing the data + * @param[out] aParsedMessage Reference to a struct instance where results will be stored + * + * @return CHIP_ERROR Any error that occurs when trying to read the message + */ + static CHIP_ERROR Parse(System::PacketBuffer * aBuffer, ReceiveAccept & aResponse); + + /** + * @brief + * Equality check method. + */ + bool operator==(const ReceiveAccept &) const; + + // All required + uint8_t mVersion; ///< The agreed upon version for the transfer + ControlMode mControlMode; ///< Agreed upon transfer control method + + // Range Control (must fill in) + bool mWideRange; ///< Set true to indicate mStartOffset and mDefLen are 64-bit, else 32-bit + uint64_t mStartOffset; ///< Chosen start offset of data. 0 for no offset + bool mDefLen; ///< Set to true if transfer has definite length + + uint16_t mMaxBlockSize; ///< Chosen max block size to use in transfer (required) + uint64_t mLength; ///< Length of transfer (only valid/required if mDefLen is set) + + // Additional metadata (optional, TLV format) + uint8_t * mMetadata; + uint16_t mMetadataLength; +}; + +} // namespace BDX +} // namespace chip diff --git a/src/transport/bdx/BUILD.gn b/src/transport/bdx/BUILD.gn new file mode 100644 index 00000000000000..b5aa6d6b41cb9f --- /dev/null +++ b/src/transport/bdx/BUILD.gn @@ -0,0 +1,32 @@ +# Copyright (c) 2020 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build_overrides/chip.gni") + +static_library("bdx") { + output_name = "libBdx" + + sources = [ + "BDXMessageUtils.h", + "BDXMessageUtils.cpp", + ] + + cflags = [ "-Wconversion" ] + + public_deps = [ + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/support", + "${chip_root}/src/system", + ] +} diff --git a/src/transport/bdx/tests/BUILD.gn b/src/transport/bdx/tests/BUILD.gn new file mode 100644 index 00000000000000..9b8e297cfee6ae --- /dev/null +++ b/src/transport/bdx/tests/BUILD.gn @@ -0,0 +1,41 @@ +# Copyright (c) 2020 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build_overrides/chip.gni") +import("//build_overrides/nlio.gni") +import("//build_overrides/nlunit_test.gni") + +import("${chip_root}/build/chip/chip_test_suite.gni") + +chip_test_suite("tests") { + output_name = "libBDXTests" + + sources = [ + "TestBDXMessageUtils.cpp", + ] + + public_deps = [ + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/support", + "${chip_root}/src/transport/bdx", + "${nlio_root}:nlio", + "${nlunit_test_root}:nlunit-test", + ] + + cflags = [ "-Wconversion" ] + + tests = [ + "TestBDXMessageUtils", + ] +} diff --git a/src/transport/bdx/tests/TestBDX.h b/src/transport/bdx/tests/TestBDX.h new file mode 100644 index 00000000000000..a3404a3f2ef40e --- /dev/null +++ b/src/transport/bdx/tests/TestBDX.h @@ -0,0 +1,6 @@ +#ifndef TESTBDX_H +#define TESTBDX_H + +int TestBDXMessageUtils(void); + +#endif // TESTBDX_H diff --git a/src/transport/bdx/tests/TestBDXMessageUtils.cpp b/src/transport/bdx/tests/TestBDXMessageUtils.cpp new file mode 100644 index 00000000000000..368b4520ad26f0 --- /dev/null +++ b/src/transport/bdx/tests/TestBDXMessageUtils.cpp @@ -0,0 +1,145 @@ +#include +#include + +#include + +#include +#include +#include + +#include + +using namespace chip; +using namespace chip::BDX; + +void TestTransferInitMessage(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + TransferInit testMsg; + testMsg.mSupportsAsync = false; + testMsg.mSupportsReceiverDrive = false; + testMsg.mSupportsSenderDrive = true; + testMsg.mSupportedVersions = 1; + + testMsg.mWideRange = true; + testMsg.mStartOffset = true; + testMsg.mDefLen = true; + + testMsg.mMaxLength = 1024; + testMsg.mMaxBlockSize = 256; + + char testFileDes[9] = { "test.txt" }; + testMsg.mFileDesLength = 9; + testMsg.mFileDesignator = reinterpret_cast(testFileDes); + + uint8_t fakeData[5] = { 7, 6, 5, 4, 3 }; + testMsg.mMetadataLength = 5; + testMsg.mMetadata = reinterpret_cast(fakeData); + + // Verify pack is successful + System::PacketBuffer * buf = System::PacketBuffer::NewWithAvailableSize(512); + err = testMsg.Pack(buf); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + // Verify successful unpack, and unpacked message is identical to original + TransferInit testMsgRcvd; + err = TransferInit::Parse(buf, testMsgRcvd); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, testMsgRcvd == testMsg); +} + +void TestSendAcceptMessage(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + SendAccept testMsg; + testMsg.mVersion = 1; + testMsg.mControlMode = kReceiverDrive; + testMsg.mMaxBlockSize = 256; + + uint8_t fakeData[5] = { 7, 6, 5, 4, 3 }; + testMsg.mMetadataLength = 5; + testMsg.mMetadata = reinterpret_cast(fakeData); + + // Verify pack is successful + System::PacketBuffer * buf = System::PacketBuffer::NewWithAvailableSize(512); + err = testMsg.Pack(buf); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + // Verify successful unpack, and unpacked message is identical to original + SendAccept testMsgRcvd; + err = SendAccept::Parse(buf, testMsgRcvd); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, testMsgRcvd == testMsg); +} + +void TestReceiveAcceptMessage(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + ReceiveAccept testMsg; + testMsg.mVersion = 1; + testMsg.mControlMode = kReceiverDrive; + + testMsg.mWideRange = true; + testMsg.mStartOffset = true; + testMsg.mDefLen = true; + + testMsg.mLength = 1024; + testMsg.mMaxBlockSize = 256; + + uint8_t fakeData[5] = { 7, 6, 5, 4, 3 }; + testMsg.mMetadataLength = 5; + testMsg.mMetadata = reinterpret_cast(fakeData); + + // Verify pack is successful + System::PacketBuffer * buf = System::PacketBuffer::NewWithAvailableSize(512); + err = testMsg.Pack(buf); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + // Verify successful unpack, and unpacked message is identical to original + ReceiveAccept testMsgRcvd; + err = ReceiveAccept::Parse(buf, testMsgRcvd); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, testMsgRcvd == testMsg); +} + +// Test Suite + +/** + * Test Suite that lists all the test functions. + */ +// clang-format off +static const nlTest sTests[] = +{ + NL_TEST_DEF("TestTransferInitMessage", TestTransferInitMessage), + NL_TEST_DEF("TestSendAcceptMessage", TestSendAcceptMessage), + NL_TEST_DEF("TestReceiveAcceptMessage", TestReceiveAcceptMessage), + + NL_TEST_SENTINEL() +}; +// clang-format on + +// clang-format off +static nlTestSuite sSuite = +{ + "Test-CHIP-BDXMessageUtils", + &sTests[0], + nullptr, + nullptr +}; +// clang-format on + +/** + * Main + */ +int TestBDXMessageUtils() +{ + // Run test suit against one context + nlTestRunner(&sSuite, nullptr); + + return (nlTestRunnerStats(&sSuite)); +} + +CHIP_REGISTER_TEST_SUITE(TestBDXMessageUtils) diff --git a/src/transport/bdx/tests/TestBDXMessageUtilsDriver.cpp b/src/transport/bdx/tests/TestBDXMessageUtilsDriver.cpp new file mode 100644 index 00000000000000..a07f50102981b1 --- /dev/null +++ b/src/transport/bdx/tests/TestBDXMessageUtilsDriver.cpp @@ -0,0 +1,10 @@ +#include "TestBDX.h" + +#include + +int main() +{ + nlTestSetOutputStyle(OUTPUT_CSV); + + return (TestBDXMessageUtils()); +}