mirror of
https://github.com/espressif/openthread.git
synced 2026-10-09 17:27:41 +00:00
[meshcop] adding SteeringData::MergeBloomFilterWith() (#12145)
This commit adds a new method, `MergeBloomFilterWith()`, to `SteeringData` to allow combining two Bloom filters. The method performs a bitwise OR operation between the current Steering Data Bloom filter and a given one. It handles cases where the given filter to merge has a shorter length than the target Bloom filter. It requires the target filter's length to be a multiple of the source's length. A new unit test, `TestSteeringDataBloomFilterMerge()`, is included to validate the merge logic with various filter length combinations.
This commit is contained in:
@@ -43,6 +43,9 @@ RegisterLogModule("MeshCoP");
|
||||
|
||||
namespace MeshCoP {
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// JoinerPskd
|
||||
|
||||
Error JoinerPskd::SetFrom(const char *aPskdString)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
@@ -99,6 +102,9 @@ exit:
|
||||
return valid;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// JoinerDiscerner
|
||||
|
||||
void JoinerDiscerner::GenerateJoinerId(Mac::ExtAddress &aJoinerId) const
|
||||
{
|
||||
aJoinerId.GenerateRandom();
|
||||
@@ -182,6 +188,9 @@ JoinerDiscerner::InfoString JoinerDiscerner::ToString(void) const
|
||||
return string;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// SteeringData
|
||||
|
||||
void SteeringData::Init(uint8_t aLength)
|
||||
{
|
||||
OT_ASSERT(aLength <= kMaxLength);
|
||||
@@ -213,12 +222,30 @@ void SteeringData::UpdateBloomFilter(const JoinerDiscerner &aDiscerner)
|
||||
|
||||
void SteeringData::UpdateBloomFilter(const HashBitIndexes &aIndexes)
|
||||
{
|
||||
OT_ASSERT((mLength > 0) && (mLength <= kMaxLength));
|
||||
OT_ASSERT(IsLengthValid());
|
||||
|
||||
SetBit(aIndexes.mIndex[0] % GetNumBits());
|
||||
SetBit(aIndexes.mIndex[1] % GetNumBits());
|
||||
}
|
||||
|
||||
Error SteeringData::MergeBloomFilterWith(const SteeringData &aOther)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
|
||||
VerifyOrExit(IsLengthValid(), error = kErrorInvalidArgs);
|
||||
VerifyOrExit(aOther.IsLengthValid(), 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;
|
||||
@@ -274,6 +301,19 @@ bool SteeringData::DoesAllMatch(uint8_t aMatch) const
|
||||
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)
|
||||
{
|
||||
Crypto::Sha256 sha256;
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "common/equatable.hpp"
|
||||
#include "common/log.hpp"
|
||||
#include "common/message.hpp"
|
||||
#include "common/num_utils.hpp"
|
||||
#include "common/numeric_limits.hpp"
|
||||
#include "common/string.hpp"
|
||||
#include "mac/mac_types.hpp"
|
||||
@@ -229,8 +230,16 @@ private:
|
||||
class SteeringData : public otSteeringData
|
||||
{
|
||||
public:
|
||||
static constexpr uint8_t kMinLength = 1; ///< 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<kInfoStringSize> InfoString;
|
||||
|
||||
/**
|
||||
* Represents the hash bit index values for the bloom filter calculated from a Joiner ID.
|
||||
*
|
||||
@@ -299,6 +308,20 @@ public:
|
||||
*/
|
||||
void 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).
|
||||
*
|
||||
@@ -340,6 +363,13 @@ public:
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -363,6 +393,7 @@ public:
|
||||
private:
|
||||
static constexpr uint8_t kPermitAll = 0xff;
|
||||
|
||||
bool IsLengthValid(void) const { return IsValueInRange(mLength, kMinLength, kMaxLength); }
|
||||
uint8_t GetNumBits(void) const { return (mLength * kBitsPerByte); }
|
||||
|
||||
uint8_t BitIndex(uint8_t aBit) const { return (mLength - 1 - (aBit / kBitsPerByte)); }
|
||||
|
||||
+144
-37
@@ -54,65 +54,170 @@ void TestSteeringData(void)
|
||||
steeringData.SetToPermitAllJoiners();
|
||||
|
||||
DumpBuffer("After SetToPermitAllJoiners()", steeringData.GetData(), steeringData.GetLength());
|
||||
VerifyOrQuit(steeringData.GetLength() == 1, "after SetToPermitAllJoiners()");
|
||||
VerifyOrQuit(steeringData.PermitsAllJoiners(), "after SetToPermitAllJoiners()");
|
||||
VerifyOrQuit(!steeringData.IsEmpty(), "after SetToPermitAllJoiners()");
|
||||
VerifyOrQuit(steeringData.Contains(joinerId1), "after SetToPermitAllJoiners()");
|
||||
VerifyOrQuit(steeringData.Contains(joinerId2), "after SetToPermitAllJoiners()");
|
||||
VerifyOrQuit(steeringData.Contains(indexes), "after SetToPermitAllJoiners()");
|
||||
VerifyOrQuit(steeringData.GetLength() == 1);
|
||||
VerifyOrQuit(steeringData.PermitsAllJoiners());
|
||||
VerifyOrQuit(!steeringData.IsEmpty());
|
||||
VerifyOrQuit(steeringData.Contains(joinerId1));
|
||||
VerifyOrQuit(steeringData.Contains(joinerId2));
|
||||
VerifyOrQuit(steeringData.Contains(indexes));
|
||||
|
||||
steeringData.Clear();
|
||||
|
||||
DumpBuffer("After Clear()", steeringData.GetData(), steeringData.GetLength());
|
||||
VerifyOrQuit(steeringData.GetLength() == 1, "after Clear()");
|
||||
VerifyOrQuit(!steeringData.PermitsAllJoiners(), "after Clear()");
|
||||
VerifyOrQuit(steeringData.IsEmpty(), "after Clear()");
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId1), "after Clear()");
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId2), "after Clear()");
|
||||
VerifyOrQuit(!steeringData.Contains(indexes), "after Clear()");
|
||||
VerifyOrQuit(steeringData.GetLength() == 1);
|
||||
VerifyOrQuit(!steeringData.PermitsAllJoiners());
|
||||
VerifyOrQuit(steeringData.IsEmpty());
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId1));
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId2));
|
||||
VerifyOrQuit(!steeringData.Contains(indexes));
|
||||
|
||||
for (uint8_t len = 1; len <= MeshCoP::SteeringData::kMaxLength; len++)
|
||||
for (uint8_t len = MeshCoP::SteeringData::kMinLength; len <= MeshCoP::SteeringData::kMaxLength; len++)
|
||||
{
|
||||
printf("\n--------------------------------------------");
|
||||
|
||||
steeringData.Init(len);
|
||||
|
||||
VerifyOrQuit(steeringData.GetLength() == len, "after Init()");
|
||||
VerifyOrQuit(steeringData.IsEmpty(), "IsEmpy() failed after Init()");
|
||||
VerifyOrQuit(!steeringData.PermitsAllJoiners(), "after Init()");
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId1), "after Init()");
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId2), "after Init()");
|
||||
VerifyOrQuit(!steeringData.Contains(indexes), "after Init()");
|
||||
VerifyOrQuit(steeringData.GetLength() == len);
|
||||
VerifyOrQuit(steeringData.IsEmpty());
|
||||
VerifyOrQuit(!steeringData.PermitsAllJoiners());
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId1));
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId2));
|
||||
VerifyOrQuit(!steeringData.Contains(indexes));
|
||||
|
||||
steeringData.UpdateBloomFilter(joinerId1);
|
||||
DumpBuffer("After UpdateBloomFilter(joinerId1)", steeringData.GetData(), steeringData.GetLength());
|
||||
VerifyOrQuit(steeringData.GetLength() == len, "after UpdateBloomFilter()");
|
||||
VerifyOrQuit(!steeringData.IsEmpty(), "IsEmpy() failed after UpdateBloomFilter()");
|
||||
VerifyOrQuit(!steeringData.PermitsAllJoiners(), "after UpdateBloomFilter");
|
||||
VerifyOrQuit(steeringData.Contains(joinerId1), "after UpdateBloomFilter");
|
||||
printf("\nAfter UpdateBloomFilter(joinerId1): %s", steeringData.ToString().AsCString());
|
||||
VerifyOrQuit(steeringData.GetLength() == len);
|
||||
VerifyOrQuit(!steeringData.IsEmpty());
|
||||
VerifyOrQuit(!steeringData.PermitsAllJoiners());
|
||||
VerifyOrQuit(steeringData.Contains(joinerId1));
|
||||
|
||||
steeringData.UpdateBloomFilter(joinerId2);
|
||||
DumpBuffer("After UpdateBloomFilter(joinerId2)", steeringData.GetData(), steeringData.GetLength());
|
||||
VerifyOrQuit(steeringData.GetLength() == len, "after UpdateBloomFilter()");
|
||||
VerifyOrQuit(!steeringData.IsEmpty(), "IsEmpy() failed after UpdateBloomFilter()");
|
||||
VerifyOrQuit(!steeringData.PermitsAllJoiners(), "after UpdateBloomFilter");
|
||||
VerifyOrQuit(steeringData.Contains(joinerId1), "after UpdateBloomFilter");
|
||||
VerifyOrQuit(steeringData.Contains(joinerId2), "after UpdateBloomFilter");
|
||||
VerifyOrQuit(steeringData.Contains(indexes), "after UpdateBloomFilter");
|
||||
printf("\nAfter UpdateBloomFilter(joinerId2): %s", steeringData.ToString().AsCString());
|
||||
VerifyOrQuit(steeringData.GetLength() == len);
|
||||
VerifyOrQuit(!steeringData.IsEmpty());
|
||||
VerifyOrQuit(!steeringData.PermitsAllJoiners());
|
||||
VerifyOrQuit(steeringData.Contains(joinerId1));
|
||||
VerifyOrQuit(steeringData.Contains(joinerId2));
|
||||
VerifyOrQuit(steeringData.Contains(indexes));
|
||||
}
|
||||
|
||||
steeringData.Init(0);
|
||||
|
||||
VerifyOrQuit(steeringData.GetLength() == 0, "after Init()");
|
||||
VerifyOrQuit(steeringData.IsEmpty(), "IsEmpy() failed after Init()");
|
||||
VerifyOrQuit(!steeringData.PermitsAllJoiners(), "after Init()");
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId1), "after Init()");
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId2), "after Init()");
|
||||
VerifyOrQuit(!steeringData.Contains(indexes), "after Init()");
|
||||
VerifyOrQuit(steeringData.GetLength() == 0);
|
||||
VerifyOrQuit(steeringData.IsEmpty());
|
||||
VerifyOrQuit(!steeringData.PermitsAllJoiners());
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId1));
|
||||
VerifyOrQuit(!steeringData.Contains(joinerId2));
|
||||
VerifyOrQuit(!steeringData.Contains(indexes));
|
||||
|
||||
printf("TestSteeringData() passed\n");
|
||||
}
|
||||
|
||||
void TestSteeringDataBloomFilterMerge(void)
|
||||
{
|
||||
static constexpr uint8_t kMaxJoinerIds = 3;
|
||||
|
||||
static const uint8_t kTestSteeringDataLengths[] = {16, 8, 4, 2, 1};
|
||||
|
||||
Instance *instance;
|
||||
MeshCoP::SteeringData steeringData;
|
||||
MeshCoP::SteeringData otherSteeringData;
|
||||
|
||||
Mac::ExtAddress joinerIds[kMaxJoinerIds];
|
||||
Mac::ExtAddress otherJoinerIds[kMaxJoinerIds];
|
||||
|
||||
instance = static_cast<Instance *>(testInitInstance());
|
||||
VerifyOrQuit(instance != nullptr);
|
||||
|
||||
printf("\n--------------------------------------------");
|
||||
printf("\nTestSteeringDataBloomFilterMerge()");
|
||||
printf("\nJoiner IDs for SteeringData");
|
||||
|
||||
for (Mac::ExtAddress &id : joinerIds)
|
||||
{
|
||||
uint8_t buffer[sizeof(Mac::ExtAddress)];
|
||||
|
||||
Random::NonCrypto::FillBuffer(buffer, sizeof(buffer));
|
||||
id.Set(buffer);
|
||||
printf("\n %s", id.ToString().AsCString());
|
||||
}
|
||||
|
||||
printf("\n--------------------------------------------");
|
||||
printf("\nJoiner IDs for OtherSteeringData");
|
||||
|
||||
for (Mac::ExtAddress &id : otherJoinerIds)
|
||||
{
|
||||
uint8_t buffer[sizeof(Mac::ExtAddress)];
|
||||
|
||||
Random::NonCrypto::FillBuffer(buffer, sizeof(buffer));
|
||||
id.Set(buffer);
|
||||
printf("\n %s", id.ToString().AsCString());
|
||||
}
|
||||
|
||||
for (uint8_t length : kTestSteeringDataLengths)
|
||||
{
|
||||
for (uint8_t otherLength : kTestSteeringDataLengths)
|
||||
{
|
||||
printf("\n--------------------------------------------");
|
||||
printf("\nLen:%u OtherLen:%u", length, otherLength);
|
||||
|
||||
steeringData.Init(length);
|
||||
otherSteeringData.Init(otherLength);
|
||||
|
||||
VerifyOrQuit(steeringData.GetLength() == length);
|
||||
VerifyOrQuit(otherSteeringData.GetLength() == otherLength);
|
||||
VerifyOrQuit(steeringData.IsEmpty());
|
||||
VerifyOrQuit(otherSteeringData.IsEmpty());
|
||||
|
||||
if (otherLength > length)
|
||||
{
|
||||
VerifyOrQuit(steeringData.MergeBloomFilterWith(otherSteeringData) != kErrorNone);
|
||||
continue;
|
||||
}
|
||||
|
||||
SuccessOrQuit(steeringData.MergeBloomFilterWith(otherSteeringData));
|
||||
|
||||
VerifyOrQuit(steeringData.GetLength() == length);
|
||||
VerifyOrQuit(otherSteeringData.GetLength() == otherLength);
|
||||
VerifyOrQuit(steeringData.IsEmpty());
|
||||
VerifyOrQuit(otherSteeringData.IsEmpty());
|
||||
|
||||
for (Mac::ExtAddress &id : joinerIds)
|
||||
{
|
||||
steeringData.UpdateBloomFilter(id);
|
||||
VerifyOrQuit(steeringData.Contains(id));
|
||||
}
|
||||
|
||||
for (Mac::ExtAddress &id : otherJoinerIds)
|
||||
{
|
||||
otherSteeringData.UpdateBloomFilter(id);
|
||||
VerifyOrQuit(otherSteeringData.Contains(id));
|
||||
}
|
||||
|
||||
printf("\nSteering Data : %s", steeringData.ToString().AsCString());
|
||||
printf("\nOther Steering Data : %s", otherSteeringData.ToString().AsCString());
|
||||
|
||||
SuccessOrQuit(steeringData.MergeBloomFilterWith(otherSteeringData));
|
||||
|
||||
printf("\nMerged Steering Data: %s", steeringData.ToString().AsCString());
|
||||
|
||||
VerifyOrQuit(steeringData.GetLength() == length);
|
||||
|
||||
for (const Mac::ExtAddress &id : joinerIds)
|
||||
{
|
||||
VerifyOrQuit(steeringData.Contains(id));
|
||||
}
|
||||
|
||||
for (const Mac::ExtAddress &id : otherJoinerIds)
|
||||
{
|
||||
VerifyOrQuit(steeringData.Contains(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("\nTestSteeringDataBloomFilterMerge() passed\n");
|
||||
}
|
||||
|
||||
void TestTimestamp(void)
|
||||
{
|
||||
MeshCoP::Timestamp t1;
|
||||
@@ -204,7 +309,9 @@ void TestTimestamp(void)
|
||||
int main(void)
|
||||
{
|
||||
ot::TestSteeringData();
|
||||
ot::TestSteeringDataBloomFilterMerge();
|
||||
ot::TestTimestamp();
|
||||
|
||||
printf("\nAll tests passed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user