From 1b86eb87d471c476da9856a8bab71b1a225c5e85 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 9 Jan 2026 16:46:19 -0800 Subject: [PATCH] [meshcop] move `SteeringData` to its own source files (#12277) The `SteeringData` class and its related methods are moved from `meshcop.hpp` and `meshcop.cpp` to new dedicated files `steering_data.hpp` and `steering_data.cpp`. This change addresses a circular include dependency issue, allowing `SteeringData` to be included more easily in other parts of the codebase, such as `meshcop_tlvs.hpp`, without requiring forward declarations or pulling in unnecessary dependencies from the larger `meshcop.hpp` header. --- src/core/BUILD.gn | 2 + src/core/CMakeLists.txt | 1 + src/core/meshcop/meshcop.cpp | 159 ----------------- src/core/meshcop/meshcop.hpp | 212 +---------------------- src/core/meshcop/meshcop_tlvs.hpp | 5 +- src/core/meshcop/steering_data.cpp | 201 ++++++++++++++++++++++ src/core/meshcop/steering_data.hpp | 268 +++++++++++++++++++++++++++++ 7 files changed, 475 insertions(+), 373 deletions(-) create mode 100644 src/core/meshcop/steering_data.cpp create mode 100644 src/core/meshcop/steering_data.hpp diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 2f6a85a32..9b39a70cd 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -424,6 +424,8 @@ openthread_core_files = [ "meshcop/panid_query_client.hpp", "meshcop/secure_transport.cpp", "meshcop/secure_transport.hpp", + "meshcop/steering_data.cpp", + "meshcop/steering_data.hpp", "meshcop/tcat_agent.cpp", "meshcop/tcat_agent.hpp", "meshcop/timestamp.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8c186953d..6522fb04e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -181,6 +181,7 @@ set(COMMON_SOURCES meshcop/network_name.cpp meshcop/panid_query_client.cpp meshcop/secure_transport.cpp + meshcop/steering_data.cpp meshcop/tcat_agent.cpp meshcop/timestamp.cpp net/checksum.cpp diff --git a/src/core/meshcop/meshcop.cpp b/src/core/meshcop/meshcop.cpp index a82bbc77a..d924e8b18 100644 --- a/src/core/meshcop/meshcop.cpp +++ b/src/core/meshcop/meshcop.cpp @@ -34,7 +34,6 @@ #include "meshcop.hpp" -#include "common/crc.hpp" #include "instance/instance.hpp" namespace ot { @@ -188,164 +187,6 @@ JoinerDiscerner::InfoString JoinerDiscerner::ToString(void) const return string; } -//--------------------------------------------------------------------------------------------------------------------- -// SteeringData - -Error SteeringData::Init(uint8_t aLength) -{ - Error error = kErrorNone; - - VerifyOrExit(IsValueInRange(aLength, kMinLength, kMaxLength), error = kErrorInvalidArgs); - - mLength = aLength; - ClearAllBytes(m8); - -exit: - return error; -} - -Error SteeringData::Init(uint8_t aLength, const uint8_t *aData) -{ - Error error; - - SuccessOrExit(error = Init(aLength)); - memcpy(m8, aData, mLength); - -exit: - return error; -} - -void SteeringData::SetToPermitAllJoiners(void) -{ - IgnoreError(Init(1)); - m8[0] = kPermitAll; -} - -Error SteeringData::UpdateBloomFilter(const Mac::ExtAddress &aJoinerId) -{ - HashBitIndexes indexes; - - CalculateHashBitIndexes(aJoinerId, indexes); - - return UpdateBloomFilter(indexes); -} - -Error SteeringData::UpdateBloomFilter(const JoinerDiscerner &aDiscerner) -{ - HashBitIndexes indexes; - - CalculateHashBitIndexes(aDiscerner, indexes); - - return UpdateBloomFilter(indexes); -} - -Error SteeringData::UpdateBloomFilter(const HashBitIndexes &aIndexes) -{ - Error error = kErrorNone; - - VerifyOrExit(IsValid(), error = kErrorInvalidArgs); - - SetBit(aIndexes.mIndex[0] % GetNumBits()); - SetBit(aIndexes.mIndex[1] % GetNumBits()); - -exit: - return error; -} - -Error SteeringData::MergeBloomFilterWith(const SteeringData &aOther) -{ - Error error = kErrorNone; - - VerifyOrExit(IsValid(), error = kErrorInvalidArgs); - VerifyOrExit(aOther.IsValid(), error = kErrorInvalidArgs); - - VerifyOrExit(GetLength() % aOther.GetLength() == 0, error = kErrorInvalidArgs); - - for (uint8_t index = 0; index < GetLength(); index++) - { - m8[index] |= aOther.m8[index % aOther.GetLength()]; - } - -exit: - return error; -} - -bool SteeringData::Contains(const Mac::ExtAddress &aJoinerId) const -{ - HashBitIndexes indexes; - - CalculateHashBitIndexes(aJoinerId, indexes); - - return Contains(indexes); -} - -bool SteeringData::Contains(const JoinerDiscerner &aDiscerner) const -{ - HashBitIndexes indexes; - - CalculateHashBitIndexes(aDiscerner, indexes); - - return Contains(indexes); -} - -bool SteeringData::Contains(const HashBitIndexes &aIndexes) const -{ - bool contains = false; - - VerifyOrExit(IsValid()); - contains = GetBit(aIndexes.mIndex[0] % GetNumBits()) && GetBit(aIndexes.mIndex[1] % GetNumBits()); - -exit: - return contains; -} - -void SteeringData::CalculateHashBitIndexes(const Mac::ExtAddress &aJoinerId, HashBitIndexes &aIndexes) -{ - aIndexes.mIndex[0] = CrcCalculator(kCrc16CcittPolynomial).Feed(aJoinerId); - aIndexes.mIndex[1] = CrcCalculator(kCrc16AnsiPolynomial).Feed(aJoinerId); -} - -void SteeringData::CalculateHashBitIndexes(const JoinerDiscerner &aDiscerner, HashBitIndexes &aIndexes) -{ - Mac::ExtAddress address; - - address.Clear(); - aDiscerner.CopyTo(address); - - CalculateHashBitIndexes(address, aIndexes); -} - -bool SteeringData::DoesAllMatch(uint8_t aMatch) const -{ - bool matches = false; - - VerifyOrExit(IsValid()); - - for (uint8_t i = 0; i < mLength; i++) - { - if (m8[i] != aMatch) - { - ExitNow(); - } - } - - matches = true; - -exit: - return matches; -} - -SteeringData::InfoString SteeringData::ToString(void) const -{ - InfoString string; - - string.Append("["); - string.AppendHexBytes(GetData(), Min(GetLength(), kMaxLength)); - string.Append("]"); - - return string; -} - //--------------------------------------------------------------------------------------------------------------------- void ComputeJoinerId(const Mac::ExtAddress &aEui64, Mac::ExtAddress &aJoinerId) diff --git a/src/core/meshcop/meshcop.hpp b/src/core/meshcop/meshcop.hpp index 88f7d86d0..a118f4b2b 100644 --- a/src/core/meshcop/meshcop.hpp +++ b/src/core/meshcop/meshcop.hpp @@ -39,7 +39,6 @@ #include #include #include -#include #include "coap/coap.hpp" #include "common/as_core_type.hpp" @@ -53,6 +52,7 @@ #include "common/string.hpp" #include "mac/mac_types.hpp" #include "meshcop/meshcop_tlvs.hpp" +#include "meshcop/steering_data.hpp" namespace ot { @@ -226,215 +226,6 @@ private: void CopyTo(Mac::ExtAddress &aExtAddress) const; }; -/** - * Represents Steering Data (bloom filter). - */ -class SteeringData : public otSteeringData -{ -public: - static constexpr uint8_t kMinLength = OT_STEERING_DATA_MIN_LENGTH; ///< Minimum Steering Data length (in bytes). - static constexpr uint8_t kMaxLength = OT_STEERING_DATA_MAX_LENGTH; ///< Maximum Steering Data length (in bytes). - - static constexpr uint16_t kInfoStringSize = 45; ///< Size of `InfoString` to use with `ToString()`. - - /** - * Defines the fixed-length `String` object returned from `ToString()`. - */ - typedef String InfoString; - - /** - * Represents the hash bit index values for the bloom filter calculated from a Joiner ID. - * - * The first hash bit index is derived using CRC16-CCITT and second one using CRC16-ANSI. - */ - struct HashBitIndexes - { - static constexpr uint8_t kNumIndexes = 2; ///< Number of hash bit indexes. - - uint16_t mIndex[kNumIndexes]; ///< The hash bit index array. - }; - - /** - * Initializes the Steering Data and clears the bloom filter. - * - * @param[in] aLength The Steering Data length (in bytes). - * - * @retval kErrorSuccess Successfully initialized the Steering Data. - * @retval kErrorInvalidArgs @p aLength is not valid. - */ - Error Init(uint8_t aLength); - - /** - * Initializes the Steering Data. - * - * @param[in] aLength The Steering Data length (in bytes). - * @param[in] aData A pointer to a buffer containing the data bytes. - * - * @retval kErrorSuccess Successfully initialized the Steering Data. - * @retval kErrorInvalidArgs @p aLength is not valid. - */ - Error Init(uint8_t aLength, const uint8_t *aData); - - /** - * Checks whether the Steering Data has a valid length. - * - * @returns TRUE if the Steering Data's length is valid (within min to max range), FALSE otherwise. - */ - bool IsValid(void) const { return IsValueInRange(mLength, kMinLength, kMaxLength); } - - /** - * Clears the bloom filter (all bits are cleared and no Joiner Id is accepted).. - * - * The Steering Data length (bloom filter length) is set to one byte with all bits cleared. - */ - void Clear(void) { IgnoreError(Init(1)); } - - /** - * Sets the bloom filter to permit all Joiner IDs. - * - * To permit all Joiner IDs, The Steering Data length (bloom filter length) is set to one byte with all bits set. - */ - void SetToPermitAllJoiners(void); - - /** - * Returns the Steering Data length (in bytes). - * - * @returns The Steering Data length (in bytes). - */ - uint8_t GetLength(void) const { return mLength; } - - /** - * Gets the Steering Data buffer (bloom filter). - * - * @returns A pointer to the Steering Data buffer. - */ - const uint8_t *GetData(void) const { return m8; } - - /** - * Gets the Steering Data buffer (bloom filter). - * - * @returns A pointer to the Steering Data buffer. - */ - uint8_t *GetData(void) { return m8; } - - /** - * Updates the bloom filter adding the given Joiner ID. - * - * @param[in] aJoinerId The Joiner ID to add to bloom filter. - * - * @retval kErrorNone Successfully updated the bloom filter. - * @retval kErrorInvalidArgs The Steering Data's length is invalid. - */ - Error UpdateBloomFilter(const Mac::ExtAddress &aJoinerId); - - /** - * Updates the bloom filter adding a given Joiner Discerner. - * - * @param[in] aDiscerner The Joiner Discerner to add to bloom filter. - * - * @retval kErrorNone Successfully updated the bloom filter. - * @retval kErrorInvalidArgs The Steering Data's length is invalid. - */ - Error UpdateBloomFilter(const JoinerDiscerner &aDiscerner); - - /** - * Merges the bloom filter by combining it with another steering data filter. - * - * Both bloom filters must have valid lengths (non-zero and not exceeding `kMaxLength`). - * - * The bloom filter from @p aOther must have a length that is a divisor of the current filter's length. - * - * @param[in] aOther The other bloom filter to combine with current filter. - * - * @retval kErrorNone Successfully merged @p aOther into the current bloom filter. - * @retval kErrorInvalidArgs The filter lengths are not valid or they cannot be merged. - */ - Error MergeBloomFilterWith(const SteeringData &aOther); - - /** - * Indicates whether the bloom filter is empty (all the bits are cleared). - * - * @returns TRUE if the bloom filter is empty, FALSE otherwise. - */ - bool IsEmpty(void) const { return DoesAllMatch(0); } - - /** - * Indicates whether the bloom filter permits all Joiner IDs (all the bits are set). - * - * @returns TRUE if the bloom filter permits all Joiners IDs, FALSE otherwise. - */ - bool PermitsAllJoiners(void) const { return (mLength > 0) && DoesAllMatch(kPermitAll); } - - /** - * Indicates whether the bloom filter contains a given Joiner ID. - * - * @param[in] aJoinerId A Joiner ID. - * - * @returns TRUE if the bloom filter contains @p aJoinerId, FALSE otherwise. - */ - bool Contains(const Mac::ExtAddress &aJoinerId) const; - - /** - * Indicates whether the bloom filter contains a given Joiner Discerner. - * - * @param[in] aDiscerner A Joiner Discerner. - * - * @returns TRUE if the bloom filter contains @p aDiscerner, FALSE otherwise. - */ - bool Contains(const JoinerDiscerner &aDiscerner) const; - - /** - * Indicates whether the bloom filter contains the hash bit indexes (derived from a Joiner ID). - * - * @param[in] aIndexes A hash bit index structure (derived from a Joiner ID). - * - * @returns TRUE if the bloom filter contains the Joiner ID mapping to @p aIndexes, FALSE otherwise. - */ - bool Contains(const HashBitIndexes &aIndexes) const; - - /** - * Converts the Steering Data to a human-readable string representation. - * - * @returns An `InfoString` representation of the Steering Data. - */ - InfoString ToString(void) const; - - /** - * Calculates the bloom filter hash bit indexes from a given Joiner ID. - * - * The first hash bit index is derived using CRC16-CCITT and second one using CRC16-ANSI. - * - * @param[in] aJoinerId The Joiner ID to calculate the hash bit indexes. - * @param[out] aIndexes A reference to a `HashBitIndexes` structure to output the calculated index values. - */ - static void CalculateHashBitIndexes(const Mac::ExtAddress &aJoinerId, HashBitIndexes &aIndexes); - - /** - * Calculates the bloom filter hash bit indexes from a given Joiner Discerner. - * - * The first hash bit index is derived using CRC16-CCITT and second one using CRC16-ANSI. - * - * @param[in] aDiscerner The Joiner Discerner to calculate the hash bit indexes. - * @param[out] aIndexes A reference to a `HashBitIndexes` structure to output the calculated index values. - */ - static void CalculateHashBitIndexes(const JoinerDiscerner &aDiscerner, HashBitIndexes &aIndexes); - -private: - static constexpr uint8_t kPermitAll = 0xff; - - uint8_t GetNumBits(void) const { return (mLength * kBitsPerByte); } - - uint8_t BitIndex(uint8_t aBit) const { return (mLength - 1 - (aBit / kBitsPerByte)); } - uint8_t BitFlag(uint8_t aBit) const { return static_cast(1U << (aBit % kBitsPerByte)); } - - bool GetBit(uint8_t aBit) const { return (m8[BitIndex(aBit)] & BitFlag(aBit)) != 0; } - void SetBit(uint8_t aBit) { m8[BitIndex(aBit)] |= BitFlag(aBit); } - void ClearBit(uint8_t aBit) { m8[BitIndex(aBit)] &= ~BitFlag(aBit); } - - bool DoesAllMatch(uint8_t aMatch) const; - Error UpdateBloomFilter(const HashBitIndexes &aIndexes); -}; - /** * Represents a Commissioning Dataset. */ @@ -592,7 +383,6 @@ void LogCertMessage(const char *aText, const Coap::Message &aMessage); DefineCoreType(otJoinerPskd, MeshCoP::JoinerPskd); DefineCoreType(otJoinerDiscerner, MeshCoP::JoinerDiscerner); -DefineCoreType(otSteeringData, MeshCoP::SteeringData); DefineCoreType(otCommissioningDataset, MeshCoP::CommissioningDataset); } // namespace ot diff --git a/src/core/meshcop/meshcop_tlvs.hpp b/src/core/meshcop/meshcop_tlvs.hpp index e319b204b..d422a8c77 100644 --- a/src/core/meshcop/meshcop_tlvs.hpp +++ b/src/core/meshcop/meshcop_tlvs.hpp @@ -50,6 +50,7 @@ #include "mac/mac_types.hpp" #include "meshcop/extended_panid.hpp" #include "meshcop/network_name.hpp" +#include "meshcop/steering_data.hpp" #include "meshcop/timestamp.hpp" #include "net/ip6_address.hpp" #include "radio/radio.hpp" @@ -318,8 +319,6 @@ typedef UintTlvInfo NetworkKeySequenceTlv; */ typedef SimpleTlvInfo MeshLocalPrefixTlv; -class SteeringData; - /** * Implements Steering Data TLV generation and parsing. */ @@ -371,7 +370,7 @@ public: Error CopyTo(SteeringData &aSteeringData) const; private: - uint8_t mSteeringData[OT_STEERING_DATA_MAX_LENGTH]; + uint8_t mSteeringData[SteeringData::kMaxLength]; } OT_TOOL_PACKED_END; /** diff --git a/src/core/meshcop/steering_data.cpp b/src/core/meshcop/steering_data.cpp new file mode 100644 index 000000000..cf9f10e80 --- /dev/null +++ b/src/core/meshcop/steering_data.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2026, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements MeshCoP Steering Data. + */ + +#include "steering_data.hpp" + +#include "common/clearable.hpp" +#include "common/code_utils.hpp" +#include "common/crc.hpp" +#include "common/num_utils.hpp" +#include "meshcop/meshcop.hpp" + +namespace ot { +namespace MeshCoP { + +Error SteeringData::Init(uint8_t aLength) +{ + Error error = kErrorNone; + + VerifyOrExit(IsValueInRange(aLength, kMinLength, kMaxLength), error = kErrorInvalidArgs); + + mLength = aLength; + ClearAllBytes(m8); + +exit: + return error; +} + +Error SteeringData::Init(uint8_t aLength, const uint8_t *aData) +{ + Error error; + + SuccessOrExit(error = Init(aLength)); + memcpy(m8, aData, mLength); + +exit: + return error; +} + +void SteeringData::SetToPermitAllJoiners(void) +{ + IgnoreError(Init(1)); + m8[0] = kPermitAll; +} + +Error SteeringData::UpdateBloomFilter(const Mac::ExtAddress &aJoinerId) +{ + HashBitIndexes indexes; + + CalculateHashBitIndexes(aJoinerId, indexes); + + return UpdateBloomFilter(indexes); +} + +Error SteeringData::UpdateBloomFilter(const JoinerDiscerner &aDiscerner) +{ + HashBitIndexes indexes; + + CalculateHashBitIndexes(aDiscerner, indexes); + + return UpdateBloomFilter(indexes); +} + +Error SteeringData::UpdateBloomFilter(const HashBitIndexes &aIndexes) +{ + Error error = kErrorNone; + + VerifyOrExit(IsValid(), error = kErrorInvalidArgs); + + SetBit(aIndexes.mIndex[0] % GetNumBits()); + SetBit(aIndexes.mIndex[1] % GetNumBits()); + +exit: + return error; +} + +Error SteeringData::MergeBloomFilterWith(const SteeringData &aOther) +{ + Error error = kErrorNone; + + VerifyOrExit(IsValid(), error = kErrorInvalidArgs); + VerifyOrExit(aOther.IsValid(), error = kErrorInvalidArgs); + + VerifyOrExit(GetLength() % aOther.GetLength() == 0, error = kErrorInvalidArgs); + + for (uint8_t index = 0; index < GetLength(); index++) + { + m8[index] |= aOther.m8[index % aOther.GetLength()]; + } + +exit: + return error; +} + +bool SteeringData::Contains(const Mac::ExtAddress &aJoinerId) const +{ + HashBitIndexes indexes; + + CalculateHashBitIndexes(aJoinerId, indexes); + + return Contains(indexes); +} + +bool SteeringData::Contains(const JoinerDiscerner &aDiscerner) const +{ + HashBitIndexes indexes; + + CalculateHashBitIndexes(aDiscerner, indexes); + + return Contains(indexes); +} + +bool SteeringData::Contains(const HashBitIndexes &aIndexes) const +{ + bool contains = false; + + VerifyOrExit(IsValid()); + contains = GetBit(aIndexes.mIndex[0] % GetNumBits()) && GetBit(aIndexes.mIndex[1] % GetNumBits()); + +exit: + return contains; +} + +void SteeringData::CalculateHashBitIndexes(const Mac::ExtAddress &aJoinerId, HashBitIndexes &aIndexes) +{ + aIndexes.mIndex[0] = CrcCalculator(kCrc16CcittPolynomial).Feed(aJoinerId); + aIndexes.mIndex[1] = CrcCalculator(kCrc16AnsiPolynomial).Feed(aJoinerId); +} + +void SteeringData::CalculateHashBitIndexes(const JoinerDiscerner &aDiscerner, HashBitIndexes &aIndexes) +{ + Mac::ExtAddress address; + + address.Clear(); + aDiscerner.CopyTo(address); + + CalculateHashBitIndexes(address, aIndexes); +} + +bool SteeringData::DoesAllMatch(uint8_t aMatch) const +{ + bool matches = false; + + VerifyOrExit(IsValid()); + + for (uint8_t i = 0; i < mLength; i++) + { + if (m8[i] != aMatch) + { + ExitNow(); + } + } + + matches = true; + +exit: + return matches; +} + +SteeringData::InfoString SteeringData::ToString(void) const +{ + InfoString string; + + string.Append("["); + string.AppendHexBytes(GetData(), Min(GetLength(), kMaxLength)); + string.Append("]"); + + return string; +} + +} // namespace MeshCoP +} // namespace ot diff --git a/src/core/meshcop/steering_data.hpp b/src/core/meshcop/steering_data.hpp new file mode 100644 index 000000000..3a0654e1c --- /dev/null +++ b/src/core/meshcop/steering_data.hpp @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2026, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for MeshCoP Steering Data. + */ + +#ifndef OT_CORE_MESHCOP_STEERING_DATA_HPP_ +#define OT_CORE_MESHCOP_STEERING_DATA_HPP_ + +#include "openthread-core-config.h" + +#include + +#include "common/as_core_type.hpp" +#include "common/bit_utils.hpp" +#include "common/code_utils.hpp" +#include "common/error.hpp" +#include "common/string.hpp" +#include "mac/mac_types.hpp" + +namespace ot { +namespace MeshCoP { + +class JoinerDiscerner; + +/** + * Represents Steering Data (bloom filter). + */ +class SteeringData : public otSteeringData +{ +public: + static constexpr uint8_t kMinLength = OT_STEERING_DATA_MIN_LENGTH; ///< Minimum Steering Data length (in bytes). + static constexpr uint8_t kMaxLength = OT_STEERING_DATA_MAX_LENGTH; ///< Maximum Steering Data length (in bytes). + + static constexpr uint16_t kInfoStringSize = 45; ///< Size of `InfoString` to use with `ToString()`. + + /** + * Defines the fixed-length `String` object returned from `ToString()`. + */ + typedef String InfoString; + + /** + * Represents the hash bit index values for the bloom filter calculated from a Joiner ID. + * + * The first hash bit index is derived using CRC16-CCITT and second one using CRC16-ANSI. + */ + struct HashBitIndexes + { + static constexpr uint8_t kNumIndexes = 2; ///< Number of hash bit indexes. + + uint16_t mIndex[kNumIndexes]; ///< The hash bit index array. + }; + + /** + * Initializes the Steering Data and clears the bloom filter. + * + * @param[in] aLength The Steering Data length (in bytes). + * + * @retval kErrorSuccess Successfully initialized the Steering Data. + * @retval kErrorInvalidArgs @p aLength is not valid. + */ + Error Init(uint8_t aLength); + + /** + * Initializes the Steering Data. + * + * @param[in] aLength The Steering Data length (in bytes). + * @param[in] aData A pointer to a buffer containing the data bytes. + * + * @retval kErrorSuccess Successfully initialized the Steering Data. + * @retval kErrorInvalidArgs @p aLength is not valid. + */ + Error Init(uint8_t aLength, const uint8_t *aData); + + /** + * Checks whether the Steering Data has a valid length. + * + * @returns TRUE if the Steering Data's length is valid (within min to max range), FALSE otherwise. + */ + bool IsValid(void) const { return IsValueInRange(mLength, kMinLength, kMaxLength); } + + /** + * Clears the bloom filter (all bits are cleared and no Joiner Id is accepted).. + * + * The Steering Data length (bloom filter length) is set to one byte with all bits cleared. + */ + void Clear(void) { IgnoreError(Init(1)); } + + /** + * Sets the bloom filter to permit all Joiner IDs. + * + * To permit all Joiner IDs, The Steering Data length (bloom filter length) is set to one byte with all bits set. + */ + void SetToPermitAllJoiners(void); + + /** + * Returns the Steering Data length (in bytes). + * + * @returns The Steering Data length (in bytes). + */ + uint8_t GetLength(void) const { return mLength; } + + /** + * Gets the Steering Data buffer (bloom filter). + * + * @returns A pointer to the Steering Data buffer. + */ + const uint8_t *GetData(void) const { return m8; } + + /** + * Gets the Steering Data buffer (bloom filter). + * + * @returns A pointer to the Steering Data buffer. + */ + uint8_t *GetData(void) { return m8; } + + /** + * Updates the bloom filter adding the given Joiner ID. + * + * @param[in] aJoinerId The Joiner ID to add to bloom filter. + * + * @retval kErrorNone Successfully updated the bloom filter. + * @retval kErrorInvalidArgs The Steering Data's length is invalid. + */ + Error UpdateBloomFilter(const Mac::ExtAddress &aJoinerId); + + /** + * Updates the bloom filter adding a given Joiner Discerner. + * + * @param[in] aDiscerner The Joiner Discerner to add to bloom filter. + * + * @retval kErrorNone Successfully updated the bloom filter. + * @retval kErrorInvalidArgs The Steering Data's length is invalid. + */ + Error UpdateBloomFilter(const JoinerDiscerner &aDiscerner); + + /** + * Merges the bloom filter by combining it with another steering data filter. + * + * Both bloom filters must have valid lengths (non-zero and not exceeding `kMaxLength`). + * + * The bloom filter from @p aOther must have a length that is a divisor of the current filter's length. + * + * @param[in] aOther The other bloom filter to combine with current filter. + * + * @retval kErrorNone Successfully merged @p aOther into the current bloom filter. + * @retval kErrorInvalidArgs The filter lengths are not valid or they cannot be merged. + */ + Error MergeBloomFilterWith(const SteeringData &aOther); + + /** + * Indicates whether the bloom filter is empty (all the bits are cleared). + * + * @returns TRUE if the bloom filter is empty, FALSE otherwise. + */ + bool IsEmpty(void) const { return DoesAllMatch(0); } + + /** + * Indicates whether the bloom filter permits all Joiner IDs (all the bits are set). + * + * @returns TRUE if the bloom filter permits all Joiners IDs, FALSE otherwise. + */ + bool PermitsAllJoiners(void) const { return (mLength > 0) && DoesAllMatch(kPermitAll); } + + /** + * Indicates whether the bloom filter contains a given Joiner ID. + * + * @param[in] aJoinerId A Joiner ID. + * + * @returns TRUE if the bloom filter contains @p aJoinerId, FALSE otherwise. + */ + bool Contains(const Mac::ExtAddress &aJoinerId) const; + + /** + * Indicates whether the bloom filter contains a given Joiner Discerner. + * + * @param[in] aDiscerner A Joiner Discerner. + * + * @returns TRUE if the bloom filter contains @p aDiscerner, FALSE otherwise. + */ + bool Contains(const JoinerDiscerner &aDiscerner) const; + + /** + * Indicates whether the bloom filter contains the hash bit indexes (derived from a Joiner ID). + * + * @param[in] aIndexes A hash bit index structure (derived from a Joiner ID). + * + * @returns TRUE if the bloom filter contains the Joiner ID mapping to @p aIndexes, FALSE otherwise. + */ + bool Contains(const HashBitIndexes &aIndexes) const; + + /** + * Converts the Steering Data to a human-readable string representation. + * + * @returns An `InfoString` representation of the Steering Data. + */ + InfoString ToString(void) const; + + /** + * Calculates the bloom filter hash bit indexes from a given Joiner ID. + * + * The first hash bit index is derived using CRC16-CCITT and second one using CRC16-ANSI. + * + * @param[in] aJoinerId The Joiner ID to calculate the hash bit indexes. + * @param[out] aIndexes A reference to a `HashBitIndexes` structure to output the calculated index values. + */ + static void CalculateHashBitIndexes(const Mac::ExtAddress &aJoinerId, HashBitIndexes &aIndexes); + + /** + * Calculates the bloom filter hash bit indexes from a given Joiner Discerner. + * + * The first hash bit index is derived using CRC16-CCITT and second one using CRC16-ANSI. + * + * @param[in] aDiscerner The Joiner Discerner to calculate the hash bit indexes. + * @param[out] aIndexes A reference to a `HashBitIndexes` structure to output the calculated index values. + */ + static void CalculateHashBitIndexes(const JoinerDiscerner &aDiscerner, HashBitIndexes &aIndexes); + +private: + static constexpr uint8_t kPermitAll = 0xff; + + uint8_t GetNumBits(void) const { return (mLength * kBitsPerByte); } + + uint8_t BitIndex(uint8_t aBit) const { return (mLength - 1 - (aBit / kBitsPerByte)); } + uint8_t BitFlag(uint8_t aBit) const { return static_cast(1U << (aBit % kBitsPerByte)); } + + bool GetBit(uint8_t aBit) const { return (m8[BitIndex(aBit)] & BitFlag(aBit)) != 0; } + void SetBit(uint8_t aBit) { m8[BitIndex(aBit)] |= BitFlag(aBit); } + void ClearBit(uint8_t aBit) { m8[BitIndex(aBit)] &= ~BitFlag(aBit); } + + bool DoesAllMatch(uint8_t aMatch) const; + Error UpdateBloomFilter(const HashBitIndexes &aIndexes); +}; + +} // namespace MeshCoP + +DefineCoreType(otSteeringData, MeshCoP::SteeringData); + +} // namespace ot + +#endif // OT_CORE_MESHCOP_STEERING_DATA_HPP_