[child] enhance Child::AddressIterator (#5269)

This commit enhances the iterator `Child::AddressIterator` which is
used to iterate over IPv6 address list of a `Child`. The new model
enables range-based `for` loops to be used. It also enables filtering
addresses based on type (e.g., iterate over multicast addresses only).
The unit test `test_child.cpp` is also updated to verify the newly
added functionalities of `AddressIterator`.
This commit is contained in:
Abtin Keshavarzian
2020-07-23 12:16:45 -07:00
committed by GitHub
parent 7a612437ed
commit 2efe99c507
8 changed files with 365 additions and 118 deletions
+16 -10
View File
@@ -273,22 +273,28 @@ otError otThreadGetChildInfoByIndex(otInstance *aInstance, uint16_t aChildIndex,
otError otThreadGetChildNextIp6Address(otInstance * aInstance,
uint16_t aChildIndex,
otChildIp6AddressIterator *aIterator,
otChildIp6AddressIterator *aIterIndex,
otIp6Address * aAddress)
{
otError error = OT_ERROR_NONE;
Instance & instance = *static_cast<Instance *>(aInstance);
Child::Ip6AddressIterator iterator;
Ip6::Address * address;
otError error = OT_ERROR_NONE;
Instance & instance = *static_cast<Instance *>(aInstance);
const Child *child;
OT_ASSERT(aIterator != nullptr && aAddress != nullptr);
OT_ASSERT(aIterIndex != nullptr && aAddress != nullptr);
address = static_cast<Ip6::Address *>(aAddress);
iterator.Set(*aIterator);
child = instance.Get<ChildTable>().GetChildAtIndex(aChildIndex);
VerifyOrExit(child != nullptr, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(child->IsStateValidOrRestoring(), error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = instance.Get<Mle::MleRouter>().GetChildNextIp6Address(aChildIndex, iterator, *address));
{
Child::AddressIterator iter(*child, *aIterIndex);
*aIterator = iterator.Get();
VerifyOrExit(!iter.IsDone(), error = OT_ERROR_NOT_FOUND);
*aAddress = *iter.GetAddress();
iter++;
*aIterIndex = iter.GetAsIndex();
}
exit:
return error;
+25
View File
@@ -369,6 +369,31 @@ uint8_t Address::PrefixMatch(const otIp6Address &aOther) const
return PrefixMatch(mFields.m8, aOther.mFields.m8, sizeof(Address));
}
bool Address::MatchesFilter(TypeFilter aFilter) const
{
bool matches = true;
switch (aFilter)
{
case kTypeAny:
break;
case kTypeUnicast:
matches = !IsUnspecified() && !IsMulticast();
break;
case kTypeMulticast:
matches = IsMulticast();
break;
case kTypeMulticastLargerThanRealmLocal:
matches = IsMulticastLargerThanRealmLocal();
break;
}
return matches;
}
otError Address::FromString(const char *aBuf)
{
otError error = OT_ERROR_NONE;
+23
View File
@@ -324,6 +324,18 @@ public:
kGlobalScope = 14, ///< Global scope
};
/**
* This enumeration defines IPv6 address type filter.
*
*/
enum TypeFilter : uint8_t
{
kTypeAny, ///< Accept any IPv6 address (unicast or multicast).
kTypeUnicast, ///< Accept unicast IPv6 addresses only.
kTypeMulticast, ///< Accept multicast IPv6 addresses only.
kTypeMulticastLargerThanRealmLocal, ///< Accept multicast IPv6 addresses with scope larger than Realm Local.
};
/**
* This type defines the fixed-length `String` object returned from `ToString()`.
*
@@ -618,6 +630,17 @@ public:
*/
uint8_t PrefixMatch(const otIp6Address &aOther) const;
/**
* This method indicates whether address matches a given type filter.
*
* @param[in] aFilter An address type filter.
*
* @retval TRUE The address matches @p aFilter.
* @retval FALSE The address does not match @p aFilter.
*
*/
bool MatchesFilter(TypeFilter aFilter) const;
/**
* This method converts an IPv6 address string to binary.
*
+7 -26
View File
@@ -3640,23 +3640,6 @@ exit:
return error;
}
otError MleRouter::GetChildNextIp6Address(uint16_t aChildIndex,
Child::Ip6AddressIterator &aIterator,
Ip6::Address & aAddress)
{
otError error = OT_ERROR_NONE;
Child * child = nullptr;
child = mChildTable.GetChildAtIndex(aChildIndex);
VerifyOrExit(child != nullptr, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(child->IsStateValidOrRestoring(), error = OT_ERROR_INVALID_ARGS);
error = child->GetNextIp6Address(aIterator, aAddress);
exit:
return error;
}
void MleRouter::RestoreChildren(void)
{
otError error = OT_ERROR_NONE;
@@ -4425,19 +4408,17 @@ otError MleRouter::AppendConnectivity(Message &aMessage)
otError MleRouter::AppendChildAddresses(Message &aMessage, Child &aChild)
{
otError error;
Ip6::Address address;
Child::Ip6AddressIterator iterator;
Tlv tlv;
AddressRegistrationEntry entry;
Lowpan::Context context;
uint8_t length = 0;
uint16_t startOffset = aMessage.GetLength();
otError error;
Tlv tlv;
AddressRegistrationEntry entry;
Lowpan::Context context;
uint8_t length = 0;
uint16_t startOffset = aMessage.GetLength();
tlv.SetType(Tlv::kAddressRegistration);
SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv)));
while (aChild.GetNextIp6Address(iterator, address) == OT_ERROR_NONE)
for (const Ip6::Address &address : aChild.IterateIp6Addresses())
{
if (address.IsMulticast() || Get<NetworkData::Leader>().GetContext(address, context) != OT_ERROR_NONE)
{
-15
View File
@@ -441,21 +441,6 @@ public:
*/
otError GetChildInfoByIndex(uint16_t aChildIndex, otChildInfo &aChildInfo);
/**
* This methods gets the next IPv6 address (using an iterator) for a given child.
*
* @param[in] aChildIndex The child index.
* @param[inout] aIterator A reference to iterator. On success the iterator will be updated to point to next
* entry in the list.
* @param[out] aAddress A reference to an IPv6 address where the child's next address is placed (on success).
*
* @retval OT_ERROR_NONE Successfully found the next address (@p aAddress and @ aIterator are updated).
* @retval OT_ERROR_NOT_FOUND The child has no subsequent IPv6 address entry.
* @retval OT_ERROR_INVALID_ARGS Child at @p aChildIndex is not valid.
*
*/
otError GetChildNextIp6Address(uint16_t aChildIndex, Child::Ip6AddressIterator &aIterator, Ip6::Address &aAddress);
/**
* This method indicates whether or not the RLOC16 is an MTD child of this device.
*
+33 -25
View File
@@ -111,6 +111,39 @@ void Neighbor::GenerateChallenge(void)
Random::Crypto::FillBuffer(mValidPending.mPending.mChallenge, sizeof(mValidPending.mPending.mChallenge)));
}
const Ip6::Address *Child::AddressIterator::GetAddress(void) const
{
// `mIndex` value of zero indicates mesh-local IPv6 address.
// Non-zero value specifies the index into address array starting
// from one for first element (i.e, `mIndex - 1` gives the array
// index).
return (mIndex == 0) ? &mMeshLocalAddress : ((mIndex < kMaxIndex) ? &mChild.mIp6Address[mIndex - 1] : nullptr);
}
void Child::AddressIterator::Update(void)
{
const Ip6::Address *address;
while (true)
{
if ((mIndex == 0) && (mChild.GetMeshLocalIp6Address(mMeshLocalAddress) != OT_ERROR_NONE))
{
mIndex++;
}
address = GetAddress();
VerifyOrExit((address != nullptr) && !address->IsUnspecified(), mIndex = kMaxIndex);
VerifyOrExit(!address->MatchesFilter(mFilter), OT_NOOP);
mIndex++;
}
exit:
return;
}
void Child::Clear(void)
{
Instance &instance = GetInstance();
@@ -138,31 +171,6 @@ exit:
return error;
}
otError Child::GetNextIp6Address(Ip6AddressIterator &aIterator, Ip6::Address &aAddress) const
{
otError error = OT_ERROR_NONE;
otChildIp6AddressIterator index;
// Index zero corresponds to the Mesh Local IPv6 address (if any).
if (aIterator.Get() == 0)
{
aIterator.Increment();
VerifyOrExit(GetMeshLocalIp6Address(aAddress) == OT_ERROR_NOT_FOUND, OT_NOOP);
}
index = aIterator.Get() - 1;
VerifyOrExit(index < kNumIp6Addresses, error = OT_ERROR_NOT_FOUND);
VerifyOrExit(!mIp6Address[index].IsUnspecified(), error = OT_ERROR_NOT_FOUND);
aAddress = mIp6Address[index];
aIterator.Increment();
exit:
return error;
}
otError Child::AddIp6Address(const Ip6::Address &aAddress)
{
otError error = OT_ERROR_NONE;
+161 -25
View File
@@ -497,6 +497,8 @@ private:
*/
class Child : public Neighbor, public IndirectSender::ChildInfo, public DataPollHandler::ChildInfo
{
class AddressIteratorBuilder;
public:
enum
{
@@ -504,53 +506,160 @@ public:
};
/**
* This class defines an iterator used by `GetNextIp6Address()` to go through IPv6 address entries of a child.
* This class defines an iterator used to go through IPv6 address entries of a child.
*
*/
class Ip6AddressIterator
class AddressIterator
{
friend class Child;
friend class AddressIteratorBuilder;
public:
/**
* This constructor initializes the iterator object.
*
* After initialization a call to `GetNextIp6Address()` would start at the first IPv6 address entry in the list.
* This type represents an index indicating the current IPv6 address entry to which the iterator is pointing.
*
*/
Ip6AddressIterator(void)
: mIndex(0)
typedef otChildIp6AddressIterator Index;
/**
* This constructor initializes the iterator associated with a given `Child` starting from beginning of the
* IPv6 address list.
*
* @param[in] aChild A reference to a child entry.
* @param[in] aFilter An IPv6 address type filter restricting iterator to certain type of addresses.
*
*/
AddressIterator(const Child &aChild, Ip6::Address::TypeFilter aFilter = Ip6::Address::kTypeAny)
: AddressIterator(aChild, 0, aFilter)
{
}
/**
* This method resets the iterator.
* This constructor initializes the iterator associated with a given `Child` starting from a given index
*
* After reset the next call to `GetNextIp6Address()` would start at the first IPv6 address entry in the list.
* @param[in] aChild A reference to the child entry.
* @param[in] aIndex An index (`Index`) with which to initialize the iterator.
* @param[in] aFilter An IPv6 address type filter restricting iterator to certain type of addresses.
*
*/
void Reset(void) { mIndex = 0; }
AddressIterator(const Child &aChild, Index aIndex, Ip6::Address::TypeFilter aFilter = Ip6::Address::kTypeAny)
: mChild(aChild)
, mFilter(aFilter)
, mIndex(aIndex)
{
Update();
}
/**
* This method sets the iterator from an `otChildIp6AddressIterator`
* This method converts the iterator into an index.
*
* @param[in] aChildAddressIterator A child address iterator
* @returns An index corresponding to the iterator.
*
*/
void Set(otChildIp6AddressIterator aChildAddressIterator) { mIndex = aChildAddressIterator; }
Index GetAsIndex(void) const { return mIndex; }
/**
* This method returns the iterator as an `otChildIp6AddressIterator`
* This method gets the iterator's associated `Child` entry.
*
* @returns The iterator as an `otChildIp6AddressIterator`
* @returns The associated child entry.
*
*/
otChildIp6AddressIterator Get(void) const { return mIndex; }
const Child &GetChild(void) const { return mChild; }
/**
* This method gets the current `Child` IPv6 Address to which the iterator is pointing.
*
* @returns A pointer to the associated IPv6 Address, or nullptr if iterator is done.
*
*/
const Ip6::Address *GetAddress(void) const;
/**
* This method indicates whether the iterator has reached end of the list.
*
* @retval TRUE There are no more entries in the list (reached end of the list).
* @retval FALSE The current entry is valid.
*
*/
bool IsDone(void) const { return (mIndex >= kMaxIndex); }
/**
* This method overloads `++` operator (pre-increment) to advance the iterator.
*
* The iterator is moved to point to the next `Address` entry. If there are no more `Ip6::Address` entries
* `IsDone()` returns `true`.
*
*/
void operator++(void) { mIndex++, Update(); }
/**
* This method overloads `++` operator (post-increment) to advance the iterator.
*
* The iterator is moved to point to the next `Address` entry. If there are no more `Ip6::Address` entries
* `IsDone()` returns `true`.
*
*/
void operator++(int) { mIndex++, Update(); }
/**
* This method overloads the `*` dereference operator and gets a reference to `Ip6::Address` to which the
* iterator is currently pointing.
*
* This method MUST be used when the iterator is not done (i.e., `IsDone()` returns `false`).
*
* @returns A reference to the `Ip6::Address` entry currently pointed by the iterator.
*
*/
const Ip6::Address &operator*(void)const { return *GetAddress(); }
/**
* This method overloads operator `==` to evaluate whether or not two `Iterator` instances are equal.
*
* This method MUST be used when the two iterators are associated with the same `Child` entry.
*
* @param[in] aOther The other `Iterator` to compare with.
*
* @retval TRUE If the two `Iterator` objects are equal.
* @retval FALSE If the two `Iterator` objects are not equal.
*
*/
bool operator==(const AddressIterator &aOther) const { return (mIndex == aOther.mIndex); }
/**
* This method overloads operator `!=` to evaluate whether or not two `Iterator` instances are unequal.
*
* This method MUST be used when the two iterators are associated with the same `Child` entry.
*
* @param[in] aOther The other `Iterator` to compare with.
*
* @retval TRUE If the two `Iterator` objects are unequal.
* @retval FALSE If the two `Iterator` objects are not unequal.
*
*/
bool operator!=(const AddressIterator &aOther) const { return !(*this == aOther); }
private:
void Increment(void) { mIndex++; }
enum IteratorType : uint8_t
{
kEndIterator,
};
otChildIp6AddressIterator mIndex;
enum : uint16_t
{
kMaxIndex = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD,
};
AddressIterator(const Child &aChild, IteratorType)
: mChild(aChild)
, mIndex(kMaxIndex)
{
}
void Update(void);
const Child & mChild;
Ip6::Address::TypeFilter mFilter;
Index mIndex;
Ip6::Address mMeshLocalAddress;
};
/**
@@ -593,16 +702,26 @@ public:
const Ip6::InterfaceIdentifier &GetMeshLocalIid(void) const { return mMeshLocalIid; }
/**
* This method gets the next IPv6 address in the list.
* This method enables range-based `for` loop iteration over all (or a subset of) IPv6 addresses.
*
* @param[inout] aIterator A reference to an IPv6 address iterator.
* @param[out] aAddress A reference to an IPv6 address to provide the next address (if any).
* This method should be used as follows: to iterate over all addresses
*
* @retval OT_ERROR_NONE Successfully found the next address and updated @p aAddress and @p aIterator.
* @retval OT_ERROR_NOT_FOUND No subsequent IPv6 address exists in the IPv6 address list.
* for (const Ip6::Address &address : child.IterateIp6Addresses()) { ... }
*
* or to iterate over a subset of IPv6 addresses determined by a given address type filter
*
* for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticast)) { ... }
*
* @param[in] aFilter An IPv6 address type filter restricting iteration to certain type of addresses (default is
* to accept any address type).
*
* @returns An IteratorBuilder instance.
*
*/
otError GetNextIp6Address(Ip6AddressIterator &aIterator, Ip6::Address &aAddress) const;
AddressIteratorBuilder IterateIp6Addresses(Ip6::Address::TypeFilter aFilter = Ip6::Address::kTypeAny) const
{
return AddressIteratorBuilder(*this, aFilter);
}
/**
* This method adds an IPv6 address to the list.
@@ -753,6 +872,23 @@ private:
kNumIp6Addresses = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD - 1,
};
class AddressIteratorBuilder
{
public:
AddressIteratorBuilder(const Child &aChild, Ip6::Address::TypeFilter aFilter)
: mChild(aChild)
, mFilter(aFilter)
{
}
AddressIterator begin(void) { return AddressIterator(mChild, mFilter); }
AddressIterator end(void) { return AddressIterator(mChild, AddressIterator::kEndIterator); }
private:
const Child & mChild;
Ip6::Address::TypeFilter mFilter;
};
uint8_t mNetworkDataVersion; ///< Current Network Data version
Ip6::InterfaceIdentifier mMeshLocalIid; ///< IPv6 address IID for mesh-local address
Ip6::Address mIp6Address[kNumIp6Addresses]; ///< Registered IPv6 addresses
+100 -17
View File
@@ -46,11 +46,9 @@ enum
void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, const Ip6::Address aAddressList[])
{
Child::Ip6AddressIterator iterator;
Ip6::Address address;
bool addressObserved[kMaxChildIp6Addresses];
bool addressIsMeshLocal[kMaxChildIp6Addresses];
bool hasMeshLocal = false;
Ip6::Address::TypeFilter filters[] = {Ip6::Address::kTypeUnicast, Ip6::Address::kTypeMulticast};
bool addressObserved[kMaxChildIp6Addresses];
bool hasMeshLocal = false;
for (uint8_t index = 0; index < aAddressListLength; index++)
{
@@ -58,16 +56,8 @@ void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, co
}
memset(addressObserved, 0, sizeof(addressObserved));
memset(addressIsMeshLocal, 0, sizeof(addressObserved));
for (uint8_t index = 0; index < aAddressListLength; index++)
{
{
addressIsMeshLocal[index] = true;
}
}
while (aChild.GetNextIp6Address(iterator, address) == OT_ERROR_NONE)
for (const Ip6::Address &address : aChild.IterateIp6Addresses())
{
bool addressIsInList = false;
@@ -81,12 +71,14 @@ void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, co
}
}
VerifyOrQuit(addressIsInList, "Child::GetNextIp6Address() returned an address not in the expected list");
VerifyOrQuit(addressIsInList, "Child::IterateIp6Addresses() returned an address not in the expected list");
}
for (uint8_t index = 0; index < aAddressListLength; index++)
{
VerifyOrQuit(addressObserved[index], "Child::GetNextIp6Address() missed an entry from the expected list");
Ip6::Address address;
VerifyOrQuit(addressObserved[index], "Child::IterateIp6Addresses() missed an entry from the expected list");
if (sInstance->Get<Mle::MleRouter>().IsMeshLocalAddress(aAddressList[index]))
{
@@ -98,9 +90,100 @@ void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, co
if (!hasMeshLocal)
{
Ip6::Address address;
VerifyOrQuit(aChild.GetMeshLocalIp6Address(address) == OT_ERROR_NOT_FOUND,
"Child::GetMeshLocalIp6Address() returned an address not in the expected list");
}
// Iterate over unicast and multicast addresses separately.
memset(addressObserved, 0, sizeof(addressObserved));
for (Ip6::Address::TypeFilter filter : filters)
{
for (const Ip6::Address &address : aChild.IterateIp6Addresses(filter))
{
bool addressIsInList = false;
switch (filter)
{
case Ip6::Address::kTypeMulticast:
VerifyOrQuit(address.IsMulticast(), "Address::TypeFilter failed");
break;
case Ip6::Address::kTypeUnicast:
VerifyOrQuit(!address.IsMulticast(), "Address::TypeFilter failed");
break;
default:
break;
}
VerifyOrQuit(address.MatchesFilter(filter), "Address::MatchesFilter() failed");
for (uint8_t index = 0; index < aAddressListLength; index++)
{
if (address == aAddressList[index])
{
VerifyOrQuit(addressObserved[index] == false,
"Child::IterateIp6Addresses() returned duplicate addr");
addressObserved[index] = true;
addressIsInList = true;
break;
}
}
VerifyOrQuit(addressIsInList, "Child::IterateIp6Addresses() returned an address not in the expected list");
}
}
for (uint8_t index = 0; index < aAddressListLength; index++)
{
VerifyOrQuit(addressObserved[index], "Child::IterateIp6Addresses() missed an entry from the expected list");
}
// Verify behavior of `Child::AddressIterator
{
Child::AddressIterator iter1(aChild);
Child::AddressIterator iter2(aChild);
Child::AddressIterator::Index iterIndex;
for (const Ip6::Address &address : aChild.IterateIp6Addresses())
{
VerifyOrQuit(iter1 == iter2, "AddressIterator:operator== failed");
VerifyOrQuit(!iter1.IsDone(), "AddressIterator::IsDone() failed");
VerifyOrQuit(*iter1.GetAddress() == address, "AddressIterator::GetAddress() failed");
VerifyOrQuit(*iter1.GetAddress() == *iter2.GetAddress(), "AddressIterator::GetAddress() failed");
iterIndex = iter1.GetAsIndex();
VerifyOrQuit(iter2.GetAsIndex() == iterIndex, "AddressIterator: GetAsIndex() failed");
{
Child::AddressIterator iter3(aChild, iterIndex);
VerifyOrQuit(iter3 == iter1, "AddressIterator(iterIndex) failed");
iter3++;
VerifyOrQuit(iter3 != iter1, "AddressIterator(iterIndex) failed");
}
iter1++;
VerifyOrQuit(iter1 != iter2, "AddressIterator:operator!= failed");
iter2++;
}
VerifyOrQuit(iter1.IsDone(), "AddressIterator::IsDone() failed");
VerifyOrQuit(iter2.IsDone(), "AddressIterator::IsDone() failed");
VerifyOrQuit(iter1 == iter2, "AddressIterator:operator== failed");
iterIndex = iter1.GetAsIndex();
VerifyOrQuit(iter2.GetAsIndex() == iterIndex, "AddressIterator: GetAsIndex() failed");
{
Child::AddressIterator iter3(aChild, iterIndex);
VerifyOrQuit(iter3 == iter1, "AddressIterator(iterIndex) failed");
}
}
}
void TestChildIp6Address(void)
@@ -110,7 +193,7 @@ void TestChildIp6Address(void)
uint8_t numAddresses;
const char * ip6Addresses[] = {
"fd00:1234::1234",
"fd6b:e251:52fb:0:12e6:b94c:1c28:c56a",
"ff6b:e251:52fb:0:12e6:b94c:1c28:c56a",
"fd00:1234::204c:3d7c:98f6:9a1b",
};