From a0ba929e3984d7f7e245c99868869966bda74111 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 27 Jun 2024 09:54:07 -0700 Subject: [PATCH 001/160] [mle] add RLOC16 related helper methods in `Mle` (#10440) This commit introduces new helper methods in the `Mle` class: - `HasRloc16()`: Checks if the device is using a given RLOC16. - `MatchesRouterId()`: Checks if this device's RLOC16 matches a given Router ID. - `HasMatchingRouterIdWith()`: Checks if this device's RLOC16 shares the same Router ID with a given RLOC16. This implies that the two devices are either directly related as parent and child or are children of the same parent within the Thread network. - `ParentRloc16ForRloc16()` derives the router RLOC16 corresponding to the parent of a given (child) RLOC16. These methods act as syntactic sugar, simplifying code and enhancing readability. --- src/core/api/thread_api.cpp | 4 +-- src/core/thread/address_resolver.cpp | 12 ++++---- src/core/thread/child_table.cpp | 2 +- src/core/thread/mesh_forwarder_ftd.cpp | 21 +++++++------ src/core/thread/mle.cpp | 4 +-- src/core/thread/mle.hpp | 36 +++++++++++++++++++++++ src/core/thread/mle_router.cpp | 2 +- src/core/thread/mle_types.hpp | 12 ++++++++ src/core/thread/network_data_leader.cpp | 2 +- src/core/thread/network_data_notifier.cpp | 2 +- src/core/thread/router_table.cpp | 32 ++++++++++---------- src/core/utils/mesh_diag.cpp | 4 +-- 12 files changed, 91 insertions(+), 42 deletions(-) diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp index eb55386f6..d09771862 100644 --- a/src/core/api/thread_api.cpp +++ b/src/core/api/thread_api.cpp @@ -81,7 +81,7 @@ otError otThreadGetLeaderRloc(otInstance *aInstance, otIp6Address *aLeaderRloc) { Error error = kErrorNone; - VerifyOrExit(AsCoreType(aInstance).Get().GetRloc16() != Mle::kInvalidRloc16, error = kErrorDetached); + VerifyOrExit(!AsCoreType(aInstance).Get().HasRloc16(Mle::kInvalidRloc16), error = kErrorDetached); AsCoreType(aInstance).Get().GetLeaderRloc(AsCoreType(aLeaderRloc)); exit: @@ -197,7 +197,7 @@ otError otThreadGetServiceAloc(otInstance *aInstance, uint8_t aServiceId, otIp6A { Error error = kErrorNone; - VerifyOrExit(AsCoreType(aInstance).Get().GetRloc16() != Mle::kInvalidRloc16, error = kErrorDetached); + VerifyOrExit(!AsCoreType(aInstance).Get().HasRloc16(Mle::kInvalidRloc16), error = kErrorDetached); AsCoreType(aInstance).Get().GetServiceAloc(aServiceId, AsCoreType(aServiceAloc)); exit: diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index 913b184a0..930bfbe05 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -381,12 +381,15 @@ void AddressResolver::UpdateSnoopedCacheEntry(const Ip6::Address &aEid, uint16_t { uint16_t numNonEvictable = 0; CacheEntry *entry; - uint16_t deviceRloc16; VerifyOrExit(Get().IsFullThreadDevice()); #if OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES - VerifyOrExit(ResolveUsingNetDataServices(aEid, deviceRloc16) != kErrorNone); + { + uint16_t rloc16; + + VerifyOrExit(ResolveUsingNetDataServices(aEid, rloc16) != kErrorNone); + } #endif VerifyOrExit(UpdateCacheEntry(aEid, aRloc16) != kErrorNone); @@ -394,13 +397,12 @@ void AddressResolver::UpdateSnoopedCacheEntry(const Ip6::Address &aEid, uint16_t // Skip if the `aRloc16` (i.e., the source of the snooped message) // is this device or an MTD (minimal) child of the device itself. - deviceRloc16 = Get().GetRloc16(); - VerifyOrExit((aRloc16 != deviceRloc16) && !Get().HasMinimalChild(aRloc16)); + VerifyOrExit(!Get().HasRloc16(aRloc16) && !Get().HasMinimalChild(aRloc16)); // Ensure that the destination of the snooped message is this device // or a minimal child of this device. - VerifyOrExit((aDest == deviceRloc16) || Get().HasMinimalChild(aDest)); + VerifyOrExit(Get().HasRloc16(aDest) || Get().HasMinimalChild(aDest)); entry = NewCacheEntry(/* aSnoopedEntry */ true); VerifyOrExit(entry != nullptr); diff --git a/src/core/thread/child_table.cpp b/src/core/thread/child_table.cpp index 2762bad31..5e28fd626 100644 --- a/src/core/thread/child_table.cpp +++ b/src/core/thread/child_table.cpp @@ -320,7 +320,7 @@ bool ChildTable::HasMinimalChild(uint16_t aRloc16) const bool hasMinimalChild = false; const Child *child; - VerifyOrExit(Mle::RouterIdMatch(aRloc16, Get().GetRloc16())); + VerifyOrExit(Get().HasMatchingRouterIdWith(aRloc16)); child = FindChild(Child::AddressMatcher(aRloc16, Child::kInStateValidOrRestoring)); VerifyOrExit(child != nullptr); diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 966b7f628..93ff6fd8d 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -174,7 +174,7 @@ void MeshForwarder::HandleResolved(const Ip6::Address &aEid, Error aError) // Pass back to IPv6 layer for DUA destination resolved // by Backbone Query if (Get().IsPrimary() && Get().IsDomainUnicast(ip6Dst) && - Get().LookUp(ip6Dst) == Get().GetRloc16()) + Get().HasRloc16(Get().LookUp(ip6Dst))) { uint8_t hopLimit; @@ -490,9 +490,9 @@ Error MeshForwarder::AnycastRouteLookup(uint8_t aServiceId, AnycastType aType, u // as the destination unless the device itself is the // parent of the `bestDest`. - uint16_t bestDestParent = Mle::Rloc16FromRouterId(Mle::RouterIdFromRloc16(bestDest)); + uint16_t bestDestParent = Mle::ParentRloc16ForRloc16(bestDest); - if (Get().GetRloc16() != bestDestParent) + if (!Get().HasRloc16(bestDestParent)) { bestDest = bestDestParent; } @@ -565,7 +565,7 @@ Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &a // child of this device, prepare the message for indirect tx // to the sleepy child and un-mark message for direct tx. - if (mle.IsRouterOrLeader() && Mle::IsChildRloc16(mMeshDest) && Mle::RouterIdMatch(mMeshDest, mle.GetRloc16())) + if (mle.IsRouterOrLeader() && Mle::IsChildRloc16(mMeshDest) && mle.HasMatchingRouterIdWith(mMeshDest)) { Child *child = Get().FindChild(mMeshDest, Child::kInStateValid); @@ -673,12 +673,11 @@ Error MeshForwarder::CheckReachability(const FrameData &aFrameData, const Mac::A Error MeshForwarder::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header) { - bool isReachable = false; - uint16_t deviceRloc16 = Get().GetRloc16(); + bool isReachable = false; if (Get().IsChild()) { - if (aMeshDest == deviceRloc16) + if (Get().HasRloc16(aMeshDest)) { isReachable = Get().HasUnicastAddress(aIp6Header.GetDestination()); } @@ -690,14 +689,14 @@ Error MeshForwarder::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aI ExitNow(); } - if (aMeshDest == deviceRloc16) + if (Get().HasRloc16(aMeshDest)) { isReachable = Get().HasUnicastAddress(aIp6Header.GetDestination()) || (Get().FindNeighbor(aIp6Header.GetDestination()) != nullptr); ExitNow(); } - if (Mle::RouterIdMatch(aMeshDest, deviceRloc16)) + if (Get().HasMatchingRouterIdWith(aMeshDest)) { isReachable = (Get().FindChild(aMeshDest, Child::kInStateValidOrRestoring) != nullptr); ExitNow(); @@ -735,7 +734,7 @@ void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSo UpdateRoutes(aFrameData, meshAddrs); - if (meshAddrs.mDestination.GetShort() == Get().GetRloc16() || + if (Get().HasRloc16(meshAddrs.mDestination.GetShort()) || Get().HasMinimalChild(meshAddrs.mDestination.GetShort())) { if (Lowpan::FragmentHeader::IsFragmentHeader(aFrameData)) @@ -838,7 +837,7 @@ void MeshForwarder::UpdateRoutes(const FrameData &aFrameData, const Mac::Address neighbor = Get().FindNeighbor(ip6Headers.GetSourceAddress()); VerifyOrExit(neighbor != nullptr && !neighbor->IsFullThreadDevice()); - if (!Mle::RouterIdMatch(aMeshAddrs.mSource.GetShort(), Get().GetRloc16())) + if (!Get().HasMatchingRouterIdWith(aMeshAddrs.mSource.GetShort())) { Get().RemoveNeighbor(*neighbor); } diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 4a009cad9..0b013e350 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -432,7 +432,7 @@ void Mle::Restore(void) mParent.SetVersion(parentInfo.GetVersion()); mParent.SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle | DeviceMode::kModeFullNetworkData)); - mParent.SetRloc16(Rloc16FromRouterId(RouterIdFromRloc16(networkInfo.GetRloc16()))); + mParent.SetRloc16(ParentRloc16ForRloc16(networkInfo.GetRloc16())); mParent.SetState(Neighbor::kStateRestored); mPreviousParentRloc = mParent.GetRloc16(); @@ -3558,7 +3558,7 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) case kRoleChild: SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, sourceAddress)); - if (!RouterIdMatch(sourceAddress, GetRloc16())) + if (!HasMatchingRouterIdWith(sourceAddress)) { IgnoreError(BecomeDetached()); ExitNow(); diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 1dfcfed13..24afe67a9 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -523,6 +523,42 @@ class Mle : public InstanceLocator, private NonCopyable */ uint16_t GetRloc16(void) const { return mRloc16; } + /** + * Indicates whether or not this device is using a given RLOC16. + * + * @param[in] aRloc16 The RLOC16 to check. + * + * @retval TRUE This device is using @p aRloc16. + * @retval FALSE This device is not using @p aRloc16. + * + */ + bool HasRloc16(uint16_t aRloc16) const { return mRloc16 == aRloc16; } + + /** + * Indicates whether or not this device RLOC16 matches a given Router ID. + * + * @param[in] aRouterId The Router ID to check. + * + * @retval TRUE This device's RLOC16 matches the @p aRouterId. + * @retval FALSE This device's RLOC16 does not match the @p aRouterId. + * + */ + bool MatchesRouterId(uint8_t aRouterId) const { return RouterIdFromRloc16(mRloc16) == aRouterId; } + + /** + * Indicates whether or not this device's RLOC16 shares the same Router ID with a given RLOC16. + * + * A shared Router ID implies that this device and the @ aRloc16 are either directly related as parent and child, + * or are children of the same parent within the Thread network. + * + * @param[in] aRloc16 The RLOC16 to check. + * + * @retval TRUE This device and @p aRloc16 have a matching router ID. + * @retval FALSE This device and @p aRloc16 do not have a matching router ID. + * + */ + bool HasMatchingRouterIdWith(uint16_t aRloc16) const { return RouterIdMatch(mRloc16, aRloc16); } + /** * Returns the mesh local RLOC IPv6 address assigned to the Thread interface. * diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index e03712345..6948f9e43 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -2786,7 +2786,7 @@ Error MleRouter::SendChildIdResponse(Child &aChild) SuccessOrExit(error = message->AppendLeaderDataTlv()); SuccessOrExit(error = message->AppendActiveAndPendingTimestampTlvs()); - if ((aChild.GetRloc16() == 0) || !RouterIdMatch(aChild.GetRloc16(), GetRloc16())) + if ((aChild.GetRloc16() == 0) || !HasMatchingRouterIdWith(aChild.GetRloc16())) { uint16_t rloc16; diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp index a228d06ee..f8ee58cbf 100644 --- a/src/core/thread/mle_types.hpp +++ b/src/core/thread/mle_types.hpp @@ -681,6 +681,18 @@ inline uint16_t CommissionerAloc16FromId(uint16_t aSessionId) */ inline uint16_t Rloc16FromRouterId(uint8_t aRouterId) { return static_cast(aRouterId << kRouterIdOffset); } +/** + * Derives the router RLOC16 corresponding to the parent of a given (child) RLOC16. + * + * If @p aRloc16 itself refers to a router, then the same RLOC16 value is returned. + * + * @param[in] aRloc16 An RLOC16. + * + * @returns The router RLOC16 corresponding to the parent associated with @p aRloc16. + * + */ +inline uint16_t ParentRloc16ForRloc16(uint16_t aRloc16) { return Rloc16FromRouterId(RouterIdFromRloc16(aRloc16)); } + /** * Indicates whether or not @p aRloc16 refers to a router. * diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp index 95b978907..efb6225bd 100644 --- a/src/core/thread/network_data_leader.cpp +++ b/src/core/thread/network_data_leader.cpp @@ -324,7 +324,7 @@ int Leader::CompareRouteEntries(int8_t aFirstPreference, // On MTD, prefer the BR that is this device itself. This handles // the uncommon case where an MTD itself may be acting as BR. - result = ThreeWayCompare((aFirstRloc == Get().GetRloc16()), (aSecondRloc == Get().GetRloc16())); + result = ThreeWayCompare((Get().HasRloc16(aFirstRloc)), Get().HasRloc16(aSecondRloc)); #endif #if OPENTHREAD_FTD diff --git a/src/core/thread/network_data_notifier.cpp b/src/core/thread/network_data_notifier.cpp index bbcd35bf3..d77c6c397 100644 --- a/src/core/thread/network_data_notifier.cpp +++ b/src/core/thread/network_data_notifier.cpp @@ -138,7 +138,7 @@ Error Notifier::RemoveStaleChildEntries(void) for (uint16_t rloc16 : rlocs) { - if (Mle::IsChildRloc16(rloc16) && Mle::RouterIdMatch(Get().GetRloc16(), rloc16) && + if (Mle::IsChildRloc16(rloc16) && Get().HasMatchingRouterIdWith(rloc16) && Get().FindChild(rloc16, Child::kInStateValid) == nullptr) { error = SendServerDataNotification(rloc16); diff --git a/src/core/thread/router_table.cpp b/src/core/thread/router_table.cpp index 16a4d98c5..e882a6f16 100644 --- a/src/core/thread/router_table.cpp +++ b/src/core/thread/router_table.cpp @@ -268,7 +268,7 @@ Router *RouterTable::FindNeighbor(uint16_t aRloc16) { Router *router = nullptr; - VerifyOrExit(aRloc16 != Get().GetRloc16()); + VerifyOrExit(!Get().HasRloc16(aRloc16)); router = FindRouter(Router::AddressMatcher(aRloc16, Router::kInStateValid)); exit: @@ -362,7 +362,7 @@ uint8_t RouterTable::GetLinkCost(const Router &aRouter) const { uint8_t rval = Mle::kMaxRouteCost; - VerifyOrExit(aRouter.GetRloc16() != Get().GetRloc16() && aRouter.IsStateValid()); + VerifyOrExit(!Get().HasRloc16(aRouter.GetRloc16()) && aRouter.IsStateValid()); rval = CostForLinkQuality(aRouter.GetTwoWayLinkQuality()); @@ -400,7 +400,6 @@ uint8_t RouterTable::GetPathCostToLeader(void) const { return GetPathCost(Get().IsAttached()); - if (aDestRloc16 == Get().GetRloc16()) + if (Get().HasRloc16(aDestRloc16)) { // Destination is this device, return cost as zero. aPathCost = 0; @@ -417,14 +416,13 @@ void RouterTable::GetNextHopAndPathCost(uint16_t aDestRloc16, uint16_t &aNextHop ExitNow(); } - destRouterId = Mle::RouterIdFromRloc16(aDestRloc16); - - router = FindRouterById(destRouterId); + router = FindRouterById(Mle::RouterIdFromRloc16(aDestRloc16)); nextHop = (router != nullptr) ? FindNextHopOf(*router) : nullptr; if (Get().IsChild()) { const Router &parent = Get().GetParent(); + bool destIsParentOrItsChild; if (parent.IsStateValid()) { @@ -436,11 +434,13 @@ void RouterTable::GetNextHopAndPathCost(uint16_t aDestRloc16, uint16_t &aNextHop // check if we have a next hop towards the destination and // add its cost to the link cost to parent. - VerifyOrExit((destRouterId == parent.GetRouterId()) || (nextHop != nullptr)); + destIsParentOrItsChild = Mle::RouterIdMatch(aDestRloc16, parent.GetRloc16()); + + VerifyOrExit(destIsParentOrItsChild || (nextHop != nullptr)); aPathCost = CostForLinkQuality(parent.GetLinkQualityIn()); - if (destRouterId != parent.GetRouterId()) + if (!destIsParentOrItsChild) { aPathCost += router->GetCost(); } @@ -450,7 +450,7 @@ void RouterTable::GetNextHopAndPathCost(uint16_t aDestRloc16, uint16_t &aNextHop } else // Role is router or leader { - if (destRouterId == Mle::RouterIdFromRloc16(Get().GetRloc16())) + if (Get().HasMatchingRouterIdWith(aDestRloc16)) { // Destination is a one of our children. @@ -590,7 +590,7 @@ void RouterTable::UpdateRoutes(const Mle::RouteTlv &aRouteTlv, uint8_t aNeighbor for (uint8_t routerId = 0, index = 0; routerId <= Mle::kMaxRouterId; index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++) { - if (routerId != Mle::RouterIdFromRloc16(Get().GetRloc16())) + if (!Get().MatchesRouterId(routerId)) { continue; } @@ -625,7 +625,7 @@ void RouterTable::UpdateRoutes(const Mle::RouteTlv &aRouteTlv, uint8_t aNeighbor router = FindRouterById(routerId); - if (router == nullptr || router->GetRloc16() == Get().GetRloc16() || router == neighbor) + if (router == nullptr || Get().HasRloc16(router->GetRloc16()) || router == neighbor) { continue; } @@ -737,8 +737,8 @@ void RouterTable::FillRouteTlv(Mle::RouteTlv &aRouteTlv, const Neighbor *aNeighb break; } - if ((routerId == Mle::RouterIdFromRloc16(Get().GetRloc16())) || - (routerId == aNeighbor->GetRouterId()) || (routerId == Get().GetLeaderId())) + if (Get().MatchesRouterId(routerId) || (routerId == aNeighbor->GetRouterId()) || + (routerId == Get().GetLeaderId())) { // Route64 TLV must contain this device and the // neighboring router to ensure that at least this @@ -775,7 +775,7 @@ void RouterTable::FillRouteTlv(Mle::RouteTlv &aRouteTlv, const Neighbor *aNeighb routerRloc16 = Mle::Rloc16FromRouterId(routerId); - if (routerRloc16 == Get().GetRloc16()) + if (Get().HasRloc16(routerRloc16)) { aRouteTlv.SetRouteData(routerIndex, kLinkQuality0, kLinkQuality0, 1); } @@ -895,7 +895,7 @@ void RouterTable::LogRouteTable(void) const string.Append(" %2d 0x%04x", router.GetRouterId(), router.GetRloc16()); - if (router.GetRloc16() == Get().GetRloc16()) + if (Get().HasRloc16(router.GetRloc16())) { string.Append(" - me"); } diff --git a/src/core/utils/mesh_diag.cpp b/src/core/utils/mesh_diag.cpp index 5e470b3f4..33c001d65 100644 --- a/src/core/utils/mesh_diag.cpp +++ b/src/core/utils/mesh_diag.cpp @@ -506,7 +506,7 @@ Error MeshDiag::RouterInfo::ParseFrom(const Message &aMessage) } mRouterId = Mle::RouterIdFromRloc16(mRloc16); - mIsThisDevice = (mRloc16 == mle.GetRloc16()); + mIsThisDevice = mle.HasRloc16(mRloc16); mIsThisDeviceParent = mle.IsChild() && (mRloc16 == mle.GetParent().GetRloc16()); mIsLeader = (mRouterId == mle.GetLeaderId()); mIsBorderRouter = aMessage.Get().ContainsBorderRouterWithRloc(mRloc16); @@ -582,7 +582,7 @@ Error MeshDiag::ChildIterator::GetNextChildInfo(ChildInfo &aChildInfo) entry.GetMode().Get(aChildInfo.mMode); aChildInfo.mLinkQuality = entry.GetLinkQuality(); - aChildInfo.mIsThisDevice = (aChildInfo.mRloc16 == mMessage->Get().GetRloc16()); + aChildInfo.mIsThisDevice = mMessage->Get().HasRloc16(aChildInfo.mRloc16); aChildInfo.mIsBorderRouter = mMessage->Get().ContainsBorderRouterWithRloc(aChildInfo.mRloc16); exit: From 356253efd6134d3f6cc2dfad3e8e5471d93178f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 08:11:47 -0700 Subject: [PATCH 002/160] github-actions: bump github/codeql-action from 3.25.3 to 3.25.11 (#10455) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.3 to 3.25.11. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/d39d31e687223d841ef683f52467bd88e9b21c14...b611370bb5703a7efb587f9d136a52ea24c5c38c) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/scorecards.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d1a4a3c4e..b12e5d817 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -66,7 +66,7 @@ jobs: sudo apt-get --no-install-recommends install -y ninja-build libreadline-dev libncurses-dev - name: Initialize CodeQL - uses: github/codeql-action/init@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/init@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -80,6 +80,6 @@ jobs: ./script/test build - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/analyze@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index ff3ada52e..f2ea58d95 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -95,6 +95,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v2.1.27 + uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v2.1.27 with: sarif_file: results.sarif From 7b9b396dd516ff0e339cfc9420c660a4b820bb5b Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 1 Jul 2024 08:12:42 -0700 Subject: [PATCH 003/160] [routing-manager] add `OnLinkPrefix::IsFavoredOver()` (#10452) This commit adds the `OnLinkPrefix::IsFavoredOver()` method to determine if an on-link prefix is eligible to be considered as a favored prefix, and if so, is favored over another prefix. A numerically smaller prefix is considered favored. Additionally, a new test case is added to `test_routing_manager` to validate the selection of favored on-link prefixes, including the the requirement of a minimum preferred lifetime of 1800 seconds for a prefix to be considered eligible. --- src/core/border_router/routing_manager.cpp | 31 +++-- src/core/border_router/routing_manager.hpp | 5 +- tests/unit/test_routing_manager.cpp | 129 +++++++++++++++++++++ 3 files changed, 154 insertions(+), 11 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 07b2c6379..36f9541bd 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -927,6 +927,27 @@ void RoutingManager::OnLinkPrefix::CopyInfoTo(PrefixTableEntry &aEntry, TimeMill aEntry.mPreferredLifetime = GetPreferredLifetime(); } +bool RoutingManager::OnLinkPrefix::IsFavoredOver(const Ip6::Prefix &aPrefix) const +{ + bool isFavored = false; + + // Validate that the `OnLinkPrefix` is eligible to be considered a + // favored on-link prefix. It must not be deprecated and have a + // preferred lifetime exceeding a minimum (1800 seconds). + + VerifyOrExit(!IsDeprecated()); + VerifyOrExit(GetPreferredLifetime() >= kFavoredMinPreferredLifetime); + + // Numerically smaller prefix is favored (unless `aPrefix` is empty). + + VerifyOrExit(aPrefix.GetLength() != 0, isFavored = true); + + isFavored = GetPrefix() < aPrefix; + +exit: + return isFavored; +} + //--------------------------------------------------------------------------------------------------------------------- // RoutePrefix @@ -1874,18 +1895,10 @@ void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const OnLinkPrefix mHasNonUlaOnLink = true; } - // Determine favored on-link prefix - - VerifyOrExit(!aOnLinkPrefix.IsDeprecated()); - VerifyOrExit(aOnLinkPrefix.GetPreferredLifetime() >= kFavoredOnLinkPrefixMinPreferredLifetime); - - if ((mFavoredOnLinkPrefix.GetLength() == 0) || (aOnLinkPrefix.GetPrefix() < mFavoredOnLinkPrefix)) + if (aOnLinkPrefix.IsFavoredOver(mFavoredOnLinkPrefix)) { mFavoredOnLinkPrefix = aOnLinkPrefix.GetPrefix(); } - -exit: - return; } void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const RoutePrefix &aRoutePrefix) diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index dec8ccc5e..5c036a58f 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -702,8 +702,11 @@ class RoutingManager : public InstanceLocator TimeMilli GetStaleTime(void) const; void AdoptValidAndPreferredLifetimesFrom(const OnLinkPrefix &aPrefix); void CopyInfoTo(PrefixTableEntry &aEntry, TimeMilli aNow) const; + bool IsFavoredOver(const Ip6::Prefix &aPrefix) const; private: + static constexpr uint32_t kFavoredMinPreferredLifetime = 1800; // In sec. + uint32_t mPreferredLifetime; }; @@ -782,8 +785,6 @@ class RoutingManager : public InstanceLocator void HandleRouterTimer(void); private: - static constexpr uint32_t kFavoredOnLinkPrefixMinPreferredLifetime = 1800; // In sec. - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - template diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp index c156a6e32..dd983cd0c 100644 --- a/tests/unit/test_routing_manager.cpp +++ b/tests/unit/test_routing_manager.cpp @@ -1168,6 +1168,16 @@ void VerifyDiscoveredRouters(const InfraRouter *aRouters, uint16_t aNumRouters) void VerifyDiscoveredRoutersIsEmpty(void) { VerifyDiscoveredRouters(nullptr, 0); } +void VerifyFavoredOnLinkPrefix(const Ip6::Prefix &aPrefix) +{ + Ip6::Prefix favoredPrefix; + + Log("VerifyFavoredOnLinkPrefix(%s)", aPrefix.ToString().AsCString()); + + SuccessOrQuit(sInstance->Get().GetFavoredOnLinkPrefix(favoredPrefix)); + VerifyOrQuit(favoredPrefix == aPrefix); +} + void InitTest(bool aEnablBorderRouting = false, bool aAfterReset = false) { uint32_t delay = 10000; @@ -1846,6 +1856,124 @@ void TestAdvNonUlaRoute(void) FinalizeTest(); } +void TestFavoredOnLinkPrefix(void) +{ + Ip6::Prefix localOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix onLinkPrefixA = PrefixFromString("2000:abba:baba:aaaa::", 64); + Ip6::Prefix onLinkPrefixB = PrefixFromString("2000:abba:baba:bbbb::", 64); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + Ip6::Address routerAddressB = AddressFromString("fd00::bbbb"); + uint16_t heapAllocations; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestFavoredOnLinkPrefix"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + VerifyFavoredOnLinkPrefix(localOnLink); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Advertise on-link prefix B from router B + + SendRouterAdvert(routerAddressB, {Pio(onLinkPrefixB, kValidLitime, kPreferredLifetime)}); + + AdvanceTime(10 * 1000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the discovered prefix table and ensure on-link prefix B is + // now the favored on-link prefix + + VerifyPrefixTable({OnLinkPrefix(onLinkPrefixB, kValidLitime, kPreferredLifetime, routerAddressB)}); + VerifyFavoredOnLinkPrefix(onLinkPrefixB); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Advertise on-link prefix A from router A with a short + // preferred lifetime (less than 1800 which is the threshold for it + // to be considered a valid favored on-link prefix). + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefixA, kValidLitime, 1799)}); + + AdvanceTime(10 * 1000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the discovered prefix table and ensure on-link prefix B is + // still the favored on-link prefix. + + VerifyPrefixTable({OnLinkPrefix(onLinkPrefixB, kValidLitime, kPreferredLifetime, routerAddressB), + OnLinkPrefix(onLinkPrefixA, kValidLitime, 1799, routerAddressA)}); + + VerifyFavoredOnLinkPrefix(onLinkPrefixB); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Advertise on-link prefix A from router A with a long + // preferred lifetime now. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefixA, kValidLitime, kPreferredLifetime)}); + + AdvanceTime(10 * 1000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the discovered prefix table and ensure that now on-link + // prefix A (which is numerically smaller) is considered as + // favored on-link prefix. + + VerifyPrefixTable({OnLinkPrefix(onLinkPrefixB, kValidLitime, kPreferredLifetime, routerAddressB), + OnLinkPrefix(onLinkPrefixA, kValidLitime, kPreferredLifetime, routerAddressA)}); + + VerifyFavoredOnLinkPrefix(onLinkPrefixA); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Deprecate on-link prefix A from router A + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefixA, kValidLitime, 0)}); + + AdvanceTime(10 * 1000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the discovered prefix table and ensure that now on-link + // prefix B is again the favored on-link prefix. + + VerifyPrefixTable({OnLinkPrefix(onLinkPrefixB, kValidLitime, kPreferredLifetime, routerAddressB), + OnLinkPrefix(onLinkPrefixA, kValidLitime, 0, routerAddressA)}); + + VerifyFavoredOnLinkPrefix(onLinkPrefixB); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + SuccessOrQuit(sInstance->Get().SetEnabled(false)); + VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength()); + + Log("End of TestFavoredOnLinkPrefix"); + FinalizeTest(); +} + void TestLocalOnLinkPrefixDeprecation(void) { static constexpr uint32_t kMaxRaTxInterval = 196; // In seconds @@ -4187,6 +4315,7 @@ int main(void) ot::TestOmrSelection(); ot::TestDefaultRoute(); ot::TestAdvNonUlaRoute(); + ot::TestFavoredOnLinkPrefix(); ot::TestLocalOnLinkPrefixDeprecation(); #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE ot::TestDomainPrefixAsOmr(); From 34484f4293dab81756d040f90c525d608fab91fe Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Mon, 1 Jul 2024 23:13:38 +0800 Subject: [PATCH 004/160] [otci] add diag commands support to otci (#10450) --- tools/otci/otci/otci.py | 191 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) diff --git a/tools/otci/otci/otci.py b/tools/otci/otci/otci.py index 93bb56a67..a3632afe6 100644 --- a/tools/otci/otci/otci.py +++ b/tools/otci/otci/otci.py @@ -2344,6 +2344,197 @@ def coap_test_set_resource_content(self, content: str): # TODO: coap parameters ["default"| ] # TODO: CoAP Secure utilities + # + # Diag Utilities + # + def diag_start(self): + """Start diagnostics mode.""" + self.execute_command('diag start') + + def diag_stop(self): + """Stop diagnostics mode.""" + self.execute_command('diag stop') + + def diag_set_channel(self, channel: int): + """Set the IEEE 802.15.4 Channel value for diagnostics module.""" + self.execute_command(f'diag channel {channel}') + + def diag_get_channel(self) -> int: + """Get the IEEE 802.15.4 Channel value for diagnostics module.""" + line = self.__parse_str(self.execute_command('diag channel')) + return int(line.split()[1]) + + def diag_set_power(self, power: int): + """Set the tx power value(dBm) for diagnostics module.""" + self.execute_command(f'diag power {power}') + + def diag_get_power(self) -> int: + """Get the tx power value(dBm) for diagnostics module.""" + line = self.__parse_str(self.execute_command('diag power')) + if not line.endswith(' dBm'): + raise UnexpectedCommandOutput([line]) + + return int(line.split()[2]) + + def diag_cw_start(self): + """Start transmitting continuous carrier wave.""" + self.execute_command('diag cw start') + + def diag_cw_stop(self): + """Stop transmitting continuous carrier wave.""" + self.execute_command('diag cw stop') + + def diag_frame(self, frame: str): + """Set the frame (hex encoded) to be used by `diag send` and `diag repeat`.""" + self.execute_command(f'diag frame {frame}') + + def diag_stream_start(self): + """Start transmitting a stream of characters.""" + self.execute_command('diag stream start') + + def diag_stream_stop(self): + """Stop transmitting a stream of characters.""" + self.execute_command('diag stream stop') + + def diag_send(self, packets: int, length: int): + """Transmit a fixed number of packets.""" + self.execute_command(f'diag send {packets} {length}') + + def diag_repeat(self, delay: int, length: int): + """Transmit packets repeatedly with a fixed interval.""" + self.execute_command(f'diag repeat {delay} {length}') + + def diag_repeat_stop(self): + """Stop repeated packet transmission.""" + self.execute_command('diag repeat stop') + + def diag_radio_sleep(self): + """Enter radio sleep mode.""" + self.execute_command('diag radio sleep') + + def diag_radio_receive(self): + """Set radio to receive mode.""" + self.execute_command('diag radio receive') + + def diag_get_radio_state(self) -> str: + """Get the state of the radio.""" + return self.__parse_str(self.execute_command('diag radio state')) + + def diag_get_stats(self) -> Dict[str, int]: + """Get statistics during diagnostics mode.""" + output = self.execute_command('diag stats') + if len(output) < 4: + raise UnexpectedCommandOutput(output) + + result = {} + + result['received_packets'] = int(output[0].split(":")[1]) + result['sent_packets'] = int(output[1].split(":")[1]) + + values = re.findall("\-?\d+", output[2]) + result['first_received_packet_rssi'] = int(values[0]) + result['first_received_packet_lqi'] = int(values[1]) + + values = re.findall("\-?\d+", output[3]) + result['last_received_packet_rssi'] = int(values[0]) + result['last_received_packet_lqi'] = int(values[1]) + + return result + + def diag_stats_clear(self): + """Clear statistics during diagnostics mode.""" + self.execute_command('diag stats clear') + + def diag_set_gpio_value(self, gpio: int, value: int): + """Set the gpio value.""" + self.execute_command(f'diag gpio set {gpio} {value}') + + def diag_get_gpio_value(self, gpio: int) -> int: + """Get the gpio value.""" + return int(self.__parse_str(self.execute_command(f'diag gpio get {gpio}'))) + + def diag_set_gpio_mode(self, gpio: int, mode: str): + """Set the gpio mode.""" + self.execute_command(f'diag gpio mode {gpio} {mode}') + + def diag_get_gpio_mode(self, gpio: int) -> str: + """Get the gpio mode.""" + return self.__parse_str(self.execute_command(f'diag gpio mode {gpio}')) + + def diag_echo(self, message: str) -> str: + """RCP echoes the given message.""" + return self.__parse_str(self.execute_command(f'diag echo {message}')) + + def diag_echo_number(self, number: int) -> str: + """RCP echoes the given message.""" + return self.__parse_str(self.execute_command(f'diag echo -n {number}')) + + def diag_get_powersettings(self) -> List[Dict[str, Any]]: + """Get the currently used power settings table.""" + result = [] + output = self.execute_command(f'diag powersettings') + + if len(output) < 3: + raise UnexpectedCommandOutput(output) + + if not output[-1].startswith('Done'): + raise UnexpectedCommandOutput(output) + + for line in output[2:-1]: + data = line.split('|') + + result.append({ + 'channel_start': int(data[1]), + 'channel_end': int(data[2]), + 'target_power': int(data[3]), + 'actual_power': int(data[4]), + 'raw_power_setting': self.__hex_to_bytes(data[5].lstrip().rstrip()), + }) + + return result + + def diag_get_channel_powersettings(self, channel: int) -> Dict[str, Any]: + """Gets the currently used power settings for the given channel.""" + result = {} + output = self.execute_command(f'diag powersettings {channel}') + + if len(output) != 4: + raise UnexpectedCommandOutput(output) + + if not output[-1].startswith('Done'): + raise UnexpectedCommandOutput(output) + + result['target_power'] = int(output[0].split(':')[1]) + result['actual_power'] = int(output[1].split(':')[1]) + result['raw_power_setting'] = self.__hex_to_bytes(output[2].split(':')[1].lstrip().rstrip()) + + return result + + def diag_get_rawpowersetting(self) -> str: + """Get the raw power setting.""" + return self.__parse_str(self.execute_command('diag rawpowersetting')) + + def diag_set_rawpowersetting(self, rawpowersetting: str): + """Set the raw power setting.""" + self.execute_command(f'diag rawpowersetting {rawpowersetting}') + + def diag_enable_rawpowersetting(self): + """Enable the raw power setting.""" + self.execute_command('diag rawpowersetting enable') + + def diag_disable_rawpowersetting(self): + """Disable the raw power setting.""" + self.execute_command('diag rawpowersetting disable') + + def is_command_supported(self, command: str) -> bool: + """Check whether the the given command is supported by the device.""" + output = self.__otcmd.execute_command(command, timeout=10) + + if re.match("Error \d+: \w*", output[-1]): + return False + + return True + # # Other TODOs # From 99b3ed4ca8aac6851d694a5b73dfac47d8d80d7a Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 1 Jul 2024 13:05:03 -0700 Subject: [PATCH 005/160] [mesh-forwarder] add `RxInfo` to encapsulate received frame info (#10451) This commit introduces a new private struct `RxInfo` in the `MeshForwarder` class, encapsulating information related to a received frame during processing. The `RxInfo` struct contains `FrameData` to track the received frame data bytes, `ThreadLinkInfo`, and `Mac::Addresses`. This simplifies the code by allowing the `RxInfo` to be passed to different methods instead of passing the same information as separate parameters. --- src/core/thread/mesh_forwarder.cpp | 129 +++++++++++-------------- src/core/thread/mesh_forwarder.hpp | 40 ++++---- src/core/thread/mesh_forwarder_ftd.cpp | 84 ++++++++-------- 3 files changed, 121 insertions(+), 132 deletions(-) diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 14e239b7f..3a78da154 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -1373,42 +1373,40 @@ bool MeshForwarder::RemoveMessageIfNoPendingTx(Message &aMessage) void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame) { - ThreadLinkInfo linkInfo; - Mac::Addresses macAddrs; - FrameData frameData; - Error error = kErrorNone; + Error error = kErrorNone; + RxInfo rxInfo; VerifyOrExit(mEnabled, error = kErrorInvalidState); - SuccessOrExit(error = aFrame.GetSrcAddr(macAddrs.mSource)); - SuccessOrExit(error = aFrame.GetDstAddr(macAddrs.mDestination)); + rxInfo.mFrameData.Init(aFrame.GetPayload(), aFrame.GetPayloadLength()); - linkInfo.SetFrom(aFrame); + SuccessOrExit(error = aFrame.GetSrcAddr(rxInfo.mMacAddrs.mSource)); + SuccessOrExit(error = aFrame.GetDstAddr(rxInfo.mMacAddrs.mDestination)); - frameData.Init(aFrame.GetPayload(), aFrame.GetPayloadLength()); + rxInfo.mLinkInfo.SetFrom(aFrame); - Get().UpdateOnReceive(macAddrs.mSource, linkInfo.IsLinkSecurityEnabled()); + Get().UpdateOnReceive(rxInfo.mMacAddrs.mSource, rxInfo.IsLinkSecurityEnabled()); switch (aFrame.GetType()) { case Mac::Frame::kTypeData: - if (Lowpan::MeshHeader::IsMeshHeader(frameData)) + if (Lowpan::MeshHeader::IsMeshHeader(rxInfo.mFrameData)) { #if OPENTHREAD_FTD - HandleMesh(frameData, macAddrs.mSource, linkInfo); + HandleMesh(rxInfo); #endif } - else if (Lowpan::FragmentHeader::IsFragmentHeader(frameData)) + else if (Lowpan::FragmentHeader::IsFragmentHeader(rxInfo.mFrameData)) { - HandleFragment(frameData, macAddrs, linkInfo); + HandleFragment(rxInfo); } - else if (Lowpan::Lowpan::IsLowpanHc(frameData)) + else if (Lowpan::Lowpan::IsLowpanHc(rxInfo.mFrameData)) { - HandleLowpanHC(frameData, macAddrs, linkInfo); + HandleLowpanHc(rxInfo); } else { - VerifyOrExit(frameData.GetLength() == 0, error = kErrorNotLowpanDataFrame); + VerifyOrExit(rxInfo.mFrameData.GetLength() == 0, error = kErrorNotLowpanDataFrame); LogFrame("Received empty payload frame", aFrame, kErrorNone); } @@ -1431,21 +1429,20 @@ void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame) } } -void MeshForwarder::HandleFragment(FrameData &aFrameData, - const Mac::Addresses &aMacAddrs, - const ThreadLinkInfo &aLinkInfo) +void MeshForwarder::HandleFragment(RxInfo &aRxInfo) { Error error = kErrorNone; Lowpan::FragmentHeader fragmentHeader; Message *message = nullptr; - SuccessOrExit(error = fragmentHeader.ParseFrom(aFrameData)); + SuccessOrExit(error = fragmentHeader.ParseFrom(aRxInfo.mFrameData)); #if OPENTHREAD_CONFIG_MULTI_RADIO - if (aLinkInfo.mLinkSecurity) + if (aRxInfo.IsLinkSecurityEnabled()) { - Neighbor *neighbor = Get().FindNeighbor(aMacAddrs.mSource, Neighbor::kInStateAnyExceptInvalid); + Neighbor *neighbor = + Get().FindNeighbor(aRxInfo.GetSrcAddr(), Neighbor::kInStateAnyExceptInvalid); if ((neighbor != nullptr) && (fragmentHeader.GetDatagramOffset() == 0)) { @@ -1476,22 +1473,22 @@ void MeshForwarder::HandleFragment(FrameData &aFrameData, uint16_t datagramSize = fragmentHeader.GetDatagramSize(); #if OPENTHREAD_FTD - UpdateRoutes(aFrameData, aMacAddrs); + UpdateRoutes(aRxInfo); #endif - SuccessOrExit(error = FrameToMessage(aFrameData, datagramSize, aMacAddrs, message)); + SuccessOrExit(error = FrameToMessage(aRxInfo, datagramSize, message)); VerifyOrExit(datagramSize >= message->GetLength(), error = kErrorParse); SuccessOrExit(error = message->SetLength(datagramSize)); message->SetDatagramTag(fragmentHeader.GetDatagramTag()); message->SetTimestampToNow(); - message->UpdateLinkInfoFrom(aLinkInfo); + message->UpdateLinkInfoFrom(aRxInfo.mLinkInfo); VerifyOrExit(Get().Accept(*message), error = kErrorDrop); #if OPENTHREAD_FTD - SendIcmpErrorIfDstUnreach(*message, aMacAddrs); + SendIcmpErrorIfDstUnreach(*message, aRxInfo.mMacAddrs); #endif // Allow re-assembly of only one message at a time on a SED by clearing @@ -1515,8 +1512,8 @@ void MeshForwarder::HandleFragment(FrameData &aFrameData, if (msg.GetLength() == fragmentHeader.GetDatagramSize() && msg.GetDatagramTag() == fragmentHeader.GetDatagramTag() && msg.GetOffset() == fragmentHeader.GetDatagramOffset() && - msg.GetOffset() + aFrameData.GetLength() <= fragmentHeader.GetDatagramSize() && - msg.IsLinkSecurityEnabled() == aLinkInfo.IsLinkSecurityEnabled()) + msg.GetOffset() + aRxInfo.mFrameData.GetLength() <= fragmentHeader.GetDatagramSize() && + msg.IsLinkSecurityEnabled() == aRxInfo.IsLinkSecurityEnabled()) { message = &msg; break; @@ -1529,17 +1526,17 @@ void MeshForwarder::HandleFragment(FrameData &aFrameData, // message with a new tag. In either case, we can safely clear any // remaining fragments stored in the reassembly list. - if (!GetRxOnWhenIdle() && (message == nullptr) && aLinkInfo.IsLinkSecurityEnabled()) + if (!GetRxOnWhenIdle() && (message == nullptr) && aRxInfo.IsLinkSecurityEnabled()) { ClearReassemblyList(); } VerifyOrExit(message != nullptr, error = kErrorDrop); - message->WriteData(message->GetOffset(), aFrameData); - message->MoveOffset(aFrameData.GetLength()); - message->AddRss(aLinkInfo.GetRss()); - message->AddLqi(aLinkInfo.GetLqi()); + message->WriteData(message->GetOffset(), aRxInfo.mFrameData); + message->MoveOffset(aRxInfo.mFrameData.GetLength()); + message->AddRss(aRxInfo.mLinkInfo.GetRss()); + message->AddLqi(aRxInfo.mLinkInfo.GetLqi()); message->SetTimestampToNow(); } @@ -1550,13 +1547,12 @@ void MeshForwarder::HandleFragment(FrameData &aFrameData, if (message->GetOffset() >= message->GetLength()) { mReassemblyList.Dequeue(*message); - IgnoreError(HandleDatagram(*message, aMacAddrs.mSource)); + IgnoreError(HandleDatagram(*message, aRxInfo.GetSrcAddr())); } } else { - LogFragmentFrameDrop(error, aFrameData.GetLength(), aMacAddrs, fragmentHeader, - aLinkInfo.IsLinkSecurityEnabled()); + LogFragmentFrameDrop(error, aRxInfo, fragmentHeader); FreeMessage(message); } } @@ -1614,21 +1610,18 @@ bool MeshForwarder::UpdateReassemblyList(void) return mReassemblyList.GetHead() != nullptr; } -Error MeshForwarder::FrameToMessage(const FrameData &aFrameData, - uint16_t aDatagramSize, - const Mac::Addresses &aMacAddrs, - Message *&aMessage) +Error MeshForwarder::FrameToMessage(const RxInfo &aRxInfo, uint16_t aDatagramSize, Message *&aMessage) { Error error = kErrorNone; - FrameData frameData = aFrameData; + FrameData frameData = aRxInfo.mFrameData; Message::Priority priority; - SuccessOrExit(error = GetFramePriority(frameData, aMacAddrs, priority)); + SuccessOrExit(error = GetFramePriority(aRxInfo, priority)); aMessage = Get().Allocate(Message::kTypeIp6, /* aReserveHeader */ 0, Message::Settings(priority)); VerifyOrExit(aMessage, error = kErrorNoBufs); - SuccessOrExit(error = Get().Decompress(*aMessage, aMacAddrs, frameData, aDatagramSize)); + SuccessOrExit(error = Get().Decompress(*aMessage, aRxInfo.mMacAddrs, frameData, aDatagramSize)); SuccessOrExit(error = aMessage->AppendData(frameData)); aMessage->MoveOffset(frameData.GetLength()); @@ -1637,36 +1630,34 @@ Error MeshForwarder::FrameToMessage(const FrameData &aFrameData, return error; } -void MeshForwarder::HandleLowpanHC(const FrameData &aFrameData, - const Mac::Addresses &aMacAddrs, - const ThreadLinkInfo &aLinkInfo) +void MeshForwarder::HandleLowpanHc(const RxInfo &aRxInfo) { Error error = kErrorNone; Message *message = nullptr; #if OPENTHREAD_FTD - UpdateRoutes(aFrameData, aMacAddrs); + UpdateRoutes(aRxInfo); #endif - SuccessOrExit(error = FrameToMessage(aFrameData, 0, aMacAddrs, message)); + SuccessOrExit(error = FrameToMessage(aRxInfo, 0, message)); - message->UpdateLinkInfoFrom(aLinkInfo); + message->UpdateLinkInfoFrom(aRxInfo.mLinkInfo); VerifyOrExit(Get().Accept(*message), error = kErrorDrop); #if OPENTHREAD_FTD - SendIcmpErrorIfDstUnreach(*message, aMacAddrs); + SendIcmpErrorIfDstUnreach(*message, aRxInfo.mMacAddrs); #endif exit: if (error == kErrorNone) { - IgnoreError(HandleDatagram(*message, aMacAddrs.mSource)); + IgnoreError(HandleDatagram(*message, aRxInfo.GetSrcAddr())); } else { - LogLowpanHcFrameDrop(error, aFrameData.GetLength(), aMacAddrs, aLinkInfo.IsLinkSecurityEnabled()); + LogLowpanHcFrameDrop(error, aRxInfo); FreeMessage(message); } } @@ -1690,14 +1681,12 @@ Error MeshForwarder::HandleDatagram(Message &aMessage, const Mac::Address &aMacS return Get().HandleDatagram(OwnedPtr(&aMessage)); } -Error MeshForwarder::GetFramePriority(const FrameData &aFrameData, - const Mac::Addresses &aMacAddrs, - Message::Priority &aPriority) +Error MeshForwarder::GetFramePriority(const RxInfo &aRxInfo, Message::Priority &aPriority) { Error error = kErrorNone; Ip6::Headers headers; - SuccessOrExit(error = headers.DecompressFrom(aFrameData, aMacAddrs, GetInstance())); + SuccessOrExit(error = headers.DecompressFrom(aRxInfo.mFrameData, aRxInfo.mMacAddrs, GetInstance())); aPriority = Ip6::Ip6::DscpToPriority(headers.GetIp6Header().GetDscp()); @@ -2025,25 +2014,21 @@ void MeshForwarder::LogFrame(const char *aActionText, const Mac::Frame &aFrame, } void MeshForwarder::LogFragmentFrameDrop(Error aError, - uint16_t aFrameLength, - const Mac::Addresses &aMacAddrs, - const Lowpan::FragmentHeader &aFragmentHeader, - bool aIsSecure) + const RxInfo &aRxInfo, + const Lowpan::FragmentHeader &aFragmentHeader) { LogNote("Dropping rx frag frame, error:%s, len:%d, src:%s, dst:%s, tag:%d, offset:%d, dglen:%d, sec:%s", - ErrorToString(aError), aFrameLength, aMacAddrs.mSource.ToString().AsCString(), - aMacAddrs.mDestination.ToString().AsCString(), aFragmentHeader.GetDatagramTag(), - aFragmentHeader.GetDatagramOffset(), aFragmentHeader.GetDatagramSize(), ToYesNo(aIsSecure)); + ErrorToString(aError), aRxInfo.mFrameData.GetLength(), aRxInfo.GetSrcAddr().ToString().AsCString(), + aRxInfo.GetDstAddr().ToString().AsCString(), aFragmentHeader.GetDatagramTag(), + aFragmentHeader.GetDatagramOffset(), aFragmentHeader.GetDatagramSize(), + ToYesNo(aRxInfo.IsLinkSecurityEnabled())); } -void MeshForwarder::LogLowpanHcFrameDrop(Error aError, - uint16_t aFrameLength, - const Mac::Addresses &aMacAddrs, - bool aIsSecure) +void MeshForwarder::LogLowpanHcFrameDrop(Error aError, const RxInfo &aRxInfo) { LogNote("Dropping rx lowpan HC frame, error:%s, len:%d, src:%s, dst:%s, sec:%s", ErrorToString(aError), - aFrameLength, aMacAddrs.mSource.ToString().AsCString(), aMacAddrs.mDestination.ToString().AsCString(), - ToYesNo(aIsSecure)); + aRxInfo.mFrameData.GetLength(), aRxInfo.GetSrcAddr().ToString().AsCString(), + aRxInfo.GetDstAddr().ToString().AsCString(), ToYesNo(aRxInfo.IsLinkSecurityEnabled())); } #else // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE) @@ -2056,11 +2041,9 @@ void MeshForwarder::LogMessage(MessageAction, const Message &, Error, const Mac: void MeshForwarder::LogFrame(const char *, const Mac::Frame &, Error) {} -void MeshForwarder::LogFragmentFrameDrop(Error, uint16_t, const Mac::Addresses &, const Lowpan::FragmentHeader &, bool) -{ -} +void MeshForwarder::LogFragmentFrameDrop(Error, const RxInfo &, const Lowpan::FragmentHeader &) {} -void MeshForwarder::LogLowpanHcFrameDrop(Error, uint16_t, const Mac::Addresses &, bool) {} +void MeshForwarder::LogLowpanHcFrameDrop(Error, const RxInfo &) {} #endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE) diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index eba8e74ba..51043abac 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -411,6 +411,17 @@ class MeshForwarder : public InstanceLocator, private NonCopyable kAnycastService, }; + struct RxInfo + { + const Mac::Address &GetSrcAddr(void) const { return mMacAddrs.mSource; } + const Mac::Address &GetDstAddr(void) const { return mMacAddrs.mDestination; } + bool IsLinkSecurityEnabled(void) const { return mLinkInfo.IsLinkSecurityEnabled(); } + + FrameData mFrameData; + ThreadLinkInfo mLinkInfo; + Mac::Addresses mMacAddrs; + }; + #if OPENTHREAD_FTD class FragmentPriorityList : public Clearable { @@ -491,20 +502,17 @@ class MeshForwarder : public InstanceLocator, private NonCopyable #endif void SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac::Addresses &aMacAddrs); - Error CheckReachability(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs); + Error CheckReachability(const RxInfo &aRxInfo); Error CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header); - void UpdateRoutes(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs); - Error FrameToMessage(const FrameData &aFrameData, - uint16_t aDatagramSize, - const Mac::Addresses &aMacAddrs, - Message *&aMessage); + void UpdateRoutes(const RxInfo &aRxInfo); + Error FrameToMessage(const RxInfo &aRxInfo, uint16_t aDatagramSize, Message *&aMessage); void GetMacDestinationAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); void GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); Message *PrepareNextDirectTransmission(void); - void HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSource, const ThreadLinkInfo &aLinkInfo); + void HandleMesh(RxInfo &aRxInfo); void ResolveRoutingLoops(uint16_t aSourceRloc16, uint16_t aDestRloc16); - void HandleFragment(FrameData &aFrameData, const Mac::Addresses &aMacAddrs, const ThreadLinkInfo &aLinkInfo); - void HandleLowpanHC(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, const ThreadLinkInfo &aLinkInfo); + void HandleFragment(RxInfo &aRxInfo); + void HandleLowpanHc(const RxInfo &aRxInfo); void PrepareMacHeaders(Mac::TxFrame &aFrame, Mac::Frame::Type aFrameType, @@ -563,13 +571,11 @@ class MeshForwarder : public InstanceLocator, private NonCopyable void HandleTimeTick(void); void ScheduleTransmissionTask(void); - Error GetFramePriority(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, Message::Priority &aPriority); + Error GetFramePriority(const RxInfo &aRxInfo, Message::Priority &aPriority); Error GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader, uint16_t aSrcRloc16, Message::Priority &aPriority); - void GetForwardFramePriority(const FrameData &aFrameData, - const Mac::Addresses &aMeshAddrs, - Message::Priority &aPriority); + void GetForwardFramePriority(const RxInfo &aRxInfo, Message::Priority &aPriority); bool CalcIePresent(const Message *aMessage); Mac::Frame::Version CalcFrameVersion(const Neighbor *aNeighbor, bool aIePresent) const; @@ -588,12 +594,8 @@ class MeshForwarder : public InstanceLocator, private NonCopyable void LogMessage(MessageAction aAction, const Message &aMessage, Error aError); void LogMessage(MessageAction aAction, const Message &aMessage, Error aError, const Mac::Address *aAddress); void LogFrame(const char *aActionText, const Mac::Frame &aFrame, Error aError); - void LogFragmentFrameDrop(Error aError, - uint16_t aFrameLength, - const Mac::Addresses &aMacAddrs, - const Lowpan::FragmentHeader &aFragmentHeader, - bool aIsSecure); - void LogLowpanHcFrameDrop(Error aError, uint16_t aFrameLength, const Mac::Addresses &aMacAddrs, bool aIsSecure); + void LogFragmentFrameDrop(Error aError, const RxInfo &aRxInfo, const Lowpan::FragmentHeader &aFragmentHeader); + void LogLowpanHcFrameDrop(Error aError, const RxInfo &aRxInfo); #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) const char *MessageActionToString(MessageAction aAction, Error aError); diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 93ff6fd8d..8f8ff10e8 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -641,12 +641,12 @@ void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac return; } -Error MeshForwarder::CheckReachability(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs) +Error MeshForwarder::CheckReachability(const RxInfo &aRxInfo) { Error error; Ip6::Headers ip6Headers; - error = ip6Headers.DecompressFrom(aFrameData, aMeshAddrs, GetInstance()); + error = ip6Headers.DecompressFrom(aRxInfo.mFrameData, aRxInfo.mMacAddrs, GetInstance()); switch (error) { @@ -660,11 +660,11 @@ Error MeshForwarder::CheckReachability(const FrameData &aFrameData, const Mac::A ExitNow(); } - error = CheckReachability(aMeshAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header()); + error = CheckReachability(aRxInfo.GetDstAddr().GetShort(), ip6Headers.GetIp6Header()); if (error == kErrorNoRoute) { - SendDestinationUnreachable(aMeshAddrs.mSource.GetShort(), ip6Headers); + SendDestinationUnreachable(aRxInfo.GetSrcAddr().GetShort(), ip6Headers); } exit: @@ -718,32 +718,36 @@ void MeshForwarder::SendDestinationUnreachable(uint16_t aMeshSource, const Ip6:: Ip6::Icmp::Header::kCodeDstUnreachNoRoute, messageInfo, aIp6Headers)); } -void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSource, const ThreadLinkInfo &aLinkInfo) +void MeshForwarder::HandleMesh(RxInfo &aRxInfo) { Error error = kErrorNone; - Mac::Addresses meshAddrs; Lowpan::MeshHeader meshHeader; + Mac::Address neighborMacSource; // Security Check: only process Mesh Header frames that had security enabled. - VerifyOrExit(aLinkInfo.IsLinkSecurityEnabled(), error = kErrorSecurity); + VerifyOrExit(aRxInfo.IsLinkSecurityEnabled(), error = kErrorSecurity); - SuccessOrExit(error = meshHeader.ParseFrom(aFrameData)); + SuccessOrExit(error = meshHeader.ParseFrom(aRxInfo.mFrameData)); - meshAddrs.mSource.SetShort(meshHeader.GetSource()); - meshAddrs.mDestination.SetShort(meshHeader.GetDestination()); + neighborMacSource = aRxInfo.GetSrcAddr(); - UpdateRoutes(aFrameData, meshAddrs); + // Switch the `aRxInfo.mMacAddrs` to the mesh header source/destination - if (Get().HasRloc16(meshAddrs.mDestination.GetShort()) || - Get().HasMinimalChild(meshAddrs.mDestination.GetShort())) + aRxInfo.mMacAddrs.mSource.SetShort(meshHeader.GetSource()); + aRxInfo.mMacAddrs.mDestination.SetShort(meshHeader.GetDestination()); + + UpdateRoutes(aRxInfo); + + if (Get().HasRloc16(aRxInfo.GetDstAddr().GetShort()) || + Get().HasMinimalChild(aRxInfo.GetDstAddr().GetShort())) { - if (Lowpan::FragmentHeader::IsFragmentHeader(aFrameData)) + if (Lowpan::FragmentHeader::IsFragmentHeader(aRxInfo.mFrameData)) { - HandleFragment(aFrameData, meshAddrs, aLinkInfo); + HandleFragment(aRxInfo); } - else if (Lowpan::Lowpan::IsLowpanHc(aFrameData)) + else if (Lowpan::Lowpan::IsLowpanHc(aRxInfo.mFrameData)) { - HandleLowpanHC(aFrameData, meshAddrs, aLinkInfo); + HandleLowpanHc(aRxInfo); } else { @@ -755,23 +759,23 @@ void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSo OwnedPtr messagePtr; Message::Priority priority = Message::kPriorityNormal; - ResolveRoutingLoops(aMacSource.GetShort(), meshAddrs.mDestination.GetShort()); + ResolveRoutingLoops(neighborMacSource.GetShort(), aRxInfo.GetDstAddr().GetShort()); - SuccessOrExit(error = CheckReachability(aFrameData, meshAddrs)); + SuccessOrExit(error = CheckReachability(aRxInfo)); meshHeader.DecrementHopsLeft(); - GetForwardFramePriority(aFrameData, meshAddrs, priority); + GetForwardFramePriority(aRxInfo, priority); messagePtr.Reset( Get().Allocate(Message::kType6lowpan, /* aReserveHeader */ 0, Message::Settings(priority))); VerifyOrExit(messagePtr != nullptr, error = kErrorNoBufs); SuccessOrExit(error = meshHeader.AppendTo(*messagePtr)); - SuccessOrExit(error = messagePtr->AppendData(aFrameData)); + SuccessOrExit(error = messagePtr->AppendData(aRxInfo.mFrameData)); - messagePtr->UpdateLinkInfoFrom(aLinkInfo); + messagePtr->UpdateLinkInfoFrom(aRxInfo.mLinkInfo); - LogMessage(kMessageReceive, *messagePtr, kErrorNone, &aMacSource); + LogMessage(kMessageReceive, *messagePtr, kErrorNone, &neighborMacSource); #if OPENTHREAD_CONFIG_MULTI_RADIO // Since the message will be forwarded, we clear the radio @@ -789,7 +793,8 @@ void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSo if (error != kErrorNone) { LogInfo("Dropping rx mesh frame, error:%s, len:%d, src:%s, sec:%s", ErrorToString(error), - aFrameData.GetLength(), aMacSource.ToString().AsCString(), ToYesNo(aLinkInfo.IsLinkSecurityEnabled())); + aRxInfo.mFrameData.GetLength(), neighborMacSource.ToString().AsCString(), + ToYesNo(aRxInfo.IsLinkSecurityEnabled())); } } @@ -814,14 +819,14 @@ void MeshForwarder::ResolveRoutingLoops(uint16_t aSourceRloc16, uint16_t aDestRl return; } -void MeshForwarder::UpdateRoutes(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs) +void MeshForwarder::UpdateRoutes(const RxInfo &aRxInfo) { Ip6::Headers ip6Headers; Neighbor *neighbor; - VerifyOrExit(!aMeshAddrs.mDestination.IsBroadcast() && aMeshAddrs.mSource.IsShort()); + VerifyOrExit(!aRxInfo.GetDstAddr().IsBroadcast() && aRxInfo.GetSrcAddr().IsShort()); - SuccessOrExit(ip6Headers.DecompressFrom(aFrameData, aMeshAddrs, GetInstance())); + SuccessOrExit(ip6Headers.DecompressFrom(aRxInfo.mFrameData, aRxInfo.mMacAddrs, GetInstance())); if (!ip6Headers.GetSourceAddress().GetIid().IsLocator() && Get().IsOnMesh(ip6Headers.GetSourceAddress())) @@ -830,14 +835,14 @@ void MeshForwarder::UpdateRoutes(const FrameData &aFrameData, const Mac::Address // inspecting packets being received only for on mesh // addresses. - Get().UpdateSnoopedCacheEntry(ip6Headers.GetSourceAddress(), aMeshAddrs.mSource.GetShort(), - aMeshAddrs.mDestination.GetShort()); + Get().UpdateSnoopedCacheEntry(ip6Headers.GetSourceAddress(), aRxInfo.GetSrcAddr().GetShort(), + aRxInfo.GetDstAddr().GetShort()); } neighbor = Get().FindNeighbor(ip6Headers.GetSourceAddress()); VerifyOrExit(neighbor != nullptr && !neighbor->IsFullThreadDevice()); - if (!Get().HasMatchingRouterIdWith(aMeshAddrs.mSource.GetShort())) + if (!Get().HasMatchingRouterIdWith(aRxInfo.GetSrcAddr().GetShort())) { Get().RemoveNeighbor(*neighbor); } @@ -960,39 +965,38 @@ Error MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader return error; } -void MeshForwarder::GetForwardFramePriority(const FrameData &aFrameData, - const Mac::Addresses &aMeshAddrs, - Message::Priority &aPriority) +void MeshForwarder::GetForwardFramePriority(const RxInfo &aRxInfo, Message::Priority &aPriority) { Error error = kErrorNone; - FrameData frameData = aFrameData; + RxInfo rxInfo = aRxInfo; bool isFragment = false; Lowpan::FragmentHeader fragmentHeader; - if (fragmentHeader.ParseFrom(frameData) == kErrorNone) + if (fragmentHeader.ParseFrom(rxInfo.mFrameData) == kErrorNone) { isFragment = true; if (fragmentHeader.GetDatagramOffset() > 0) { // Get priority from the pre-buffered info - ExitNow(error = GetFragmentPriority(fragmentHeader, aMeshAddrs.mSource.GetShort(), aPriority)); + ExitNow(error = GetFragmentPriority(fragmentHeader, rxInfo.GetSrcAddr().GetShort(), aPriority)); } } // Get priority from IPv6 header or UDP destination port directly - error = GetFramePriority(frameData, aMeshAddrs, aPriority); + error = GetFramePriority(rxInfo, aPriority); exit: if (error != kErrorNone) { LogNote("Failed to get forwarded frame priority, error:%s, len:%d, src:%s, dst:%s", ErrorToString(error), - frameData.GetLength(), aMeshAddrs.mSource.ToString().AsCString(), - aMeshAddrs.mDestination.ToString().AsCString()); + rxInfo.mFrameData.GetLength(), rxInfo.GetSrcAddr().ToString().AsCString(), + rxInfo.GetDstAddr().ToString().AsCString()); } else if (isFragment) { - UpdateFragmentPriority(fragmentHeader, frameData.GetLength(), aMeshAddrs.mSource.GetShort(), aPriority); + UpdateFragmentPriority(fragmentHeader, rxInfo.mFrameData.GetLength(), rxInfo.GetSrcAddr().GetShort(), + aPriority); } } From 6aa6f4560686d0ca481e874bdbac1622f4d64729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Bida?= Date: Tue, 2 Jul 2024 04:14:53 +0200 Subject: [PATCH 006/160] [tcat] implement decommissioning in tcat_agent (#10415) Commits adds handling of TCAT TLV 0x60 `kTlvDecommission`. --- src/core/meshcop/tcat_agent.cpp | 25 +++++++- src/core/meshcop/tcat_agent.hpp | 1 + .../scripts/expect/cli-tcat-decommission.exp | 64 +++++++++++++++++++ tools/tcat_ble_client/bbtc.py | 3 +- tools/tcat_ble_client/cli/base_commands.py | 16 +++++ tools/tcat_ble_client/cli/cli.py | 4 +- 6 files changed, 110 insertions(+), 3 deletions(-) create mode 100755 tests/scripts/expect/cli-tcat-decommission.exp diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index 48dcba714..86e543966 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -410,7 +410,9 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncommingMessage, Message &aOut response = true; error = kErrorNone; break; - + case kTlvDecommission: + error = HandleDecomission(); + break; default: error = kErrorInvalidCommand; } @@ -479,6 +481,27 @@ Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncommingMess return error; } +Error TcatAgent::HandleDecomission(void) +{ + Error error = kErrorNone; + + IgnoreReturnValue(otThreadSetEnabled(&GetInstance(), false)); + Get().Clear(); + Get().Clear(); + + error = Get().ErasePersistentInfo(); + +#if !OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + { + NetworkKey networkKey; + networkKey.Clear(); + Get().SetNetworkKey(networkKey); + } +#endif + + return error; +} + Error TcatAgent::HandleStartThreadInterface(void) { Error error; diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp index 468c40ffe..a95b42cfd 100644 --- a/src/core/meshcop/tcat_agent.hpp +++ b/src/core/meshcop/tcat_agent.hpp @@ -352,6 +352,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable Error HandleSingleTlv(const Message &aIncommingMessage, Message &aOutgoingMessage); Error HandleSetActiveOperationalDataset(const Message &aIncommingMessage, uint16_t aOffset, uint16_t aLength); + Error HandleDecomission(void); Error HandleStartThreadInterface(void); bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags, diff --git a/tests/scripts/expect/cli-tcat-decommission.exp b/tests/scripts/expect/cli-tcat-decommission.exp new file mode 100755 index 000000000..c0488c031 --- /dev/null +++ b/tests/scripts/expect/cli-tcat-decommission.exp @@ -0,0 +1,64 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2024, 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. +# + +source "tests/scripts/expect/_common.exp" + +spawn_node 1 "cli" + +switch_node 1 +send "tcat start\n" +expect_line "Done" + +spawn python "tools/tcat_ble_client/bbtc.py" --simulation 1 --cert_path "tools/tcat_ble_client/auth" +set py_client "$spawn_id" +expect_line "Done" +send "commission\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x00" + +send "thread start\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x00" + +send "decommission\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" + +send "exit\n" +expect eof + +switch_node 1 +send "tcat stop\n" +expect_line "Done" + +send "dataset active\n" +expect_line "Error 23: NotFound" + +send "networkkey\n" +expect_line "00000000000000000000000000000000" +expect_line "Done" diff --git a/tools/tcat_ble_client/bbtc.py b/tools/tcat_ble_client/bbtc.py index d2c7ea687..2824e5aca 100755 --- a/tools/tcat_ble_client/bbtc.py +++ b/tools/tcat_ble_client/bbtc.py @@ -125,7 +125,8 @@ async def get_device_by_args(args): elif args.scan: tcat_devices = await ble_scanner.scan_tcat_devices() device = select_device_by_user_input(tcat_devices) - device = await BleStream.create(device.address, BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, BBTC_RX_CHAR_UUID) + if device: + device = await BleStream.create(device.address, BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, BBTC_RX_CHAR_UUID) elif args.simulation: device = UdpStream("127.0.0.1", int(args.simulation)) diff --git a/tools/tcat_ble_client/cli/base_commands.py b/tools/tcat_ble_client/cli/base_commands.py index a865b8711..20d48dcb3 100644 --- a/tools/tcat_ble_client/cli/base_commands.py +++ b/tools/tcat_ble_client/cli/base_commands.py @@ -87,6 +87,22 @@ async def execute_default(self, args, context): return CommandResultTLV(tlv_response) +class DecommissionCommand(Command): + + def get_help_string(self) -> str: + return 'Stop Thread interface and decommission device from current network.' + + async def execute_default(self, args, context): + bless: BleStreamSecure = context['ble_sstream'] + print('Disabling Thread and decommissioning device...') + data = (TLV(TcatTLVType.DECOMMISSION.value, bytes()).to_bytes()) + response = await bless.send_with_resp(data) + if not response: + return + tlv_response = TLV.from_bytes(response) + return CommandResultTLV(tlv_response) + + class ThreadStartCommand(Command): def get_help_string(self) -> str: diff --git a/tools/tcat_ble_client/cli/cli.py b/tools/tcat_ble_client/cli/cli.py index bfe6cd3d1..838295805 100644 --- a/tools/tcat_ble_client/cli/cli.py +++ b/tools/tcat_ble_client/cli/cli.py @@ -29,7 +29,8 @@ import readline import shlex from ble.ble_stream_secure import BleStreamSecure -from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, ThreadStateCommand, ScanCommand) +from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, ThreadStateCommand, + ScanCommand) from cli.dataset_commands import (DatasetCommand) from dataset.dataset import ThreadDataset from typing import Optional @@ -42,6 +43,7 @@ def __init__(self, dataset: ThreadDataset, ble_sstream: Optional[BleStreamSecure 'help': HelpCommand(), 'hello': HelloCommand(), 'commission': CommissionCommand(), + 'decommission': DecommissionCommand(), 'dataset': DatasetCommand(), 'thread': ThreadStateCommand(), 'scan': ScanCommand(), From a448bce9157c9e3f087e99499c7ad58c2ec7139a Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 1 Jul 2024 19:15:17 -0700 Subject: [PATCH 007/160] [core] add `OffsetRange` class (#10436) This commit introduces the `OffsetRange` class, which represents a range of offsets within a `Message` or data buffer (i.e., a starting offset and a length indicating the number of bytes in the range). The class provides methods for common operations: - Getting the start or end offset, or the remaining length. - Checking if the range is empty or contains a certain number of bytes. - Advancing the start offset by a given amount, ensuring it never goes beyond the end offset. - Shrinking the range's length. The new `OffsetRange` class simplifies methods that previously used separate `aOffset` and `aLength` input parameters, improving code readability and maintainability. It also facilitates reading and processing content within a specified range. For example, `Tlv::FindTlvValueOffsetRange()` now directly returns an `OffsetRange` indicating the location of the TLV value. The `Message` class is also updated with new `Read()` and `Append()` method variants that accept `OffsetRange` arguments. These methods ensure that read content is contained within the specified `OffsetRange`. --- src/core/BUILD.gn | 2 + src/core/CMakeLists.txt | 1 + src/core/backbone_router/bbr_manager.cpp | 20 +-- src/core/common/message.cpp | 21 +++ src/core/common/message.hpp | 64 +++++++ src/core/common/offset_range.cpp | 73 ++++++++ src/core/common/offset_range.hpp | 172 ++++++++++++++++++ src/core/common/tlvs.cpp | 43 ++--- src/core/common/tlvs.hpp | 39 ++-- src/core/meshcop/border_agent.cpp | 38 ++-- src/core/meshcop/commissioner.cpp | 10 +- src/core/meshcop/dataset.cpp | 8 +- src/core/meshcop/dataset.hpp | 9 +- src/core/meshcop/dataset_manager.cpp | 14 +- src/core/meshcop/dataset_manager_ftd.cpp | 4 +- src/core/meshcop/dataset_updater.cpp | 12 +- src/core/meshcop/joiner_router.cpp | 14 +- src/core/meshcop/meshcop_tlvs.cpp | 7 +- src/core/meshcop/tcat_agent.cpp | 8 +- src/core/thread/discover_scanner.cpp | 30 ++-- src/core/thread/link_metrics.cpp | 136 +++++++------- src/core/thread/link_metrics.hpp | 10 +- src/core/thread/mle.cpp | 53 +++--- src/core/thread/mle_router.cpp | 49 +++-- src/core/thread/mle_types.cpp | 14 +- src/core/thread/mle_types.hpp | 10 +- src/core/thread/mlr_manager.cpp | 17 +- src/core/thread/network_data_leader.cpp | 20 +-- src/core/thread/network_data_leader.hpp | 14 +- src/core/thread/network_data_leader_ftd.cpp | 20 ++- src/core/thread/network_diagnostic.cpp | 24 ++- src/core/utils/mesh_diag.cpp | 34 ++-- src/core/utils/mesh_diag.hpp | 6 +- tests/unit/CMakeLists.txt | 1 + tests/unit/test_lowpan.cpp | 5 +- tests/unit/test_offset_range.cpp | 187 ++++++++++++++++++++ tests/unit/test_tlv.cpp | 46 ++--- 37 files changed, 854 insertions(+), 381 deletions(-) create mode 100644 src/core/common/offset_range.cpp create mode 100644 src/core/common/offset_range.hpp create mode 100644 tests/unit/test_offset_range.cpp diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 247d7d96b..622d8df2c 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -432,6 +432,8 @@ openthread_core_files = [ "common/notifier.hpp", "common/num_utils.hpp", "common/numeric_limits.hpp", + "common/offset_range.cpp", + "common/offset_range.hpp", "common/owned_ptr.hpp", "common/owning_list.hpp", "common/pool.hpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index dbc13b2e3..e9c496746 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -112,6 +112,7 @@ set(COMMON_SOURCES common/log.cpp common/message.cpp common/notifier.cpp + common/offset_range.cpp common/preference.cpp common/random.cpp common/settings.cpp diff --git a/src/core/backbone_router/bbr_manager.cpp b/src/core/backbone_router/bbr_manager.cpp index 7a90d1c71..2269c10aa 100644 --- a/src/core/backbone_router/bbr_manager.cpp +++ b/src/core/backbone_router/bbr_manager.cpp @@ -149,7 +149,7 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, ThreadStatusTlv::MlrStatus status = ThreadStatusTlv::kMlrSuccess; Config config; - uint16_t addressesOffset, addressesLength; + OffsetRange offsetRange; Ip6::Address address; Ip6::Address addresses[Ip6AddressesTlv::kMaxAddresses]; uint8_t failedAddressNum = 0; @@ -188,11 +188,10 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, processTimeoutTlv = hasCommissionerSessionIdTlv && (Tlv::Find(aMessage, timeout) == kErrorNone); - VerifyOrExit(Tlv::FindTlvValueOffset(aMessage, Ip6AddressesTlv::kIp6Addresses, addressesOffset, addressesLength) == - kErrorNone, + VerifyOrExit(Tlv::FindTlvValueOffsetRange(aMessage, Ip6AddressesTlv::kIp6Addresses, offsetRange) == kErrorNone, error = kErrorParse); - VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, status = ThreadStatusTlv::kMlrGeneralFailure); - VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= Ip6AddressesTlv::kMaxAddresses, + VerifyOrExit(offsetRange.GetLength() % sizeof(Ip6::Address) == 0, status = ThreadStatusTlv::kMlrGeneralFailure); + VerifyOrExit(offsetRange.GetLength() / sizeof(Ip6::Address) <= Ip6AddressesTlv::kMaxAddresses, status = ThreadStatusTlv::kMlrGeneralFailure); if (!processTimeoutTlv) @@ -220,9 +219,10 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, expireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(timeout); - for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address)) + while (!offsetRange.IsEmpty()) { - IgnoreError(aMessage.Read(addressesOffset + offset, address)); + IgnoreError(aMessage.Read(offsetRange, address)); + offsetRange.AdvanceOffset(sizeof(Ip6::Address)); if (timeout == 0) { @@ -581,7 +581,7 @@ template <> void Manager::HandleTmf(Coap::Message &aMessage, bool proactive; Ip6::Address dua; Ip6::InterfaceIdentifier meshLocalIid; - uint16_t networkNameOffset, networkNameLength; + OffsetRange offsetRange; uint32_t timeSinceLastTransaction; uint16_t srcRloc16 = Mle::kInvalidRloc16; @@ -595,9 +595,7 @@ template <> void Manager::HandleTmf(Coap::Message &aMessage, SuccessOrExit(error = Tlv::Find(aMessage, dua)); SuccessOrExit(error = Tlv::Find(aMessage, meshLocalIid)); SuccessOrExit(error = Tlv::Find(aMessage, timeSinceLastTransaction)); - - SuccessOrExit(error = - Tlv::FindTlvValueOffset(aMessage, ThreadTlv::kNetworkName, networkNameOffset, networkNameLength)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, ThreadTlv::kNetworkName, offsetRange)); error = Tlv::Find(aMessage, srcRloc16); VerifyOrExit(error == kErrorNone || error == kErrorNotFound); diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index 72dd7d66f..989877ce5 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -412,6 +412,11 @@ Error Message::AppendBytes(const void *aBuf, uint16_t aLength) return error; } +Error Message::AppendBytesFromMessage(const Message &aMessage, const OffsetRange &aOffsetRange) +{ + return AppendBytesFromMessage(aMessage, aOffsetRange.GetOffset(), aOffsetRange.GetLength()); +} + Error Message::AppendBytesFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength) { Error error = kErrorNone; @@ -649,11 +654,27 @@ uint16_t Message::ReadBytes(uint16_t aOffset, void *aBuf, uint16_t aLength) cons return static_cast(bufPtr - reinterpret_cast(aBuf)); } +uint16_t Message::ReadBytes(const OffsetRange &aOffsetRange, void *aBuf) const +{ + return ReadBytes(aOffsetRange.GetOffset(), aBuf, aOffsetRange.GetLength()); +} + Error Message::Read(uint16_t aOffset, void *aBuf, uint16_t aLength) const { return (ReadBytes(aOffset, aBuf, aLength) == aLength) ? kErrorNone : kErrorParse; } +Error Message::Read(const OffsetRange &aOffsetRange, void *aBuf, uint16_t aLength) const +{ + Error error = kErrorNone; + + VerifyOrExit(aOffsetRange.Contains(aLength), error = kErrorParse); + VerifyOrExit(ReadBytes(aOffsetRange.GetOffset(), aBuf, aLength) == aLength, error = kErrorParse); + +exit: + return error; +} + bool Message::CompareBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength, ByteMatcher aMatcher) const { uint16_t bytesToCompare = aLength; diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 739d23776..64d1307b4 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -52,6 +52,7 @@ #include "common/linked_list.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" +#include "common/offset_range.hpp" #include "common/pool.hpp" #include "common/timer.hpp" #include "common/type_traits.hpp" @@ -707,6 +708,19 @@ class Message : public otMessage, public Buffer, public GetProvider */ Error AppendBytesFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength); + /** + * Appends bytes read from another or potentially the same message to the end of the current message. + * + * @param[in] aMessage The message to read the bytes from (it can be the same as the current message). + * @param[in] aOffsetRange The offset range in @p aMessage to read bytes from. + * + * @retval kErrorNone Successfully appended the bytes. + * @retval kErrorNoBufs Insufficient available buffers to grow the message. + * @retval kErrorParse Not enough bytes in @p aMessage to read @p aOffsetRange. + * + */ + Error AppendBytesFromMessage(const Message &aMessage, const OffsetRange &aOffsetRange); + /** * Appends an object to the end of the message. * @@ -757,6 +771,17 @@ class Message : public otMessage, public Buffer, public GetProvider */ uint16_t ReadBytes(uint16_t aOffset, void *aBuf, uint16_t aLength) const; + /** + * Reads bytes from the message. + * + * @param[in] aOffsetRange The offset range in the message to read bytes from. + * @param[out] aBuf A pointer to a data buffer to copy the read bytes into. + * + * @returns The number of bytes read. + * + */ + uint16_t ReadBytes(const OffsetRange &aOffsetRange, void *aBuf) const; + /** * Reads a given number of bytes from the message. * @@ -773,6 +798,22 @@ class Message : public otMessage, public Buffer, public GetProvider */ Error Read(uint16_t aOffset, void *aBuf, uint16_t aLength) const; + /** + * Reads a given number of bytes from the message. + * + * If there are fewer bytes available in the message or @p aOffsetRange than the requested @p aLength, the + * available bytes are read and copied into @p aBuf. In this case `kErrorParse` will be returned. + * + * @param[in] aOffsetRange The offset range in the message to read from. + * @param[out] aBuf A pointer to a data buffer to copy the read bytes into. + * @param[in] aLength Number of bytes to read. + * + * @retval kErrorNone Requested bytes were successfully read from message. + * @retval kErrorParse Not enough bytes remaining to read the requested @p aLength. + * + */ + Error Read(const OffsetRange &aOffsetRange, void *aBuf, uint16_t aLength) const; + /** * Reads an object from the message. * @@ -796,6 +837,29 @@ class Message : public otMessage, public Buffer, public GetProvider return Read(aOffset, &aObject, sizeof(ObjectType)); } + /** + * Reads an object from the message. + * + * If there are fewer bytes available in the message or @p aOffsetRange than the requested object size, the + * available bytes will be read and copied into @p aObject (@p aObject will be read partially). In this case + * `kErrorParse` will be returned. + * + * @tparam ObjectType The object type to read from the message. + * + * @param[in] aOffsetRange The offset range in the message to read from. + * @param[out] aObject A reference to the object to read into. + * + * @retval kErrorNone Object @p aObject was successfully read from message. + * @retval kErrorParse Not enough bytes remaining in message to read the entire object. + * + */ + template Error Read(const OffsetRange &aOffsetRange, ObjectType &aObject) const + { + static_assert(!TypeTraits::IsPointer::kValue, "ObjectType must not be a pointer"); + + return Read(aOffsetRange, &aObject, sizeof(ObjectType)); + } + /** * Compares the bytes in the message at a given offset with a given byte array. * diff --git a/src/core/common/offset_range.cpp b/src/core/common/offset_range.cpp new file mode 100644 index 000000000..cbc39ddb5 --- /dev/null +++ b/src/core/common/offset_range.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024, 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 helper method for an offset range. + */ + +#include "offset_range.hpp" + +#include "common/code_utils.hpp" +#include "common/message.hpp" +#include "common/num_utils.hpp" +#include "common/numeric_limits.hpp" + +namespace ot { + +void OffsetRange::Init(uint16_t aOffset, uint16_t aLength) +{ + uint16_t maxLength = NumericLimits::kMax - aOffset; + + mOffset = aOffset; + mLength = Min(aLength, maxLength); +} + +void OffsetRange::InitFromRange(uint16_t aStartOffset, uint16_t aEndOffset) +{ + Init(aStartOffset, Max(aStartOffset, aEndOffset) - aStartOffset); +} + +void OffsetRange::InitFromMessageOffsetToEnd(const Message &aMessage) +{ + InitFromRange(aMessage.GetOffset(), aMessage.GetLength()); +} + +void OffsetRange::InitFromMessageFullLength(const Message &aMessage) { Init(0, aMessage.GetLength()); } + +void OffsetRange::AdvanceOffset(uint32_t aLength) +{ + uint16_t length = static_cast(Min(aLength, mLength)); + + mOffset += length; + mLength -= length; +} + +void OffsetRange::ShrinkLength(uint16_t aLength) { mLength = Min(mLength, aLength); } + +} // namespace ot diff --git a/src/core/common/offset_range.hpp b/src/core/common/offset_range.hpp new file mode 100644 index 000000000..9b5b4772e --- /dev/null +++ b/src/core/common/offset_range.hpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2024, 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 an offset range. + */ + +#ifndef OFFSET_RANGE_HPP_ +#define OFFSET_RANGE_HPP_ + +#include "openthread-core-config.h" + +#include +#include + +#include "common/clearable.hpp" + +namespace ot { + +class Message; + +/** + * Represents an offset range. + * + */ +class OffsetRange : public Clearable +{ +public: + /** + * Initializes the `OffsetRange`. + * + * @param[in] aOffset The start offset. + * @param[in] aLength The range length (number of bytes). + * + */ + void Init(uint16_t aOffset, uint16_t aLength); + + /** + * Initializes the `OffsetRange` from given start and end offsets. + * + * The range is inclusive of the start offset (@p aStartOffset) but exclusive of the end offset (@p aEndOffset). + * + * @param[in] aStartOffset The start offset (inclusive). + * @param[in] aEndOffset The end offset (exclusive). + * + */ + void InitFromRange(uint16_t aStartOffset, uint16_t aEndOffset); + + /** + * Initializes the `OffsetRange` from a given `Message` from its offset to the message end. + * + * The start offset of the range is set to `aMessage.GetOffset()`, and the end offset is set to include all bytes + * in the message up to its current length `aMessage.GetLength()`. + * + * @param[in] aMessage The `Message` to initialize the `OffsetRange` from. + * + */ + void InitFromMessageOffsetToEnd(const Message &aMessage); + + /** + * Initializes the `OffsetRange` from a given `Message` from zero offset up to to its full length. + * + * The start offset of the range is set to zero, and the end offset is set to include full length of @p aMessage. + * + * @param[in] aMessage The `Message` to initialize the `OffsetRange` from. + * + */ + void InitFromMessageFullLength(const Message &aMessage); + + /** + * Gets the start offset of the `OffsetRange` + * + * @returns The start offset. + * + */ + uint16_t GetOffset(void) const { return mOffset; } + + /** + * Gets the end offset of the `OffsetRange`. + * + * This offset is exclusive, meaning it marks the position immediately after the last byte within the range. + * + * @returns The end offset. + * + */ + uint16_t GetEndOffset(void) const { return (mOffset + mLength); } + + /** + * Gets the `OffsetRange` length. + * + * @returns The length of the `OffsetRange` in bytes. + * + */ + uint16_t GetLength(void) const { return mLength; } + + /** + * Indicates whether or not the `OffsetRange` is empty. + * + * @retval TRUE The `OffsetRange` is empty. + * @retval FALSE The `OffsetRange` is not empty (contains at least one byte). + * + */ + bool IsEmpty(void) const { return (mLength == 0); } + + /** + * Indicates whether or not the `OffsetRange` contains a given number of bytes. + * + * @param[in] aLength The length to check. + * + * @retval TRUE The `OffsetRange` contains @p aLength or more bytes. + * @retval FALSE The `OffsetRange` does not contain @p aLength bytes. + * + */ + bool Contains(uint32_t aLength) const { return aLength <= mLength; } + + /** + * Advances the start offset forward by the given number of bytes. + * + * This method ensures the start offset does not go beyond the end offset of the `OffsetRange`. If @p aLength is + * greater than the available bytes in the `OffsetRange`, the start offset is adjusted to the end offset, + * effectively shrinking the range to zero length. + * + * @param[in] aLength The number of bytes to advance the start offset. + * + */ + void AdvanceOffset(uint32_t aLength); + + /** + * Shrinks the `OffsetRange` length to a given length. + * + * If the current length of the `OffsetRange` is longer than @p aLength, the offset range is shortened to + * @p aLength. If the range is already shorter or the same, it remains unchanged. + * + * @param[in] aLength The new length to use. + * + */ + void ShrinkLength(uint16_t aLength); + +private: + uint16_t mOffset; + uint16_t mLength; +}; + +} // namespace ot + +#endif // OFFSET_RANGE_HPP_ diff --git a/src/core/common/tlvs.cpp b/src/core/common/tlvs.cpp index 2848e7f0c..0fcf4bbe9 100644 --- a/src/core/common/tlvs.cpp +++ b/src/core/common/tlvs.cpp @@ -56,19 +56,15 @@ const uint8_t *Tlv::GetValue(void) const Error Tlv::AppendTo(Message &aMessage) const { return aMessage.AppendBytes(this, static_cast(GetSize())); } -Error Tlv::ParseAndSkipTlv(const Message &aMessage, uint16_t &aOffset) +Error Tlv::ParseAndSkipTlv(const Message &aMessage, OffsetRange &aOffsetRange) { Error error; ParsedInfo info; - SuccessOrExit(error = info.ParseFrom(aMessage, aOffset)); - - // `ParseFrom()` has already validated that the entire TLV is - // present within `aMessage`. This ensures that `aOffset + mSize` - // is less than `aMessage.GetLength()`, and therefore we cannot - // have an overflow here. + SuccessOrExit(error = info.ParseFrom(aMessage, aOffsetRange.GetOffset())); - aOffset += info.mSize; + VerifyOrExit(aOffsetRange.Contains(info.mSize), error = kErrorParse); + aOffsetRange.AdvanceOffset(info.mSize); exit: return error; @@ -96,35 +92,18 @@ Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tl aMessage.ReadBytes(info.mOffset, &aTlv, aMaxSize); aOffset = info.mOffset; -exit: - return error; -} -Error Tlv::FindTlvValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aValueOffset, uint16_t &aLength) -{ - Error error; - ParsedInfo info; - - SuccessOrExit(error = info.FindIn(aMessage, aType)); - - aValueOffset = info.mValueOffset; - aLength = info.mLength; - exit: return error; } -Error Tlv::FindTlvValueStartEndOffsets(const Message &aMessage, - uint8_t aType, - uint16_t &aValueStartOffset, - uint16_t &aValueEndOffset) +Error Tlv::FindTlvValueOffsetRange(const Message &aMessage, uint8_t aType, OffsetRange &aOffsetRange) { Error error; ParsedInfo info; SuccessOrExit(error = info.FindIn(aMessage, aType)); - aValueStartOffset = info.mValueOffset; - aValueEndOffset = info.mValueOffset + info.mLength; + aOffsetRange.Init(info.mValueOffset, info.mLength); exit: return error; @@ -291,13 +270,11 @@ template Error Tlv::FindUintTlv(const Message &aMessage, uint8_t aType Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, void *aValue, uint16_t aLength) { - Error error; - uint16_t offset; - uint16_t length; + Error error; + OffsetRange offsetRange; - SuccessOrExit(error = FindTlvValueOffset(aMessage, aType, offset, length)); - VerifyOrExit(length >= aLength, error = kErrorParse); - aMessage.ReadBytes(offset, aValue, aLength); + SuccessOrExit(error = FindTlvValueOffsetRange(aMessage, aType, offsetRange)); + error = aMessage.Read(offsetRange, aValue, aLength); exit: return error; diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index 7baf529a3..3ba23abcb 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -42,6 +42,7 @@ #include "common/const_cast.hpp" #include "common/encoding.hpp" #include "common/error.hpp" +#include "common/offset_range.hpp" #include "common/type_traits.hpp" namespace ot { @@ -245,19 +246,19 @@ class Tlv // Static methods for reading/finding/appending TLVs in a `Message`. /** - * Parses a TLV in a message at a given offset, validating that it is fully contained within the message and then - * updating the offset to skip over the entire parsed TLV. + * Parses a TLV in a message from a given offset range, validating that it is fully contained within the offset + * range and the message, and then updating the offset range to skip over the entire parsed TLV. * * Can be used independent of whether the read TLV (from the message) is an Extended TLV or not. * - * @param[in] aMessage The message to read from. - * @param[in,out] aOffset The offset to read from. On success, it is updated to point after the parsed TLV. + * @param[in] aMessage The message to read from. + * @param[in,out] aOffsetRange The offset range to read from. On success, it is updated to skip the TLV. * - * @retval kErrorNone Successfully parsed a TLV from @p aMessage. @p aOffset is updated. + * @retval kErrorNone Successfully parsed a TLV from @p aMessage. @p aOffsetRange is updated. * @retval kErrorParse The TLV was not well-formed or was not fully contained in @p aMessage. * */ - static Error ParseAndSkipTlv(const Message &aMessage, uint16_t &aOffset); + static Error ParseAndSkipTlv(const Message &aMessage, OffsetRange &aOffsetRange); /** * Reads a TLV's value in a message at a given offset expecting a minimum length for the value. @@ -405,39 +406,19 @@ class Tlv } /** - * Finds the offset and length of TLV value for a given TLV type within @p aMessage. + * Finds the offset range of the TLV value for a given TLV type within @p aMessage. * * Can be used independent of whether the read TLV (from message) is an Extended TLV or not. * * @param[in] aMessage A reference to the message. * @param[in] aType The Type value to search for. - * @param[out] aValueOffset The offset where the value starts. - * @param[out] aLength The length of the value. + * @param[out] aOffsetRange A reference to return the offset range of the TLV value when found. * * @retval kErrorNone Successfully found the TLV. * @retval kErrorNotFound Could not find the TLV with Type @p aType. * */ - static Error FindTlvValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aValueOffset, uint16_t &aLength); - - /** - * Finds the start and end offset of TLV value for a given TLV type with @p aMessage. - * - * Can be used independent of whether the read TLV (from message) is an Extended TLV or not. - * - * @param[in] aMessage A reference to the message. - * @param[in] aType The Type value to search for. - * @param[out] aValueStartOffset The offset where the value starts. - * @param[out] aValueEndOffset The offset immediately after the last byte of value. - * - * @retval kErrorNone Successfully found the TLV. - * @retval kErrorNotFound Could not find the TLV with Type @p aType. - * - */ - static Error FindTlvValueStartEndOffsets(const Message &aMessage, - uint8_t aType, - uint16_t &aValueStartOffset, - uint16_t &aValueEndOffset); + static Error FindTlvValueOffsetRange(const Message &aMessage, uint8_t aType, OffsetRange &aOffsetRange); /** * Searches for a TLV with a given type in a message, ensures its length is same or larger than diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index ef82c3965..ae3cc10e1 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -317,22 +317,20 @@ template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, co Error error = kErrorNone; Message *message = nullptr; Ip6::MessageInfo messageInfo; - uint16_t offset; - uint16_t length; + OffsetRange offsetRange; UdpEncapsulationTlvHeader udpEncapHeader; VerifyOrExit(mState != kStateStopped); - SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Tlv::kUdpEncapsulation, offset, length)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Tlv::kUdpEncapsulation, offsetRange)); - SuccessOrExit(error = aMessage.Read(offset, udpEncapHeader)); - offset += sizeof(UdpEncapsulationTlvHeader); - length -= sizeof(UdpEncapsulationTlvHeader); + SuccessOrExit(error = aMessage.Read(offsetRange, udpEncapHeader)); + offsetRange.AdvanceOffset(sizeof(UdpEncapsulationTlvHeader)); VerifyOrExit(udpEncapHeader.GetSourcePort() > 0 && udpEncapHeader.GetDestinationPort() > 0, error = kErrorDrop); VerifyOrExit((message = Get().NewMessage()) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offset, length)); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange)); messageInfo.SetSockPort(udpEncapHeader.GetSourcePort()); messageInfo.SetSockAddr(mCommissionerAloc.GetAddress()); @@ -376,16 +374,18 @@ bool BorderAgent::HandleUdpReceive(const Message &aMessage, const Ip6::MessageIn { ExtendedTlv extTlv; UdpEncapsulationTlvHeader udpEncapHeader; - uint16_t udpLength = aMessage.GetLength() - aMessage.GetOffset(); + OffsetRange offsetRange; + + offsetRange.InitFromMessageOffsetToEnd(aMessage); extTlv.SetType(Tlv::kUdpEncapsulation); - extTlv.SetLength(sizeof(UdpEncapsulationTlvHeader) + udpLength); + extTlv.SetLength(sizeof(UdpEncapsulationTlvHeader) + offsetRange.GetLength()); SuccessOrExit(error = message->Append(extTlv)); udpEncapHeader.SetSourcePort(aMessageInfo.GetPeerPort()); udpEncapHeader.SetDestinationPort(aMessageInfo.GetSockPort()); SuccessOrExit(error = message->Append(udpEncapHeader)); - SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(), udpLength)); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange)); } SuccessOrExit(error = Tlv::Append(*message, aMessageInfo.GetPeerAddr())); @@ -427,10 +427,12 @@ template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, co Error BorderAgent::ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage) { - Error error; + Error error; + OffsetRange offsetRange; + + offsetRange.InitFromMessageOffsetToEnd(aMessage); + SuccessOrExit(error = aForwardMessage.AppendBytesFromMessage(aMessage, offsetRange)); - SuccessOrExit(error = aForwardMessage.AppendBytesFromMessage(aMessage, aMessage.GetOffset(), - aMessage.GetLength() - aMessage.GetOffset())); SuccessOrExit(error = SendMessage(aForwardMessage)); LogInfo("Sent to commissioner"); @@ -498,6 +500,7 @@ template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, co uint16_t joinerRouterRloc; Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); + OffsetRange offsetRange; VerifyOrExit(mState != kStateStopped); @@ -508,8 +511,8 @@ template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, co message = Get().NewPriorityNonConfirmablePostMessage(kUriRelayTx); VerifyOrExit(message != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(), - aMessage.GetLength() - aMessage.GetOffset())); + offsetRange.InitFromMessageOffsetToEnd(aMessage); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange)); messageInfo.SetSockAddrToRlocPeerAddrTo(joinerRouterRloc); messageInfo.SetSockPortToTmf(); @@ -531,6 +534,7 @@ Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::Mes Coap::Message *message = nullptr; bool petition = false; bool separate = false; + OffsetRange offsetRange; VerifyOrExit(mState != kStateStopped); @@ -558,8 +562,8 @@ Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::Mes message = Get().NewPriorityConfirmablePostMessage(aUri); VerifyOrExit(message != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(), - aMessage.GetLength() - aMessage.GetOffset())); + offsetRange.InitFromMessageOffsetToEnd(aMessage); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange)); messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc(); messageInfo.SetSockPortToTmf(); diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp index c3987e82c..ce379dc99 100644 --- a/src/core/meshcop/commissioner.cpp +++ b/src/core/meshcop/commissioner.cpp @@ -902,8 +902,7 @@ template <> void Commissioner::HandleTmf(Coap::Message &aMessage, c Ip6::InterfaceIdentifier joinerIid; uint16_t joinerRloc; Ip6::MessageInfo joinerMessageInfo; - uint16_t startOffset; - uint16_t endOffset; + OffsetRange offsetRange; VerifyOrExit(mState == kStateActive, error = kErrorInvalidState); @@ -913,8 +912,7 @@ template <> void Commissioner::HandleTmf(Coap::Message &aMessage, c SuccessOrExit(error = Tlv::Find(aMessage, joinerIid)); SuccessOrExit(error = Tlv::Find(aMessage, joinerRloc)); - SuccessOrExit( - error = Tlv::FindTlvValueStartEndOffsets(aMessage, Tlv::kJoinerDtlsEncapsulation, startOffset, endOffset)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Tlv::kJoinerDtlsEncapsulation, offsetRange)); if (!Get().IsConnectionActive()) { @@ -951,8 +949,8 @@ template <> void Commissioner::HandleTmf(Coap::Message &aMessage, c LogInfo("Received %s (%s, 0x%04x)", UriToString(), mJoinerIid.ToString().AsCString(), mJoinerRloc); - aMessage.SetOffset(startOffset); - SuccessOrExit(error = aMessage.SetLength(endOffset)); + aMessage.SetOffset(offsetRange.GetOffset()); + SuccessOrExit(error = aMessage.SetLength(offsetRange.GetEndOffset())); joinerMessageInfo.SetPeerAddr(Get().GetMeshLocalEid()); joinerMessageInfo.GetPeerAddr().SetIid(mJoinerIid); diff --git a/src/core/meshcop/dataset.cpp b/src/core/meshcop/dataset.cpp index cb0c53e9a..ca25268fe 100644 --- a/src/core/meshcop/dataset.cpp +++ b/src/core/meshcop/dataset.cpp @@ -337,14 +337,14 @@ void Dataset::SetFrom(const Info &aDatasetInfo) // `mUpdateTime` is already set by `WriteTlvsFrom()`. } -Error Dataset::SetFrom(const Message &aMessage, uint16_t aOffset, uint16_t aLength) +Error Dataset::SetFrom(const Message &aMessage, const OffsetRange &aOffsetRange) { Error error = kErrorNone; - VerifyOrExit(aLength <= kMaxLength, error = kErrorInvalidArgs); + VerifyOrExit(aOffsetRange.GetLength() <= kMaxLength, error = kErrorInvalidArgs); - SuccessOrExit(error = aMessage.Read(aOffset, mTlvs, aLength)); - mLength = static_cast(aLength); + SuccessOrExit(error = aMessage.Read(aOffsetRange, mTlvs, aOffsetRange.GetLength())); + mLength = static_cast(aOffsetRange.GetLength()); mUpdateTime = TimerMilli::GetNow(); diff --git a/src/core/meshcop/dataset.hpp b/src/core/meshcop/dataset.hpp index d458b2c78..75e448ead 100644 --- a/src/core/meshcop/dataset.hpp +++ b/src/core/meshcop/dataset.hpp @@ -633,16 +633,15 @@ class Dataset /** * Sets the Dataset by reading the TLVs bytes from given message. * - * @param[in] aMessage The message to read from. - * @param[in] aOffset The offset in @p aMessage to start reading the Dataset TLVs. - * @param[in] aLength The dataset length in bytes. + * @param[in] aMessage The message to read from. + * @param[in] aOffsetRange The offset range in @p aMessage to read the Dataset TLVs. * * @retval kErrorNone Successfully set the Dataset. - * @retval kInvalidArgs The @p aLength is longer than `kMaxLength`. + * @retval kInvalidArgs The given offset range length is longer than `kMaxLength`. * @retval kErrorParse Could not read or parse the dataset from @p aMessage. * */ - Error SetFrom(const Message &aMessage, uint16_t aOffset, uint16_t aLength); + Error SetFrom(const Message &aMessage, const OffsetRange &aOffsetRange); /** * Returns a pointer to the start of Dataset TLVs sequence. diff --git a/src/core/meshcop/dataset_manager.cpp b/src/core/meshcop/dataset_manager.cpp index 8038af1fb..de2c1d886 100644 --- a/src/core/meshcop/dataset_manager.cpp +++ b/src/core/meshcop/dataset_manager.cpp @@ -532,17 +532,17 @@ void DatasetManager::HandleMgmtSetResponse(Coap::Message *aMessage, const Ip6::M void DatasetManager::HandleGet(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const { - TlvList tlvList; - uint8_t tlvType; - uint16_t offset; - uint16_t length; + TlvList tlvList; + uint8_t tlvType; + OffsetRange offsetRange; - SuccessOrExit(Tlv::FindTlvValueOffset(aMessage, Tlv::kGet, offset, length)); + SuccessOrExit(Tlv::FindTlvValueOffsetRange(aMessage, Tlv::kGet, offsetRange)); - for (; length > 0; length--, offset++) + while (!offsetRange.IsEmpty()) { - IgnoreError(aMessage.Read(offset, tlvType)); + IgnoreError(aMessage.Read(offsetRange, tlvType)); tlvList.Add(tlvType); + offsetRange.AdvanceOffset(sizeof(uint8_t)); } // MGMT_PENDING_GET.rsp must include Delay Timer TLV (Thread 1.1.1 diff --git a/src/core/meshcop/dataset_manager_ftd.cpp b/src/core/meshcop/dataset_manager_ftd.cpp index 956a5fa58..47e6eaa71 100644 --- a/src/core/meshcop/dataset_manager_ftd.cpp +++ b/src/core/meshcop/dataset_manager_ftd.cpp @@ -71,6 +71,7 @@ Error DatasetManager::ProcessSetOrReplaceRequest(MgmtCommand aCommand, { Error error = kErrorParse; Dataset dataset; + OffsetRange offsetRange; Timestamp activeTimestamp; ChannelTlvValue channelValue; uint16_t sessionId; @@ -81,7 +82,8 @@ Error DatasetManager::ProcessSetOrReplaceRequest(MgmtCommand aCommand, aInfo.Clear(); - SuccessOrExit(dataset.SetFrom(aMessage, aMessage.GetOffset(), aMessage.GetLength() - aMessage.GetOffset())); + offsetRange.InitFromMessageOffsetToEnd(aMessage); + SuccessOrExit(dataset.SetFrom(aMessage, offsetRange)); SuccessOrExit(dataset.ValidateTlvs()); // Verify that the request includes timestamps that are diff --git a/src/core/meshcop/dataset_updater.cpp b/src/core/meshcop/dataset_updater.cpp index fb0a0ef8e..f512f8057 100644 --- a/src/core/meshcop/dataset_updater.cpp +++ b/src/core/meshcop/dataset_updater.cpp @@ -161,14 +161,16 @@ void DatasetUpdater::HandleNotifierEvents(Events aEvents) void DatasetUpdater::HandleDatasetChanged(Dataset::Type aType) { - Dataset requestedDataset; - Dataset newDataset; - Timestamp newTimestamp; - Timestamp requestedTimestamp; + Dataset requestedDataset; + Dataset newDataset; + Timestamp newTimestamp; + Timestamp requestedTimestamp; + OffsetRange offsetRange; VerifyOrExit(IsUpdateOngoing()); - SuccessOrExit(requestedDataset.SetFrom(*mDataset, /* aOffset */ 0, mDataset->GetLength())); + offsetRange.InitFromMessageFullLength(*mDataset); + SuccessOrExit(requestedDataset.SetFrom(*mDataset, offsetRange)); if (aType == Dataset::kActive) { diff --git a/src/core/meshcop/joiner_router.cpp b/src/core/meshcop/joiner_router.cpp index b9f4a57c4..8c31dba51 100644 --- a/src/core/meshcop/joiner_router.cpp +++ b/src/core/meshcop/joiner_router.cpp @@ -133,6 +133,7 @@ void JoinerRouter::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &a Tmf::MessageInfo messageInfo(GetInstance()); ExtendedTlv tlv; uint16_t borderAgentRloc; + OffsetRange offsetRange; LogInfo("JoinerRouter::HandleUdpReceive"); @@ -145,10 +146,12 @@ void JoinerRouter::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &a SuccessOrExit(error = Tlv::Append(*message, aMessageInfo.GetPeerAddr().GetIid())); SuccessOrExit(error = Tlv::Append(*message, Get().GetRloc16())); + offsetRange.InitFromMessageOffsetToEnd(aMessage); + tlv.SetType(Tlv::kJoinerDtlsEncapsulation); - tlv.SetLength(aMessage.GetLength() - aMessage.GetOffset()); + tlv.SetLength(offsetRange.GetLength()); SuccessOrExit(error = message->Append(tlv)); - SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(), tlv.GetLength())); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange)); messageInfo.SetSockAddrToRlocPeerAddrTo(borderAgentRloc); @@ -168,8 +171,7 @@ template <> void JoinerRouter::HandleTmf(Coap::Message &aMessage, c uint16_t joinerPort; Ip6::InterfaceIdentifier joinerIid; Kek kek; - uint16_t offset; - uint16_t length; + OffsetRange offsetRange; Message *message = nullptr; Message::Settings settings(Message::kNoLinkSecurity, Message::kPriorityNet); Ip6::MessageInfo messageInfo; @@ -181,11 +183,11 @@ template <> void JoinerRouter::HandleTmf(Coap::Message &aMessage, c SuccessOrExit(error = Tlv::Find(aMessage, joinerPort)); SuccessOrExit(error = Tlv::Find(aMessage, joinerIid)); - SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Tlv::kJoinerDtlsEncapsulation, offset, length)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Tlv::kJoinerDtlsEncapsulation, offsetRange)); VerifyOrExit((message = mSocket.NewMessage(0, settings)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offset, length)); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange)); messageInfo.GetPeerAddr().SetToLinkLocalAddress(joinerIid); messageInfo.SetPeerPort(joinerPort); diff --git a/src/core/meshcop/meshcop_tlvs.cpp b/src/core/meshcop/meshcop_tlvs.cpp index c4ea22d3b..74d6a39d3 100644 --- a/src/core/meshcop/meshcop_tlvs.cpp +++ b/src/core/meshcop/meshcop_tlvs.cpp @@ -146,12 +146,15 @@ Error ChannelMaskTlv::FindIn(const Message &aMessage, uint32_t &aChannelMask) { Error error; EntriesData entriesData; + OffsetRange offsetRange; entriesData.Clear(); entriesData.mMessage = &aMessage; - SuccessOrExit(error = FindTlvValueOffset(aMessage, Tlv::kChannelMask, entriesData.mOffset, entriesData.mLength)); - error = entriesData.Parse(aChannelMask); + SuccessOrExit(error = FindTlvValueOffsetRange(aMessage, Tlv::kChannelMask, offsetRange)); + entriesData.mOffset = offsetRange.GetOffset(); + entriesData.mLength = offsetRange.GetLength(); + error = entriesData.Parse(aChannelMask); exit: return error; diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index 86e543966..11c185922 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -462,10 +462,12 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncommingMessage, Message &aOut Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncommingMessage, uint16_t aOffset, uint16_t aLength) { - Dataset dataset; - Error error; + Dataset dataset; + OffsetRange offsetRange; + Error error; - SuccessOrExit(error = dataset.SetFrom(aIncommingMessage, aOffset, aLength)); + offsetRange.Init(aOffset, aLength); + SuccessOrExit(error = dataset.SetFrom(aIncommingMessage, offsetRange)); SuccessOrExit(error = dataset.ValidateTlvs()); if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags, diff --git a/src/core/thread/discover_scanner.cpp b/src/core/thread/discover_scanner.cpp index 03e2ca4eb..594251a3e 100644 --- a/src/core/thread/discover_scanner.cpp +++ b/src/core/thread/discover_scanner.cpp @@ -312,8 +312,7 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const MeshCoP::Tlv meshcopTlv; MeshCoP::DiscoveryResponseTlv discoveryResponse; ScanResult result; - uint16_t offset; - uint16_t end; + OffsetRange offsetRange; bool didCheckSteeringData = false; Mle::Log(Mle::kMessageReceive, Mle::kTypeDiscoveryResponse, aRxInfo.mMessageInfo.GetPeerAddr()); @@ -321,7 +320,7 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const VerifyOrExit(mState == kStateScanning, error = kErrorDrop); // Find MLE Discovery TLV - SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aRxInfo.mMessage, Tlv::kDiscovery, offset, end)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRxInfo.mMessage, Tlv::kDiscovery, offsetRange)); ClearAllBytes(result); result.mDiscover = true; @@ -333,35 +332,35 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&result.mExtAddress)); // Process MeshCoP TLVs - while (offset < end) + while (!offsetRange.IsEmpty()) { - SuccessOrExit(error = aRxInfo.mMessage.Read(offset, meshcopTlv)); + SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, meshcopTlv)); if (meshcopTlv.IsExtended()) { - SuccessOrExit(error = Tlv::ParseAndSkipTlv(aRxInfo.mMessage, offset)); - VerifyOrExit(offset <= end, error = kErrorParse); + SuccessOrExit(error = Tlv::ParseAndSkipTlv(aRxInfo.mMessage, offsetRange)); continue; } - VerifyOrExit(meshcopTlv.GetSize() + offset <= aRxInfo.mMessage.GetLength(), error = kErrorParse); + VerifyOrExit(offsetRange.Contains(meshcopTlv.GetSize()), error = kErrorParse); switch (meshcopTlv.GetType()) { case MeshCoP::Tlv::kDiscoveryResponse: - SuccessOrExit(error = aRxInfo.mMessage.Read(offset, discoveryResponse)); + SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, discoveryResponse)); VerifyOrExit(discoveryResponse.IsValid(), error = kErrorParse); result.mVersion = discoveryResponse.GetVersion(); result.mIsNative = discoveryResponse.IsNativeCommissioner(); break; case MeshCoP::Tlv::kExtendedPanId: - SuccessOrExit(error = Tlv::Read(aRxInfo.mMessage, offset, + SuccessOrExit(error = Tlv::Read(aRxInfo.mMessage, offsetRange.GetOffset(), AsCoreType(&result.mExtendedPanId))); break; case MeshCoP::Tlv::kNetworkName: - SuccessOrExit(error = Tlv::Read(aRxInfo.mMessage, offset, result.mNetworkName.m8)); + SuccessOrExit(error = Tlv::Read(aRxInfo.mMessage, offsetRange.GetOffset(), + result.mNetworkName.m8)); break; case MeshCoP::Tlv::kSteeringData: @@ -377,7 +376,8 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const steeringData.Init(dataLength); - SuccessOrExit(error = Tlv::ReadTlvValue(aRxInfo.mMessage, offset, steeringData.GetData(), dataLength)); + SuccessOrExit(error = Tlv::ReadTlvValue(aRxInfo.mMessage, offsetRange.GetOffset(), + steeringData.GetData(), dataLength)); if (mEnableFiltering) { @@ -389,15 +389,15 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const break; case MeshCoP::Tlv::kJoinerUdpPort: - SuccessOrExit(error = - Tlv::Read(aRxInfo.mMessage, offset, result.mJoinerUdpPort)); + SuccessOrExit(error = Tlv::Read(aRxInfo.mMessage, offsetRange.GetOffset(), + result.mJoinerUdpPort)); break; default: break; } - offset += sizeof(meshcopTlv) + meshcopTlv.GetLength(); + offsetRange.AdvanceOffset(meshcopTlv.GetSize()); } VerifyOrExit(!mEnableFiltering || didCheckSteeringData); diff --git a/src/core/thread/link_metrics.cpp b/src/core/thread/link_metrics.cpp index 840d28e34..7cd068f1a 100644 --- a/src/core/thread/link_metrics.cpp +++ b/src/core/thread/link_metrics.cpp @@ -123,11 +123,9 @@ Error Initiator::AppendLinkMetricsQueryTlv(Message &aMessage, const QueryInfo &a return error; } -void Initiator::HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t aLength, const Ip6::Address &aAddress) +void Initiator::HandleReport(const Message &aMessage, OffsetRange &aOffsetRange, const Ip6::Address &aAddress) { Error error = kErrorNone; - uint16_t offset = aOffset; - uint16_t endOffset = aOffset + aLength; bool hasStatus = false; bool hasReport = false; Tlv tlv; @@ -142,18 +140,17 @@ void Initiator::HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t values.Clear(); - while (offset < endOffset) + while (!aOffsetRange.IsEmpty()) { - SuccessOrExit(error = aMessage.Read(offset, tlv)); + SuccessOrExit(error = aMessage.Read(aOffsetRange, tlv)); if (tlv.IsExtended()) { - SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offset)); - VerifyOrExit(offset <= endOffset, error = kErrorParse); + SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, aOffsetRange)); continue; } - VerifyOrExit(tlv.GetSize() + offset <= endOffset, error = kErrorParse); + VerifyOrExit(aOffsetRange.Contains(tlv.GetSize()), error = kErrorParse); // The report must contain either: // - One or more Report Sub-TLVs (in case of success), or @@ -163,7 +160,7 @@ void Initiator::HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t { case StatusSubTlv::kType: VerifyOrExit(!hasStatus && !hasReport, error = kErrorDrop); - SuccessOrExit(error = Tlv::Read(aMessage, offset, status)); + SuccessOrExit(error = Tlv::Read(aMessage, aOffsetRange.GetOffset(), status)); hasStatus = true; break; @@ -171,7 +168,7 @@ void Initiator::HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t VerifyOrExit(!hasStatus, error = kErrorDrop); // Read the report sub-TLV assuming minimum length - SuccessOrExit(error = aMessage.Read(offset, &reportTlv, sizeof(Tlv) + ReportSubTlv::kMinLength)); + SuccessOrExit(error = aMessage.Read(aOffsetRange, &reportTlv, sizeof(Tlv) + ReportSubTlv::kMinLength)); VerifyOrExit(reportTlv.IsValid(), error = kErrorParse); hasReport = true; @@ -187,7 +184,7 @@ void Initiator::HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t { // If Type ID indicates metric value has 4 bytes length, we // read the full `reportTlv`. - SuccessOrExit(error = aMessage.Read(offset, reportTlv)); + SuccessOrExit(error = aMessage.Read(aOffsetRange.GetOffset(), reportTlv)); } switch (typeId) @@ -220,7 +217,7 @@ void Initiator::HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t break; } - offset += sizeof(Tlv) + tlv.GetLength(); + aOffsetRange.AdvanceOffset(tlv.GetSize()); } VerifyOrExit(hasStatus || hasReport); @@ -310,37 +307,34 @@ Error Initiator::SendMgmtRequestEnhAckProbing(const Ip6::Address &aDestination, Error Initiator::HandleManagementResponse(const Message &aMessage, const Ip6::Address &aAddress) { - Error error = kErrorNone; - uint16_t offset; - uint16_t endOffset; - uint8_t status; - bool hasStatus = false; + Error error = kErrorNone; + OffsetRange offsetRange; + uint8_t status; + bool hasStatus = false; VerifyOrExit(mMgmtResponseCallback.IsSet()); - SuccessOrExit( - error = Tlv::FindTlvValueStartEndOffsets(aMessage, Mle::Tlv::Type::kLinkMetricsManagement, offset, endOffset)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Mle::Tlv::Type::kLinkMetricsManagement, offsetRange)); - while (offset < endOffset) + while (!offsetRange.IsEmpty()) { Tlv tlv; - SuccessOrExit(error = aMessage.Read(offset, tlv)); + SuccessOrExit(error = aMessage.Read(offsetRange, tlv)); if (tlv.IsExtended()) { - SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offset)); - VerifyOrExit(offset <= endOffset, error = kErrorParse); + SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offsetRange)); continue; } - VerifyOrExit(tlv.GetSize() + offset <= endOffset, error = kErrorParse); + VerifyOrExit(offsetRange.Contains(tlv.GetSize()), error = kErrorParse); switch (tlv.GetType()) { case StatusSubTlv::kType: VerifyOrExit(!hasStatus, error = kErrorParse); - SuccessOrExit(error = Tlv::Read(aMessage, offset, status)); + SuccessOrExit(error = Tlv::Read(aMessage, offsetRange.GetOffset(), status)); hasStatus = true; break; @@ -348,7 +342,7 @@ Error Initiator::HandleManagementResponse(const Message &aMessage, const Ip6::Ad break; } - offset += sizeof(Tlv) + tlv.GetLength(); + offsetRange.AdvanceOffset(tlv.GetSize()); } VerifyOrExit(hasStatus, error = kErrorParse); @@ -440,7 +434,7 @@ Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, N bool hasQueryId = false; uint16_t length; uint16_t offset; - uint16_t endOffset; + OffsetRange offsetRange; MetricsValues values; values.Clear(); @@ -449,40 +443,43 @@ Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, N // Parse MLE Link Metrics Query TLV and its sub-TLVs from // `aRequestMessage`. - SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aRequestMessage, Mle::Tlv::Type::kLinkMetricsQuery, offset, - endOffset)); + SuccessOrExit(error = + Tlv::FindTlvValueOffsetRange(aRequestMessage, Mle::Tlv::Type::kLinkMetricsQuery, offsetRange)); - while (offset < endOffset) + while (!offsetRange.IsEmpty()) { - SuccessOrExit(error = aRequestMessage.Read(offset, tlv)); + OffsetRange tlvOffsetRange; + + SuccessOrExit(error = aRequestMessage.Read(offsetRange, tlv)); if (tlv.IsExtended()) { - SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offset)); - VerifyOrExit(offset <= endOffset, error = kErrorParse); + SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offsetRange)); continue; } - VerifyOrExit(tlv.GetSize() + offset <= endOffset, error = kErrorParse); + VerifyOrExit(offsetRange.Contains(tlv.GetSize()), error = kErrorParse); + + tlvOffsetRange = offsetRange; + tlvOffsetRange.ShrinkLength(tlv.GetSize()); switch (tlv.GetType()) { case SubTlv::kQueryId: - SuccessOrExit(error = Tlv::Read(aRequestMessage, offset, queryId)); + SuccessOrExit(error = Tlv::Read(aRequestMessage, tlvOffsetRange.GetOffset(), queryId)); hasQueryId = true; break; case SubTlv::kQueryOptions: - SuccessOrExit(error = ReadTypeIdsFromMessage(aRequestMessage, offset + sizeof(tlv), - static_cast(offset + tlv.GetSize()), - values.GetMetrics())); + tlvOffsetRange.AdvanceOffset(sizeof(tlv)); + SuccessOrExit(error = ReadTypeIdsFromMessage(aRequestMessage, tlvOffsetRange, values.GetMetrics())); break; default: break; } - offset += static_cast(tlv.GetSize()); + offsetRange.AdvanceOffset(tlv.GetSize()); } VerifyOrExit(hasQueryId, error = kErrorParse); @@ -539,31 +536,39 @@ Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, N Error Subject::HandleManagementRequest(const Message &aMessage, Neighbor &aNeighbor, Status &aStatus) { Error error = kErrorNone; - uint16_t offset; - uint16_t endOffset; - uint16_t tlvEndOffset; + OffsetRange offsetRange; FwdProbingRegSubTlv fwdProbingSubTlv; EnhAckConfigSubTlv enhAckConfigSubTlv; Metrics metrics; - SuccessOrExit( - error = Tlv::FindTlvValueStartEndOffsets(aMessage, Mle::Tlv::Type::kLinkMetricsManagement, offset, endOffset)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Mle::Tlv::Type::kLinkMetricsManagement, offsetRange)); // Set sub-TLV lengths to zero to indicate that we have // not yet seen them in the message. fwdProbingSubTlv.SetLength(0); enhAckConfigSubTlv.SetLength(0); - for (; offset < endOffset; offset = tlvEndOffset) + while (!offsetRange.IsEmpty()) { - Tlv tlv; - uint16_t minTlvSize; - Tlv *subTlv; + Tlv tlv; + uint16_t minTlvSize; + Tlv *subTlv; + OffsetRange tlvOffsetRange; - SuccessOrExit(error = aMessage.Read(offset, tlv)); + SuccessOrExit(error = aMessage.Read(offsetRange, tlv)); - VerifyOrExit(offset + tlv.GetSize() <= endOffset, error = kErrorParse); - tlvEndOffset = static_cast(offset + tlv.GetSize()); + if (tlv.IsExtended()) + { + SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offsetRange)); + continue; + } + + VerifyOrExit(offsetRange.Contains(tlv.GetSize()), error = kErrorParse); + + tlvOffsetRange = offsetRange; + tlvOffsetRange.ShrinkLength(tlv.GetSize()); + + offsetRange.AdvanceOffset(tlv.GetSize()); switch (tlv.GetType()) { @@ -588,8 +593,10 @@ Error Subject::HandleManagementRequest(const Message &aMessage, Neighbor &aNeigh VerifyOrExit(tlv.GetSize() >= minTlvSize, error = kErrorParse); // Read `subTlv` with its `minTlvSize`, followed by the Type IDs. - SuccessOrExit(error = aMessage.Read(offset, subTlv, minTlvSize)); - SuccessOrExit(error = ReadTypeIdsFromMessage(aMessage, offset + minTlvSize, tlvEndOffset, metrics)); + SuccessOrExit(error = aMessage.Read(tlvOffsetRange.GetOffset(), subTlv, minTlvSize)); + + tlvOffsetRange.AdvanceOffset(minTlvSize); + SuccessOrExit(error = ReadTypeIdsFromMessage(aMessage, tlvOffsetRange, metrics)); } if (fwdProbingSubTlv.GetLength() != 0) @@ -609,13 +616,11 @@ Error Subject::HandleManagementRequest(const Message &aMessage, Neighbor &aNeigh Error Subject::HandleLinkProbe(const Message &aMessage, uint8_t &aSeriesId) { - Error error = kErrorNone; - uint16_t offset; - uint16_t length; + Error error = kErrorNone; + OffsetRange offsetRange; - SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Mle::Tlv::Type::kLinkProbe, offset, length)); - VerifyOrExit(length >= sizeof(aSeriesId), error = kErrorParse); - error = aMessage.Read(offset, aSeriesId); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Mle::Tlv::Type::kLinkProbe, offsetRange)); + error = aMessage.Read(offsetRange, aSeriesId); exit: return error; @@ -662,20 +667,17 @@ Error Subject::AppendReportSubTlvToMessage(Message &aMessage, const MetricsValue void Subject::Free(SeriesInfo &aSeriesInfo) { mSeriesInfoPool.Free(aSeriesInfo); } -Error Subject::ReadTypeIdsFromMessage(const Message &aMessage, - uint16_t aStartOffset, - uint16_t aEndOffset, - Metrics &aMetrics) +Error Subject::ReadTypeIdsFromMessage(const Message &aMessage, OffsetRange &aOffsetRange, Metrics &aMetrics) { Error error = kErrorNone; aMetrics.Clear(); - for (uint16_t offset = aStartOffset; offset < aEndOffset; offset++) + while (!aOffsetRange.IsEmpty()) { uint8_t typeId; - SuccessOrExit(aMessage.Read(offset, typeId)); + SuccessOrExit(aMessage.Read(aOffsetRange, typeId)); switch (typeId) { @@ -702,7 +704,7 @@ Error Subject::ReadTypeIdsFromMessage(const Message &aMessage, default: if (TypeId::IsExtended(typeId)) { - offset += sizeof(uint8_t); // Skip the additional second byte. + aOffsetRange.AdvanceOffset(sizeof(uint8_t)); // Skip the additional second byte. } else { @@ -710,6 +712,8 @@ Error Subject::ReadTypeIdsFromMessage(const Message &aMessage, } break; } + + aOffsetRange.AdvanceOffset(sizeof(uint8_t)); } exit: diff --git a/src/core/thread/link_metrics.hpp b/src/core/thread/link_metrics.hpp index 7c734bf4a..34294b66e 100644 --- a/src/core/thread/link_metrics.hpp +++ b/src/core/thread/link_metrics.hpp @@ -148,12 +148,11 @@ class Initiator : public InstanceLocator, private NonCopyable * Handles the received Link Metrics report contained in @p aMessage. * * @param[in] aMessage A reference to the message. - * @param[in] aOffset The offset in bytes where the metrics report sub-TLVs start. - * @param[in] aLength The length of the metrics report sub-TLVs in bytes. + * @param[in] aOffsetRange The offset range in @p aMessage where the metrics report sub-TLVs are present. * @param[in] aAddress A reference to the source address of the message. * */ - void HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t aLength, const Ip6::Address &aAddress); + void HandleReport(const Message &aMessage, OffsetRange &aOffsetRange, const Ip6::Address &aAddress); /** * Sends an MLE Link Metrics Management Request to configure/clear a Forward Tracking Series. @@ -342,10 +341,7 @@ class Subject : public InstanceLocator, private NonCopyable static constexpr uint16_t kMaxSeriesSupported = OPENTHREAD_CONFIG_MLE_LINK_METRICS_SERIES_MTD; #endif - static Error ReadTypeIdsFromMessage(const Message &aMessage, - uint16_t aStartOffset, - uint16_t aEndOffset, - Metrics &aMetrics); + static Error ReadTypeIdsFromMessage(const Message &aMessage, OffsetRange &aOffsetRange, Metrics &aMetrics); static Error AppendReportSubTlvToMessage(Message &aMessage, const MetricsValues &aValues); Status ConfigureForwardTrackingSeries(uint8_t aSeriesId, diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 0b013e350..c614ed060 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -2842,12 +2842,11 @@ void Mle::HandleDataResponse(RxInfo &aRxInfo) #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE { - uint16_t offset; - uint16_t length; + OffsetRange offsetRange; - if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kLinkMetricsReport, offset, length) == kErrorNone) + if (Tlv::FindTlvValueOffsetRange(aRxInfo.mMessage, Tlv::kLinkMetricsReport, offsetRange) == kErrorNone) { - Get().HandleReport(aRxInfo.mMessage, offset, length, + Get().HandleReport(aRxInfo.mMessage, offsetRange, aRxInfo.mMessageInfo.GetPeerAddr()); } } @@ -4948,10 +4947,9 @@ Error Mle::TxMessage::AppendSteeringDataTlv(void) bool Mle::RxMessage::ContainsTlv(Tlv::Type aTlvType) const { - uint16_t offset; - uint16_t length; + OffsetRange offsetRange; - return Tlv::FindTlvValueOffset(*this, aTlvType, offset, length) == kErrorNone; + return Tlv::FindTlvValueOffsetRange(*this, aTlvType, offsetRange) == kErrorNone; } Error Mle::RxMessage::ReadModeTlv(DeviceMode &aMode) const @@ -4979,12 +4977,11 @@ Error Mle::RxMessage::ReadVersionTlv(uint16_t &aVersion) const Error Mle::RxMessage::ReadChallengeOrResponse(uint8_t aTlvType, RxChallenge &aRxChallenge) const { - Error error; - uint16_t offset; - uint16_t length; + Error error; + OffsetRange offsetRange; - SuccessOrExit(error = Tlv::FindTlvValueOffset(*this, aTlvType, offset, length)); - error = aRxChallenge.ReadFrom(*this, offset, length); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(*this, aTlvType, offsetRange)); + error = aRxChallenge.ReadFrom(*this, offsetRange); exit: return error; @@ -5049,15 +5046,14 @@ Error Mle::RxMessage::ReadLeaderDataTlv(LeaderData &aLeaderData) const Error Mle::RxMessage::ReadAndSetNetworkDataTlv(const LeaderData &aLeaderData) const { - Error error; - uint16_t offset; - uint16_t length; + Error error; + OffsetRange offsetRange; - SuccessOrExit(error = Tlv::FindTlvValueOffset(*this, Tlv::kNetworkData, offset, length)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(*this, Tlv::kNetworkData, offsetRange)); error = Get().SetNetworkData(aLeaderData.GetDataVersion(NetworkData::kFullSet), aLeaderData.GetDataVersion(NetworkData::kStableSubset), - Get().GetNetworkDataType(), *this, offset, length); + Get().GetNetworkDataType(), *this, offsetRange); exit: return error; } @@ -5078,12 +5074,11 @@ Error Mle::RxMessage::ReadAndSaveDataset(MeshCoP::Dataset::Type aDatasetType, Error error = kErrorNone; Tlv::Type tlvType = (aDatasetType == MeshCoP::Dataset::kActive) ? Tlv::kActiveDataset : Tlv::kPendingDataset; MeshCoP::Dataset dataset; - uint16_t offset; - uint16_t length; + OffsetRange offsetRange; - SuccessOrExit(error = Tlv::FindTlvValueOffset(*this, tlvType, offset, length)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(*this, tlvType, offsetRange)); - SuccessOrExit(error = dataset.SetFrom(*this, offset, length)); + SuccessOrExit(error = dataset.SetFrom(*this, offsetRange)); SuccessOrExit(error = dataset.ValidateTlvs()); SuccessOrExit(error = dataset.WriteTimestamp(aDatasetType, aTimestamp)); @@ -5103,19 +5098,15 @@ Error Mle::RxMessage::ReadAndSaveDataset(MeshCoP::Dataset::Type aDatasetType, Error Mle::RxMessage::ReadTlvRequestTlv(TlvList &aTlvList) const { - Error error; - uint16_t offset; - uint16_t length; + Error error; + OffsetRange offsetRange; - SuccessOrExit(error = Tlv::FindTlvValueOffset(*this, Tlv::kTlvRequest, offset, length)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(*this, Tlv::kTlvRequest, offsetRange)); - if (length > aTlvList.GetMaxSize()) - { - length = aTlvList.GetMaxSize(); - } + offsetRange.ShrinkLength(aTlvList.GetMaxSize()); - ReadBytes(offset, aTlvList.GetArrayBuffer(), length); - aTlvList.SetLength(static_cast(length)); + ReadBytes(offsetRange, aTlvList.GetArrayBuffer()); + aTlvList.SetLength(static_cast(offsetRange.GetLength())); exit: return error; diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 6948f9e43..c4aad8ce4 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -1761,11 +1761,10 @@ Error MleRouter::SetMaxChildIpAddresses(uint8_t aMaxIpAddresses) Error MleRouter::ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild) { - Error error; - uint16_t offset; - uint16_t endOffset; - uint8_t count = 0; - uint8_t storedCount = 0; + Error error; + OffsetRange offsetRange; + uint8_t count = 0; + uint8_t storedCount = 0; #if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE Ip6::Address oldDua; #endif @@ -1775,8 +1774,7 @@ Error MleRouter::ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild) OT_UNUSED_VARIABLE(storedCount); - SuccessOrExit(error = - Tlv::FindTlvValueStartEndOffsets(aRxInfo.mMessage, Tlv::kAddressRegistration, offset, endOffset)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRxInfo.mMessage, Tlv::kAddressRegistration, offsetRange)); #if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE if (aChild.GetDomainUnicastAddress(oldDua) != kErrorNone) @@ -1807,14 +1805,14 @@ Error MleRouter::ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild) aChild.ClearIp6Addresses(); - while (offset < endOffset) + while (!offsetRange.IsEmpty()) { uint8_t controlByte; Ip6::Address address; // Read out the control byte (first byte in entry) - SuccessOrExit(error = aRxInfo.mMessage.Read(offset, controlByte)); - offset++; + SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, controlByte)); + offsetRange.AdvanceOffset(sizeof(uint8_t)); count++; address.Clear(); @@ -1828,9 +1826,8 @@ Error MleRouter::ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild) uint8_t contextId = AddressRegistrationTlv::GetContextId(controlByte); Lowpan::Context context; - VerifyOrExit(offset + sizeof(Ip6::InterfaceIdentifier) <= endOffset, error = kErrorParse); - IgnoreError(aRxInfo.mMessage.Read(offset, address.GetIid())); - offset += sizeof(Ip6::InterfaceIdentifier); + IgnoreError(aRxInfo.mMessage.Read(offsetRange, address.GetIid())); + offsetRange.AdvanceOffset(sizeof(Ip6::InterfaceIdentifier)); if (Get().GetContext(contextId, context) != kErrorNone) { @@ -1845,9 +1842,8 @@ Error MleRouter::ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild) { // Uncompressed entry contains the full IPv6 address. - VerifyOrExit(offset + sizeof(Ip6::Address) <= endOffset, error = kErrorParse); - IgnoreError(aRxInfo.mMessage.Read(offset, address)); - offset += sizeof(Ip6::Address); + IgnoreError(aRxInfo.mMessage.Read(offsetRange, address)); + offsetRange.AdvanceOffset(sizeof(Ip6::Address)); } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE @@ -2624,8 +2620,7 @@ void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo) MeshCoP::Tlv meshcopTlv; MeshCoP::DiscoveryRequestTlv discoveryRequestTlv; MeshCoP::ExtendedPanId extPanId; - uint16_t offset; - uint16_t end; + OffsetRange offsetRange; Log(kMessageReceive, kTypeDiscoveryRequest, aRxInfo.mMessageInfo.GetPeerAddr()); @@ -2633,31 +2628,31 @@ void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo) VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState); - SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aRxInfo.mMessage, Tlv::kDiscovery, offset, end)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRxInfo.mMessage, Tlv::kDiscovery, offsetRange)); - while (offset < end) + while (!offsetRange.IsEmpty()) { - SuccessOrExit(error = aRxInfo.mMessage.Read(offset, meshcopTlv)); + SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, meshcopTlv)); if (meshcopTlv.IsExtended()) { - SuccessOrExit(error = Tlv::ParseAndSkipTlv(aRxInfo.mMessage, offset)); - VerifyOrExit(offset <= end, error = kErrorParse); + SuccessOrExit(error = Tlv::ParseAndSkipTlv(aRxInfo.mMessage, offsetRange)); continue; } - VerifyOrExit(meshcopTlv.GetSize() + offset <= aRxInfo.mMessage.GetLength(), error = kErrorParse); + VerifyOrExit(offsetRange.Contains(meshcopTlv.GetSize())); switch (meshcopTlv.GetType()) { case MeshCoP::Tlv::kDiscoveryRequest: - SuccessOrExit(error = aRxInfo.mMessage.Read(offset, discoveryRequestTlv)); + SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, discoveryRequestTlv)); VerifyOrExit(discoveryRequestTlv.IsValid(), error = kErrorParse); break; case MeshCoP::Tlv::kExtendedPanId: - SuccessOrExit(error = Tlv::Read(aRxInfo.mMessage, offset, extPanId)); + SuccessOrExit( + error = Tlv::Read(aRxInfo.mMessage, offsetRange.GetOffset(), extPanId)); VerifyOrExit(Get().GetExtPanId() != extPanId, error = kErrorDrop); break; @@ -2666,7 +2661,7 @@ void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo) break; } - offset += sizeof(meshcopTlv) + meshcopTlv.GetLength(); + offsetRange.AdvanceOffset(meshcopTlv.GetSize()); } if (discoveryRequestTlv.IsValid()) diff --git a/src/core/thread/mle_types.cpp b/src/core/thread/mle_types.cpp index 0c87ce6e9..9d61bbe12 100644 --- a/src/core/thread/mle_types.cpp +++ b/src/core/thread/mle_types.cpp @@ -160,17 +160,19 @@ void TxChallenge::GenerateRandom(void) { IgnoreError(Random::Crypto::Fill(*this) //--------------------------------------------------------------------------------------------------------------------- // RxChallenge -Error RxChallenge::ReadFrom(const Message &aMessage, uint16_t aOffset, uint16_t aLength) +Error RxChallenge::ReadFrom(const Message &aMessage, const OffsetRange &aOffsetRange) { - Error error = kErrorNone; + Error error = kErrorNone; + OffsetRange offsetRange = aOffsetRange; Clear(); - aLength = Min(aLength, kMaxSize); - VerifyOrExit(kMinSize <= aLength, error = kErrorParse); + offsetRange.ShrinkLength(kMaxSize); - SuccessOrExit(error = aMessage.Read(aOffset, mArray.GetArrayBuffer(), aLength)); - mArray.SetLength(static_cast(aLength)); + VerifyOrExit(offsetRange.Contains(kMinSize), error = kErrorParse); + + SuccessOrExit(error = aMessage.Read(offsetRange, mArray.GetArrayBuffer(), offsetRange.GetLength())); + mArray.SetLength(static_cast(offsetRange.GetLength())); exit: return error; diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp index f8ee58cbf..88be01597 100644 --- a/src/core/thread/mle_types.hpp +++ b/src/core/thread/mle_types.hpp @@ -51,6 +51,7 @@ #include "common/encoding.hpp" #include "common/equatable.hpp" #include "common/numeric_limits.hpp" +#include "common/offset_range.hpp" #include "common/string.hpp" #include "mac/mac_types.hpp" #include "meshcop/extended_panid.hpp" @@ -526,15 +527,14 @@ class RxChallenge * * If the given @p aLength is longer than `kMaxSize`, only `kMaxSize` bytes will be read. * - * @param[in] aMessage The message to read the challenge from. - * @param[in] aOffset The offset in @p aMessage to read from. - * @param[in] aLength Number of bytes to read. + * @param[in] aMessage The message to read the challenge from. + * @param[in] aOffsetRange The offset range in @p aMessage to read from. * * @retval kErrorNone Successfully read the challenge data from @p aMessage. - * @retval kErrorParse Not enough bytes to read, or invalid @p aLength (smaller than `kMinSize`). + * @retval kErrorParse Not enough bytes to read, or invalid length (smaller than `kMinSize`). * */ - Error ReadFrom(const Message &aMessage, uint16_t aOffset, uint16_t aLength); + Error ReadFrom(const Message &aMessage, const OffsetRange &aOffsetRange); /** * Compares the `RxChallenge` with a given `TxChallenge`. diff --git a/src/core/thread/mlr_manager.cpp b/src/core/thread/mlr_manager.cpp index 43f896e70..1b9f85df3 100644 --- a/src/core/thread/mlr_manager.cpp +++ b/src/core/thread/mlr_manager.cpp @@ -482,8 +482,8 @@ Error MlrManager::ParseMlrResponse(Error aResult, uint8_t &aStatus, AddressArray &aFailedAddresses) { - Error error; - uint16_t addressesOffset, addressesLength; + Error error; + OffsetRange offsetRange; aStatus = ThreadStatusTlv::kMlrGeneralFailure; @@ -492,15 +492,16 @@ Error MlrManager::ParseMlrResponse(Error aResult, SuccessOrExit(error = Tlv::Find(*aMessage, aStatus)); - if (ThreadTlv::FindTlvValueOffset(*aMessage, Ip6AddressesTlv::kIp6Addresses, addressesOffset, addressesLength) == - kErrorNone) + if (ThreadTlv::FindTlvValueOffsetRange(*aMessage, Ip6AddressesTlv::kIp6Addresses, offsetRange) == kErrorNone) { - VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, error = kErrorParse); - VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= Ip6AddressesTlv::kMaxAddresses, error = kErrorParse); + VerifyOrExit(offsetRange.GetLength() % sizeof(Ip6::Address) == 0, error = kErrorParse); + VerifyOrExit(offsetRange.GetLength() / sizeof(Ip6::Address) <= Ip6AddressesTlv::kMaxAddresses, + error = kErrorParse); - for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address)) + while (!offsetRange.IsEmpty()) { - IgnoreError(aMessage->Read(addressesOffset + offset, *aFailedAddresses.PushBack())); + IgnoreError(aMessage->Read(offsetRange, *aFailedAddresses.PushBack())); + offsetRange.AdvanceOffset(sizeof(Ip6::Address)); } } diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp index efb6225bd..f032f4c0b 100644 --- a/src/core/thread/network_data_leader.cpp +++ b/src/core/thread/network_data_leader.cpp @@ -426,19 +426,19 @@ Error Leader::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) co return error; } -Error Leader::SetNetworkData(uint8_t aVersion, - uint8_t aStableVersion, - Type aType, - const Message &aMessage, - uint16_t aOffset, - uint16_t aLength) +Error Leader::SetNetworkData(uint8_t aVersion, + uint8_t aStableVersion, + Type aType, + const Message &aMessage, + const OffsetRange &aOffsetRange) { - Error error = kErrorNone; + Error error = kErrorNone; + uint16_t length = aOffsetRange.GetLength(); - VerifyOrExit(aLength <= kMaxSize, error = kErrorParse); - SuccessOrExit(error = aMessage.Read(aOffset, GetBytes(), aLength)); + VerifyOrExit(length <= kMaxSize, error = kErrorParse); + SuccessOrExit(error = aMessage.Read(aOffsetRange.GetOffset(), GetBytes(), length)); - SetLength(static_cast(aLength)); + SetLength(static_cast(length)); mVersion = aVersion; mStableVersion = aStableVersion; diff --git a/src/core/thread/network_data_leader.hpp b/src/core/thread/network_data_leader.hpp index 72610fc62..0c13a2f67 100644 --- a/src/core/thread/network_data_leader.hpp +++ b/src/core/thread/network_data_leader.hpp @@ -168,19 +168,17 @@ class Leader : public MutableNetworkData, private NonCopyable * @param[in] aStableVersion The Stable Version value. * @param[in] aType The Network Data type to set, the full set or stable subset. * @param[in] aMessage A reference to the message. - * @param[in] aOffset The offset in @p aMessage pointing to start of Network Data. - * @param[in] aLength The length of Network Data. + * @param[in] aOffsetRange The offset range in @p aMessage to read from. * * @retval kErrorNone Successfully set the network data. * @retval kErrorParse Network Data in @p aMessage is not valid. * */ - Error SetNetworkData(uint8_t aVersion, - uint8_t aStableVersion, - Type aType, - const Message &aMessage, - uint16_t aOffset, - uint16_t aLength); + Error SetNetworkData(uint8_t aVersion, + uint8_t aStableVersion, + Type aType, + const Message &aMessage, + const OffsetRange &aOffsetRange); /** * Gets the Commissioning Dataset from Network Data. diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp index 4b8b4cb52..a174ba7c7 100644 --- a/src/core/thread/network_data_leader_ftd.cpp +++ b/src/core/thread/network_data_leader_ftd.cpp @@ -206,25 +206,25 @@ template <> void Leader::HandleTmf(Coap::Message &aMessage, template <> void Leader::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - uint16_t length; - uint16_t offset; Coap::Message *response = nullptr; + OffsetRange offsetRange; VerifyOrExit(Get().IsLeader() && !mWaitingForNetDataSync); response = Get().NewPriorityResponseMessage(aMessage); VerifyOrExit(response != nullptr); - if (Tlv::FindTlvValueOffset(aMessage, MeshCoP::Tlv::kGet, offset, length) == kErrorNone) + if (Tlv::FindTlvValueOffsetRange(aMessage, MeshCoP::Tlv::kGet, offsetRange) == kErrorNone) { // Append the requested sub-TLV types given in Get TLV. - for (; length > 0; offset++, length--) + while (!offsetRange.IsEmpty()) { uint8_t type; const MeshCoP::Tlv *subTlv; - IgnoreError(aMessage.Read(offset, type)); + IgnoreError(aMessage.Read(offsetRange, type)); + offsetRange.AdvanceOffset(sizeof(type)); subTlv = FindCommissioningDataSubTlv(type); @@ -1334,12 +1334,14 @@ Error Leader::SetCommissioningData(const void *aData, uint8_t aDataLength) Error Leader::SetCommissioningData(const Message &aMessage) { - Error error = kErrorNone; - uint16_t dataLength = aMessage.GetLength() - aMessage.GetOffset(); + Error error = kErrorNone; + OffsetRange offsetRange; CommissioningDataTlv *dataTlv; - SuccessOrExit(error = UpdateCommissioningData(dataLength, dataTlv)); - aMessage.ReadBytes(aMessage.GetOffset(), dataTlv->GetValue(), dataLength); + offsetRange.InitFromMessageOffsetToEnd(aMessage); + + SuccessOrExit(error = UpdateCommissioningData(offsetRange.GetLength(), dataTlv)); + aMessage.ReadBytes(offsetRange, dataTlv->GetValue()); exit: return error; diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp index 6e168c290..03080471f 100644 --- a/src/core/thread/network_diagnostic.cpp +++ b/src/core/thread/network_diagnostic.cpp @@ -239,17 +239,17 @@ Error Server::AppendMacCounters(Message &aMessage) Error Server::AppendRequestedTlvs(const Message &aRequest, Message &aResponse) { - Error error; - uint16_t offset; - uint16_t endOffset; + Error error; + OffsetRange offsetRange; - SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aRequest, Tlv::kTypeList, offset, endOffset)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRequest, Tlv::kTypeList, offsetRange)); - for (; offset < endOffset; offset++) + while (!offsetRange.IsEmpty()) { uint8_t tlvType; - SuccessOrExit(error = aRequest.Read(offset, tlvType)); + SuccessOrExit(error = aRequest.Read(offsetRange, tlvType)); + offsetRange.AdvanceOffset(sizeof(tlvType)); SuccessOrExit(error = AppendDiagTlv(tlvType, aResponse)); } @@ -536,9 +536,7 @@ void Server::PrepareAndSendAnswers(const Ip6::Address &aDestination, const Messa Coap::Message *answer; Error error; AnswerInfo info; - uint16_t offset; - uint16_t length; - uint16_t endOffset; + OffsetRange offsetRange; AnswerTlv answerTlv; if (Tlv::Find(aRequest, info.mQueryId) == kErrorNone) @@ -550,14 +548,14 @@ void Server::PrepareAndSendAnswers(const Ip6::Address &aDestination, const Messa SuccessOrExit(error = AllocateAnswer(answer, info)); - SuccessOrExit(error = Tlv::FindTlvValueOffset(aRequest, Tlv::kTypeList, offset, length)); - endOffset = offset + length; + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRequest, Tlv::kTypeList, offsetRange)); - for (; offset < endOffset; offset++) + while (!offsetRange.IsEmpty()) { uint8_t tlvType; - SuccessOrExit(error = aRequest.Read(offset, tlvType)); + SuccessOrExit(error = aRequest.Read(offsetRange, tlvType)); + offsetRange.AdvanceOffset(sizeof(tlvType)); switch (tlvType) { diff --git a/src/core/utils/mesh_diag.cpp b/src/core/utils/mesh_diag.cpp index 33c001d65..9b8981e5b 100644 --- a/src/core/utils/mesh_diag.cpp +++ b/src/core/utils/mesh_diag.cpp @@ -379,8 +379,7 @@ bool MeshDiag::ProcessRouterNeighborTableAnswer(Coap::Message &aMessage, const I bool MeshDiag::ProcessChildrenIp6AddrsAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { bool didPorcess = false; - uint16_t offset; - uint16_t endOffset; + OffsetRange offsetRange; ChildIp6AddressListTlvValue tlvValue; Ip6AddrIterator ip6AddrIterator; @@ -388,11 +387,11 @@ bool MeshDiag::ProcessChildrenIp6AddrsAnswer(Coap::Message &aMessage, const Ip6: while (true) { - SuccessOrExit(Tlv::FindTlvValueStartEndOffsets(aMessage, ChildIp6AddressListTlv::kType, offset, endOffset)); + SuccessOrExit(Tlv::FindTlvValueOffsetRange(aMessage, ChildIp6AddressListTlv::kType, offsetRange)); didPorcess = true; - if (offset == endOffset) + if (offsetRange.IsEmpty()) { // We reached end of the list mState = kStateIdle; @@ -404,13 +403,11 @@ bool MeshDiag::ProcessChildrenIp6AddrsAnswer(Coap::Message &aMessage, const Ip6: // Read the `ChildIp6AddressListTlvValue` (which contains the // child RLOC16) and then prepare the `Ip6AddrIterator`. - VerifyOrExit(offset + sizeof(tlvValue) <= endOffset); - IgnoreError(aMessage.Read(offset, tlvValue)); - offset += sizeof(tlvValue); + SuccessOrExit(aMessage.Read(offsetRange, tlvValue)); + offsetRange.AdvanceOffset(sizeof(tlvValue)); - ip6AddrIterator.mMessage = &aMessage; - ip6AddrIterator.mCurOffset = offset; - ip6AddrIterator.mEndOffset = endOffset; + ip6AddrIterator.mMessage = &aMessage; + ip6AddrIterator.mOffsetRange = offsetRange; mQueryChildrenIp6Addrs.mCallback.InvokeIfSet(kErrorPending, tlvValue.GetRloc16(), &ip6AddrIterator); @@ -418,7 +415,7 @@ bool MeshDiag::ProcessChildrenIp6AddrsAnswer(Coap::Message &aMessage, const Ip6: // callback. VerifyOrExit(mState == kStateQueryChildrenIp6Addrs); - aMessage.SetOffset(endOffset); + aMessage.SetOffset(offsetRange.GetEndOffset()); } exit: @@ -531,7 +528,7 @@ Error MeshDiag::Ip6AddrIterator::InitFrom(const Message &aMessage) { Error error; - SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aMessage, Ip6AddressListTlv::kType, mCurOffset, mEndOffset)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Ip6AddressListTlv::kType, mOffsetRange)); mMessage = &aMessage; exit: @@ -543,10 +540,9 @@ Error MeshDiag::Ip6AddrIterator::GetNextAddress(Ip6::Address &aAddress) Error error = kErrorNone; VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); - VerifyOrExit(mCurOffset + sizeof(Ip6::Address) <= mEndOffset, error = kErrorNotFound); - IgnoreError(mMessage->Read(mCurOffset, aAddress)); - mCurOffset += sizeof(Ip6::Address); + VerifyOrExit(mMessage->Read(mOffsetRange, aAddress) == kErrorNone, error = kErrorNotFound); + mOffsetRange.AdvanceOffset(sizeof(Ip6::Address)); exit: return error; @@ -559,7 +555,8 @@ Error MeshDiag::ChildIterator::InitFrom(const Message &aMessage, uint16_t aParen { Error error; - SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aMessage, ChildTableTlv::kType, mCurOffset, mEndOffset)); + SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, ChildTableTlv::kType, mOffsetRange)); + mMessage = &aMessage; mParentRloc16 = aParentRloc16; @@ -573,10 +570,9 @@ Error MeshDiag::ChildIterator::GetNextChildInfo(ChildInfo &aChildInfo) ChildTableEntry entry; VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); - VerifyOrExit(mCurOffset + sizeof(ChildTableEntry) <= mEndOffset, error = kErrorNotFound); - IgnoreError(mMessage->Read(mCurOffset, entry)); - mCurOffset += sizeof(ChildTableEntry); + VerifyOrExit(mMessage->Read(mOffsetRange, entry) == kErrorNone, error = kErrorNotFound); + mOffsetRange.AdvanceOffset(sizeof(ChildTableEntry)); aChildInfo.mRloc16 = mParentRloc16 + entry.GetChildId(); entry.GetMode().Get(aChildInfo.mMode); diff --git a/src/core/utils/mesh_diag.hpp b/src/core/utils/mesh_diag.hpp index 3e7b04c11..3869d8dce 100644 --- a/src/core/utils/mesh_diag.hpp +++ b/src/core/utils/mesh_diag.hpp @@ -105,8 +105,7 @@ class MeshDiag : public InstanceLocator Error InitFrom(const Message &aMessage); const Message *mMessage; - uint16_t mCurOffset; - uint16_t mEndOffset; + OffsetRange mOffsetRange; }; /** @@ -153,8 +152,7 @@ class MeshDiag : public InstanceLocator Error InitFrom(const Message &aMessage, uint16_t aParentRloc16); const Message *mMessage; - uint16_t mCurOffset; - uint16_t mEndOffset; + OffsetRange mOffsetRange; uint16_t mParentRloc16; }; diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 92aad7622..e936417c1 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -207,6 +207,7 @@ ot_unit_test(ndproxy_table) ot_unit_test(netif) ot_unit_test(network_data) ot_unit_test(network_name) +ot_unit_test(offset_range) ot_unit_test(pool) ot_unit_test(power_calibration) ot_unit_test(priority_queue) diff --git a/tests/unit/test_lowpan.cpp b/tests/unit/test_lowpan.cpp index a630086ac..6e2cbc7c9 100644 --- a/tests/unit/test_lowpan.cpp +++ b/tests/unit/test_lowpan.cpp @@ -103,6 +103,7 @@ void TestIphcVector::GetUncompressedStream(Message &aMessage) static void Init(void) { otMeshLocalPrefix meshLocalPrefix = {{0xfd, 0x00, 0xca, 0xfe, 0xfa, 0xce, 0x12, 0x34}}; + OffsetRange offsetRange; sInstance->Get().SetMeshLocalPrefix(static_cast(meshLocalPrefix)); @@ -127,8 +128,10 @@ static void Init(void) SuccessOrQuit(message->AppendBytes(mockNetworkData, sizeof(mockNetworkData))); + offsetRange.Init(2, 0x20); + IgnoreError( - sInstance->Get().SetNetworkData(0, 0, NetworkData::kStableSubset, *message, 2, 0x20)); + sInstance->Get().SetNetworkData(0, 0, NetworkData::kStableSubset, *message, offsetRange)); } /** diff --git a/tests/unit/test_offset_range.cpp b/tests/unit/test_offset_range.cpp new file mode 100644 index 000000000..c648c856b --- /dev/null +++ b/tests/unit/test_offset_range.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2024, 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. + */ + +#include "common/message.hpp" +#include "common/offset_range.hpp" +#include "instance/instance.hpp" + +#include "test_platform.h" +#include "test_util.hpp" + +namespace ot { + +void TestOffsetRange(void) +{ + Instance *instance; + Message *message; + OffsetRange offsetRange; + + instance = testInitInstance(); + VerifyOrQuit(instance != nullptr); + + // Empty `OffsetRange` + + offsetRange.Clear(); + VerifyOrQuit(offsetRange.GetOffset() == 0); + VerifyOrQuit(offsetRange.GetLength() == 0); + VerifyOrQuit(offsetRange.GetEndOffset() == 0); + VerifyOrQuit(offsetRange.IsEmpty()); + + offsetRange.ShrinkLength(10); + VerifyOrQuit(offsetRange.GetOffset() == 0); + VerifyOrQuit(offsetRange.GetLength() == 0); + VerifyOrQuit(offsetRange.GetEndOffset() == 0); + VerifyOrQuit(offsetRange.IsEmpty()); + + offsetRange.AdvanceOffset(20); + VerifyOrQuit(offsetRange.GetOffset() == 0); + VerifyOrQuit(offsetRange.GetLength() == 0); + VerifyOrQuit(offsetRange.GetEndOffset() == 0); + VerifyOrQuit(offsetRange.IsEmpty()); + + // Empty `OffsetRange` with non-zero starting offset + + offsetRange.Init(100, 0); + VerifyOrQuit(offsetRange.GetOffset() == 100); + VerifyOrQuit(offsetRange.GetLength() == 0); + VerifyOrQuit(offsetRange.GetEndOffset() == 100); + VerifyOrQuit(offsetRange.IsEmpty()); + + offsetRange.ShrinkLength(10); + VerifyOrQuit(offsetRange.GetOffset() == 100); + VerifyOrQuit(offsetRange.GetLength() == 0); + VerifyOrQuit(offsetRange.GetEndOffset() == 100); + VerifyOrQuit(offsetRange.IsEmpty()); + + offsetRange.AdvanceOffset(20); + VerifyOrQuit(offsetRange.GetOffset() == 100); + VerifyOrQuit(offsetRange.GetLength() == 0); + VerifyOrQuit(offsetRange.GetEndOffset() == 100); + VerifyOrQuit(offsetRange.IsEmpty()); + + // Non-empty `OffsetRange` + + offsetRange.Init(200, 10); + VerifyOrQuit(offsetRange.GetOffset() == 200); + VerifyOrQuit(offsetRange.GetLength() == 10); + VerifyOrQuit(offsetRange.GetEndOffset() == 210); + VerifyOrQuit(!offsetRange.IsEmpty()); + VerifyOrQuit(offsetRange.Contains(10)); + VerifyOrQuit(!offsetRange.Contains(11)); + + offsetRange.ShrinkLength(10); + VerifyOrQuit(offsetRange.GetOffset() == 200); + VerifyOrQuit(offsetRange.GetLength() == 10); + VerifyOrQuit(offsetRange.GetEndOffset() == 210); + + offsetRange.ShrinkLength(20); + VerifyOrQuit(offsetRange.GetOffset() == 200); + VerifyOrQuit(offsetRange.GetLength() == 10); + VerifyOrQuit(offsetRange.GetEndOffset() == 210); + + offsetRange.ShrinkLength(5); + VerifyOrQuit(offsetRange.GetOffset() == 200); + VerifyOrQuit(offsetRange.GetLength() == 5); + VerifyOrQuit(offsetRange.GetEndOffset() == 205); + VerifyOrQuit(!offsetRange.Contains(10)); + VerifyOrQuit(!offsetRange.Contains(6)); + VerifyOrQuit(offsetRange.Contains(5)); + + offsetRange.AdvanceOffset(4); + VerifyOrQuit(offsetRange.GetOffset() == 204); + VerifyOrQuit(offsetRange.GetLength() == 1); + VerifyOrQuit(offsetRange.GetEndOffset() == 205); + VerifyOrQuit(!offsetRange.IsEmpty()); + VerifyOrQuit(offsetRange.Contains(1)); + VerifyOrQuit(!offsetRange.Contains(2)); + + offsetRange.AdvanceOffset(1); + VerifyOrQuit(offsetRange.GetOffset() == 205); + VerifyOrQuit(offsetRange.GetLength() == 0); + VerifyOrQuit(offsetRange.GetEndOffset() == 205); + VerifyOrQuit(offsetRange.IsEmpty()); + + // `InitFromRange()` + + offsetRange.InitFromRange(300, 400); + VerifyOrQuit(offsetRange.GetOffset() == 300); + VerifyOrQuit(offsetRange.GetLength() == 100); + VerifyOrQuit(offsetRange.GetEndOffset() == 400); + VerifyOrQuit(!offsetRange.IsEmpty()); + VerifyOrQuit(offsetRange.Contains(100)); + VerifyOrQuit(!offsetRange.Contains(101)); + + offsetRange.AdvanceOffset(101); + VerifyOrQuit(offsetRange.GetOffset() == 400); + VerifyOrQuit(offsetRange.GetLength() == 0); + VerifyOrQuit(offsetRange.GetEndOffset() == 400); + VerifyOrQuit(offsetRange.IsEmpty()); + + // Init from a `Message` from offset or full length + + message = instance->Get().Allocate(Message::kTypeOther); + VerifyOrQuit(message != nullptr); + + SuccessOrQuit(message->SetLength(120)); + VerifyOrQuit(message->GetOffset() == 0); + + offsetRange.InitFromMessageOffsetToEnd(*message); + VerifyOrQuit(offsetRange.GetOffset() == 0); + VerifyOrQuit(offsetRange.GetLength() == 120); + VerifyOrQuit(offsetRange.GetEndOffset() == 120); + + offsetRange.InitFromMessageFullLength(*message); + VerifyOrQuit(offsetRange.GetOffset() == 0); + VerifyOrQuit(offsetRange.GetLength() == 120); + VerifyOrQuit(offsetRange.GetEndOffset() == 120); + + message->SetOffset(40); + VerifyOrQuit(message->GetOffset() == 40); + + offsetRange.InitFromMessageOffsetToEnd(*message); + VerifyOrQuit(offsetRange.GetOffset() == 40); + VerifyOrQuit(offsetRange.GetLength() == 80); + VerifyOrQuit(offsetRange.GetEndOffset() == 120); + + offsetRange.InitFromMessageFullLength(*message); + VerifyOrQuit(offsetRange.GetOffset() == 0); + VerifyOrQuit(offsetRange.GetLength() == 120); + VerifyOrQuit(offsetRange.GetEndOffset() == 120); + + message->Free(); + testFreeInstance(instance); +} + +} // namespace ot + +int main(void) +{ + ot::TestOffsetRange(); + printf("All tests passed\n"); + return 0; +} diff --git a/tests/unit/test_tlv.cpp b/tests/unit/test_tlv.cpp index 703abe3e5..165782642 100644 --- a/tests/unit/test_tlv.cpp +++ b/tests/unit/test_tlv.cpp @@ -45,7 +45,7 @@ void TestTlv(void) Tlv tlv; ExtendedTlv extTlv; uint16_t offset; - uint16_t valueOffset; + OffsetRange offsetRange; uint16_t length; uint8_t buffer[4]; @@ -57,7 +57,7 @@ void TestTlv(void) VerifyOrQuit(message->GetOffset() == 0); VerifyOrQuit(message->GetLength() == 0); - VerifyOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 1, valueOffset, length) == kErrorNotFound); + VerifyOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 1, offsetRange) == kErrorNotFound); VerifyOrQuit(Tlv::ReadTlvValue(*message, 0, buffer, 1) == kErrorParse); // Add an empty TLV with type 1 and check that we can find it @@ -68,9 +68,9 @@ void TestTlv(void) tlv.SetLength(0); SuccessOrQuit(message->Append(tlv)); - SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 1, valueOffset, length)); - VerifyOrQuit(valueOffset == sizeof(Tlv)); - VerifyOrQuit(length == 0); + SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 1, offsetRange)); + VerifyOrQuit(offsetRange.GetOffset() == sizeof(Tlv)); + VerifyOrQuit(offsetRange.GetLength() == 0); SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 0)); VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1) == kErrorParse); @@ -82,9 +82,9 @@ void TestTlv(void) extTlv.SetLength(0); SuccessOrQuit(message->Append(extTlv)); - SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 2, valueOffset, length)); - VerifyOrQuit(valueOffset == offset + sizeof(ExtendedTlv)); - VerifyOrQuit(length == 0); + SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 2, offsetRange)); + VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(ExtendedTlv)); + VerifyOrQuit(offsetRange.GetLength() == 0); SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 0)); VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1) == kErrorParse); @@ -97,9 +97,9 @@ void TestTlv(void) SuccessOrQuit(message->Append(tlv)); SuccessOrQuit(message->Append(0xff)); - SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 3, valueOffset, length)); - VerifyOrQuit(valueOffset == offset + sizeof(Tlv)); - VerifyOrQuit(length == 1); + SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 3, offsetRange)); + VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(Tlv)); + VerifyOrQuit(offsetRange.GetLength() == 1); SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1)); VerifyOrQuit(buffer[0] == 0x0ff); VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 2) == kErrorParse); @@ -114,9 +114,9 @@ void TestTlv(void) SuccessOrQuit(message->Append(0x12)); SuccessOrQuit(message->Append(0x34)); - SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 4, valueOffset, length)); - VerifyOrQuit(valueOffset == offset + sizeof(ExtendedTlv)); - VerifyOrQuit(length == 2); + SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 4, offsetRange)); + VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(ExtendedTlv)); + VerifyOrQuit(offsetRange.GetLength() == 2); SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1)); VerifyOrQuit(buffer[0] == 0x12); SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 2)); @@ -132,15 +132,15 @@ void TestTlv(void) tlv.SetLength(1); SuccessOrQuit(message->Append(tlv)); - VerifyOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 5, valueOffset, length) != kErrorNone); + VerifyOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 5, offsetRange) != kErrorNone); VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 0) == kErrorParse); // Add the missing value. SuccessOrQuit(message->Append(0xaa)); - SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 5, valueOffset, length)); - VerifyOrQuit(valueOffset == offset + sizeof(Tlv)); - VerifyOrQuit(length == 1); + SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 5, offsetRange)); + VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(Tlv)); + VerifyOrQuit(offsetRange.GetLength() == 1); SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1)); VerifyOrQuit(buffer[0] == 0xaa); VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 2) == kErrorParse); @@ -154,14 +154,14 @@ void TestTlv(void) SuccessOrQuit(message->Append(extTlv)); SuccessOrQuit(message->Append(0xbb)); - VerifyOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 6, valueOffset, length) != kErrorNone); + VerifyOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 6, offsetRange) != kErrorNone); VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1) == kErrorParse); SuccessOrQuit(message->Append(0xcc)); - SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 6, valueOffset, length) != kErrorNone); - VerifyOrQuit(valueOffset == offset + sizeof(ExtendedTlv)); - VerifyOrQuit(length == 2); + SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 6, offsetRange) != kErrorNone); + VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(ExtendedTlv)); + VerifyOrQuit(offsetRange.GetLength() == 2); SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 2)); VerifyOrQuit(buffer[0] == 0xbb); VerifyOrQuit(buffer[1] == 0xcc); @@ -176,7 +176,7 @@ void TestTlv(void) SuccessOrQuit(message->Append(extTlv)); SuccessOrQuit(message->Append(0x11)); - VerifyOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 7, valueOffset, length) != kErrorNone); + VerifyOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 7, offsetRange) != kErrorNone); VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1) == kErrorParse); message->Free(); From 2d3df2acee645406c08617206b0844c1cda1dea4 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Tue, 2 Jul 2024 10:16:10 +0800 Subject: [PATCH 008/160] [posix] add rcp capability diag command to check the Spinel interface speed (#10443) --- src/lib/spinel/radio_spinel.cpp | 6 ++ src/lib/spinel/radio_spinel.hpp | 9 +++ src/posix/platform/README_RCP_CAPS_DIAG.md | 11 +++ src/posix/platform/rcp_caps_diag.cpp | 87 ++++++++++++++++++++++ src/posix/platform/rcp_caps_diag.hpp | 9 +++ 5 files changed, 122 insertions(+) diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 605d1deba..ae2622e69 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -1771,6 +1771,12 @@ void RadioSpinel::SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void mOutputContext = aContext; } +void RadioSpinel::GetDiagOutputCallback(otPlatDiagOutputCallback &aCallback, void *&aContext) +{ + aCallback = mOutputCallback; + aContext = mOutputContext; +} + otError RadioSpinel::PlatDiagProcess(const char *aString) { return Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString); diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 4d30ff893..62027c2d8 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -695,6 +695,15 @@ class RadioSpinel : private Logger * */ void SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void *aContext); + + /** + * Gets the diag output callback. + * + * @param[out] aCallback A reference to a function that is called on outputting diag messages. + * @param[out] aContext A reference to the user context. + * + */ + void GetDiagOutputCallback(otPlatDiagOutputCallback &aCallback, void *&aContext); #endif /** diff --git a/src/posix/platform/README_RCP_CAPS_DIAG.md b/src/posix/platform/README_RCP_CAPS_DIAG.md index faf9909e8..d920792cb 100644 --- a/src/posix/platform/README_RCP_CAPS_DIAG.md +++ b/src/posix/platform/README_RCP_CAPS_DIAG.md @@ -8,6 +8,7 @@ This module provides diag commands for checking RCP capabilities. - [capflags](#capflags) - [spinel](#spinel) +- [spinelspeed](#spinelspeed) - [srcmatchtable](#srcmatchtable) ## Command Details @@ -114,6 +115,16 @@ PROP_VALUE_SET RADIO_COEX_ENABLE -------------------------- OK Done ``` +### spinelspeed + +Check the speed of Spinel interface. + +```bash +> diag rcpcaps spinelspeed +SpinelSpeed ----------------------------------------------- 34414843 bps +Done +``` + ### srcmatchtable Check the source match table size supported by the RCP. diff --git a/src/posix/platform/rcp_caps_diag.cpp b/src/posix/platform/rcp_caps_diag.cpp index e972f0e48..4ce5f78f8 100644 --- a/src/posix/platform/rcp_caps_diag.cpp +++ b/src/posix/platform/rcp_caps_diag.cpp @@ -28,6 +28,8 @@ #include "rcp_caps_diag.hpp" +#include "lib/utils/math.hpp" + #if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE namespace ot { namespace Posix { @@ -465,6 +467,10 @@ otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength) { ProcessSpinel(); } + else if (strcmp(aArgs[1], "spinelspeed") == 0) + { + ProcessSpinelSpeed(); + } else { error = OT_ERROR_INVALID_COMMAND; @@ -680,6 +686,87 @@ void RcpCapsDiag::OutputExtendedSrcMatchTableSize(void) OutputFormat("ExtendedSrcMatchTableSize", num); } +void RcpCapsDiag::HandleDiagOutput(const char *aFormat, va_list aArguments, void *aContext) +{ + static_cast(aContext)->HandleDiagOutput(aFormat, aArguments); +} + +void RcpCapsDiag::HandleDiagOutput(const char *aFormat, va_list aArguments) +{ + int rval; + + VerifyOrExit(mDiagOutput != nullptr && mDiagOutputLength != 0); + rval = vsnprintf(mDiagOutput, mDiagOutputLength, aFormat, aArguments); + VerifyOrExit(rval >= 0); + + rval = (rval > mDiagOutputLength) ? mDiagOutputLength : rval; + mDiagOutput += rval; + mDiagOutputLength -= rval; + +exit: + return; +} + +void RcpCapsDiag::ProcessSpinelSpeed(void) +{ + static constexpr uint32_t kUsPerSec = 1000000; + static constexpr uint8_t kBitsPerByte = 8; + static constexpr uint8_t kSpinelHeaderSize = 4; + static constexpr uint8_t kZeroTerminatorSize = 1; + static constexpr char kEchoCmd[] = "echo "; + static constexpr uint8_t kEchoPayloadLength = 200; + static constexpr uint8_t kNumTests = 100; + + otError error = OT_ERROR_NONE; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {0}; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + uint16_t echoPayloadLength; + uint64_t startTimestamp; + uint64_t endTimestamp; + uint64_t sumTime = 0; + uint64_t sumLength = 0; + uint32_t speed; + otPlatDiagOutputCallback callback; + void *context; + + mRadioSpinel.GetDiagOutputCallback(callback, context); + mRadioSpinel.SetDiagOutputCallback(HandleDiagOutput, this); + + strncpy(cmd, kEchoCmd, sizeof(cmd) - 1); + echoPayloadLength = static_cast(sizeof(cmd) - strlen(cmd) - 1); + echoPayloadLength = Lib::Utils::Min(kEchoPayloadLength, echoPayloadLength); + memset(cmd + strlen(cmd), '1', echoPayloadLength); + + for (uint16_t i = 0; i < kNumTests; i++) + { + output[0] = '\0'; + mDiagOutput = output; + mDiagOutputLength = sizeof(output); + startTimestamp = otPlatTimeGet(); + + SuccessOrExit(error = mRadioSpinel.PlatDiagProcess(cmd)); + + endTimestamp = otPlatTimeGet(); + sumTime += endTimestamp - startTimestamp; + sumLength += kSpinelHeaderSize + strlen(cmd) + kZeroTerminatorSize + kSpinelHeaderSize + strlen(output) + + kZeroTerminatorSize; + } + + mRadioSpinel.SetDiagOutputCallback(callback, context); + +exit: + if (error == OT_ERROR_NONE) + { + speed = static_cast((sumLength * kBitsPerByte * kUsPerSec) / sumTime); + snprintf(output, sizeof(output), "%lu bps", ToUlong(speed)); + OutputFormat("SpinelSpeed", output); + } + else + { + Output("Failed to test the Spinel speed: %s", otThreadErrorToString(error)); + } +} + void RcpCapsDiag::OutputFormat(const char *aName, const char *aValue) { static constexpr uint8_t kMaxNameLength = 56; diff --git a/src/posix/platform/rcp_caps_diag.hpp b/src/posix/platform/rcp_caps_diag.hpp index 0431f6167..59719b450 100644 --- a/src/posix/platform/rcp_caps_diag.hpp +++ b/src/posix/platform/rcp_caps_diag.hpp @@ -62,6 +62,8 @@ class RcpCapsDiag : mRadioSpinel(aRadioSpinel) , mOutputCallback(nullptr) , mOutputContext(nullptr) + , mDiagOutput(nullptr) + , mDiagOutputLength(0) { } @@ -111,6 +113,7 @@ class RcpCapsDiag static constexpr uint16_t kMaxNumChildren = 512; void ProcessSpinel(void); + void ProcessSpinelSpeed(void); void ProcessCapabilityFlags(void); void ProcessSrcMatchTable(void); void TestSpinelCommands(Category aCategory); @@ -125,6 +128,10 @@ class RcpCapsDiag bool IsSpinelCapabilitySupported(const uint8_t *aCapsData, spinel_size_t aCapsLength, uint32_t aCapability); void OutputExtendedSrcMatchTableSize(void); void OutputShortSrcMatchTableSize(void); + + static void HandleDiagOutput(const char *aFormat, va_list aArguments, void *aContext); + void HandleDiagOutput(const char *aFormat, va_list aArguments); + void OutputFormat(const char *aName, const char *aValue); void OutputFormat(const char *aName, uint32_t aValue); void OutputResult(const SpinelEntry &aEntry, otError error); @@ -139,6 +146,8 @@ class RcpCapsDiag Spinel::RadioSpinel &mRadioSpinel; otPlatDiagOutputCallback mOutputCallback; void *mOutputContext; + char *mDiagOutput; + uint16_t mDiagOutputLength; }; } // namespace Posix From 41b91a982b8297babf008104e00ecbaa58ca4da7 Mon Sep 17 00:00:00 2001 From: Li Cao Date: Tue, 2 Jul 2024 10:16:39 +0800 Subject: [PATCH 009/160] [spinel] allow custom config file for lib spinel (#10449) --- src/lib/CMakeLists.txt | 8 ++++++++ src/lib/spinel/CMakeLists.txt | 6 ++++++ src/lib/spinel/openthread-spinel-config.h | 8 ++++++++ 3 files changed, 22 insertions(+) diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 31e6c01ef..631f912a7 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -26,6 +26,14 @@ # POSSIBILITY OF SUCH DAMAGE. # +add_library(ot-lib-config INTERFACE) + +set(OT_LIB_CONFIG "" CACHE STRING "Lib project-specific config header file") +if (OT_LIB_CONFIG) + target_compile_definitions(ot-lib-config INTERFACE "OPENTHREAD_PROJECT_LIB_CONFIG_FILE=\"${OT_LIB_CONFIG}\"") + message(STATUS "OT_LIB_CONFIG=\"${OT_LIB_CONFIG}\"") +endif() + add_subdirectory(hdlc) add_subdirectory(platform) add_subdirectory(spinel) diff --git a/src/lib/spinel/CMakeLists.txt b/src/lib/spinel/CMakeLists.txt index 5ff22c0f5..74c3a95f4 100644 --- a/src/lib/spinel/CMakeLists.txt +++ b/src/lib/spinel/CMakeLists.txt @@ -99,17 +99,23 @@ target_sources(openthread-spinel-ncp PRIVATE ${COMMON_SOURCES}) target_sources(openthread-spinel-rcp PRIVATE ${COMMON_SOURCES}) target_link_libraries(openthread-radio-spinel + INTERFACE + ot-lib-config PRIVATE ot-config ) target_link_libraries(openthread-spinel-ncp + INTERFACE + ot-lib-config PRIVATE ot-config-ftd ot-config ) target_link_libraries(openthread-spinel-rcp + INTERFACE + ot-lib-config PRIVATE ot-config-radio ot-config diff --git a/src/lib/spinel/openthread-spinel-config.h b/src/lib/spinel/openthread-spinel-config.h index f12b33187..b49bfc96f 100644 --- a/src/lib/spinel/openthread-spinel-config.h +++ b/src/lib/spinel/openthread-spinel-config.h @@ -34,6 +34,14 @@ #ifndef OPENTHREAD_SPINEL_CONFIG_H_ #define OPENTHREAD_SPINEL_CONFIG_H_ +/** + * Include project specific lib config file if defined. + * + */ +#ifdef OPENTHREAD_PROJECT_LIB_CONFIG_FILE +#include OPENTHREAD_PROJECT_LIB_CONFIG_FILE +#endif + /** * @def OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE * From 6f12c8164e055d270a49bb81d9af0c507fc8750d Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 1 Jul 2024 19:20:07 -0700 Subject: [PATCH 010/160] [mesh-forwarder] restore `FrameData` in `GetForwardFramePriority()` (#10459) This commit updates `GetForwardFramePriority()` to save the original `aRxInfo.mFrameData` before parsing the fragment header. The header parsing may modify `mFrameData` to skip over the parsed portion. The original `FrameData` is restored before returning, ensuring that forwarded frames include the fragment header and eliminating the need to create a copy of `aRxInfo`. --- src/core/thread/mesh_forwarder.hpp | 2 +- src/core/thread/mesh_forwarder_ftd.cpp | 29 +++++++++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index 51043abac..b7d3f7c44 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -575,7 +575,7 @@ class MeshForwarder : public InstanceLocator, private NonCopyable Error GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader, uint16_t aSrcRloc16, Message::Priority &aPriority); - void GetForwardFramePriority(const RxInfo &aRxInfo, Message::Priority &aPriority); + void GetForwardFramePriority(RxInfo &aRxInfo, Message::Priority &aPriority); bool CalcIePresent(const Message *aMessage); Mac::Frame::Version CalcFrameVersion(const Neighbor *aNeighbor, bool aIePresent) const; diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 8f8ff10e8..e326fe33e 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -965,39 +965,52 @@ Error MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader return error; } -void MeshForwarder::GetForwardFramePriority(const RxInfo &aRxInfo, Message::Priority &aPriority) +void MeshForwarder::GetForwardFramePriority(RxInfo &aRxInfo, Message::Priority &aPriority) { + // Determines the message priority to use for forwarding a + // received mesh-header LowPAN frame towards its final + // destination. + Error error = kErrorNone; - RxInfo rxInfo = aRxInfo; bool isFragment = false; Lowpan::FragmentHeader fragmentHeader; + FrameData savedFrameData; + + // We save the `aRxInfo.mFrameData` before parsing the fragment + // header which may update it to skip over the parsed header. We + // restore the original frame data on `aRxInfo` before + // returning. - if (fragmentHeader.ParseFrom(rxInfo.mFrameData) == kErrorNone) + savedFrameData = aRxInfo.mFrameData; + + if (fragmentHeader.ParseFrom(aRxInfo.mFrameData) == kErrorNone) { isFragment = true; if (fragmentHeader.GetDatagramOffset() > 0) { // Get priority from the pre-buffered info - ExitNow(error = GetFragmentPriority(fragmentHeader, rxInfo.GetSrcAddr().GetShort(), aPriority)); + ExitNow(error = GetFragmentPriority(fragmentHeader, aRxInfo.GetSrcAddr().GetShort(), aPriority)); } } // Get priority from IPv6 header or UDP destination port directly - error = GetFramePriority(rxInfo, aPriority); + error = GetFramePriority(aRxInfo, aPriority); exit: if (error != kErrorNone) { LogNote("Failed to get forwarded frame priority, error:%s, len:%d, src:%s, dst:%s", ErrorToString(error), - rxInfo.mFrameData.GetLength(), rxInfo.GetSrcAddr().ToString().AsCString(), - rxInfo.GetDstAddr().ToString().AsCString()); + aRxInfo.mFrameData.GetLength(), aRxInfo.GetSrcAddr().ToString().AsCString(), + aRxInfo.GetDstAddr().ToString().AsCString()); } else if (isFragment) { - UpdateFragmentPriority(fragmentHeader, rxInfo.mFrameData.GetLength(), rxInfo.GetSrcAddr().GetShort(), + UpdateFragmentPriority(fragmentHeader, aRxInfo.mFrameData.GetLength(), aRxInfo.GetSrcAddr().GetShort(), aPriority); } + + aRxInfo.mFrameData = savedFrameData; } // LCOV_EXCL_START From d87be657c6beb35eeced27df1871e835ddae57f7 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 2 Jul 2024 08:52:28 -0700 Subject: [PATCH 011/160] [srp-client] enhance selection of TX jitter based on trigger reason (#10357) This commit improves the `Srp::Client` mechanism for applying random jitter delays before sending update messages to the server. It now supports different jitter ranges based on specific triggers. Since trigger events are often network-wide, potentially causing simultaneous SRP re-registration from many nodes, longer jitter intervals are used to distribute the resulting SRP update transmissions and avoid congestion. The following triggers are covered: - Server switch: Client switching to a new server while already connected to a discovered server. This occurs when a new server entry appears in Network Data which is preferred over the current selection. - Server restart: Client was previously connected to a server that disappeared from Network Data. Later, the same or a new server is discovered in Network Data. - SLAAC address add or remove: This is generally triggered by updates to SLAAC prefixes in Network Data (e.g., OMR prefix changes). - First registration after attach: - If the device is attaching to an established Thread mesh (e.g., after a reboot or initial pairing), the Network Data it receives should already include a server entry, leading to a quick server selection after attach. If server selection occurs within a short window, a shorter TX jitter is used, allowing the device to register quickly and become discoverable. - If server discovery takes longer, a longer TX jitter is used. This situation can indicate a server/BR starting up or a network-wide restart of many nodes (e.g., due to a power outage). This commit introduces `TxJitter` class to manage the requested TX jitter based on a trigger reason. It tracks the time of the event that triggered a longer jitter request. If the update message is sent immediately after the trigger event, the requested maximum jitter is applied. However, if the update message is sent later, the maximum jitter is adjusted proportionally to the time elapsed since the trigger event. If the elapsed time exceeds the requested maximum jitter interval, the default short jitter is applied to avoid unnecessary registration delay. --- .../config/openthread-core-config-check.h | 9 + src/core/config/srp_client.h | 32 --- src/core/net/netif.cpp | 4 + src/core/net/srp_client.cpp | 255 ++++++++++++++++-- src/core/net/srp_client.hpp | 83 +++++- .../test_dnssd_name_with_special_chars.py | 2 +- .../thread-cert/test_srp_auto_host_address.py | 14 +- .../test_srp_client_remove_host.py | 2 +- .../test_srp_client_save_server_info.py | 2 +- tests/scripts/thread-cert/test_srp_lease.py | 4 +- .../test_srp_many_services_mtu_check.py | 2 +- .../test_srp_register_services_diff_lease.py | 2 +- .../test_srp_server_anycast_mode.py | 2 +- .../test_srp_server_reboot_port.py | 4 +- .../scripts/thread-cert/test_srp_sub_type.py | 2 +- tests/scripts/thread-cert/test_srp_ttl.py | 1 + .../toranj/cli/test-400-srp-client-server.py | 3 +- .../cli/test-501-multi-br-failure-recovery.py | 1 + ...st-502-multi-br-leader-failure-recovery.py | 1 + tests/toranj/openthread-core-toranj-config.h | 2 + tests/unit/test_srp_adv_proxy.cpp | 2 +- tests/unit/test_srp_server.cpp | 2 +- 22 files changed, 345 insertions(+), 86 deletions(-) diff --git a/src/core/config/openthread-core-config-check.h b/src/core/config/openthread-core-config-check.h index 1f3a7c59c..53a9544df 100644 --- a/src/core/config/openthread-core-config-check.h +++ b/src/core/config/openthread-core-config-check.h @@ -678,4 +678,13 @@ "OPENTHREAD_LIB_SPINEL_RX_FRAME_BUFFER_SIZE. Pass the macro to source code under"\ "src/lib/spinel." #endif + +#ifdef OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MIN_DELAY +#error "OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MIN_DELAY was removed." +#endif + +#ifdef OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MAX_DELAY +#error "OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MAX_DELAY was removed." +#endif + #endif // OPENTHREAD_CORE_CONFIG_CHECK_H_ diff --git a/src/core/config/srp_client.h b/src/core/config/srp_client.h index 34c9abfb6..c5eaca64b 100644 --- a/src/core/config/srp_client.h +++ b/src/core/config/srp_client.h @@ -225,38 +225,6 @@ #define OPENTHREAD_CONFIG_SRP_CLIENT_EARLY_LEASE_RENEW_FACTOR_DENOMINATOR 2 #endif -/** - * @def OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MIN_DELAY - * - * Specifies the minimum value (in msec) for the short random delay wait time before sending an update message. - * - * The random delay is chosen uniformly from the min up to max value `OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MAX_DELAY`. - * - * When there is a change (e.g., a new service is added/removed) that requires an update, the SRP client will wait for - * a short delay before preparing and sending an SRP update message to server. This allows user to provide more change - * that are then all sent in same update message. The delay is only applied on the first change that triggers an - * update message transmission. Subsequent changes (API calls) while waiting for the tx to start will not reset the - * delay timer. - * - */ -#ifndef OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MIN_DELAY -#define OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MIN_DELAY 10 -#endif - -/** - * @def OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MIN_DELAY - * - * Specifies the maximum value (in msec) for the short random delay wait time before sending an update message. - * - * The random delay is chosen uniformly from the min `OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MIN_DELAY` up to max value. - * - * See `OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MIN_DELAY` for more details. - * - */ -#ifndef OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MAX_DELAY -#define OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MAX_DELAY 700 -#endif - /** * @def OPENTHREAD_CONFIG_SRP_CLIENT_MIN_RETRY_WAIT_INTERVAL * diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp index 0701436c1..999507662 100644 --- a/src/core/net/netif.cpp +++ b/src/core/net/netif.cpp @@ -415,6 +415,10 @@ void Netif::SignalUnicastAddressChange(AddressEvent aEvent, const UnicastAddress Get().Signal(event); +#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE + Get().HandleUnicastAddressEvent(aEvent, aAddress); +#endif + #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE Get().RecordAddressEvent(aEvent, aAddress); #endif diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index ebc28ce51..33b65ff0d 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -175,6 +175,96 @@ bool Client::Service::Matches(const Service &aOther) const return (strcmp(GetName(), aOther.GetName()) == 0) && (strcmp(GetInstanceName(), aOther.GetInstanceName()) == 0); } +//--------------------------------------------------------------------- +// Client::TxJitter + +const uint32_t Client::TxJitter::kMaxJitters[] = { + Client::kMaxTxJitterOnDeviceReboot, // (0) kOnDeviceReboot + Client::kMaxTxJitterOnServerStart, // (1) kOnServerStart + Client::kMaxTxJitterOnServerRestart, // (2) kOnServerRestart + Client::kMaxTxJitterOnServerSwitch, // (3) kOnServerSwitch + Client::kMaxTxJitterOnSlaacAddrAdd, // (4) kOnSlaacAddrAdd + Client::kMaxTxJitterOnSlaacAddrRemove, // (5) kOnSlaacAddrRemove +}; + +void Client::TxJitter::Request(Reason aReason) +{ + static_assert(0 == kOnDeviceReboot, "kOnDeviceReboot value is incorrect"); + static_assert(1 == kOnServerStart, "kOnServerStart value is incorrect"); + static_assert(2 == kOnServerRestart, "kOnServerRestart value is incorrect"); + static_assert(3 == kOnServerSwitch, "kOnServerSwitch value is incorrect"); + static_assert(4 == kOnSlaacAddrAdd, "kOnSlaacAddrAdd value is incorrect"); + static_assert(5 == kOnSlaacAddrRemove, "kOnSlaacAddrRemove value is incorrect"); + + uint32_t maxJitter = kMaxJitters[aReason]; + + LogInfo("Requesting max tx jitter %lu (%s)", ToUlong(maxJitter), ReasonToString(aReason)); + + if (mRequestedMax != 0) + { + // If we have a previous request, adjust the `mRequestedMax` + // based on the time elapsed since that request was made. + + uint32_t duration = TimerMilli::GetNow() - mRequestTime; + + mRequestedMax = (mRequestedMax > duration) ? mRequestedMax - duration : 0; + } + + mRequestedMax = Max(mRequestedMax, maxJitter); + mRequestTime = TimerMilli::GetNow(); +} + +uint32_t Client::TxJitter::DetermineDelay(void) +{ + uint32_t delay; + uint32_t maxJitter = kMaxTxJitterDefault; + + if (mRequestedMax != 0) + { + uint32_t duration = TimerMilli::GetNow() - mRequestTime; + + if (duration >= mRequestedMax) + { + LogInfo("Requested max tx jitter %lu already expired", ToUlong(mRequestedMax)); + } + else + { + maxJitter = Max(mRequestedMax - duration, kMaxTxJitterDefault); + LogInfo("Applying remaining max jitter %lu", ToUlong(maxJitter)); + } + + mRequestedMax = 0; + } + + delay = Random::NonCrypto::GetUint32InRange(kMinTxJitter, maxJitter); + LogInfo("Use random tx jitter %lu from [%lu, %lu]", ToUlong(delay), ToUlong(kMinTxJitter), ToUlong(maxJitter)); + + return delay; +} + +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) +const char *Client::TxJitter::ReasonToString(Reason aReason) +{ + static const char *const kReasonStrings[] = { + "OnDeviceReboot", // (0) kOnDeviceReboot + "OnServerStart", // (1) kOnServerStart + "OnServerRestart", // (2) kOnServerRestart + "OnServerSwitch", // (3) kOnServerSwitch + "OnSlaacAddrAdd", // (4) kOnSlaacAddrAdd + "OnSlaacAddrRemove", // (5) kOnSlaacAddrRemove + }; + + static_assert(0 == kOnDeviceReboot, "kOnDeviceReboot value is incorrect"); + static_assert(1 == kOnServerStart, "kOnServerStart value is incorrect"); + static_assert(2 == kOnServerRestart, "kOnServerRestart value is incorrect"); + static_assert(3 == kOnServerSwitch, "kOnServerSwitch value is incorrect"); + static_assert(4 == kOnSlaacAddrAdd, "kOnSlaacAddrAdd value is incorrect"); + static_assert(5 == kOnSlaacAddrRemove, "kOnSlaacAddrRemove value is incorrect"); + + return kReasonStrings[aReason]; +} +#endif + //--------------------------------------------------------------------- // Client::AutoStart @@ -183,7 +273,7 @@ bool Client::Service::Matches(const Service &aOther) const Client::AutoStart::AutoStart(void) { Clear(); - mState = kDefaultMode ? kSelectedNone : kDisabled; + mState = kDefaultMode ? kFirstTimeSelecting : kDisabled; } bool Client::AutoStart::HasSelectedServer(void) const @@ -193,7 +283,8 @@ bool Client::AutoStart::HasSelectedServer(void) const switch (mState) { case kDisabled: - case kSelectedNone: + case kFirstTimeSelecting: + case kReselecting: break; case kSelectedUnicastPreferred: @@ -224,18 +315,20 @@ void Client::AutoStart::InvokeCallback(const Ip6::SockAddr *aServerSockAddr) con const char *Client::AutoStart::StateToString(State aState) { static const char *const kStateStrings[] = { - "Disabled", // (0) kDisabled - "Idle", // (1) kSelectedNone - "Unicast-prf", // (2) kSelectedUnicastPreferred - "Anycast", // (3) kSelectedAnycast - "Unicast", // (4) kSelectedUnicast + "Disabled", // (0) kDisabled + "1stTimeSelect", // (1) kFirstTimeSelecting + "Reselect", // (2) kReselecting + "Unicast-prf", // (3) kSelectedUnicastPreferred + "Anycast", // (4) kSelectedAnycast + "Unicast", // (5) kSelectedUnicast }; static_assert(0 == kDisabled, "kDisabled value is incorrect"); - static_assert(1 == kSelectedNone, "kSelectedNone value is incorrect"); - static_assert(2 == kSelectedUnicastPreferred, "kSelectedUnicastPreferred value is incorrect"); - static_assert(3 == kSelectedAnycast, "kSelectedAnycast value is incorrect"); - static_assert(4 == kSelectedUnicast, "kSelectedUnicast value is incorrect"); + static_assert(1 == kFirstTimeSelecting, "kFirstTimeSelecting value is incorrect"); + static_assert(2 == kReselecting, "kReselecting value is incorrect"); + static_assert(3 == kSelectedUnicastPreferred, "kSelectedUnicastPreferred value is incorrect"); + static_assert(4 == kSelectedAnycast, "kSelectedAnycast value is incorrect"); + static_assert(5 == kSelectedUnicast, "kSelectedUnicast value is incorrect"); return kStateStrings[aState]; } @@ -270,6 +363,9 @@ Client::Client(Instance &aInstance) , mSocket(aInstance, *this) , mDomainName(kDefaultDomainName) , mTimer(aInstance) +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + , mGuardTimer(aInstance) +#endif { mHostInfo.Init(); @@ -444,6 +540,10 @@ void Client::HandleRoleChanged(void) { if (Get().IsAttached()) { +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + ApplyAutoStartGuardOnAttach(); +#endif + VerifyOrExit(GetState() == kStatePaused); Resume(); } @@ -524,6 +624,36 @@ Error Client::SetHostAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddre return error; } +void Client::HandleUnicastAddressEvent(Ip6::Netif::AddressEvent aEvent, const Ip6::Netif::UnicastAddress &aAddress) +{ + // This callback from `Netif` signals an impending addition or + // removal of a unicast address, occurring before `Notifier` + // events. If `AutoAddress` is enabled, we check whether the + // address origin is SLAAC (e.g., an OMR address) and request a + // longer `TxJitter`. This helps randomize the next SRP + // update transmission time when triggered by an OMR prefix + // change. + + VerifyOrExit(IsRunning()); + VerifyOrExit(mHostInfo.IsAutoAddressEnabled()); + + VerifyOrExit(aAddress.GetOrigin() == Ip6::Netif::kOriginSlaac); + +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + // The `mGuardTimer`, started by `ApplyAutoStartGuardOnAttach()`, + // tracks a guard interval after the attach event. If an + // address change occurs within this short window, we do not + // apply a longer TX jitter, as this likely indicates a device + // reboot. + VerifyOrExit(!mGuardTimer.IsRunning()); +#endif + + mTxJitter.Request((aEvent == Ip6::Netif::kAddressAdded) ? TxJitter::kOnSlaacAddrAdd : TxJitter::kOnSlaacAddrRemove); + +exit: + return; +} + bool Client::ShouldUpdateHostAutoAddresses(void) const { bool shouldUpdate = false; @@ -736,7 +866,7 @@ void Client::SetState(State aState) break; case kStateToUpdate: - mTimer.Start(Random::NonCrypto::GetUint32InRange(kUpdateTxMinDelay, kUpdateTxMaxDelay)); + mTimer.Start(mTxJitter.DetermineDelay()); break; case kStateUpdating: @@ -778,7 +908,8 @@ bool Client::ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStat switch (mAutoStart.GetState()) { case AutoStart::kDisabled: - case AutoStart::kSelectedNone: + case AutoStart::kFirstTimeSelecting: + case AutoStart::kReselecting: break; case AutoStart::kSelectedUnicastPreferred: @@ -2056,19 +2187,46 @@ void Client::EnableAutoStartMode(AutoStartCallback aCallback, void *aContext) VerifyOrExit(mAutoStart.GetState() == AutoStart::kDisabled); - mAutoStart.SetState(AutoStart::kSelectedNone); + mAutoStart.SetState(AutoStart::kFirstTimeSelecting); + ApplyAutoStartGuardOnAttach(); + ProcessAutoStart(); exit: return; } +void Client::ApplyAutoStartGuardOnAttach(void) +{ + VerifyOrExit(Get().IsAttached()); + VerifyOrExit(!IsRunning()); + VerifyOrExit(mAutoStart.GetState() == AutoStart::kFirstTimeSelecting); + + // The `mGuardTimer` tracks a guard interval after the attach + // event while `AutoStart` has yet to select a server for the + // first time. + // + // This is used by `ProcessAutoStart()` to apply different TX + // jitter values. If server selection occurs within this short + // window, a shorter TX jitter is used. This typically represents + // the device rebooting or being paired. + // + // The guard time is also checked when handling SLAAC address change + // events, to decide whether or not to request longer TX jitter. + + mGuardTimer.Start(kGuardTimeAfterAttachToUseShorterTxJitter); + +exit: + return; +} + void Client::ProcessAutoStart(void) { Ip6::SockAddr serverSockAddr; DnsSrpAnycast::Info anycastInfo; DnsSrpUnicast::Info unicastInfo; - bool shouldRestart = false; + AutoStart::State oldAutoStartState = mAutoStart.GetState(); + bool shouldRestart = false; // If auto start mode is enabled, we check the Network Data entries // to discover and select the preferred SRP server to register with. @@ -2083,7 +2241,7 @@ void Client::ProcessAutoStart(void) if (IsRunning()) { - VerifyOrExit(mAutoStart.GetState() != AutoStart::kSelectedNone); + VerifyOrExit(mAutoStart.HasSelectedServer()); } // There are three types of entries in Network Data: @@ -2131,15 +2289,69 @@ void Client::ProcessAutoStart(void) Stop(kRequesterAuto, kResetRetryInterval); } - if (!serverSockAddr.GetAddress().IsUnspecified()) + if (serverSockAddr.GetAddress().IsUnspecified()) { - IgnoreError(Start(serverSockAddr, kRequesterAuto)); + if (mAutoStart.HasSelectedServer()) + { + mAutoStart.SetState(AutoStart::kReselecting); + } + + ExitNow(); } - else + + // Before calling `Start()`, determine the trigger reason for + // starting the client with the newly discovered server based on + // `AutoStart` state transitions. This reason is then used to + // select the appropriate TX jitter interval (randomizing the + // initial SRP update transmission to the new server). + + switch (oldAutoStartState) { - mAutoStart.SetState(AutoStart::kSelectedNone); + case AutoStart::kDisabled: + break; + + case AutoStart::kFirstTimeSelecting: + + // If the device is attaching to an established Thread mesh + // (e.g., after a reboot or pairing), the Network Data it + // receives should already include a server entry, leading to + // a quick server selection after attachment. The `mGuardTimer`, + // started by `ApplyAutoStartGuardOnAttach()`, tracks a guard + // interval after the attach event. If server selection + // occurs within this short window, a shorter TX jitter is + // used (`TxJitter::kOnDeviceReboot`), allowing the device to + // register quickly and become discoverable. + // + // If server discovery takes longer, a longer TX jitter + // is used (`TxJitter::kOnServerStart`). This situation + // can indicate a server/BR starting up or a network-wide + // restart of many nodes (e.g., due to a power outage). + + if (mGuardTimer.IsRunning()) + { + mTxJitter.Request(TxJitter::kOnDeviceReboot); + } + else + { + mTxJitter.Request(TxJitter::kOnServerStart); + } + + break; + + case AutoStart::kReselecting: + // Server is restarted (or possibly a new server started). + mTxJitter.Request(TxJitter::kOnServerRestart); + break; + + case AutoStart::kSelectedUnicastPreferred: + case AutoStart::kSelectedAnycast: + case AutoStart::kSelectedUnicast: + mTxJitter.Request(TxJitter::kOnServerSwitch); + break; } + IgnoreError(Start(serverSockAddr, kRequesterAuto)); + exit: return; } @@ -2230,7 +2442,8 @@ void Client::SelectNextServer(bool aDisallowSwitchOnRegisteredHost) case AutoStart::kSelectedAnycast: case AutoStart::kDisabled: - case AutoStart::kSelectedNone: + case AutoStart::kFirstTimeSelecting: + case AutoStart::kReselecting: ExitNow(); } diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index f45b95364..077a5a2cb 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -49,6 +49,7 @@ #include "crypto/ecdsa.hpp" #include "net/dns_types.hpp" #include "net/ip6.hpp" +#include "net/netif.hpp" #include "net/udp6.hpp" #include "thread/network_data_service.hpp" @@ -71,6 +72,7 @@ namespace Srp { class Client : public InstanceLocator, private NonCopyable { friend class ot::Notifier; + friend class ot::Ip6::Netif; using DnsSrpUnicast = NetworkData::Service::DnsSrpUnicast; using DnsSrpAnycast = NetworkData::Service::DnsSrpAnycast; @@ -853,13 +855,28 @@ class Client : public InstanceLocator, private NonCopyable OPENTHREAD_CONFIG_SRP_CLIENT_EARLY_LEASE_RENEW_FACTOR_DENOMINATOR; // ------------------------------- - // When there is a change (e.g., a new service is added/removed) - // that requires an update, the SRP client will wait for a short - // delay as specified by `kUpdateTxDelay` before sending an SRP - // update to server. This allows the user to provide more change - // that are then all sent in same update message. - static constexpr uint32_t kUpdateTxMinDelay = OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MIN_DELAY; // in msec. - static constexpr uint32_t kUpdateTxMaxDelay = OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MAX_DELAY; // in msec. + // TX jitter constants + // + // When changes trigger a new SRP update message transmission a random + // jitter delay is applied before sending the update message to server. + // This can occur due to changes in client services or host info, + // or `AutoStart` selecting a server for the first time or switching + // to a new server thus requiring re-registration. + // + // The constants below specify jitter ranges applied based on + // different trigger reasons. All values are in milliseconds. + // Also see `TxJitter` class. + + static constexpr uint32_t kMinTxJitter = 10; + static constexpr uint32_t kMaxTxJitterDefault = 500; + static constexpr uint32_t kMaxTxJitterOnDeviceReboot = 700; + static constexpr uint32_t kMaxTxJitterOnServerStart = 10 * Time::kOneSecondInMsec; + static constexpr uint32_t kMaxTxJitterOnServerRestart = 10 * Time::kOneSecondInMsec; + static constexpr uint32_t kMaxTxJitterOnServerSwitch = 10 * Time::kOneSecondInMsec; + static constexpr uint32_t kMaxTxJitterOnSlaacAddrAdd = 10 * Time::kOneSecondInMsec; + static constexpr uint32_t kMaxTxJitterOnSlaacAddrRemove = 10 * Time::kOneSecondInMsec; + + static constexpr uint32_t kGuardTimeAfterAttachToUseShorterTxJitter = 1000; // ------------------------------- // Retry related constants @@ -945,16 +962,47 @@ class Client : public InstanceLocator, private NonCopyable kForServicesAppendedInMessage, }; + class TxJitter : public Clearable + { + // Manages the random TX jitter to use when sending SRP update + // messages. + + public: + enum Reason + { + kOnDeviceReboot, + kOnServerStart, + kOnServerRestart, + kOnServerSwitch, + kOnSlaacAddrAdd, + kOnSlaacAddrRemove, + }; + + TxJitter(void) { Clear(); } + void Request(Reason aReason); + uint32_t DetermineDelay(void); + + private: + static const uint32_t kMaxJitters[]; +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) + static const char *ReasonToString(Reason aReason); +#endif + + uint32_t mRequestedMax; + TimeMilli mRequestTime; + }; + #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE class AutoStart : public Clearable { public: enum State : uint8_t{ - kDisabled, // AutoStart is disabled. - kSelectedNone, // AutoStart is enabled but not yet selected any servers. - kSelectedUnicastPreferred, // AutoStart selected a preferred unicast entry (address in service data). - kSelectedAnycast, // AutoStart selected an anycast entry with `mAnycastSeqNum`. - kSelectedUnicast, // AutoStart selected a unicast entry (address in server data). + kDisabled, // Disabled. + kFirstTimeSelecting, // Trying to select a server for the first time since AutoStart was enabled. + kReselecting, // Trying to select a server again (previously selected server was removed). + kSelectedUnicastPreferred, // Has selected a preferred unicast entry (address in service data). + kSelectedAnycast, // Has selected an anycast entry with `mAnycastSeqNum`. + kSelectedUnicast, // Has selected a unicast entry (address in server data). }; AutoStart(void); @@ -1012,6 +1060,7 @@ class Client : public InstanceLocator, private NonCopyable void Pause(void); void HandleNotifierEvents(Events aEvents); void HandleRoleChanged(void); + void HandleUnicastAddressEvent(Ip6::Netif::AddressEvent aEvent, const Ip6::Netif::UnicastAddress &aAddress); bool ShouldUpdateHostAutoAddresses(void) const; bool ShouldHostAutoAddressRegister(const Ip6::Netif::UnicastAddress &aUnicastAddress) const; Error UpdateHostInfoStateOnAddressChange(void); @@ -1056,8 +1105,10 @@ class Client : public InstanceLocator, private NonCopyable bool ShouldRenewEarly(const Service &aService) const; void HandleTimer(void); #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + void ApplyAutoStartGuardOnAttach(void); void ProcessAutoStart(void); Error SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::Info &aInfo) const; + void HandleGuardTimer(void) {} #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE void SelectNextServer(bool aDisallowSwitchOnRegisteredHost); #endif @@ -1077,6 +1128,10 @@ class Client : public InstanceLocator, private NonCopyable using DelayTimer = TimerMilliIn; using ClientSocket = Ip6::Udp::SocketIn; +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + using GuardTimer = TimerMilliIn; +#endif + State mState; uint8_t mTxFailureRetryCount : 4; bool mShouldRemoveKeyLease : 1; @@ -1097,6 +1152,7 @@ class Client : public InstanceLocator, private NonCopyable uint32_t mKeyLease; uint32_t mDefaultLease; uint32_t mDefaultKeyLease; + TxJitter mTxJitter; ClientSocket mSocket; @@ -1106,7 +1162,8 @@ class Client : public InstanceLocator, private NonCopyable LinkedList mServices; DelayTimer mTimer; #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE - AutoStart mAutoStart; + GuardTimer mGuardTimer; + AutoStart mAutoStart; #endif }; diff --git a/tests/scripts/thread-cert/test_dnssd_name_with_special_chars.py b/tests/scripts/thread-cert/test_dnssd_name_with_special_chars.py index 6adf538bc..62c13e740 100755 --- a/tests/scripts/thread-cert/test_dnssd_name_with_special_chars.py +++ b/tests/scripts/thread-cert/test_dnssd_name_with_special_chars.py @@ -84,7 +84,7 @@ def test(self): server.srp_server_set_enabled(True) client.srp_client_enable_auto_start_mode() - self.simulator.go(5) + self.simulator.go(15) # Register a single service with the instance name containing special chars client.srp_client_set_host_name('host1') diff --git a/tests/scripts/thread-cert/test_srp_auto_host_address.py b/tests/scripts/thread-cert/test_srp_auto_host_address.py index 1612a30cf..2f86db58f 100755 --- a/tests/scripts/thread-cert/test_srp_auto_host_address.py +++ b/tests/scripts/thread-cert/test_srp_auto_host_address.py @@ -74,6 +74,7 @@ def test(self): client.start() self.simulator.go(15) self.assertEqual(client.get_state(), 'leader') + client.srp_client_stop() server.start() self.simulator.go(5) @@ -88,8 +89,9 @@ def test(self): #------------------------------------------------------------------- # Check auto start mode on SRP client + client.srp_client_enable_auto_start_mode() self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled') - self.simulator.go(2) + self.simulator.go(15) self.assertEqual(client.srp_client_get_state(), 'Enabled') @@ -135,7 +137,7 @@ def test(self): client.add_prefix('fd00:abba:cafe:bee::/64', 'paos') client.register_netdata() - self.simulator.go(5) + self.simulator.go(15) slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:abba:cafe:bee:')] self.assertEqual(len(slaac_addr), 1) @@ -147,7 +149,7 @@ def test(self): client.add_prefix('fd00:9:8:7::/64', 'paos') client.register_netdata() - self.simulator.go(5) + self.simulator.go(15) slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:9:8:7:')] self.assertEqual(len(slaac_addr), 1) @@ -160,7 +162,7 @@ def test(self): client.add_prefix('fd00:a:b:c::/64', 'aos') client.register_netdata() - self.simulator.go(5) + self.simulator.go(15) slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:a:b:c:')] self.assertEqual(len(slaac_addr), 1) @@ -173,7 +175,7 @@ def test(self): client.remove_prefix('fd00:abba:cafe:bee::/64') client.register_netdata() - self.simulator.go(5) + self.simulator.go(15) self.check_registered_addresses(client, server) @@ -184,7 +186,7 @@ def test(self): client.remove_prefix('fd00:9:8:7::/64') client.register_netdata() - self.simulator.go(5) + self.simulator.go(15) self.check_registered_addresses(client, server) diff --git a/tests/scripts/thread-cert/test_srp_client_remove_host.py b/tests/scripts/thread-cert/test_srp_client_remove_host.py index 298014518..681893914 100755 --- a/tests/scripts/thread-cert/test_srp_client_remove_host.py +++ b/tests/scripts/thread-cert/test_srp_client_remove_host.py @@ -81,7 +81,7 @@ def test(self): server.srp_server_set_enabled(True) client.srp_client_enable_auto_start_mode() - self.simulator.go(5) + self.simulator.go(15) #------------------------------------------------------------------------------------- # Register a single service and verify that it worked. diff --git a/tests/scripts/thread-cert/test_srp_client_save_server_info.py b/tests/scripts/thread-cert/test_srp_client_save_server_info.py index 5018aad08..ca657897d 100755 --- a/tests/scripts/thread-cert/test_srp_client_save_server_info.py +++ b/tests/scripts/thread-cert/test_srp_client_save_server_info.py @@ -48,7 +48,7 @@ SERVER2 = 3 SERVER3 = 4 -WAIT_TIME = 5 +WAIT_TIME = 16 MAX_ITER = 5 diff --git a/tests/scripts/thread-cert/test_srp_lease.py b/tests/scripts/thread-cert/test_srp_lease.py index b259c21a5..863c91a38 100755 --- a/tests/scripts/thread-cert/test_srp_lease.py +++ b/tests/scripts/thread-cert/test_srp_lease.py @@ -114,7 +114,7 @@ def test(self): # Start the client again, the same service should be successfully registered. client.srp_client_enable_auto_start_mode() - self.simulator.go(2) + self.simulator.go(15) self.check_host_and_service(server, client) @@ -132,7 +132,7 @@ def test(self): # Start the client again, the same service should be successfully registered. client.srp_client_enable_auto_start_mode() - self.simulator.go(2) + self.simulator.go(15) self.check_host_and_service(server, client) diff --git a/tests/scripts/thread-cert/test_srp_many_services_mtu_check.py b/tests/scripts/thread-cert/test_srp_many_services_mtu_check.py index 65dfd2e99..37221c5f4 100755 --- a/tests/scripts/thread-cert/test_srp_many_services_mtu_check.py +++ b/tests/scripts/thread-cert/test_srp_many_services_mtu_check.py @@ -84,7 +84,7 @@ def test(self): server.srp_server_set_enabled(True) client.srp_client_enable_auto_start_mode() - self.simulator.go(5) + self.simulator.go(15) # Register 8 services with long name, 6 sub-types and long txt record. # The 8 services won't be fit in a single MTU (1280 bytes) UDP message diff --git a/tests/scripts/thread-cert/test_srp_register_services_diff_lease.py b/tests/scripts/thread-cert/test_srp_register_services_diff_lease.py index 6247527d8..3399dd310 100755 --- a/tests/scripts/thread-cert/test_srp_register_services_diff_lease.py +++ b/tests/scripts/thread-cert/test_srp_register_services_diff_lease.py @@ -84,7 +84,7 @@ def test(self): server.srp_server_set_enabled(True) client.srp_client_enable_auto_start_mode() - self.simulator.go(5) + self.simulator.go(15) client.srp_client_set_host_name('host') client.srp_client_enable_auto_host_address() diff --git a/tests/scripts/thread-cert/test_srp_server_anycast_mode.py b/tests/scripts/thread-cert/test_srp_server_anycast_mode.py index 5086a7baa..7bae2e72b 100755 --- a/tests/scripts/thread-cert/test_srp_server_anycast_mode.py +++ b/tests/scripts/thread-cert/test_srp_server_anycast_mode.py @@ -147,7 +147,7 @@ def test(self): # server and uses the proper address and port number. client.srp_client_enable_auto_start_mode() - self.simulator.go(5) + self.simulator.go(15) if addr_mode == 'anycast': server_alocs = server.get_ip6_address(config.ADDRESS_TYPE.ALOC) diff --git a/tests/scripts/thread-cert/test_srp_server_reboot_port.py b/tests/scripts/thread-cert/test_srp_server_reboot_port.py index b38bcfe9e..d7f0a4c34 100755 --- a/tests/scripts/thread-cert/test_srp_server_reboot_port.py +++ b/tests/scripts/thread-cert/test_srp_server_reboot_port.py @@ -109,7 +109,7 @@ def test(self): client.srp_client_set_host_name('my-host') client.srp_client_set_host_address('2001::1') client.srp_client_add_service('my-service', '_ipps._tcp', 12345, 0, 0, ['abc', 'def=', 'xyz=XYZ']) - self.simulator.go(5) + self.simulator.go(16) self.check_host_and_service(server, client, '2001::1') ports = [server.get_srp_server_port()] @@ -129,7 +129,7 @@ def test(self): # re-registered. # server.srp_server_set_enabled(True) - self.simulator.go(5) + self.simulator.go(16) self.assertEqual(client.srp_client_get_state(), 'Enabled') self.assertEqual(client.srp_client_get_server_address(), server.get_mleid()) self.assertNotEqual(old_port, server.get_srp_server_port()) diff --git a/tests/scripts/thread-cert/test_srp_sub_type.py b/tests/scripts/thread-cert/test_srp_sub_type.py index e9b6fdc4a..bbd8efa17 100755 --- a/tests/scripts/thread-cert/test_srp_sub_type.py +++ b/tests/scripts/thread-cert/test_srp_sub_type.py @@ -80,7 +80,7 @@ def test(self): server.srp_server_set_enabled(True) client.srp_client_enable_auto_start_mode() - self.simulator.go(5) + self.simulator.go(15) # Register a single service with 3 subtypes and verify that it worked. diff --git a/tests/scripts/thread-cert/test_srp_ttl.py b/tests/scripts/thread-cert/test_srp_ttl.py index 9c3a149db..70a60721e 100755 --- a/tests/scripts/thread-cert/test_srp_ttl.py +++ b/tests/scripts/thread-cert/test_srp_ttl.py @@ -85,6 +85,7 @@ def test(self): self.simulator.go(config.ROUTER_STARTUP_DELAY) self.assertEqual(client.get_state(), 'router') + self.simulator.go(15) self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled') client.srp_client_set_host_name('my-host') diff --git a/tests/toranj/cli/test-400-srp-client-server.py b/tests/toranj/cli/test-400-srp-client-server.py index 0e3edb2bc..2a3cd32ab 100755 --- a/tests/toranj/cli/test-400-srp-client-server.py +++ b/tests/toranj/cli/test-400-srp-client-server.py @@ -61,11 +61,12 @@ verify(server.srp_server_get_state() == 'disabled') verify(server.srp_server_get_addr_mode() == 'unicast') verify(client.srp_client_get_state() == 'Disabled') -verify(client.srp_client_get_auto_start_mode() == 'Enabled') # Start server and client and register single service server.srp_server_enable() +client.srp_client_enable_auto_start_mode() +verify(client.srp_client_get_auto_start_mode() == 'Enabled') client.srp_client_set_host_name('host') client.srp_client_set_host_address('fd00::cafe') client.srp_client_add_service('ins', '_test._udp', 777, 2, 1) diff --git a/tests/toranj/cli/test-501-multi-br-failure-recovery.py b/tests/toranj/cli/test-501-multi-br-failure-recovery.py index ecf41b999..338c955c5 100755 --- a/tests/toranj/cli/test-501-multi-br-failure-recovery.py +++ b/tests/toranj/cli/test-501-multi-br-failure-recovery.py @@ -147,6 +147,7 @@ # Register SRP services on all nodes for node in nodes_non_br: + node.srp_client_enable_auto_start_mode() verify(node.srp_client_get_auto_start_mode() == 'Enabled') node.srp_client_set_host_name('host' + str(node.index)) node.srp_client_enable_auto_host_address() diff --git a/tests/toranj/cli/test-502-multi-br-leader-failure-recovery.py b/tests/toranj/cli/test-502-multi-br-leader-failure-recovery.py index 71d2ebc1b..6b6e9dfa9 100755 --- a/tests/toranj/cli/test-502-multi-br-leader-failure-recovery.py +++ b/tests/toranj/cli/test-502-multi-br-leader-failure-recovery.py @@ -147,6 +147,7 @@ # Register SRP services on all nodes for node in nodes_non_br: + node.srp_client_enable_auto_start_mode() verify(node.srp_client_get_auto_start_mode() == 'Enabled') node.srp_client_set_host_name('host' + str(node.index)) node.srp_client_enable_auto_host_address() diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h index 33686ca7a..9f6b66a8d 100644 --- a/tests/toranj/openthread-core-toranj-config.h +++ b/tests/toranj/openthread-core-toranj-config.h @@ -175,6 +175,8 @@ #define OPENTHREAD_CONFIG_SRP_CLIENT_DOMAIN_NAME_API_ENABLE 1 +#define OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE 0 + #define OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE 1 #define OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE 1 diff --git a/tests/unit/test_srp_adv_proxy.cpp b/tests/unit/test_srp_adv_proxy.cpp index 08b4a9470..dec83df65 100644 --- a/tests/unit/test_srp_adv_proxy.cpp +++ b/tests/unit/test_srp_adv_proxy.cpp @@ -940,7 +940,7 @@ void TestSrpAdvProxy(void) sProcessedClientCallback = false; - AdvanceTime(5 * 1000); + AdvanceTime(15 * 1000); // This time we should only see new host registration // since that's the only thing that changes diff --git a/tests/unit/test_srp_server.cpp b/tests/unit/test_srp_server.cpp index f0218e004..2f655f2bf 100644 --- a/tests/unit/test_srp_server.cpp +++ b/tests/unit/test_srp_server.cpp @@ -892,7 +892,7 @@ void TestUpdateLeaseShortVariant(void) srpClient->EnableAutoStartMode(nullptr, nullptr); VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); - AdvanceTime(2000); + AdvanceTime(15 * 1000); VerifyOrQuit(srpClient->IsRunning()); SuccessOrQuit(srpClient->SetHostName(kHostName)); From aebecca9649704c1318228e6da562eaa7c1ed156 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Tue, 2 Jul 2024 23:53:02 +0800 Subject: [PATCH 012/160] [cmake] disable c extensions (#10460) CMake by default enables c extensions. As a result, gnu99 is used instead of c99. This commit explicitly disables c extensions. --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e8959834c..54aa143b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ add_library(ot-config-mtd INTERFACE) add_library(ot-config-radio INTERFACE) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD 11) +set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_C_STANDARD 99) message(STATUS "OpenThread Source Directory: ${PROJECT_SOURCE_DIR}") @@ -92,6 +93,7 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang") set(OT_CFLAGS $<$:${OT_CFLAGS} -Wall -Wextra -Wshadow> $<$:${OT_CFLAGS} -Wall -Wextra -Wshadow -Wno-c++14-compat -fno-exceptions> + $<$:-Wc99-extensions> ) endif() From 3701ce96ddc4bbcf785f3f1ae01b71e6ccff47bb Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 3 Jul 2024 11:15:28 -0700 Subject: [PATCH 013/160] [tlv] use `OffsetRange` and make `ParsedInfo` public (#10461) This commit updates `Tlv` helper methods to use `OffsetRange` in their implementation. It also makes the `Tlv::ParsedInfo` type public. This struct represents information about a parsed TLV in a message, including the TLV type, the `OffsetRange` for the full TLV, and the `OffsetRange` where the TLV's value resides in the message. This replaces and enhances the `ParseAndSkipTlv()` method. The `ParsedInfo` provides two methods: - `ParseFrom()`: Parses a TLV at a given offset or offset range. - `FindIn()`: Searches for a given TLV type in a message. These methods handle both extended and regular TLV formats, validating that the parsed TLV is well-formed and fully contained within the message and the given `OffsetRange`. Modules that iterate over a sequence of TLVs in a message are updated to use `ParsedInfo` and its methods, simplifying the code. --- src/core/common/tlvs.cpp | 114 +++++++++-------------- src/core/common/tlvs.hpp | 84 ++++++++++++----- src/core/thread/discover_scanner.cpp | 34 +++---- src/core/thread/link_metrics.cpp | 130 +++++++++++---------------- src/core/thread/link_metrics.hpp | 2 +- src/core/thread/mle_router.cpp | 15 ++-- 6 files changed, 173 insertions(+), 206 deletions(-) diff --git a/src/core/common/tlvs.cpp b/src/core/common/tlvs.cpp index 0fcf4bbe9..6f02bc405 100644 --- a/src/core/common/tlvs.cpp +++ b/src/core/common/tlvs.cpp @@ -56,20 +56,6 @@ const uint8_t *Tlv::GetValue(void) const Error Tlv::AppendTo(Message &aMessage) const { return aMessage.AppendBytes(this, static_cast(GetSize())); } -Error Tlv::ParseAndSkipTlv(const Message &aMessage, OffsetRange &aOffsetRange) -{ - Error error; - ParsedInfo info; - - SuccessOrExit(error = info.ParseFrom(aMessage, aOffsetRange.GetOffset())); - - VerifyOrExit(aOffsetRange.Contains(info.mSize), error = kErrorParse); - aOffsetRange.AdvanceOffset(info.mSize); - -exit: - return error; -} - Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv) { uint16_t offset; @@ -84,13 +70,9 @@ Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tl SuccessOrExit(error = info.FindIn(aMessage, aType)); - if (aMaxSize > info.mSize) - { - aMaxSize = info.mSize; - } - - aMessage.ReadBytes(info.mOffset, &aTlv, aMaxSize); - aOffset = info.mOffset; + info.mTlvOffsetRange.ShrinkLength(aMaxSize); + aMessage.ReadBytes(info.mTlvOffsetRange, &aTlv); + aOffset = info.mTlvOffsetRange.GetOffset(); exit: return error; @@ -102,8 +84,7 @@ Error Tlv::FindTlvValueOffsetRange(const Message &aMessage, uint8_t aType, Offse ParsedInfo info; SuccessOrExit(error = info.FindIn(aMessage, aType)); - - aOffsetRange.Init(info.mValueOffset, info.mLength); + aOffsetRange = info.mValueOffsetRange; exit: return error; @@ -111,49 +92,47 @@ Error Tlv::FindTlvValueOffsetRange(const Message &aMessage, uint8_t aType, Offse Error Tlv::ParsedInfo::ParseFrom(const Message &aMessage, uint16_t aOffset) { - // This method reads and parses the TLV info from `aMessage` at - // `aOffset`. This can be used independent of whether the TLV is - // extended or not. It validates that the entire TLV can be read - // from `aMessage`. Returns `kErrorNone` when successfully parsed, - // otherwise `kErrorParse`. + OffsetRange offsetRange; + offsetRange.InitFromRange(aOffset, aMessage.GetLength()); + return ParseFrom(aMessage, offsetRange); +} + +Error Tlv::ParsedInfo::ParseFrom(const Message &aMessage, const OffsetRange &aOffsetRange) +{ Error error; Tlv tlv; ExtendedTlv extTlv; - uint16_t headerSize; + uint32_t headerSize; + uint32_t size; - SuccessOrExit(error = aMessage.Read(aOffset, tlv)); + SuccessOrExit(error = aMessage.Read(aOffsetRange, tlv)); + + mType = tlv.GetType(); if (!tlv.IsExtended()) { - mType = tlv.GetType(); - mLength = tlv.GetLength(); - headerSize = sizeof(Tlv); + mIsExtended = false; + headerSize = sizeof(Tlv); + size = headerSize + tlv.GetLength(); } else { - SuccessOrExit(error = aMessage.Read(aOffset, extTlv)); + SuccessOrExit(error = aMessage.Read(aOffsetRange, extTlv)); - mType = extTlv.GetType(); - mLength = extTlv.GetLength(); - headerSize = sizeof(ExtendedTlv); + mIsExtended = true; + headerSize = sizeof(ExtendedTlv); + size = headerSize + extTlv.GetLength(); } - // We know that we could successfully read `tlv` or `extTlv` - // (`headerSize` bytes) from the message, so the calculation of the - // remaining length as `aMessage.GetLength() - aOffset - headerSize` - // cannot underflow. - - VerifyOrExit(mLength <= aMessage.GetLength() - aOffset - headerSize, error = kErrorParse); + mTlvOffsetRange = aOffsetRange; + VerifyOrExit(mTlvOffsetRange.Contains(size), error = kErrorParse); + mTlvOffsetRange.ShrinkLength(static_cast(size)); - // Now that we know the entire TLV is contained within the - // `aMessage`, we can safely calculate `mValueOffset` and `mSize` - // as `uint16_t` and know that there will be no overflow. + VerifyOrExit(mTlvOffsetRange.GetEndOffset() <= aMessage.GetLength(), error = kErrorParse); - mType = tlv.GetType(); - mOffset = aOffset; - mValueOffset = aOffset + headerSize; - mSize = mLength + headerSize; + mValueOffsetRange = mTlvOffsetRange; + mValueOffsetRange.AdvanceOffset(headerSize); exit: return error; @@ -161,18 +140,14 @@ Error Tlv::ParsedInfo::ParseFrom(const Message &aMessage, uint16_t aOffset) Error Tlv::ParsedInfo::FindIn(const Message &aMessage, uint8_t aType) { - // This method searches within `aMessage` starting from - // `aMessage.GetOffset()` for a TLV type `aType` and parsed its - // info and validates that the entire TLV can be read from - // `aMessage`. Returns `kErrorNone` when found, otherwise - // `kErrorNotFound`. + Error error = kErrorNotFound; + OffsetRange offsetRange; - Error error = kErrorNotFound; - uint16_t offset = aMessage.GetOffset(); + offsetRange.InitFromMessageOffsetToEnd(aMessage); while (true) { - SuccessOrExit(ParseFrom(aMessage, offset)); + SuccessOrExit(ParseFrom(aMessage, offsetRange)); if (mType == aType) { @@ -180,11 +155,7 @@ Error Tlv::ParsedInfo::FindIn(const Message &aMessage, uint8_t aType) ExitNow(); } - // `ParseFrom()` already validated that `offset + mSize` is - // less than `aMessage.GetLength()` and therefore we can not - // have an overflow here. - - offset += mSize; + offsetRange.AdvanceOffset(mTlvOffsetRange.GetLength()); } exit: @@ -195,14 +166,12 @@ Error Tlv::ReadStringTlv(const Message &aMessage, uint16_t aOffset, uint8_t aMax { Error error = kErrorNone; ParsedInfo info; - uint16_t length; SuccessOrExit(error = info.ParseFrom(aMessage, aOffset)); - length = Min(info.mLength, static_cast(aMaxStringLength)); - - aMessage.ReadBytes(info.mValueOffset, aValue, length); - aValue[length] = '\0'; + info.mValueOffsetRange.ShrinkLength(aMaxStringLength); + aMessage.ReadBytes(info.mValueOffsetRange, aValue); + aValue[info.mValueOffsetRange.GetLength()] = kNullChar; exit: return error; @@ -231,9 +200,10 @@ Error Tlv::ReadTlvValue(const Message &aMessage, uint16_t aOffset, void *aValue, SuccessOrExit(error = info.ParseFrom(aMessage, aOffset)); - VerifyOrExit(info.mLength >= aMinLength, error = kErrorParse); + VerifyOrExit(info.mValueOffsetRange.Contains(aMinLength), error = kErrorParse); + info.mValueOffsetRange.ShrinkLength(aMinLength); - aMessage.ReadBytes(info.mValueOffset, aValue, aMinLength); + aMessage.ReadBytes(info.mValueOffsetRange, aValue); exit: return error; @@ -245,7 +215,7 @@ Error Tlv::FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStr ParsedInfo info; SuccessOrExit(error = info.FindIn(aMessage, aType)); - error = ReadStringTlv(aMessage, info.mOffset, aMaxStringLength, aValue); + error = ReadStringTlv(aMessage, info.mTlvOffsetRange.GetOffset(), aMaxStringLength, aValue); exit: return error; @@ -257,7 +227,7 @@ template Error Tlv::FindUintTlv(const Message &aMessage, uin ParsedInfo info; SuccessOrExit(error = info.FindIn(aMessage, aType)); - error = ReadUintTlv(aMessage, info.mOffset, aValue); + error = ReadUintTlv(aMessage, info.mTlvOffsetRange.GetOffset(), aValue); exit: return error; diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index 3ba23abcb..f89d0d643 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -246,19 +246,69 @@ class Tlv // Static methods for reading/finding/appending TLVs in a `Message`. /** - * Parses a TLV in a message from a given offset range, validating that it is fully contained within the offset - * range and the message, and then updating the offset range to skip over the entire parsed TLV. - * - * Can be used independent of whether the read TLV (from the message) is an Extended TLV or not. - * - * @param[in] aMessage The message to read from. - * @param[in,out] aOffsetRange The offset range to read from. On success, it is updated to skip the TLV. - * - * @retval kErrorNone Successfully parsed a TLV from @p aMessage. @p aOffsetRange is updated. - * @retval kErrorParse The TLV was not well-formed or was not fully contained in @p aMessage. + * Represents information for a parsed TLV from a message. * */ - static Error ParseAndSkipTlv(const Message &aMessage, OffsetRange &aOffsetRange); + struct ParsedInfo + { + /** + * Parses the TLV from a given message at given offset, ensures the TLV is well-formed and its header and + * value are fully contained in the message. + * + * Can be used independent of whether the TLV is an Extended TLV or not. + * + * @param[in] aMessage The message to read from. + * @param[in] aOffset The offset in @p aMessage. + * + * @retval kErrorNone Successfully parsed the TLV. + * @retval kErrorParse The TLV was not well-formed or not fully contained in @p aMessage. + * + */ + Error ParseFrom(const Message &aMessage, uint16_t aOffset); + + /** + * Parses the TLV from a given message for a given offset range, ensures the TLV is well-formed and its header + * and value are fully contained in the offset range and the message. + * + * Can be used independent of whether the TLV is an Extended TLV or not. + * + * @param[in] aMessage The message to read from. + * @param[in] aOffsetRange The offset range in @p aMessage. + * + * @retval kErrorNone Successfully parsed the TLV. + * @retval kErrorParse The TLV was not well-formed or not contained in @p aOffsetRange or @p aMessage. + * + */ + Error ParseFrom(const Message &aMessage, const OffsetRange &aOffsetRange); + + /** + * Searches in a given message starting from message offset for a TLV of given type and if found, parses + * the TLV and validates that the entire TLV is present in the message. + * + * Can be used independent of whether the TLV is an Extended TLV or not. + * + * @param[in] aMessage The message to search in. + * @param[in] aType The TLV type to search for. + * + * @retval kErrorNone Successfully found and parsed the TLV. + * @retval kErrorNotFound Could not find the TLV, or the TLV was not well-formed. + * + */ + Error FindIn(const Message &aMessage, uint8_t aType); + + /** + * Returns the full TLV size in bytes. + * + * @returns The TLV size in bytes. + * + */ + uint16_t GetSize(void) const { return mTlvOffsetRange.GetLength(); } + + uint8_t mType; ///< The TLV type + bool mIsExtended; ///< Whether the TLV is extended or not. + OffsetRange mTlvOffsetRange; ///< Offset range containing the full TLV. + OffsetRange mValueOffsetRange; ///< Offset range containing the TLV's value. + }; /** * Reads a TLV's value in a message at a given offset expecting a minimum length for the value. @@ -686,18 +736,6 @@ class Tlv static const uint8_t kExtendedLength = 255; // Extended Length value. private: - struct ParsedInfo - { - Error ParseFrom(const Message &aMessage, uint16_t aOffset); - Error FindIn(const Message &aMessage, uint8_t aType); - - uint8_t mType; - uint16_t mLength; - uint16_t mOffset; - uint16_t mValueOffset; - uint16_t mSize; - }; - static Error FindTlv(const Message &aMessage, uint8_t aType, void *aValue, uint16_t aLength); static Error ReadStringTlv(const Message &aMessage, uint16_t aOffset, uint8_t aMaxStringLength, char *aValue); static Error FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, char *aValue); diff --git a/src/core/thread/discover_scanner.cpp b/src/core/thread/discover_scanner.cpp index 594251a3e..a79333657 100644 --- a/src/core/thread/discover_scanner.cpp +++ b/src/core/thread/discover_scanner.cpp @@ -309,10 +309,10 @@ void DiscoverScanner::HandleTimer(void) void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const { Error error = kErrorNone; - MeshCoP::Tlv meshcopTlv; MeshCoP::DiscoveryResponseTlv discoveryResponse; ScanResult result; OffsetRange offsetRange; + Tlv::ParsedInfo tlvInfo; bool didCheckSteeringData = false; Mle::Log(Mle::kMessageReceive, Mle::kTypeDiscoveryResponse, aRxInfo.mMessageInfo.GetPeerAddr()); @@ -331,20 +331,16 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&result.mExtAddress)); - // Process MeshCoP TLVs - while (!offsetRange.IsEmpty()) + for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(tlvInfo.GetSize())) { - SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, meshcopTlv)); + SuccessOrExit(error = tlvInfo.ParseFrom(aRxInfo.mMessage, offsetRange)); - if (meshcopTlv.IsExtended()) + if (tlvInfo.mIsExtended) { - SuccessOrExit(error = Tlv::ParseAndSkipTlv(aRxInfo.mMessage, offsetRange)); continue; } - VerifyOrExit(offsetRange.Contains(meshcopTlv.GetSize()), error = kErrorParse); - - switch (meshcopTlv.GetType()) + switch (tlvInfo.mType) { case MeshCoP::Tlv::kDiscoveryResponse: SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, discoveryResponse)); @@ -364,20 +360,14 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const break; case MeshCoP::Tlv::kSteeringData: - if (meshcopTlv.GetLength() > 0) + if (!tlvInfo.mValueOffsetRange.IsEmpty()) { - MeshCoP::SteeringData &steeringData = AsCoreType(&result.mSteeringData); - uint8_t dataLength = MeshCoP::SteeringData::kMaxLength; - - if (meshcopTlv.GetLength() < dataLength) - { - dataLength = meshcopTlv.GetLength(); - } - - steeringData.Init(dataLength); + MeshCoP::SteeringData &steeringData = AsCoreType(&result.mSteeringData); + OffsetRange valueOffsetRange = tlvInfo.mValueOffsetRange; - SuccessOrExit(error = Tlv::ReadTlvValue(aRxInfo.mMessage, offsetRange.GetOffset(), - steeringData.GetData(), dataLength)); + valueOffsetRange.ShrinkLength(MeshCoP::SteeringData::kMaxLength); + steeringData.Init(static_cast(valueOffsetRange.GetLength())); + aRxInfo.mMessage.ReadBytes(valueOffsetRange, steeringData.GetData()); if (mEnableFiltering) { @@ -396,8 +386,6 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const default: break; } - - offsetRange.AdvanceOffset(meshcopTlv.GetSize()); } VerifyOrExit(!mEnableFiltering || didCheckSteeringData); diff --git a/src/core/thread/link_metrics.cpp b/src/core/thread/link_metrics.cpp index 7cd068f1a..90381e3ca 100644 --- a/src/core/thread/link_metrics.cpp +++ b/src/core/thread/link_metrics.cpp @@ -125,14 +125,14 @@ Error Initiator::AppendLinkMetricsQueryTlv(Message &aMessage, const QueryInfo &a void Initiator::HandleReport(const Message &aMessage, OffsetRange &aOffsetRange, const Ip6::Address &aAddress) { - Error error = kErrorNone; - bool hasStatus = false; - bool hasReport = false; - Tlv tlv; - ReportSubTlv reportTlv; - MetricsValues values; - uint8_t status; - uint8_t typeId; + Error error = kErrorNone; + bool hasStatus = false; + bool hasReport = false; + Tlv::ParsedInfo tlvInfo; + ReportSubTlv reportTlv; + MetricsValues values; + uint8_t status; + uint8_t typeId; OT_UNUSED_VARIABLE(error); @@ -140,23 +140,20 @@ void Initiator::HandleReport(const Message &aMessage, OffsetRange &aOffsetRange, values.Clear(); - while (!aOffsetRange.IsEmpty()) + for (; !aOffsetRange.IsEmpty(); aOffsetRange.AdvanceOffset(tlvInfo.GetSize())) { - SuccessOrExit(error = aMessage.Read(aOffsetRange, tlv)); + SuccessOrExit(error = tlvInfo.ParseFrom(aMessage, aOffsetRange)); - if (tlv.IsExtended()) + if (tlvInfo.mIsExtended) { - SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, aOffsetRange)); continue; } - VerifyOrExit(aOffsetRange.Contains(tlv.GetSize()), error = kErrorParse); - // The report must contain either: // - One or more Report Sub-TLVs (in case of success), or // - A single Status Sub-TLV (in case of failure). - switch (tlv.GetType()) + switch (tlvInfo.mType) { case StatusSubTlv::kType: VerifyOrExit(!hasStatus && !hasReport, error = kErrorDrop); @@ -216,8 +213,6 @@ void Initiator::HandleReport(const Message &aMessage, OffsetRange &aOffsetRange, break; } - - aOffsetRange.AdvanceOffset(tlv.GetSize()); } VerifyOrExit(hasStatus || hasReport); @@ -307,30 +302,26 @@ Error Initiator::SendMgmtRequestEnhAckProbing(const Ip6::Address &aDestination, Error Initiator::HandleManagementResponse(const Message &aMessage, const Ip6::Address &aAddress) { - Error error = kErrorNone; - OffsetRange offsetRange; - uint8_t status; - bool hasStatus = false; + Error error = kErrorNone; + OffsetRange offsetRange; + Tlv::ParsedInfo tlvInfo; + uint8_t status; + bool hasStatus = false; VerifyOrExit(mMgmtResponseCallback.IsSet()); SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Mle::Tlv::Type::kLinkMetricsManagement, offsetRange)); - while (!offsetRange.IsEmpty()) + for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(tlvInfo.GetSize())) { - Tlv tlv; + SuccessOrExit(error = tlvInfo.ParseFrom(aMessage, offsetRange)); - SuccessOrExit(error = aMessage.Read(offsetRange, tlv)); - - if (tlv.IsExtended()) + if (tlvInfo.mIsExtended) { - SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offsetRange)); continue; } - VerifyOrExit(offsetRange.Contains(tlv.GetSize()), error = kErrorParse); - - switch (tlv.GetType()) + switch (tlvInfo.mType) { case StatusSubTlv::kType: VerifyOrExit(!hasStatus, error = kErrorParse); @@ -341,8 +332,6 @@ Error Initiator::HandleManagementResponse(const Message &aMessage, const Ip6::Ad default: break; } - - offsetRange.AdvanceOffset(tlv.GetSize()); } VerifyOrExit(hasStatus, error = kErrorParse); @@ -428,14 +417,15 @@ Subject::Subject(Instance &aInstance) Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, Neighbor &aNeighbor) { - Error error = kErrorNone; - Tlv tlv; - uint8_t queryId; - bool hasQueryId = false; - uint16_t length; - uint16_t offset; - OffsetRange offsetRange; - MetricsValues values; + Error error = kErrorNone; + Tlv tlv; + Tlv::ParsedInfo tlvInfo; + uint8_t queryId; + bool hasQueryId = false; + uint16_t length; + uint16_t offset; + OffsetRange offsetRange; + MetricsValues values; values.Clear(); @@ -446,40 +436,31 @@ Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, N SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRequestMessage, Mle::Tlv::Type::kLinkMetricsQuery, offsetRange)); - while (!offsetRange.IsEmpty()) + for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(tlvInfo.GetSize())) { - OffsetRange tlvOffsetRange; - - SuccessOrExit(error = aRequestMessage.Read(offsetRange, tlv)); + SuccessOrExit(error = tlvInfo.ParseFrom(aRequestMessage, offsetRange)); - if (tlv.IsExtended()) + if (tlvInfo.mIsExtended) { - SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offsetRange)); continue; } - VerifyOrExit(offsetRange.Contains(tlv.GetSize()), error = kErrorParse); - - tlvOffsetRange = offsetRange; - tlvOffsetRange.ShrinkLength(tlv.GetSize()); - - switch (tlv.GetType()) + switch (tlvInfo.mType) { case SubTlv::kQueryId: - SuccessOrExit(error = Tlv::Read(aRequestMessage, tlvOffsetRange.GetOffset(), queryId)); + SuccessOrExit(error = + Tlv::Read(aRequestMessage, tlvInfo.mTlvOffsetRange.GetOffset(), queryId)); hasQueryId = true; break; case SubTlv::kQueryOptions: - tlvOffsetRange.AdvanceOffset(sizeof(tlv)); - SuccessOrExit(error = ReadTypeIdsFromMessage(aRequestMessage, tlvOffsetRange, values.GetMetrics())); + SuccessOrExit(error = + ReadTypeIdsFromMessage(aRequestMessage, tlvInfo.mValueOffsetRange, values.GetMetrics())); break; default: break; } - - offsetRange.AdvanceOffset(tlv.GetSize()); } VerifyOrExit(hasQueryId, error = kErrorParse); @@ -537,6 +518,7 @@ Error Subject::HandleManagementRequest(const Message &aMessage, Neighbor &aNeigh { Error error = kErrorNone; OffsetRange offsetRange; + Tlv::ParsedInfo tlvInfo; FwdProbingRegSubTlv fwdProbingSubTlv; EnhAckConfigSubTlv enhAckConfigSubTlv; Metrics metrics; @@ -548,29 +530,22 @@ Error Subject::HandleManagementRequest(const Message &aMessage, Neighbor &aNeigh fwdProbingSubTlv.SetLength(0); enhAckConfigSubTlv.SetLength(0); - while (!offsetRange.IsEmpty()) + for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(tlvInfo.GetSize())) { - Tlv tlv; uint16_t minTlvSize; Tlv *subTlv; OffsetRange tlvOffsetRange; - SuccessOrExit(error = aMessage.Read(offsetRange, tlv)); + SuccessOrExit(error = tlvInfo.ParseFrom(aMessage, offsetRange)); - if (tlv.IsExtended()) + if (tlvInfo.mIsExtended) { - SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offsetRange)); continue; } - VerifyOrExit(offsetRange.Contains(tlv.GetSize()), error = kErrorParse); - - tlvOffsetRange = offsetRange; - tlvOffsetRange.ShrinkLength(tlv.GetSize()); - - offsetRange.AdvanceOffset(tlv.GetSize()); + tlvOffsetRange = tlvInfo.mTlvOffsetRange; - switch (tlv.GetType()) + switch (tlvInfo.mType) { case SubTlv::kFwdProbingReg: subTlv = &fwdProbingSubTlv; @@ -590,10 +565,10 @@ Error Subject::HandleManagementRequest(const Message &aMessage, Neighbor &aNeigh VerifyOrExit(fwdProbingSubTlv.GetLength() == 0, error = kErrorParse); VerifyOrExit(enhAckConfigSubTlv.GetLength() == 0, error = kErrorParse); - VerifyOrExit(tlv.GetSize() >= minTlvSize, error = kErrorParse); + VerifyOrExit(tlvInfo.GetSize() >= minTlvSize, error = kErrorParse); // Read `subTlv` with its `minTlvSize`, followed by the Type IDs. - SuccessOrExit(error = aMessage.Read(tlvOffsetRange.GetOffset(), subTlv, minTlvSize)); + SuccessOrExit(error = aMessage.Read(tlvOffsetRange, subTlv, minTlvSize)); tlvOffsetRange.AdvanceOffset(minTlvSize); SuccessOrExit(error = ReadTypeIdsFromMessage(aMessage, tlvOffsetRange, metrics)); @@ -667,17 +642,18 @@ Error Subject::AppendReportSubTlvToMessage(Message &aMessage, const MetricsValue void Subject::Free(SeriesInfo &aSeriesInfo) { mSeriesInfoPool.Free(aSeriesInfo); } -Error Subject::ReadTypeIdsFromMessage(const Message &aMessage, OffsetRange &aOffsetRange, Metrics &aMetrics) +Error Subject::ReadTypeIdsFromMessage(const Message &aMessage, const OffsetRange &aOffsetRange, Metrics &aMetrics) { - Error error = kErrorNone; + Error error = kErrorNone; + OffsetRange offsetRange = aOffsetRange; aMetrics.Clear(); - while (!aOffsetRange.IsEmpty()) + while (!offsetRange.IsEmpty()) { uint8_t typeId; - SuccessOrExit(aMessage.Read(aOffsetRange, typeId)); + SuccessOrExit(aMessage.Read(offsetRange, typeId)); switch (typeId) { @@ -704,7 +680,7 @@ Error Subject::ReadTypeIdsFromMessage(const Message &aMessage, OffsetRange &aOff default: if (TypeId::IsExtended(typeId)) { - aOffsetRange.AdvanceOffset(sizeof(uint8_t)); // Skip the additional second byte. + offsetRange.AdvanceOffset(sizeof(uint8_t)); // Skip the additional second byte. } else { @@ -713,7 +689,7 @@ Error Subject::ReadTypeIdsFromMessage(const Message &aMessage, OffsetRange &aOff break; } - aOffsetRange.AdvanceOffset(sizeof(uint8_t)); + offsetRange.AdvanceOffset(sizeof(uint8_t)); } exit: diff --git a/src/core/thread/link_metrics.hpp b/src/core/thread/link_metrics.hpp index 34294b66e..ebe923f8b 100644 --- a/src/core/thread/link_metrics.hpp +++ b/src/core/thread/link_metrics.hpp @@ -341,7 +341,7 @@ class Subject : public InstanceLocator, private NonCopyable static constexpr uint16_t kMaxSeriesSupported = OPENTHREAD_CONFIG_MLE_LINK_METRICS_SERIES_MTD; #endif - static Error ReadTypeIdsFromMessage(const Message &aMessage, OffsetRange &aOffsetRange, Metrics &aMetrics); + static Error ReadTypeIdsFromMessage(const Message &aMessage, const OffsetRange &aOffsetRange, Metrics &aMetrics); static Error AppendReportSubTlvToMessage(Message &aMessage, const MetricsValues &aValues); Status ConfigureForwardTrackingSeries(uint8_t aSeriesId, diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index c4aad8ce4..a776e533e 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -2617,7 +2617,7 @@ void MleRouter::SetSteeringData(const Mac::ExtAddress *aExtAddress) void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo) { Error error = kErrorNone; - MeshCoP::Tlv meshcopTlv; + Tlv::ParsedInfo tlvInfo; MeshCoP::DiscoveryRequestTlv discoveryRequestTlv; MeshCoP::ExtendedPanId extPanId; OffsetRange offsetRange; @@ -2630,19 +2630,16 @@ void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo) SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRxInfo.mMessage, Tlv::kDiscovery, offsetRange)); - while (!offsetRange.IsEmpty()) + for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(tlvInfo.GetSize())) { - SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, meshcopTlv)); + SuccessOrExit(error = tlvInfo.ParseFrom(aRxInfo.mMessage, offsetRange)); - if (meshcopTlv.IsExtended()) + if (tlvInfo.mIsExtended) { - SuccessOrExit(error = Tlv::ParseAndSkipTlv(aRxInfo.mMessage, offsetRange)); continue; } - VerifyOrExit(offsetRange.Contains(meshcopTlv.GetSize())); - - switch (meshcopTlv.GetType()) + switch (tlvInfo.mType) { case MeshCoP::Tlv::kDiscoveryRequest: SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, discoveryRequestTlv)); @@ -2660,8 +2657,6 @@ void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo) default: break; } - - offsetRange.AdvanceOffset(meshcopTlv.GetSize()); } if (discoveryRequestTlv.IsValid()) From 408f3f205fea4b1351c1a2143ef86a8a90469882 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 3 Jul 2024 11:15:55 -0700 Subject: [PATCH 014/160] [mesh-forwarder] add `RxInfo::ToString()` to simplify logging (#10462) This commit adds `RxInfo::ToString()`, which generates a common log string about an `RxInfo` using the "len:%d, src:%s, dst:%s, sec:%s" format, providing frame length, source and destination addresses, and whether link security is used. This is then used in different logging methods, simplifying the code. --- src/core/thread/mesh_forwarder.cpp | 22 ++++++++++++++-------- src/core/thread/mesh_forwarder.hpp | 5 +++++ src/core/thread/mesh_forwarder_ftd.cpp | 5 ++--- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 3a78da154..f5b1afe56 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -2017,18 +2017,24 @@ void MeshForwarder::LogFragmentFrameDrop(Error aError, const RxInfo &aRxInfo, const Lowpan::FragmentHeader &aFragmentHeader) { - LogNote("Dropping rx frag frame, error:%s, len:%d, src:%s, dst:%s, tag:%d, offset:%d, dglen:%d, sec:%s", - ErrorToString(aError), aRxInfo.mFrameData.GetLength(), aRxInfo.GetSrcAddr().ToString().AsCString(), - aRxInfo.GetDstAddr().ToString().AsCString(), aFragmentHeader.GetDatagramTag(), - aFragmentHeader.GetDatagramOffset(), aFragmentHeader.GetDatagramSize(), - ToYesNo(aRxInfo.IsLinkSecurityEnabled())); + LogNote("Dropping rx frag frame, error:%s, %s, tag:%d, offset:%d, dglen:%d", ErrorToString(aError), + aRxInfo.ToString().AsCString(), aFragmentHeader.GetDatagramTag(), aFragmentHeader.GetDatagramOffset(), + aFragmentHeader.GetDatagramSize()); } void MeshForwarder::LogLowpanHcFrameDrop(Error aError, const RxInfo &aRxInfo) { - LogNote("Dropping rx lowpan HC frame, error:%s, len:%d, src:%s, dst:%s, sec:%s", ErrorToString(aError), - aRxInfo.mFrameData.GetLength(), aRxInfo.GetSrcAddr().ToString().AsCString(), - aRxInfo.GetDstAddr().ToString().AsCString(), ToYesNo(aRxInfo.IsLinkSecurityEnabled())); + LogNote("Dropping rx lowpan HC frame, error:%s, %s", ErrorToString(aError), aRxInfo.ToString().AsCString()); +} + +MeshForwarder::RxInfo::InfoString MeshForwarder::RxInfo::ToString(void) const +{ + InfoString string; + + string.Append("len:%d, src:%s, dst:%s, sec:%s", mFrameData.GetLength(), GetSrcAddr().ToString().AsCString(), + GetDstAddr().ToString().AsCString(), ToYesNo(IsLinkSecurityEnabled())); + + return string; } #else // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE) diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index b7d3f7c44..d337dfdb6 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -413,9 +413,14 @@ class MeshForwarder : public InstanceLocator, private NonCopyable struct RxInfo { + static constexpr uint16_t kInfoStringSize = 70; + + typedef String InfoString; + const Mac::Address &GetSrcAddr(void) const { return mMacAddrs.mSource; } const Mac::Address &GetDstAddr(void) const { return mMacAddrs.mDestination; } bool IsLinkSecurityEnabled(void) const { return mLinkInfo.IsLinkSecurityEnabled(); } + InfoString ToString(void) const; FrameData mFrameData; ThreadLinkInfo mLinkInfo; diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index e326fe33e..0d84847d4 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -1000,9 +1000,8 @@ void MeshForwarder::GetForwardFramePriority(RxInfo &aRxInfo, Message::Priority & exit: if (error != kErrorNone) { - LogNote("Failed to get forwarded frame priority, error:%s, len:%d, src:%s, dst:%s", ErrorToString(error), - aRxInfo.mFrameData.GetLength(), aRxInfo.GetSrcAddr().ToString().AsCString(), - aRxInfo.GetDstAddr().ToString().AsCString()); + LogNote("Failed to get forwarded frame priority, error:%s, %s", ErrorToString(error), + aRxInfo.ToString().AsCString()); } else if (isFragment) { From 52484080761b0706fbd3e3f732f4225faae10b6a Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 3 Jul 2024 11:17:47 -0700 Subject: [PATCH 015/160] [ip6] use `OffsetRange` for parsing options in extension headers (#10466) This commit updates `Ip6` and `Lowpan` to use `OffsetRange` when iterating and parsing options in an HBH extension header. It also simplifies the `RemoveMplOption()` method by adding an `Action` enum, which determines how to remove the MPL option: whether to shrink the HBH header, fully remove the HBH header (if it contains no other options), or replace MPL option with padding. --- src/core/net/ip6.cpp | 127 ++++++++++++++++++++--------------- src/core/net/ip6.hpp | 1 + src/core/net/ip6_headers.cpp | 9 ++- src/core/net/ip6_headers.hpp | 11 ++- src/core/net/ip6_mpl.cpp | 6 +- src/core/net/ip6_mpl.hpp | 12 ++-- src/core/thread/lowpan.cpp | 9 +-- 7 files changed, 98 insertions(+), 77 deletions(-) diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index a8ce030e4..2d3b1922b 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -309,30 +309,35 @@ Error Ip6::InsertMplOption(Message &aMessage, Header &aHeader) Error Ip6::RemoveMplOption(Message &aMessage) { - Error error = kErrorNone; + enum Action : uint8_t + { + kNoMplOption, + kShrinkHbh, + kRemoveHbh, + kReplaceMplWithPad, + }; + + Error error = kErrorNone; + Action action = kNoMplOption; Header ip6Header; HopByHopHeader hbh; Option option; - uint16_t offset; - uint16_t endOffset; - uint16_t mplOffset = 0; - uint8_t mplLength = 0; - bool remove = false; - - offset = 0; - IgnoreError(aMessage.Read(offset, ip6Header)); - offset += sizeof(ip6Header); - VerifyOrExit(ip6Header.GetNextHeader() == kProtoHopOpts); + OffsetRange offsetRange; + OffsetRange mplOffsetRange; + PadOption padOption; + + offsetRange.InitFromMessageFullLength(aMessage); - IgnoreError(aMessage.Read(offset, hbh)); - endOffset = offset + hbh.GetSize(); - VerifyOrExit(aMessage.GetLength() >= endOffset, error = kErrorParse); + IgnoreError(aMessage.Read(offsetRange, ip6Header)); + offsetRange.AdvanceOffset(sizeof(ip6Header)); - offset += sizeof(hbh); + VerifyOrExit(ip6Header.GetNextHeader() == kProtoHopOpts); + + SuccessOrExit(error = ReadHopByHopHeader(aMessage, offsetRange, hbh)); - for (; offset < endOffset; offset += option.GetSize()) + for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize())) { - IgnoreError(option.ParseFrom(aMessage, offset, endOffset)); + SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange)); if (option.IsPadding()) { @@ -342,44 +347,49 @@ Error Ip6::RemoveMplOption(Message &aMessage) if (option.GetType() == MplOption::kType) { // If multiple MPL options exist, discard packet - VerifyOrExit(mplOffset == 0, error = kErrorParse); + VerifyOrExit(action == kNoMplOption, error = kErrorParse); - mplOffset = offset; - mplLength = option.GetLength(); + // `Option::ParseFrom()` already validated that the entire + // option is present in the `offsetRange`. - VerifyOrExit(mplLength <= sizeof(MplOption) - sizeof(Option), error = kErrorParse); + mplOffsetRange = offsetRange; + mplOffsetRange.ShrinkLength(option.GetSize()); - if (mplOffset == sizeof(ip6Header) + sizeof(hbh) && hbh.GetLength() == 0) + VerifyOrExit(option.GetSize() <= sizeof(MplOption), error = kErrorParse); + + if (mplOffsetRange.GetOffset() == sizeof(ip6Header) + sizeof(hbh) && hbh.GetLength() == 0) { // First and only IPv6 Option, remove IPv6 HBH Option header - remove = true; + action = kRemoveHbh; } - else if (mplOffset + ExtensionHeader::kLengthUnitSize == endOffset) + else if (mplOffsetRange.GetOffset() + ExtensionHeader::kLengthUnitSize == offsetRange.GetEndOffset()) { - // Last IPv6 Option, remove the last 8 bytes - remove = true; + // Last IPv6 Option, shrink the last 8 bytes + action = kShrinkHbh; } } - else + else if (action != kNoMplOption) { // Encountered another option, now just replace // MPL Option with Pad Option - remove = false; + action = kReplaceMplWithPad; } } - // verify that IPv6 Options header is properly formed - VerifyOrExit(offset == endOffset, error = kErrorParse); - - if (remove) + switch (action) { + case kNoMplOption: + break; + + case kShrinkHbh: + case kRemoveHbh: // Last IPv6 Option, shrink HBH Option header by // 8 bytes (`kLengthUnitSize`) - aMessage.RemoveHeader(endOffset - ExtensionHeader::kLengthUnitSize, ExtensionHeader::kLengthUnitSize); + aMessage.RemoveHeader(offsetRange.GetEndOffset() - ExtensionHeader::kLengthUnitSize, + ExtensionHeader::kLengthUnitSize); - if (mplOffset == sizeof(ip6Header) + sizeof(hbh)) + if (action == kRemoveHbh) { - // Remove entire HBH header ip6Header.SetNextHeader(hbh.GetNextHeader()); } else @@ -393,14 +403,12 @@ Error Ip6::RemoveMplOption(Message &aMessage) ip6Header.SetPayloadLength(ip6Header.GetPayloadLength() - ExtensionHeader::kLengthUnitSize); aMessage.Write(0, ip6Header); - } - else if (mplOffset != 0) - { - // Replace MPL Option with Pad Option - PadOption padOption; + break; - padOption.InitForPadSize(sizeof(Option) + mplLength); - aMessage.WriteBytes(mplOffset, &padOption, padOption.GetSize()); + case kReplaceMplWithPad: + padOption.InitForPadSize(static_cast(mplOffsetRange.GetLength())); + aMessage.WriteBytes(mplOffsetRange.GetOffset(), &padOption, padOption.GetSize()); + break; } exit: @@ -501,24 +509,37 @@ void Ip6::HandleSendQueue(void) } } +Error Ip6::ReadHopByHopHeader(const Message &aMessage, OffsetRange &aOffsetRange, HopByHopHeader &aHbhHeader) const +{ + // Reads the HBH header from the message at the given offset range. + // On success, updates `aOffsetRange` to indicate the location of + // options within the HBH header. + + Error error; + + SuccessOrExit(error = aMessage.Read(aOffsetRange, aHbhHeader)); + VerifyOrExit(aOffsetRange.Contains(aHbhHeader.GetSize()), error = kErrorParse); + aOffsetRange.ShrinkLength(aHbhHeader.GetSize()); + aOffsetRange.AdvanceOffset(sizeof(HopByHopHeader)); + +exit: + return error; +} + Error Ip6::HandleOptions(Message &aMessage, Header &aHeader, bool &aReceive) { Error error = kErrorNone; HopByHopHeader hbhHeader; Option option; - uint16_t offset = aMessage.GetOffset(); - uint16_t endOffset; - - SuccessOrExit(error = aMessage.Read(offset, hbhHeader)); + OffsetRange offsetRange; - endOffset = offset + hbhHeader.GetSize(); - VerifyOrExit(endOffset <= aMessage.GetLength(), error = kErrorParse); + offsetRange.InitFromMessageOffsetToEnd(aMessage); - offset += sizeof(HopByHopHeader); + SuccessOrExit(error = ReadHopByHopHeader(aMessage, offsetRange, hbhHeader)); - for (; offset < endOffset; offset += option.GetSize()) + for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize())) { - SuccessOrExit(error = option.ParseFrom(aMessage, offset, endOffset)); + SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange)); if (option.IsPadding()) { @@ -527,14 +548,14 @@ Error Ip6::HandleOptions(Message &aMessage, Header &aHeader, bool &aReceive) if (option.GetType() == MplOption::kType) { - SuccessOrExit(error = mMpl.ProcessOption(aMessage, offset, aHeader.GetSource(), aReceive)); + SuccessOrExit(error = mMpl.ProcessOption(aMessage, offsetRange, aHeader.GetSource(), aReceive)); continue; } VerifyOrExit(option.GetAction() == Option::kActionSkip, error = kErrorDrop); } - aMessage.SetOffset(offset); + aMessage.SetOffset(offsetRange.GetEndOffset()); exit: return error; diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp index 6e484ea98..03414ec32 100644 --- a/src/core/net/ip6.hpp +++ b/src/core/net/ip6.hpp @@ -402,6 +402,7 @@ class Ip6 : public InstanceLocator, private NonCopyable void UpdateReassemblyList(void); void SendIcmpError(Message &aMessage, Icmp::Header::Type aIcmpType, Icmp::Header::Code aIcmpCode); #endif + Error ReadHopByHopHeader(const Message &aMessage, OffsetRange &aOffsetRange, HopByHopHeader &aHbhHeader) const; Error AddMplOption(Message &aMessage, Header &aHeader); Error PrepareMulticastToLargerThanRealmLocal(Message &aMessage, const Header &aHeader); Error InsertMplOption(Message &aMessage, Header &aHeader); diff --git a/src/core/net/ip6_headers.cpp b/src/core/net/ip6_headers.cpp index ff9585f6f..6e2262bcc 100644 --- a/src/core/net/ip6_headers.cpp +++ b/src/core/net/ip6_headers.cpp @@ -69,14 +69,14 @@ bool Header::IsValid(void) const //--------------------------------------------------------------------------------------------------------------------- // Option -Error Option::ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t aEndOffset) +Error Option::ParseFrom(const Message &aMessage, const OffsetRange &aOffsetRange) { Error error; // Read the Type first to check for the Pad1 Option. // If it is not, then we read the full `Option` header. - SuccessOrExit(error = aMessage.Read(aOffset, this, sizeof(mType))); + SuccessOrExit(error = aMessage.Read(aOffsetRange, this, sizeof(mType))); if (mType == kTypePad1) { @@ -84,9 +84,8 @@ Error Option::ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t aEnd ExitNow(); } - SuccessOrExit(error = aMessage.Read(aOffset, *this)); - - VerifyOrExit(aOffset + GetSize() <= aEndOffset, error = kErrorParse); + SuccessOrExit(error = aMessage.Read(aOffsetRange, *this)); + VerifyOrExit(aOffsetRange.Contains(GetSize()), error = kErrorParse); exit: return error; diff --git a/src/core/net/ip6_headers.hpp b/src/core/net/ip6_headers.hpp index 8289c53b0..9e8d151b8 100644 --- a/src/core/net/ip6_headers.hpp +++ b/src/core/net/ip6_headers.hpp @@ -517,17 +517,16 @@ class Option * Parses and validates the IPv6 Option from a given message. * * The Option is read from @p aOffset in @p aMessage. This method then checks that the entire Option is present - * in @p aMessage before the @p aEndOffset. + * within @p aOffsetRange. * - * @param[in] aMessage The IPv6 message. - * @param[in] aOffset The offset in @p aMessage to read the IPv6 Option. - * @param[in] aEndOffset The end offset in @p aMessage. + * @param[in] aMessage The IPv6 message. + * @param[in] aOffsetRange The offset range in @p aMessage to read the IPv6 Option. * * @retval kErrorNone Successfully parsed the IPv6 option from @p aMessage. - * @retval kErrorParse Malformed IPv6 Option or Option is not contained within @p aMessage by @p aEndOffset. + * @retval kErrorParse Malformed IPv6 Option or Option is not contained within @p aMessage and @p aOffsetRange. * */ - Error ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t aEndOffset); + Error ParseFrom(const Message &aMessage, const OffsetRange &aOffsetRange); protected: static constexpr uint8_t kTypePad1 = 0x00; ///< Pad1 Option Type. diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp index 4f3964d7b..3bd873d07 100644 --- a/src/core/net/ip6_mpl.cpp +++ b/src/core/net/ip6_mpl.cpp @@ -90,14 +90,14 @@ void Mpl::InitOption(MplOption &aOption, const Address &aAddress) aOption.SetSequence(mSequence++); } -Error Mpl::ProcessOption(Message &aMessage, uint16_t aOffset, const Address &aAddress, bool &aReceive) +Error Mpl::ProcessOption(Message &aMessage, const OffsetRange &aOffsetRange, const Address &aAddress, bool &aReceive) { Error error; MplOption option; // Read the min size bytes first, then check the expected // `SeedIdLength` and read the full `MplOption` if needed. - SuccessOrExit(error = aMessage.Read(aOffset, &option, MplOption::kMinSize)); + SuccessOrExit(error = aMessage.Read(aOffsetRange, &option, MplOption::kMinSize)); switch (option.GetSeedIdLength()) { @@ -108,7 +108,7 @@ Error Mpl::ProcessOption(Message &aMessage, uint16_t aOffset, const Address &aAd break; case MplOption::kSeedIdLength2: - SuccessOrExit(error = aMessage.Read(aOffset, option)); + SuccessOrExit(error = aMessage.Read(aOffsetRange, option)); break; case MplOption::kSeedIdLength8: diff --git a/src/core/net/ip6_mpl.hpp b/src/core/net/ip6_mpl.hpp index 76fe15984..53b8304e7 100644 --- a/src/core/net/ip6_mpl.hpp +++ b/src/core/net/ip6_mpl.hpp @@ -191,17 +191,17 @@ class Mpl : public InstanceLocator, private NonCopyable * MPL Seed it allows to send the first MPL Data Message directly, then sets up Trickle * timer expirations for subsequent retransmissions. * - * @param[in] aMessage A reference to the message. - * @param[in] aOffset The offset in @p aMessage to read the MPL option. - * @param[in] aAddress A reference to the IPv6 Source Address. - * @param[out] aReceive Set to FALSE if the MPL message is a duplicate and must not - * go through the receiving process again, untouched otherwise. + * @param[in] aMessage A reference to the message. + * @param[in] aOffsetRange The offset range in @p aMessage to read the MPL option. + * @param[in] aAddress A reference to the IPv6 Source Address. + * @param[out] aReceive Set to FALSE if the MPL message is a duplicate and must not + * go through the receiving process again, untouched otherwise. * * @retval kErrorNone Successfully processed the MPL option. * @retval kErrorDrop The MPL message is a duplicate and should be dropped. * */ - Error ProcessOption(Message &aMessage, uint16_t aOffset, const Address &aAddress, bool &aReceive); + Error ProcessOption(Message &aMessage, const OffsetRange &aOffsetRange, const Address &aAddress, bool &aReceive); #if OPENTHREAD_FTD /** diff --git a/src/core/thread/lowpan.cpp b/src/core/thread/lowpan.cpp index 3860f7ba0..e05bbd628 100644 --- a/src/core/thread/lowpan.cpp +++ b/src/core/thread/lowpan.cpp @@ -470,14 +470,15 @@ Error Lowpan::CompressExtensionHeader(Message &aMessage, FrameBuilder &aFrameBui // Pad1 or PadN option MAY be elided by the compressor." if (aNextHeader == Ip6::kProtoHopOpts || aNextHeader == Ip6::kProtoDstOpts) { - uint16_t offset = aMessage.GetOffset(); - uint16_t endOffset = offset + len; + OffsetRange offsetRange; bool hasOption = false; Ip6::Option option; - for (; offset < endOffset; offset += option.GetSize()) + offsetRange.Init(aMessage.GetOffset(), len); + + for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize())) { - SuccessOrExit(error = option.ParseFrom(aMessage, offset, endOffset)); + SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange)); hasOption = true; } From 41ded02814df00ddcfe820f0d7f2541ea4a82042 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 3 Jul 2024 11:21:04 -0700 Subject: [PATCH 016/160] [routing-manager] add support for tracking peer BRs (#10445) This commit adds a new feature to the `RoutingManager` to enable tracking information about peer Thread Border Routers that are connected to the same Thread network. When enabled, the `RoutingManager` will maintain a record of advertised RIO/PIO prefixes discovered from received Router Advertisement (RA) messages of peer BRs. When disabled (the existing behavior), such entries are not tracked. When tracked, these entries are marked to be disregarded and are not considered in any decision-making processes, such as selecting favored on-link prefixes or determining routes to publish in the Thread Network Data. They are primarily intended for debugging and telemetry (information gathering) purposes. --- src/core/border_router/routing_manager.cpp | 119 +++++++++++++++++---- src/core/border_router/routing_manager.hpp | 20 +++- src/core/config/border_routing.h | 17 +++ 3 files changed, 131 insertions(+), 25 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 36f9541bd..62c90b6d0 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1159,6 +1159,7 @@ void RoutingManager::RxRaTracker::ProcessPrefixInfoOption(const PrefixInfoOption { Ip6::Prefix prefix; Entry *entry; + bool disregard; VerifyOrExit(aPio.IsValid()); aPio.GetPrefix(prefix); @@ -1169,7 +1170,15 @@ void RoutingManager::RxRaTracker::ProcessPrefixInfoOption(const PrefixInfoOption ExitNow(); } - VerifyOrExit(prefix != Get().mOnLinkPrefixManager.GetLocalPrefix()); + // Disregard the PIO prefix if it matches our local on-link prefix, + // as this indicates it's likely from a peer Border Router connected + // to the same Thread mesh. + + disregard = (prefix == Get().mOnLinkPrefixManager.GetLocalPrefix()); + +#if !OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + VerifyOrExit(!disregard); +#endif LogPrefixInfoOption(prefix, aPio.GetValidLifetime(), aPio.GetPreferredLifetime()); @@ -1198,6 +1207,8 @@ void RoutingManager::RxRaTracker::ProcessPrefixInfoOption(const PrefixInfoOption entry->AdoptValidAndPreferredLifetimesFrom(newPrefix); } + entry->SetDisregardFlag(disregard); + exit: return; } @@ -1206,16 +1217,16 @@ void RoutingManager::RxRaTracker::ProcessRouteInfoOption(const RouteInfoOption & { Ip6::Prefix prefix; Entry *entry; + bool disregard; VerifyOrExit(aRio.IsValid()); aRio.GetPrefix(prefix); VerifyOrExit(!prefix.IsLinkLocal() && !prefix.IsMulticast()); - VerifyOrExit(Get().mOmrPrefixManager.GetLocalPrefix().GetPrefix() != prefix); - // Disregard our own advertised OMR prefixes and those currently - // present in the Thread Network Data. + // present in the Thread Network Data. This implies it is likely + // from a peer Thread BR connected to the same Thread mesh. // // There should be eventual parity between the `RioAdvertiser` // prefixes and the OMR prefixes in Network Data, but temporary @@ -1223,8 +1234,13 @@ void RoutingManager::RxRaTracker::ProcessRouteInfoOption(const RouteInfoOption & // required to update Network Data (registering with leader). So // both checks are necessary. - VerifyOrExit(!Get().mRioAdvertiser.HasAdvertised(prefix)); - VerifyOrExit(!Get().ContainsOmrPrefix(prefix)); + disregard = (Get().mOmrPrefixManager.GetLocalPrefix().GetPrefix() == prefix) || + Get().mRioAdvertiser.HasAdvertised(prefix) || + Get().ContainsOmrPrefix(prefix); + +#if !OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + VerifyOrExit(!disregard); +#endif LogRouteInfoOption(prefix, aRio.GetRouteLifetime(), aRio.GetPreference()); @@ -1250,6 +1266,8 @@ void RoutingManager::RxRaTracker::ProcessRouteInfoOption(const RouteInfoOption & entry->SetFrom(aRio); } + entry->SetDisregardFlag(disregard); + exit: return; } @@ -1319,14 +1337,29 @@ template void RoutingManager::RxRaTracker::Entry: void RoutingManager::RxRaTracker::HandleLocalOnLinkPrefixChanged(void) { const Ip6::Prefix &prefix = Get().mOnLinkPrefixManager.GetLocalPrefix(); - bool didRemove = false; + bool didChange = false; + + // When `TRACK_PEER_BR_INFO_ENABLE` is enabled, we mark + // to disregard any on-link prefix entries matching the new + // local on-link prefix. Otherwise, we can remove and free + // them. for (Router &router : mRouters) { - didRemove |= router.mOnLinkPrefixes.RemoveAndFreeAllMatching(prefix); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + OnLinkPrefix *entry = router.mOnLinkPrefixes.FindMatching(prefix); + + if ((entry != nullptr) && !entry->ShouldDisregard()) + { + entry->SetDisregardFlag(true); + didChange = true; + } +#else + didChange |= router.mOnLinkPrefixes.RemoveAndFreeAllMatching(prefix); +#endif } - VerifyOrExit(didRemove); + VerifyOrExit(didChange); Evaluate(); @@ -1338,7 +1371,7 @@ void RoutingManager::RxRaTracker::HandleNetDataChange(void) { NetworkData::Iterator iterator = NetworkData::kIteratorInit; NetworkData::OnMeshPrefixConfig prefixConfig; - bool didRemove = false; + bool didChange = false; while (Get().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone) { @@ -1349,11 +1382,21 @@ void RoutingManager::RxRaTracker::HandleNetDataChange(void) for (Router &router : mRouters) { - didRemove |= router.mRoutePrefixes.RemoveAndFreeAllMatching(prefixConfig.GetPrefix()); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + RoutePrefix *entry = router.mRoutePrefixes.FindMatching(prefixConfig.GetPrefix()); + + if ((entry != nullptr) && !entry->ShouldDisregard()) + { + entry->SetDisregardFlag(true); + didChange = true; + } +#else + didChange |= router.mRoutePrefixes.RemoveAndFreeAllMatching(prefixConfig.GetPrefix()); +#endif } } - if (didRemove) + if (didChange) { Evaluate(); } @@ -1395,13 +1438,13 @@ void RoutingManager::RxRaTracker::Evaluate(void) { DecisionFactors oldFactors = mDecisionFactors; TimeMilli now = TimerMilli::GetNow(); - NextFireTime routerTimeout(now); + NextFireTime routerTimeoutTime(now); NextFireTime entryExpireTime(now); NextFireTime staleTime(now); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Remove expired prefix entries in routers and then remove any - // router that has with no prefix entries or flags. + // router that has no prefix entries or flags. mRouters.RemoveAndFreeAllMatching(Router::EmptyChecker(now)); @@ -1413,18 +1456,24 @@ void RoutingManager::RxRaTracker::Evaluate(void) for (Router &router : mRouters) { + router.mAllEntriesDisregarded = true; + mDecisionFactors.UpdateFlagsFrom(router); for (OnLinkPrefix &entry : router.mOnLinkPrefixes) { mDecisionFactors.UpdateFrom(entry); entry.SetStaleTimeCalculated(false); + + router.mAllEntriesDisregarded &= entry.ShouldDisregard(); } for (RoutePrefix &entry : router.mRoutePrefixes) { mDecisionFactors.UpdateFrom(entry); entry.SetStaleTimeCalculated(false); + + router.mAllEntriesDisregarded &= entry.ShouldDisregard(); } } @@ -1446,11 +1495,12 @@ void RoutingManager::RxRaTracker::Evaluate(void) // their entries, `DetermineStaleTimeFor()` will consider all // matching entries and mark "StaleTimeCalculated" flag on them. - for (const Router &router : mRouters) + for (Router &router : mRouters) { if (router.ShouldCheckReachability()) { - routerTimeout.UpdateIfEarlier(router.mTimeout); + router.DetermineReachabilityTimeout(); + routerTimeoutTime.UpdateIfEarlier(router.mTimeoutTime); } for (const OnLinkPrefix &entry : router.mOnLinkPrefixes) @@ -1486,7 +1536,7 @@ void RoutingManager::RxRaTracker::Evaluate(void) staleTime.UpdateIfEarlier(CalculateExpirationTime(mLocalRaHeaderUpdateTime, interval)); } - mRouterTimer.FireAt(routerTimeout); + mRouterTimer.FireAt(routerTimeoutTime); mExpirationTimer.FireAt(entryExpireTime); mStaleTimer.FireAt(staleTime); } @@ -1590,7 +1640,7 @@ void RoutingManager::RxRaTracker::HandleRouterTimer(void) for (Router &router : mRouters) { - if (!router.ShouldCheckReachability() || (router.mTimeout > now)) + if (!router.ShouldCheckReachability() || (router.mTimeoutTime > now)) { continue; } @@ -1599,8 +1649,8 @@ void RoutingManager::RxRaTracker::HandleRouterTimer(void) if (router.IsReachable()) { - router.mTimeout = now + ((router.mNsProbeCount < Router::kMaxNsProbes) ? Router::kNsProbeRetryInterval - : Router::kNsProbeTimeout); + router.mTimeoutTime = now + ((router.mNsProbeCount < Router::kMaxNsProbes) ? Router::kNsProbeRetryInterval + : Router::kNsProbeTimeout); SendNeighborSolicitToRouter(router); } else @@ -1817,7 +1867,27 @@ void RoutingManager::RxRaTracker::Router::ResetReachabilityState(void) mNsProbeCount = 0; mLastUpdateTime = TimerMilli::GetNow(); - mTimeout = mLastUpdateTime + Random::NonCrypto::AddJitter(kReachableTimeout, kJitter); + mTimeoutTime = mLastUpdateTime + Random::NonCrypto::AddJitter(kReachableInterval, kJitter); +} + +void RoutingManager::RxRaTracker::Router::DetermineReachabilityTimeout(void) +{ + uint32_t interval; + + VerifyOrExit(ShouldCheckReachability()); + VerifyOrExit(mNsProbeCount == 0); + + // If all of the router's prefix entries are marked as + // disregarded (excluded from any decisions), it indicates that + // this router is likely a peer BR connected to the same Thread + // mesh. We use a longer reachability check interval for such + // peer BRs. + + interval = mAllEntriesDisregarded ? kPeerBrReachableInterval : kReachableInterval; + mTimeoutTime = mLastUpdateTime + Random::NonCrypto::AddJitter(interval, kJitter); + +exit: + return; } bool RoutingManager::RxRaTracker::Router::Matches(const EmptyChecker &aChecker) @@ -1886,6 +1956,8 @@ void RoutingManager::RxRaTracker::DecisionFactors::UpdateFlagsFrom(const Router void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const OnLinkPrefix &aOnLinkPrefix) { + VerifyOrExit(!aOnLinkPrefix.ShouldDisregard()); + if (aOnLinkPrefix.GetPrefix().IsUniqueLocal()) { mHasUlaOnLink = true; @@ -1903,10 +1975,15 @@ void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const OnLinkPrefix void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const RoutePrefix &aRoutePrefix) { + VerifyOrExit(!aRoutePrefix.ShouldDisregard()); + if (!mHasNonUlaRoute) { mHasNonUlaRoute = !aRoutePrefix.GetPrefix().IsUniqueLocal(); } + +exit: + return; } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 5c036a58f..17bcbd4b2 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -677,12 +677,16 @@ class RoutingManager : public InstanceLocator void SetStaleTimeCalculated(bool aFlag) { mStaleTimeCalculated = aFlag; } bool IsStaleTimeCalculated(void) const { return mStaleTimeCalculated; } + void SetDisregardFlag(bool aFlag) { mDisregard = aFlag; } + bool ShouldDisregard(void) const { return mDisregard; } + protected: LifetimedPrefix(void) = default; TimeMilli CalculateExpirationTime(uint32_t aLifetime) const; Ip6::Prefix mPrefix; + bool mDisregard : 1; bool mStaleTimeCalculated : 1; uint32_t mValidLifetime; TimeMilli mLastUpdateTime; @@ -808,9 +812,15 @@ class RoutingManager : public InstanceLocator struct Router : public Clearable { - // The timeout (in msec) for router to be considered reachable - // before starting the Neighbor Solicitation (NS) probes. - static constexpr uint32_t kReachableTimeout = OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT; + // Reachability timeout intervals before starting Neighbor + // Solicitation (NS) probes. Generally 60 seconds (± 2 + // seconds jitter) is used. For peer BRs a longer timeout + // of 200 seconds (3 min and 20 seconds) is used. This is + // selected to be longer than regular RA beacon interval + // of 3 minutes (± 15 seconds jitter). + + static constexpr uint32_t kReachableInterval = OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT; + static constexpr uint32_t kPeerBrReachableInterval = Time::kOneSecondInMsec * 200; static constexpr uint8_t kMaxNsProbes = 5; // Max number of NS probe attempts. static constexpr uint32_t kNsProbeRetryInterval = 1000; // In msec. Time between NS probe attempts. @@ -824,6 +834,7 @@ class RoutingManager : public InstanceLocator bool IsReachable(void) const { return mNsProbeCount <= kMaxNsProbes; } bool ShouldCheckReachability(void) const; void ResetReachabilityState(void); + void DetermineReachabilityTimeout(void); bool Matches(const Ip6::Address &aAddress) const { return aAddress == mAddress; } bool Matches(const EmptyChecker &aChecker); void CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow) const; @@ -835,12 +846,13 @@ class RoutingManager : public InstanceLocator OnLinkPrefixList mOnLinkPrefixes; RoutePrefixList mRoutePrefixes; TimeMilli mLastUpdateTime; - TimeMilli mTimeout; + TimeMilli mTimeoutTime; uint8_t mNsProbeCount; bool mManagedAddressConfigFlag : 1; bool mOtherConfigFlag : 1; bool mStubRouterFlag : 1; bool mIsLocalDevice : 1; + bool mAllEntriesDisregarded : 1; }; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/core/config/border_routing.h b/src/core/config/border_routing.h index 344cde4a5..7a91d6a39 100644 --- a/src/core/config/border_routing.h +++ b/src/core/config/border_routing.h @@ -71,6 +71,23 @@ #define OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE 1 #endif +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + * + * Define to 1 to allow the Routing Manager to track information (e.g., advertised prefixes) about peer Thread + * Border Routers that are connected to the same Thread network. + * + * When enabled, the Routing Manager will maintain a record of advertised RIO/PIO prefixes discovered from received + * Router Advertisements of peer BRs. These entries are disregarded in decision-making (e.g., selecting favored + * on-link prefix or determining which route to publish in the Thread Network Data). + * + * It is recommended to enable this feature alongside `OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE`. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE +#define OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE +#endif + /** * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS * From 5a9c768d4489c1a95ac59f994179f703cb184c7f Mon Sep 17 00:00:00 2001 From: Li Cao Date: Thu, 4 Jul 2024 02:23:00 +0800 Subject: [PATCH 017/160] [spinel] merge spinel config files (#10468) This commit removes `spinel-config.h` and moves the configs to `openthread-spinel-config.h` so that the definitions can also be overridden by definitions in customized lib config file. --- src/lib/spinel/logger.cpp | 3 +- src/lib/spinel/openthread-spinel-config.h | 31 +++++++++++ src/lib/spinel/spinel-config.h | 68 ----------------------- src/lib/spinel/spinel_interface.hpp | 2 +- 4 files changed, 34 insertions(+), 70 deletions(-) delete mode 100644 src/lib/spinel/spinel-config.h diff --git a/src/lib/spinel/logger.cpp b/src/lib/spinel/logger.cpp index 07b358f94..4ed74cf13 100644 --- a/src/lib/spinel/logger.cpp +++ b/src/lib/spinel/logger.cpp @@ -28,6 +28,8 @@ #include "logger.hpp" +#include "openthread-spinel-config.h" + #include #include #include @@ -36,7 +38,6 @@ #include #include -#include "lib/spinel/spinel-config.h" #include "lib/spinel/spinel.h" #include "lib/utils/math.hpp" #include "lib/utils/utils.hpp" diff --git a/src/lib/spinel/openthread-spinel-config.h b/src/lib/spinel/openthread-spinel-config.h index b49bfc96f..0669de27e 100644 --- a/src/lib/spinel/openthread-spinel-config.h +++ b/src/lib/spinel/openthread-spinel-config.h @@ -42,6 +42,37 @@ #include OPENTHREAD_PROJECT_LIB_CONFIG_FILE #endif +/** + * @def OPENTHREAD_LIB_SPINEL_RX_FRAME_BUFFER_SIZE + * + * Specifies the rx frame buffer size used by `SpinelInterface` in RCP host (posix) code. This is applicable/used when + * `RadioSpinel` platform is used. + * + */ +#ifndef OPENTHREAD_LIB_SPINEL_RX_FRAME_BUFFER_SIZE +#define OPENTHREAD_LIB_SPINEL_RX_FRAME_BUFFER_SIZE 8192 +#endif + +/** + * @def OPENTHREAD_LIB_SPINEL_LOG_MAX_SIZE + * + * The maximum log string size (number of chars). + * + */ +#ifndef OPENTHREAD_LIB_SPINEL_LOG_MAX_SIZE +#define OPENTHREAD_LIB_SPINEL_LOG_MAX_SIZE 1024 +#endif + +/** + * @def OPENTHREAD_LIB_SPINEL_NCP_LOG_MAX_SIZE + * + * The maximum OpenThread log string size (number of chars) supported by NCP using Spinel `StreamWrite`. + * + */ +#ifndef OPENTHREAD_LIB_SPINEL_NCP_LOG_MAX_SIZE +#define OPENTHREAD_LIB_SPINEL_NCP_LOG_MAX_SIZE 150 +#endif + /** * @def OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE * diff --git a/src/lib/spinel/spinel-config.h b/src/lib/spinel/spinel-config.h deleted file mode 100644 index 253b6e916..000000000 --- a/src/lib/spinel/spinel-config.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2024, 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 compile-time configuration constants for lib spinel. - */ - -#ifndef SPINEL_CONFIG_H_ -#define SPINEL_CONFIG_H_ - -/** - * @def OPENTHREAD_LIB_SPINEL_RX_FRAME_BUFFER_SIZE - * - * Specifies the rx frame buffer size used by `SpinelInterface` in RCP host (posix) code. This is applicable/used when - * `RadioSpinel` platform is used. - * - */ -#ifndef OPENTHREAD_LIB_SPINEL_RX_FRAME_BUFFER_SIZE -#define OPENTHREAD_LIB_SPINEL_RX_FRAME_BUFFER_SIZE 8192 -#endif - -/** - * @def OPENTHREAD_LIB_SPINEL_LOG_MAX_SIZE - * - * The maximum log string size (number of chars). - * - */ -#ifndef OPENTHREAD_LIB_SPINEL_LOG_MAX_SIZE -#define OPENTHREAD_LIB_SPINEL_LOG_MAX_SIZE 1024 -#endif - -/** - * @def OPENTHREAD_LIB_SPINEL_NCP_LOG_MAX_SIZE - * - * The maximum OpenThread log string size (number of chars) supported by NCP using Spinel `StreamWrite`. - * - */ -#ifndef OPENTHREAD_LIB_SPINEL_NCP_LOG_MAX_SIZE -#define OPENTHREAD_LIB_SPINEL_NCP_LOG_MAX_SIZE 150 -#endif - -#endif // SPINEL_CONFIG_H_ diff --git a/src/lib/spinel/spinel_interface.hpp b/src/lib/spinel/spinel_interface.hpp index c2dfae349..4a110fb0a 100644 --- a/src/lib/spinel/spinel_interface.hpp +++ b/src/lib/spinel/spinel_interface.hpp @@ -35,7 +35,7 @@ #ifndef SPINEL_SPINEL_INTERFACE_HPP_ #define SPINEL_SPINEL_INTERFACE_HPP_ -#include "spinel-config.h" +#include "openthread-spinel-config.h" #include "lib/spinel/multi_frame_buffer.hpp" #include "lib/spinel/radio_spinel_metrics.h" From be79db724d09d2584dfc24564eff9f22bd7fb92e Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Wed, 3 Jul 2024 12:57:09 -0700 Subject: [PATCH 018/160] [routing-manager] add missing `exit` label (#10472) --- src/core/border_router/routing_manager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 62c90b6d0..f0cb6a24c 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1971,6 +1971,9 @@ void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const OnLinkPrefix { mFavoredOnLinkPrefix = aOnLinkPrefix.GetPrefix(); } + +exit: + return; } void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const RoutePrefix &aRoutePrefix) From e7837eb104eaeadb719b7f996d0d53cbd9ee4ef6 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 3 Jul 2024 15:19:03 -0700 Subject: [PATCH 019/160] [mesh-forwarder] use `RemoveMessageIfNoPendingTx()` on direct tx fails (#10471) This commit updates `PrepareNextDirectTransmission()` to use `RemoveMessageIfNoPendingTx()` if a message cannot be prepared for direct transmission. This ensures the message is not dequeued and freed if it is also queued for indirect transmission. --- src/core/thread/mesh_forwarder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index f5b1afe56..2452c94f9 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -654,7 +654,7 @@ Message *MeshForwarder::PrepareNextDirectTransmission(void) #endif LogMessage(kMessageDrop, *curMessage, error); FinalizeMessageDirectTx(*curMessage, error); - mSendQueue.DequeueAndFree(*curMessage); + RemoveMessageIfNoPendingTx(*curMessage); continue; } } From c49cd72792703f2b4d00636a512498bbf4761cfa Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 3 Jul 2024 16:38:15 -0700 Subject: [PATCH 020/160] [mle] remove now undefined `UpdateChildAddresses()` (#10475) This method was refactored in 25fe46d8dd (#8963) but its declaration was not removed. --- src/core/thread/mle_router.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 3cf3f406b..ac39177cf 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -643,7 +643,6 @@ class MleRouter : public Mle void StopLeader(void); void SynchronizeChildNetworkData(void); Error ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild); - Error UpdateChildAddresses(const Message &aMessage, uint16_t aOffset, uint16_t aLength, Child &aChild); bool HasNeighborWithGoodLinkQuality(void) const; #if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE void SignalDuaAddressEvent(const Child &aChild, const Ip6::Address &aOldDua) const; From 000f5fcb30d7e2853a4d1bb0f7d21cf0e52c00aa Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 3 Jul 2024 16:39:04 -0700 Subject: [PATCH 021/160] [meshcop] update `ChannelMaskTlv` to use `OffsetRange` (#10465) This commit updates `ChannelMaskTlv` to use `OffsetRange` when iterating over and parsing the list of entries contained in the TLV. --- src/core/meshcop/meshcop_tlvs.cpp | 39 +++++++++++-------------------- src/core/meshcop/meshcop_tlvs.hpp | 3 +-- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/core/meshcop/meshcop_tlvs.cpp b/src/core/meshcop/meshcop_tlvs.cpp index 74d6a39d3..f4a2afe46 100644 --- a/src/core/meshcop/meshcop_tlvs.cpp +++ b/src/core/meshcop/meshcop_tlvs.cpp @@ -136,8 +136,8 @@ Error ChannelMaskTlv::ReadChannelMask(uint32_t &aChannelMask) const EntriesData entriesData; entriesData.Clear(); - entriesData.mData = &mEntriesStart; - entriesData.mLength = GetLength(); + entriesData.mData = &mEntriesStart; + entriesData.mOffsetRange.Init(0, GetLength()); return entriesData.Parse(aChannelMask); } @@ -152,9 +152,8 @@ Error ChannelMaskTlv::FindIn(const Message &aMessage, uint32_t &aChannelMask) entriesData.mMessage = &aMessage; SuccessOrExit(error = FindTlvValueOffsetRange(aMessage, Tlv::kChannelMask, offsetRange)); - entriesData.mOffset = offsetRange.GetOffset(); - entriesData.mLength = offsetRange.GetLength(); - error = entriesData.Parse(aChannelMask); + entriesData.mOffsetRange = offsetRange; + error = entriesData.Parse(aChannelMask); exit: return error; @@ -165,9 +164,8 @@ Error ChannelMaskTlv::EntriesData::Parse(uint32_t &aChannelMask) // Validates and parses the Channel Mask TLV entries for each // channel page and if successful updates `aChannelMask` to // return the combined mask for all channel pages supported by - // radio. The entries can be either contained in `mMessage` from - // `mOffset` (when `mMessage` is non-null) or be in a buffer - // `mData`. `mLength` gives the number of bytes for all entries. + // radio. The entries can be either contained in `mMessage` + // (when `mMessage` is non-null) or be in a buffer `mData`. Error error = kErrorParse; Entry readEntry; @@ -176,11 +174,11 @@ Error ChannelMaskTlv::EntriesData::Parse(uint32_t &aChannelMask) aChannelMask = 0; - VerifyOrExit(mLength > 0); // At least one entry. + VerifyOrExit(!mOffsetRange.IsEmpty()); // At least one entry. - while (mLength > 0) + while (!mOffsetRange.IsEmpty()) { - VerifyOrExit(mLength > kEntryHeaderSize); + VerifyOrExit(mOffsetRange.Contains(kEntryHeaderSize)); if (mMessage != nullptr) { @@ -188,17 +186,17 @@ Error ChannelMaskTlv::EntriesData::Parse(uint32_t &aChannelMask) // validating the entry and that the entry's channel page // is supported by radio, we read the full `Entry`. - mMessage->ReadBytes(mOffset, &readEntry, kEntryHeaderSize); + mMessage->ReadBytes(mOffsetRange.GetOffset(), &readEntry, kEntryHeaderSize); entry = &readEntry; } else { - entry = reinterpret_cast(mData); + entry = reinterpret_cast(&mData[mOffsetRange.GetOffset()]); } size = kEntryHeaderSize + entry->GetMaskLength(); - VerifyOrExit(size <= mLength); + VerifyOrExit(mOffsetRange.Contains(size)); if (Radio::SupportsChannelPage(entry->GetChannelPage())) { @@ -209,22 +207,13 @@ Error ChannelMaskTlv::EntriesData::Parse(uint32_t &aChannelMask) if (mMessage != nullptr) { - IgnoreError(mMessage->Read(mOffset, readEntry)); + IgnoreError(mMessage->Read(mOffsetRange, readEntry)); } aChannelMask |= (entry->GetMask() & Radio::ChannelMaskForPage(entry->GetChannelPage())); } - mLength -= size; - - if (mMessage != nullptr) - { - mOffset += size; - } - else - { - mData += size; - } + mOffsetRange.AdvanceOffset(size); } error = kErrorNone; diff --git a/src/core/meshcop/meshcop_tlvs.hpp b/src/core/meshcop/meshcop_tlvs.hpp index e35abf834..9d9b6f3a2 100644 --- a/src/core/meshcop/meshcop_tlvs.hpp +++ b/src/core/meshcop/meshcop_tlvs.hpp @@ -753,8 +753,7 @@ class ChannelMaskTlv : public Tlv, public TlvInfo const uint8_t *mData; const Message *mMessage; - uint16_t mOffset; - uint16_t mLength; + OffsetRange mOffsetRange; }; uint8_t mEntriesStart; From e72ef4354a44c6c65cea1747d7541564e5a51305 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 5 Jul 2024 09:49:03 -0700 Subject: [PATCH 022/160] [test] harden `test_advertising_proxy` (#10474) This commit makes two changes in `test_advertising_proxy`: - In the test step where the server is restarted, a longer wait time is used to account for the longer jitter interval used by SRP client in such a case. - Due to the use of short lease time (10 seconds) in this test, the client will refresh the registered services quickly. Therefore, in `check_host_and_service()`, any of `Registered`, `ToRefresh`, or `Refreshing` states are accepted as indicating successful registration. --- .../border_router/test_advertising_proxy.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/scripts/thread-cert/border_router/test_advertising_proxy.py b/tests/scripts/thread-cert/border_router/test_advertising_proxy.py index 2ae828b55..f12a8c555 100755 --- a/tests/scripts/thread-cert/border_router/test_advertising_proxy.py +++ b/tests/scripts/thread-cert/border_router/test_advertising_proxy.py @@ -174,7 +174,7 @@ def test(self): self.assertIsNone(host.discover_mdns_service('my-service-1', '_ipps._tcp', 'my-host')) server.srp_server_set_enabled(True) - self.simulator.go(LEASE) + self.simulator.go(20) self.check_host_and_service(server, client, '2001::2', 'my-service') self.check_host_and_service(server, client, '2001::2', 'my-service-1') @@ -188,11 +188,7 @@ def test(self): client.srp_client_remove_service('my-service-1', '_ipps._tcp') - # We previously had self.simulator.go(5) but got the issue that the client is scheduling - # the refresh timer with half of the lease time (10 seconds) and there will be chances - # that the client will be just in status "kToRefresh" after self.simulator.go(5). This will - # fail the checks in self.check_host_and_service() so updated to wait for 2 seconds. - self.simulator.go(2) + self.simulator.go(5) self.check_host_and_service(server, client, '2001::2', 'my-service') self.host_check_mdns_service(host, '2001::2', 'my-service') @@ -297,8 +293,14 @@ def check_host_and_service(self, server, client, host_addrs, service_instance, s self.assertEqual(int(client_service['priority']), 0) self.assertEqual(int(client_service['weight']), 0) - # Verify that the client received a SUCCESS response for the server. - self.assertEqual(client_service['state'], 'Registered') + # Verify the client successfully registered its service with + # the server. Due to the short lease times (10 seconds) used + # in this test, the client will refresh the registered + # service quickly. During the test, we accept any of the + # following states as indicating successful registration: + # `Registered`, `ToRefresh`, or `Refreshing`. + + self.assertIn(client_service['state'], ['Registered', 'ToRefresh', 'Refreshing']) server_services = server.srp_server_get_services() print(server_services) From fbdb4b6c8f28229cf6b513d2f49bcadaf2625684 Mon Sep 17 00:00:00 2001 From: Rongli Sun Date: Sat, 6 Jul 2024 00:51:33 +0800 Subject: [PATCH 023/160] [test] supplement state bitmap test (#10318) --- .../test_publish_meshcop_service.py | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py index 67567946e..26b9b1408 100755 --- a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py +++ b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py @@ -167,18 +167,39 @@ def check_meshcop_service_by_data(self, br, service_data): state_bitmap = int.from_bytes(sb_data, byteorder='big') logging.info(bin(state_bitmap)) self.assertEqual((state_bitmap & 7), 1) # connection mode = PskC - if br.get_state() == 'disabled': - self.assertEqual((state_bitmap >> 3 & 3), 0) # Thread is disabled - elif br.get_state() == 'detached': - self.assertEqual((state_bitmap >> 3 & 3), 1) # Thread is detached - else: - self.assertEqual((state_bitmap >> 3 & 3), 2) # Thread is attached + sb_thread_interface_status = state_bitmap >> 3 & 3 + sb_thread_role = state_bitmap >> 9 & 3 + device_role = br.get_state() + + if device_role == 'disabled': + # Thread interface is not active and is not initialized with a set of valid operation network parameters + self.assertEqual(sb_thread_interface_status, 0) + self.assertEqual(sb_thread_role, 0) # Thread role is disabled + + elif device_role == 'detached': + # Thread interface is initialized with a set of valid operation network parameters, but is not actively participating in a network + self.assertEqual(sb_thread_interface_status, 1) + self.assertEqual(sb_thread_role, 0) # Thread role is detached + + elif device_role == 'child': + # Thread interface is initialized with a set of valid operation network parameters, and is actively part of a Network + self.assertEqual(sb_thread_interface_status, 2) + self.assertEqual(sb_thread_role, 1) # Thread role is child + + elif device_role == 'router': + # Thread interface is initialized with a set of valid operation network parameters, and is actively part of a Network + self.assertEqual(sb_thread_interface_status, 2) + self.assertEqual(sb_thread_role, 2) # Thread role is router + + elif device_role == 'leader': + # Thread interface is initialized with a set of valid operation network parameters, and is actively part of a Network + self.assertEqual(sb_thread_interface_status, 2) + self.assertEqual(sb_thread_role, 3) # Thread role is leader + self.assertEqual((state_bitmap >> 5 & 3), 1) # high availability - self.assertEqual((state_bitmap >> 7 & 1), - br.get_state() not in ['disabled', 'detached'] and + self.assertEqual((state_bitmap >> 7 & 1), device_role not in ['disabled', 'detached'] and br.get_backbone_router_state() != 'Disabled') # BBR is enabled or not - self.assertEqual((state_bitmap >> 8 & 1), - br.get_state() not in ['disabled', 'detached'] and + self.assertEqual((state_bitmap >> 8 & 1), device_role not in ['disabled', 'detached'] and br.get_backbone_router_state() == 'Primary') # BBR is primary or not self.assertEqual(service_data['txt']['nn'], br.get_network_name()) self.assertEqual(service_data['txt']['rv'], '1') From 686aab7589aa60c2dea21ef4bebb9820f0940767 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Sat, 6 Jul 2024 00:54:20 +0800 Subject: [PATCH 024/160] [csl] move csl related functions to sub_mac_csl.cpp (#10477) This commit moves csl receiver related functions to sub_mac_csl_receiver.cpp to simply the sub_mac.cpp to improve the code readability. This commit is a pure refactoring and doesn't contain any logic or code changes. --- src/core/BUILD.gn | 1 + src/core/CMakeLists.txt | 2 + src/core/mac/sub_mac.cpp | 259 +-------------------- src/core/mac/sub_mac.hpp | 3 + src/core/mac/sub_mac_csl_receiver.cpp | 315 ++++++++++++++++++++++++++ 5 files changed, 324 insertions(+), 256 deletions(-) create mode 100644 src/core/mac/sub_mac_csl_receiver.cpp diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 622d8df2c..90cc73ecb 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -506,6 +506,7 @@ openthread_core_files = [ "mac/sub_mac.cpp", "mac/sub_mac.hpp", "mac/sub_mac_callbacks.cpp", + "mac/sub_mac_csl_receiver.cpp", "meshcop/announce_begin_client.cpp", "meshcop/announce_begin_client.hpp", "meshcop/border_agent.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e9c496746..8e712c8fd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -144,6 +144,7 @@ set(COMMON_SOURCES mac/mac_types.cpp mac/sub_mac.cpp mac/sub_mac_callbacks.cpp + mac/sub_mac_csl_receiver.cpp meshcop/announce_begin_client.cpp meshcop/border_agent.cpp meshcop/commissioner.cpp @@ -291,6 +292,7 @@ set(RADIO_COMMON_SOURCES mac/mac_types.cpp mac/sub_mac.cpp mac/sub_mac_callbacks.cpp + mac/sub_mac_csl_receiver.cpp radio/radio.cpp radio/radio_callbacks.cpp radio/radio_platform.cpp diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index edeba2e8b..036fddd60 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -96,13 +96,7 @@ void SubMac::Init(void) mTimer.Stop(); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - mCslPeriod = 0; - mCslChannel = 0; - mCslPeerShort = 0; - mIsCslSampling = false; - mCslSampleTime = TimeMicro{0}; - mCslLastSync = TimeMicro{0}; - mCslTimer.Stop(); + CslInit(); #endif } @@ -275,85 +269,6 @@ Error SubMac::Receive(uint8_t aChannel) return error; } -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -void SubMac::CslSample(void) -{ -#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE - VerifyOrExit(!mRadioFilterEnabled, IgnoreError(Get().Sleep())); -#endif - - SetState(kStateCslSample); - - if (mIsCslSampling && !RadioSupportsReceiveTiming()) - { - IgnoreError(Get().Receive(mCslChannel)); - ExitNow(); - } - -#if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE - IgnoreError(Get().Sleep()); // Don't actually sleep for debugging -#endif - -exit: - return; -} -#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - -#if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE -void SubMac::LogReceived(RxFrame *aFrame) -{ - static constexpr uint8_t kLogStringSize = 72; - - String logString; - Address dst; - int32_t deviation; - uint32_t sampleTime, ahead, after; - - IgnoreError(aFrame->GetDstAddr(dst)); - - VerifyOrExit((dst.GetType() == Address::kTypeShort && dst.GetShort() == GetShortAddress()) || - (dst.GetType() == Address::kTypeExtended && dst.GetExtended() == GetExtAddress())); - - LogDebg("Received frame in state (SubMac %s, CSL %s), timestamp %lu", StateToString(mState), - mIsCslSampling ? "CslSample" : "CslSleep", - ToUlong(static_cast(aFrame->mInfo.mRxInfo.mTimestamp))); - - VerifyOrExit(mState == kStateCslSample); - - GetCslWindowEdges(ahead, after); - ahead -= kMinReceiveOnAhead + kCslReceiveTimeAhead; - - sampleTime = mCslSampleTime.GetValue() - mCslPeriod * kUsPerTenSymbols; - deviation = aFrame->mInfo.mRxInfo.mTimestamp + kRadioHeaderPhrDuration - sampleTime; - - // This logs three values (all in microseconds): - // - Absolute sample time in which the CSL receiver expected the MHR of the received frame. - // - Allowed margin around that time accounting for accuracy and uncertainty from both devices. - // - Real deviation on the reception of the MHR with regards to expected sample time. This can - // be due to clocks drift and/or CSL Phase rounding error. - // This means that a deviation absolute value greater than the margin would result in the frame - // not being received out of the debug mode. - logString.Append("Expected sample time %lu, margin ±%lu, deviation %ld", ToUlong(sampleTime), ToUlong(ahead), - static_cast(deviation)); - - // Treat as a warning when the deviation is not within the margins. Neither kCslReceiveTimeAhead - // or kMinReceiveOnAhead/kMinReceiveOnAfter are considered for the margin since they have no - // impact on understanding possible deviation errors between transmitter and receiver. So in this - // case only `ahead` is used, as an allowable max deviation in both +/- directions. - if ((deviation + ahead > 0) && (deviation < static_cast(ahead))) - { - LogDebg("%s", logString.AsCString()); - } - else - { - LogWarn("%s", logString.AsCString()); - } - -exit: - return; -} -#endif - void SubMac::HandleReceiveDone(RxFrame *aFrame, Error aError) { if (mPcapCallback.IsSet() && (aFrame != nullptr) && (aError == kErrorNone)) @@ -367,22 +282,8 @@ void SubMac::HandleReceiveDone(RxFrame *aFrame, Error aError) } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - if (aFrame != nullptr && aError == kErrorNone) - { -#if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE - LogReceived(aFrame); -#endif - // Assuming the risk of the parent missing the Enh-ACK in favor of smaller CSL receive window - if ((mCslPeriod > 0) && aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck) - { -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC - mCslLastSync = TimerMicro::GetNow(); -#else - mCslLastSync = TimeMicro(static_cast(aFrame->mInfo.mRxInfo.mTimestamp)); + UpdateCslLastSyncTimestamp(aFrame, aError); #endif - } - } -#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE if (!mRadioFilterEnabled) @@ -638,16 +539,7 @@ void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aErro mCallbacks.RecordCcaStatus(ccaSuccess, aFrame.GetChannel()); } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - // Actual synchronization timestamp should be from the sent frame instead of the current time. - // Assuming the error here since it is bounded and has very small effect on the final window duration. - if (aAckFrame != nullptr && aFrame.GetHeaderIe(CslIe::kHeaderIeId) != nullptr) - { -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC - mCslLastSync = TimerMicro::GetNow(); -#else - mCslLastSync = TimeMicro(static_cast(otPlatRadioGetNow(&GetInstance()))); -#endif - } + UpdateCslLastSyncTimestamp(aFrame, aAckFrame); #endif break; @@ -1140,150 +1032,5 @@ const char *SubMac::StateToString(State aState) // LCOV_EXCL_STOP -//--------------------------------------------------------------------------------------------------------------------- -// CSL Receiver methods - -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -bool SubMac::UpdateCsl(uint16_t aPeriod, uint8_t aChannel, otShortAddress aShortAddr, const otExtAddress *aExtAddr) -{ - bool diffPeriod = aPeriod != mCslPeriod; - bool diffChannel = aChannel != mCslChannel; - bool diffPeer = aShortAddr != mCslPeerShort; - bool retval = diffPeriod || diffChannel || diffPeer; - - VerifyOrExit(retval); - mCslChannel = aChannel; - - VerifyOrExit(diffPeriod || diffPeer); - mCslPeriod = aPeriod; - mCslPeerShort = aShortAddr; - IgnoreError(Get().EnableCsl(aPeriod, aShortAddr, aExtAddr)); - - mCslTimer.Stop(); - if (mCslPeriod > 0) - { - mCslSampleTime = TimeMicro(static_cast(otPlatRadioGetNow(&GetInstance()))); - mIsCslSampling = false; - HandleCslTimer(); - } - -exit: - return retval; -} - -void SubMac::HandleCslTimer(Timer &aTimer) { aTimer.Get().HandleCslTimer(); } - -void SubMac::HandleCslTimer(void) -{ - /* - * CSL sample timing diagram - * |<---------------------------------Sample--------------------------------->|<--------Sleep--------->| - * | | | - * |<--Ahead-->|<--UnCert-->|<--Drift-->|<--Drift-->|<--UnCert-->|<--MinWin-->| | - * | | | | | | | | - * ---|-----------|------------|-----------|-----------|------------|------------|----------//------------|--- - * -timeAhead CslPhase +timeAfter -timeAhead - * - * The handler works in different ways when the radio supports receive-timing and doesn't. - * - * When the radio supports receive-timing: - * The handler will be called once per CSL period. When the handler is called, it will set the timer to - * fire at the next CSL sample time and call `Radio::ReceiveAt` to start sampling for the current CSL period. - * The timer fires some time before the actual sample time. After `Radio::ReceiveAt` is called, the radio will - * remain in sleep state until the actual sample time. - * Note that it never call `Radio::Sleep` explicitly. The radio will fall into sleep after `ReceiveAt` ends. This - * will be done by the platform as part of the `otPlatRadioReceiveAt` API. - * - * Timer fires Timer fires - * ^ ^ - * x-|------------|-------------------------------------x-|------------|---------------------------------------| - * sample sleep sample sleep - * - * When the radio doesn't support receive-timing: - * The handler will be called twice per CSL period: at the beginning of sample and sleep. When the handler is - * called, it will explicitly change the radio state due to the current state by calling `Radio::Receive` or - * `Radio::Sleep`. - * - * Timer fires Timer fires Timer fires Timer fires - * ^ ^ ^ ^ - * |------------|---------------------------------------|------------|---------------------------------------| - * sample sleep sample sleep - * - */ - uint32_t periodUs = mCslPeriod * kUsPerTenSymbols; - uint32_t timeAhead, timeAfter, winStart, winDuration; - - GetCslWindowEdges(timeAhead, timeAfter); - - if (mIsCslSampling) - { - mIsCslSampling = false; - mCslTimer.FireAt(mCslSampleTime - timeAhead); - if (mState == kStateCslSample) - { -#if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE - IgnoreError(Get().Sleep()); // Don't actually sleep for debugging -#endif - LogDebg("CSL sleep %lu", ToUlong(mCslTimer.GetNow().GetValue())); - } - } - else - { - if (RadioSupportsReceiveTiming()) - { - mCslTimer.FireAt(mCslSampleTime - timeAhead + periodUs); - timeAhead -= kCslReceiveTimeAhead; - winStart = mCslSampleTime.GetValue() - timeAhead; - } - else - { - mCslTimer.FireAt(mCslSampleTime + timeAfter); - mIsCslSampling = true; - winStart = ot::TimerMicro::GetNow().GetValue(); - } - - winDuration = timeAhead + timeAfter; - mCslSampleTime += periodUs; - - Get().UpdateCslSampleTime(mCslSampleTime.GetValue()); - - // Schedule reception window for any state except RX - so that CSL RX Window has lower priority - // than scanning or RX after the data poll. - if (RadioSupportsReceiveTiming() && (mState != kStateDisabled) && (mState != kStateReceive)) - { - IgnoreError(Get().ReceiveAt(mCslChannel, winStart, winDuration)); - } - else if (mState == kStateCslSample) - { - IgnoreError(Get().Receive(mCslChannel)); - } - - LogDebg("CSL window start %lu, duration %lu", ToUlong(winStart), ToUlong(winDuration)); - } -} - -void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter) -{ - uint32_t semiPeriod = mCslPeriod * kUsPerTenSymbols / 2; - uint32_t curTime, elapsed, semiWindow; - -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC - curTime = TimerMicro::GetNow().GetValue(); -#else - curTime = static_cast(otPlatRadioGetNow(&GetInstance())); -#endif - - elapsed = curTime - mCslLastSync.GetValue(); - - semiWindow = - static_cast(static_cast(elapsed) * - (Get().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) / 1000000); - semiWindow += mCslParentAccuracy.GetUncertaintyInMicrosec() + Get().GetCslUncertainty() * 10; - - aAhead = Min(semiPeriod, semiWindow + kMinReceiveOnAhead + kCslReceiveTimeAhead); - aAfter = Min(semiPeriod, semiWindow + kMinReceiveOnAfter); -} -#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - } // namespace Mac } // namespace ot diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 211babd0b..5bd43c25b 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -526,6 +526,9 @@ class SubMac : public InstanceLocator, private NonCopyable private: #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + void CslInit(void); + void UpdateCslLastSyncTimestamp(TxFrame &aFrame, RxFrame *aAckFrame); + void UpdateCslLastSyncTimestamp(RxFrame *aFrame, Error aError); static void HandleCslTimer(Timer &aTimer); void HandleCslTimer(void); void GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter); diff --git a/src/core/mac/sub_mac_csl_receiver.cpp b/src/core/mac/sub_mac_csl_receiver.cpp new file mode 100644 index 000000000..74f15e2b3 --- /dev/null +++ b/src/core/mac/sub_mac_csl_receiver.cpp @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2024, 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 the CSL receiver of the subset of IEEE 802.15.4 MAC primitives. + */ + +#include "sub_mac.hpp" + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + +#include "common/code_utils.hpp" +#include "common/locator_getters.hpp" +#include "common/log.hpp" +#include "instance/instance.hpp" + +namespace ot { +namespace Mac { + +RegisterLogModule("SubMac"); + +void SubMac::CslInit(void) +{ + mCslPeriod = 0; + mCslChannel = 0; + mCslPeerShort = 0; + mIsCslSampling = false; + mCslSampleTime = TimeMicro{0}; + mCslLastSync = TimeMicro{0}; + mCslTimer.Stop(); +} + +void SubMac::UpdateCslLastSyncTimestamp(TxFrame &aFrame, RxFrame *aAckFrame) +{ + // Actual synchronization timestamp should be from the sent frame instead of the current time. + // Assuming the error here since it is bounded and has very small effect on the final window duration. + if (aAckFrame != nullptr && aFrame.GetHeaderIe(CslIe::kHeaderIeId) != nullptr) + { +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC + mCslLastSync = TimerMicro::GetNow(); +#else + mCslLastSync = TimeMicro(static_cast(otPlatRadioGetNow(&GetInstance()))); +#endif + } +} + +void SubMac::UpdateCslLastSyncTimestamp(RxFrame *aFrame, Error aError) +{ + VerifyOrExit(aFrame != nullptr && aError == kErrorNone); + +#if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE + LogReceived(aFrame); +#endif + + // Assuming the risk of the parent missing the Enh-ACK in favor of smaller CSL receive window + if ((mCslPeriod > 0) && aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck) + { +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC + mCslLastSync = TimerMicro::GetNow(); +#else + mCslLastSync = TimeMicro(static_cast(aFrame->mInfo.mRxInfo.mTimestamp)); +#endif + } + +exit: + return; +} + +void SubMac::CslSample(void) +{ +#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE + VerifyOrExit(!mRadioFilterEnabled, IgnoreError(Get().Sleep())); +#endif + + SetState(kStateCslSample); + + if (mIsCslSampling && !RadioSupportsReceiveTiming()) + { + IgnoreError(Get().Receive(mCslChannel)); + ExitNow(); + } + +#if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE + IgnoreError(Get().Sleep()); // Don't actually sleep for debugging +#endif + +exit: + return; +} + +bool SubMac::UpdateCsl(uint16_t aPeriod, uint8_t aChannel, otShortAddress aShortAddr, const otExtAddress *aExtAddr) +{ + bool diffPeriod = aPeriod != mCslPeriod; + bool diffChannel = aChannel != mCslChannel; + bool diffPeer = aShortAddr != mCslPeerShort; + bool retval = diffPeriod || diffChannel || diffPeer; + + VerifyOrExit(retval); + mCslChannel = aChannel; + + VerifyOrExit(diffPeriod || diffPeer); + mCslPeriod = aPeriod; + mCslPeerShort = aShortAddr; + IgnoreError(Get().EnableCsl(aPeriod, aShortAddr, aExtAddr)); + + mCslTimer.Stop(); + if (mCslPeriod > 0) + { + mCslSampleTime = TimeMicro(static_cast(otPlatRadioGetNow(&GetInstance()))); + mIsCslSampling = false; + HandleCslTimer(); + } + +exit: + return retval; +} + +void SubMac::HandleCslTimer(Timer &aTimer) { aTimer.Get().HandleCslTimer(); } + +void SubMac::HandleCslTimer(void) +{ + /* + * CSL sample timing diagram + * |<---------------------------------Sample--------------------------------->|<--------Sleep--------->| + * | | | + * |<--Ahead-->|<--UnCert-->|<--Drift-->|<--Drift-->|<--UnCert-->|<--MinWin-->| | + * | | | | | | | | + * ---|-----------|------------|-----------|-----------|------------|------------|----------//------------|--- + * -timeAhead CslPhase +timeAfter -timeAhead + * + * The handler works in different ways when the radio supports receive-timing and doesn't. + * + * When the radio supports receive-timing: + * The handler will be called once per CSL period. When the handler is called, it will set the timer to + * fire at the next CSL sample time and call `Radio::ReceiveAt` to start sampling for the current CSL period. + * The timer fires some time before the actual sample time. After `Radio::ReceiveAt` is called, the radio will + * remain in sleep state until the actual sample time. + * Note that it never call `Radio::Sleep` explicitly. The radio will fall into sleep after `ReceiveAt` ends. This + * will be done by the platform as part of the `otPlatRadioReceiveAt` API. + * + * Timer fires Timer fires + * ^ ^ + * x-|------------|-------------------------------------x-|------------|---------------------------------------| + * sample sleep sample sleep + * + * When the radio doesn't support receive-timing: + * The handler will be called twice per CSL period: at the beginning of sample and sleep. When the handler is + * called, it will explicitly change the radio state due to the current state by calling `Radio::Receive` or + * `Radio::Sleep`. + * + * Timer fires Timer fires Timer fires Timer fires + * ^ ^ ^ ^ + * |------------|---------------------------------------|------------|---------------------------------------| + * sample sleep sample sleep + * + */ + uint32_t periodUs = mCslPeriod * kUsPerTenSymbols; + uint32_t timeAhead, timeAfter, winStart, winDuration; + + GetCslWindowEdges(timeAhead, timeAfter); + + if (mIsCslSampling) + { + mIsCslSampling = false; + mCslTimer.FireAt(mCslSampleTime - timeAhead); + if (mState == kStateCslSample) + { +#if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE + IgnoreError(Get().Sleep()); // Don't actually sleep for debugging +#endif + LogDebg("CSL sleep %lu", ToUlong(mCslTimer.GetNow().GetValue())); + } + } + else + { + if (RadioSupportsReceiveTiming()) + { + mCslTimer.FireAt(mCslSampleTime - timeAhead + periodUs); + timeAhead -= kCslReceiveTimeAhead; + winStart = mCslSampleTime.GetValue() - timeAhead; + } + else + { + mCslTimer.FireAt(mCslSampleTime + timeAfter); + mIsCslSampling = true; + winStart = ot::TimerMicro::GetNow().GetValue(); + } + + winDuration = timeAhead + timeAfter; + mCslSampleTime += periodUs; + + Get().UpdateCslSampleTime(mCslSampleTime.GetValue()); + + // Schedule reception window for any state except RX - so that CSL RX Window has lower priority + // than scanning or RX after the data poll. + if (RadioSupportsReceiveTiming() && (mState != kStateDisabled) && (mState != kStateReceive)) + { + IgnoreError(Get().ReceiveAt(mCslChannel, winStart, winDuration)); + } + else if (mState == kStateCslSample) + { + IgnoreError(Get().Receive(mCslChannel)); + } + + LogDebg("CSL window start %lu, duration %lu", ToUlong(winStart), ToUlong(winDuration)); + } +} + +void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter) +{ + uint32_t semiPeriod = mCslPeriod * kUsPerTenSymbols / 2; + uint32_t curTime, elapsed, semiWindow; + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC + curTime = TimerMicro::GetNow().GetValue(); +#else + curTime = static_cast(otPlatRadioGetNow(&GetInstance())); +#endif + + elapsed = curTime - mCslLastSync.GetValue(); + + semiWindow = + static_cast(static_cast(elapsed) * + (Get().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) / 1000000); + semiWindow += mCslParentAccuracy.GetUncertaintyInMicrosec() + Get().GetCslUncertainty() * 10; + + aAhead = Min(semiPeriod, semiWindow + kMinReceiveOnAhead + kCslReceiveTimeAhead); + aAfter = Min(semiPeriod, semiWindow + kMinReceiveOnAfter); +} + +#if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE +void SubMac::LogReceived(RxFrame *aFrame) +{ + static constexpr uint8_t kLogStringSize = 72; + + String logString; + Address dst; + int32_t deviation; + uint32_t sampleTime, ahead, after; + + IgnoreError(aFrame->GetDstAddr(dst)); + + VerifyOrExit((dst.GetType() == Address::kTypeShort && dst.GetShort() == GetShortAddress()) || + (dst.GetType() == Address::kTypeExtended && dst.GetExtended() == GetExtAddress())); + + LogDebg("Received frame in state (SubMac %s, CSL %s), timestamp %lu", StateToString(mState), + mIsCslSampling ? "CslSample" : "CslSleep", + ToUlong(static_cast(aFrame->mInfo.mRxInfo.mTimestamp))); + + VerifyOrExit(mState == kStateCslSample); + + GetCslWindowEdges(ahead, after); + ahead -= kMinReceiveOnAhead + kCslReceiveTimeAhead; + + sampleTime = mCslSampleTime.GetValue() - mCslPeriod * kUsPerTenSymbols; + deviation = aFrame->mInfo.mRxInfo.mTimestamp + kRadioHeaderPhrDuration - sampleTime; + + // This logs three values (all in microseconds): + // - Absolute sample time in which the CSL receiver expected the MHR of the received frame. + // - Allowed margin around that time accounting for accuracy and uncertainty from both devices. + // - Real deviation on the reception of the MHR with regards to expected sample time. This can + // be due to clocks drift and/or CSL Phase rounding error. + // This means that a deviation absolute value greater than the margin would result in the frame + // not being received out of the debug mode. + logString.Append("Expected sample time %lu, margin ±%lu, deviation %ld", ToUlong(sampleTime), ToUlong(ahead), + static_cast(deviation)); + + // Treat as a warning when the deviation is not within the margins. Neither kCslReceiveTimeAhead + // or kMinReceiveOnAhead/kMinReceiveOnAfter are considered for the margin since they have no + // impact on understanding possible deviation errors between transmitter and receiver. So in this + // case only `ahead` is used, as an allowable max deviation in both +/- directions. + if ((deviation + ahead > 0) && (deviation < static_cast(ahead))) + { + LogDebg("%s", logString.AsCString()); + } + else + { + LogWarn("%s", logString.AsCString()); + } + +exit: + return; +} +#endif + +} // namespace Mac +} // namespace ot + +#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE From 71dd8a2d3a543aab40935ff29f3c16aa3df55212 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Sat, 6 Jul 2024 00:56:41 +0800 Subject: [PATCH 025/160] [test] add script to test which diag commands RCP supports (#10463) This commit implements a test script tools/cp-caps/rcp_caps_test.py to test which diag commands RCP supports using the DUT and reference device. --- tools/cp-caps/README.md | 125 ++++++++++ tools/cp-caps/rcp_caps_test.py | 414 +++++++++++++++++++++++++++++++++ tools/cp-caps/requirements.txt | 7 + tools/otci/otci/otci.py | 16 +- 4 files changed, 558 insertions(+), 4 deletions(-) create mode 100644 tools/cp-caps/README.md create mode 100644 tools/cp-caps/rcp_caps_test.py create mode 100644 tools/cp-caps/requirements.txt diff --git a/tools/cp-caps/README.md b/tools/cp-caps/README.md new file mode 100644 index 000000000..6eab16592 --- /dev/null +++ b/tools/cp-caps/README.md @@ -0,0 +1,125 @@ +# RCP Capabilities Test + +This test is used for testing RCP capabilities. + +## Test Topology + +``` + +-------+ + +---------------| PC |----------------+ + | +-------+ | + | ADB/SSH | ADB/SSH/SERIAL + | | ++-------+ +------------------+ +| DUT |<-----------Thread-------->| Reference Device | ++-------+ +------------------+ + +``` + +- PC : The computer to run the test script. +- DUT : The device under test. +- Reference Device : The device that supports all tested features. + +### Python Dependences + +Before running the test script on PC, testers should install dependences first. + +```bash +$ pip3 install -r ./tools/cp-caps/requirements.txt ./tools/otci +``` + +### Reference Device + +The [nRF52840DK][ot-nrf528xx-nrf52840] is set as the reference device by default. Testers can also select the other Thread device as the reference device. + +[ot-nrf528xx-nrf52840]: https://github.com/openthread/ot-nrf528xx/blob/main/src/nrf52840/README.md + +Quick guide to setting up the nRF52840DK: + +```bash +$ git clone git@github.com:openthread/ot-nrf528xx.git +$ cd ot-nrf528xx/ +$ git submodule update --init +$ ./script/bootstrap +$ ./script/build nrf52840 UART_trans -DOT_DIAGNOSTIC=ON +$ arm-none-eabi-objcopy -O ihex build/bin/ot-cli-ftd ot-cli-ftd.hex +$ nrfjprog -f nrf52 --chiperase --program ot-cli-ftd.hex --reset +``` + +## Test Commands + +### Help + +Show help info. + +```bash +$ python3 ./tools/cp-caps/rcp_caps_test.py -h +usage: rcp_caps_test.py [-h] [-d] [-v] + +This script is used for testing RCP capabilities. + +options: + -h, --help show this help message and exit + -d, --diag-commands test whether the RCP supports all diag commands + -v, --verbose output verbose information + +Device Interfaces: + DUT_SSH= Connect to the DUT via ssh + DUT_ADB_TCP= Connect to the DUT via adb tcp + DUT_ADB_USB= Connect to the DUT via adb usb + REF_CLI_SERIAL= Connect to the reference device via cli serial port + REF_ADB_USB= Connect to the reference device via adb usb + REF_SSH= Connect to the reference device via ssh + +Example: + DUT_ADB_USB=1169UC2F2T0M95OR REF_CLI_SERIAL=/dev/ttyACM0 python3 ./tools/cp-caps/rcp_caps_test.py -d +``` + +### Test Diag Commands + +The parameter `-d` or `--diag-commands` starts to test all diag commands. + +Following environment variables are used to configure diag command parameters: + +- DUT_DIAG_GPIO: Diag gpio value. The default value is `0` if it is not set. +- DUT_DIAG_RAW_POWER_SETTING: Diag raw power setting value. The default value is `112233` if it is not set. +- DUT_DIAG_POWER: Diag power value. The default value is `10` if it is not set. + +> Note: If you meet the error `LIBUSB_ERROR_BUSY` when you are using the ADB usb interface, please run the command `adb kill-server` to kill the adb server. + +```bash +$ DUT_ADB_USB=1269UCKFZTAM95OR REF_CLI_SERIAL=/dev/ttyACM0 DUT_DIAG_GPIO=2 DUT_DIAG_RAW_POWER_SETTING=44556688 DUT_DIAG_POWER=11 python3 ./tools/cp-caps/rcp_caps_test.py -d +diag channel --------------------------------------------- OK +diag channel 20 ------------------------------------------ OK +diag power ----------------------------------------------- OK +diag power 11 -------------------------------------------- OK +diag radio sleep ----------------------------------------- OK +diag radio receive --------------------------------------- OK +diag radio state ----------------------------------------- OK +diag repeat 10 64 ---------------------------------------- OK +diag repeat stop ----------------------------------------- OK +diag send 100 64 ----------------------------------------- OK +diag stats ----------------------------------------------- OK +diag stats clear ----------------------------------------- OK +diag frame 00010203040506070809 -------------------------- OK +diag echo 0123456789 ------------------------------------- OK +diag echo -n 10 ------------------------------------------ OK +diag cw start -------------------------------------------- OK +diag cw stop --------------------------------------------- OK +diag stream start ---------------------------------------- OK +diag stream stop ----------------------------------------- OK +diag stats ----------------------------------------------- OK +diag stats clear ----------------------------------------- OK +diag rawpowersetting enable ------------------------------ NotSupported +diag rawpowersetting 44556688 ---------------------------- NotSupported +diag rawpowersetting ------------------------------------- NotSupported +diag rawpowersetting disable ----------------------------- NotSupported +diag powersettings --------------------------------------- NotSupported +diag powersettings 20 ------------------------------------ NotSupported +diag gpio mode 2 ----------------------------------------- NotSupported +diag gpio mode 2 in -------------------------------------- NotSupported +diag gpio mode 2 out ------------------------------------- NotSupported +diag gpio get 2 ------------------------------------------ NotSupported +diag gpio set 2 0 ---------------------------------------- NotSupported +diag gpio set 2 1 ---------------------------------------- NotSupported +``` diff --git a/tools/cp-caps/rcp_caps_test.py b/tools/cp-caps/rcp_caps_test.py new file mode 100644 index 000000000..755fb58dd --- /dev/null +++ b/tools/cp-caps/rcp_caps_test.py @@ -0,0 +1,414 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024, 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. +# + +import argparse +import logging +import os +import sys +import textwrap + +from typing import List + +import otci +from otci import OTCI + +logging.basicConfig(level=logging.WARNING) + + +class RcpCaps(object): + """ + This class represents an OpenThread RCP capability test instance. + """ + + def __init__(self): + self.__dut = self.__connect_dut() + self.__ref = self.__connect_reference_device() + + def test_diag_commands(self): + """Test all diag commands.""" + self.__dut.factory_reset() + self.__ref.factory_reset() + + ret = self.__dut.is_command_supported('diag start') + if ret is False: + print('All diag commands are not supported') + return + + self.__dut.diag_start() + self.__ref.diag_start() + + self.__test_diag_channel() + self.__test_diag_power() + self.__test_diag_radio() + self.__test_diag_repeat() + self.__test_diag_send() + self.__test_diag_frame() + self.__test_diag_echo() + self.__test_diag_utils() + self.__test_diag_rawpowersetting() + self.__test_diag_powersettings() + self.__test_diag_gpio_mode() + self.__test_diag_gpio_value() + + self.__ref.diag_stop() + self.__dut.diag_stop() + + # + # Private methods + # + def __test_diag_channel(self): + channel = 20 + commands = ['diag channel', f'diag channel {channel}'] + + if self.__support_commands(commands): + self.__dut.diag_set_channel(channel) + value = self.__dut.diag_get_channel() + ret = value == channel + else: + ret = False + + self.__output_results(commands, ret) + + def __test_diag_power(self): + power = self.__get_dut_diag_power() + commands = ['diag power', f'diag power {power}'] + + if self.__support_commands(commands): + self.__dut.diag_set_power(power) + value = self.__dut.diag_get_power() + ret = value == power + else: + ret = False + + self.__output_results(commands, ret) + + def __test_diag_radio(self): + commands = ['diag radio receive', 'diag radio sleep', 'diag radio state'] + + if self.__support_commands(commands): + self.__dut.diag_radio_receive() + receive_state = self.__dut.diag_get_radio_state() + self.__dut.wait(0.1) + self.__dut.diag_radio_sleep() + sleep_state = self.__dut.diag_get_radio_state() + + ret = sleep_state == 'sleep' and receive_state == 'receive' + else: + ret = False + + self.__output_results(commands, ret) + + def __test_diag_gpio_value(self): + gpio = self.__get_dut_diag_gpio() + commands = [f'diag gpio get {gpio}', f'diag gpio set {gpio} 0', f'diag gpio set {gpio} 1'] + + if self.__support_commands(commands): + self.__dut.diag_set_gpio_value(gpio, 0) + value_0 = self.__dut.diag_get_gpio_value(gpio) + self.__dut.diag_set_gpio_value(gpio, 1) + value_1 = self.__dut.diag_get_gpio_value(gpio) + + ret = value_0 == 0 and value_1 == 1 + else: + ret = False + + self.__output_results(commands, ret) + + def __test_diag_gpio_mode(self): + gpio = self.__get_dut_diag_gpio() + commands = [f'diag gpio mode {gpio}', f'diag gpio mode {gpio} in', f'diag gpio mode {gpio} out'] + + if self.__support_commands(commands): + self.__dut.diag_set_gpio_mode(gpio, 'in') + mode_in = self.__dut.diag_get_gpio_mode(gpio) + self.__dut.diag_set_gpio_value(gpio, 'out') + mode_out = self.__dut.diag_get_gpio_mode(gpio) + + ret = mode_in == 'in' and mode_out == 'out' + else: + ret = False + + self.__output_results(commands, ret) + + def __test_diag_echo(self): + echo_msg = '0123456789' + cmd_diag_echo = f'diag echo {echo_msg}' + cmd_diag_echo_num = f'diag echo -n 10' + + if self.__dut.is_command_supported(cmd_diag_echo): + reply = self.__dut.diag_echo(echo_msg) + ret = reply == echo_msg + else: + ret = False + self.__output_format_bool(cmd_diag_echo, ret) + + if self.__dut.is_command_supported(cmd_diag_echo_num): + reply = self.__dut.diag_echo_number(10) + ret = reply == echo_msg + else: + ret = False + self.__output_format_bool(cmd_diag_echo_num, ret) + + def __test_diag_utils(self): + commands = [ + 'diag cw start', 'diag cw stop', 'diag stream start', 'diag stream stop', 'diag stats', 'diag stats clear' + ] + + for command in commands: + ret = self.__dut.is_command_supported(command) + self.__output_format_bool(command, ret) + + def __test_diag_rawpowersetting(self): + rawpowersetting = self.__get_dut_diag_raw_power_setting() + commands = [ + 'diag rawpowersetting enable', f'diag rawpowersetting {rawpowersetting}', 'diag rawpowersetting', + 'diag rawpowersetting disable' + ] + + if self.__support_commands(commands): + self.__dut.diag_enable_rawpowersetting() + self.__dut.diag_set_rawpowersetting(rawpowersetting) + reply = self.__dut.diag_get_rawpowersetting() + self.__dut.diag_disable_rawpowersetting() + + ret = reply == rawpowersetting + else: + ret = False + + self.__output_results(commands, ret) + + def __test_diag_powersettings(self): + commands = ['diag powersettings', 'diag powersettings 20'] + + if self.__support_commands(commands): + powersettings = self.__dut.diag_get_powersettings() + ret = len(powersettings) > 0 + else: + ret = False + + self.__output_results(commands, ret) + + def __test_diag_send(self): + packets = 100 + threshold = 80 + length = 64 + channel = 20 + commands = [f'diag send {packets} {length}', f'diag stats', f'diag stats clear'] + + if self.__support_commands(commands): + self.__dut.wait(1) + self.__dut.diag_set_channel(channel) + self.__ref.diag_set_channel(channel) + self.__ref.diag_radio_receive() + + self.__dut.diag_stats_clear() + self.__ref.diag_stats_clear() + + self.__dut.diag_send(packets, length) + self.__dut.wait(1) + dut_stats = self.__dut.diag_get_stats() + ref_stats = self.__ref.diag_get_stats() + + ret = dut_stats['sent_packets'] == packets and ref_stats['received_packets'] > threshold + else: + ret = False + + self.__output_results(commands, ret) + + def __test_diag_repeat(self): + delay = 10 + threshold = 80 + length = 64 + channel = 20 + cmd_diag_repeat = f'diag repeat {delay} {length}' + cmd_diag_repeat_stop = 'diag repeat stop' + commands = [cmd_diag_repeat, 'diag repeat stop', 'diag stats', 'diag stats clear'] + + if self.__support_commands(commands): + self.__dut.diag_set_channel(channel) + self.__ref.diag_set_channel(channel) + self.__ref.diag_radio_receive() + + self.__dut.diag_stats_clear() + self.__ref.diag_stats_clear() + + self.__dut.diag_repeat(delay, length) + self.__dut.wait(1) + self.__dut.diag_repeat_stop() + dut_stats = self.__dut.diag_get_stats() + ref_stats = self.__ref.diag_get_stats() + + ret = dut_stats['sent_packets'] > threshold and ref_stats['received_packets'] > threshold + else: + ret = False + + self.__output_format_bool(cmd_diag_repeat, ret) + self.__output_format_bool(cmd_diag_repeat_stop, ret) + + def __test_diag_frame(self): + packets = 100 + threshold = 80 + channel = 20 + frame = '00010203040506070809' + cmd_diag_frame = f'diag frame {frame}' + commands = [cmd_diag_frame, f'diag send {packets}', f'diag stats', f'diag stats clear'] + + if self.__support_commands(commands): + self.__dut.wait(1) + self.__dut.diag_set_channel(channel) + self.__ref.diag_set_channel(channel) + self.__ref.diag_radio_receive() + + self.__dut.diag_stats_clear() + self.__ref.diag_stats_clear() + + self.__ref.diag_frame(frame) + self.__dut.diag_send(packets, None) + self.__dut.wait(1) + dut_stats = self.__dut.diag_get_stats() + ref_stats = self.__ref.diag_get_stats() + + ret = dut_stats['sent_packets'] == packets and ref_stats['received_packets'] > threshold + else: + ret = False + + self.__output_format_bool(cmd_diag_frame, ret) + + def __support_commands(self, commands: List[str]) -> bool: + ret = True + + for command in commands: + if self.__dut.is_command_supported(command) is False: + ret = False + break + + return ret + + def __output_results(self, commands: List[str], support: bool): + for command in commands: + self.__output_format_bool(command, support) + + def __get_dut_diag_power(self) -> int: + return int(os.getenv('DUT_DIAG_POWER', '10')) + + def __get_dut_diag_gpio(self) -> int: + return int(os.getenv('DUT_DIAG_GPIO', '0')) + + def __get_dut_diag_raw_power_setting(self) -> str: + return os.getenv('DUT_DIAG_RAW_POWER_SETTING', '112233') + + def __connect_dut(self) -> OTCI: + if os.getenv('DUT_ADB_TCP'): + node = otci.connect_otbr_adb_tcp(os.getenv('DUT_ADB_TCP')) + elif os.getenv('DUT_ADB_USB'): + node = otci.connect_otbr_adb_usb(os.getenv('DUT_ADB_USB')) + elif os.getenv('DUT_SSH'): + node = otci.connect_otbr_ssh(os.getenv('DUT_SSH')) + else: + self.__fail("Please set DUT_ADB_TCP, DUT_ADB_USB or DUT_SSH to connect to the DUT device.") + + return node + + def __connect_reference_device(self) -> OTCI: + if os.getenv('REF_CLI_SERIAL'): + node = otci.connect_cli_serial(os.getenv('REF_CLI_SERIAL')) + elif os.getenv('REF_SSH'): + node = otci.connect_otbr_ssh(os.getenv('REF_SSH')) + elif os.getenv('REF_ADB_USB'): + node = otci.connect_otbr_adb_usb(os.getenv('REF_ADB_USB')) + else: + self.__fail("Please set REF_CLI_SERIAL, REF_SSH or REF_ADB_USB to connect to the reference device.") + + return node + + def __output_format_string(self, name: str, value: str): + prefix = '{0:-<58}'.format('{} '.format(name)) + print(f'{prefix} {value}') + + def __output_format_bool(self, name: str, value: bool): + self.__output_format_string(name, 'OK' if value else 'NotSupported') + + def __fail(self, value: str): + print(f'{value}') + sys.exit(1) + + +def parse_arguments(): + """Parse all arguments.""" + description_msg = 'This script is used for testing RCP capabilities.' + epilog_msg = textwrap.dedent( + 'Device Interfaces:\r\n' + ' DUT_SSH= Connect to the DUT via ssh\r\n' + ' DUT_ADB_TCP= Connect to the DUT via adb tcp\r\n' + ' DUT_ADB_USB= Connect to the DUT via adb usb\r\n' + ' REF_CLI_SERIAL= Connect to the reference device via cli serial port\r\n' + ' REF_ADB_USB= Connect to the reference device via adb usb\r\n' + ' REF_SSH= Connect to the reference device via ssh\r\n' + '\r\n' + 'Example:\r\n' + f' DUT_ADB_USB=1169UC2F2T0M95OR REF_CLI_SERIAL=/dev/ttyACM0 python3 {sys.argv[0]} -d\r\n') + + parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, + description=description_msg, + epilog=epilog_msg) + + parser.add_argument( + '-d', + '--diag-commands', + action='store_true', + default=False, + help='test whether the RCP supports all diag commands', + ) + + parser.add_argument( + '-v', + '--verbose', + action='store_true', + default=False, + help='output verbose information', + ) + + return parser.parse_args() + + +def main(): + arguments = parse_arguments() + + if arguments.verbose is True: + logger = logging.getLogger() + logger.setLevel(logging.DEBUG) + + rcp_caps = RcpCaps() + + if arguments.diag_commands is True: + rcp_caps.test_diag_commands() + + +if __name__ == '__main__': + main() diff --git a/tools/cp-caps/requirements.txt b/tools/cp-caps/requirements.txt new file mode 100644 index 000000000..740c30c16 --- /dev/null +++ b/tools/cp-caps/requirements.txt @@ -0,0 +1,7 @@ +adb_shell>=0.4.4 +adb_shell[usb]>=0.4.4 +build>=1.2.1 +otci-openthread +paramiko>=3.4.0 +pyserial>=3.5 +pyspinel>=1.0.3 diff --git a/tools/otci/otci/otci.py b/tools/otci/otci/otci.py index a3632afe6..eb01ba3cb 100644 --- a/tools/otci/otci/otci.py +++ b/tools/otci/otci/otci.py @@ -2396,13 +2396,21 @@ def diag_stream_stop(self): """Stop transmitting a stream of characters.""" self.execute_command('diag stream stop') - def diag_send(self, packets: int, length: int): + def diag_send(self, packets: int, length: Optional[int] = None): """Transmit a fixed number of packets.""" - self.execute_command(f'diag send {packets} {length}') + if length is None: + command = f'diag send {packets}' + else: + command = f'diag send {packets} {length}' + self.execute_command(command) - def diag_repeat(self, delay: int, length: int): + def diag_repeat(self, delay: int, length: Optional[int] = None): """Transmit packets repeatedly with a fixed interval.""" - self.execute_command(f'diag repeat {delay} {length}') + if length is None: + command = f'diag repeat {delay}' + else: + command = f'diag repeat {delay} {length}' + self.execute_command(command) def diag_repeat_stop(self): """Stop repeated packet transmission.""" From 4c0d8f2e5992c40dbb92829245bd35d68674a9bc Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 5 Jul 2024 12:40:00 -0700 Subject: [PATCH 026/160] [srp-client] apply short random jitter to lease renew time (#10473) This commit updates `Srp::Client` to apply a short random jitter (15 seconds) when calculating the lease renew time. The lease is renewed close to its expiration, using a guard interval of 120 seconds (renewing 120 seconds before expiration). The jitter is added to distribute client refreshes, in case many clients registered their services around the same time. --- src/core/net/srp_client.cpp | 10 ++++++---- src/core/net/srp_client.hpp | 3 +++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index 33b65ff0d..29ae91a99 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -1857,12 +1857,14 @@ void Client::ProcessResponse(Message &aMessage) // and the lease time. `kLeaseRenewGuardInterval` is used to // ensure that we renew the lease before server expires it. In the // unlikely (but maybe useful for testing) case where the accepted - // lease interval is too short (shorter than the guard time) we - // just use half of the accepted lease interval. + // lease interval is too short (shorter than twice the guard time) + // we just use half of the accepted lease interval. - if (mLease > kLeaseRenewGuardInterval) + if (mLease > 2 * kLeaseRenewGuardInterval) { - mLeaseRenewTime += Time::SecToMsec(mLease - kLeaseRenewGuardInterval); + uint32_t interval = Time::SecToMsec(mLease - kLeaseRenewGuardInterval); + + mLeaseRenewTime += Random::NonCrypto::AddJitter(interval, kLeaseRenewJitter); } else { diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index 077a5a2cb..284ff789c 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -829,6 +829,9 @@ class Client : public InstanceLocator, private NonCopyable // to renew the lease. Value is in seconds. static constexpr uint32_t kLeaseRenewGuardInterval = OPENTHREAD_CONFIG_SRP_CLIENT_LEASE_RENEW_GUARD_INTERVAL; + // Lease renew time jitter (in msec). + static constexpr uint16_t kLeaseRenewJitter = 15 * 1000; // 15 second + // Max allowed lease time to avoid timer roll-over (~24.8 days). static constexpr uint32_t kMaxLease = (Timer::kMaxDelay / 1000) - 1; From b6d46327f47f90fdf44314718bbb5ae8a98a4980 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Tue, 9 Jul 2024 05:58:38 +0800 Subject: [PATCH 027/160] [otci] add network management API to otci (#10482) This commit adds high level Thread network management API 'create_dataset()', 'join()', 'leave()' and 'wait_for()' to otci. --- tools/otci/otci/otci.py | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tools/otci/otci/otci.py b/tools/otci/otci/otci.py index eb01ba3cb..e8e54c049 100644 --- a/tools/otci/otci/otci.py +++ b/tools/otci/otci/otci.py @@ -1790,6 +1790,11 @@ def set_dataset_bytes(self, dataset: str, data: bytes) -> None: self.execute_command(cmd) + def get_dataset_tlvs_bytes(self) -> bytes: + """Gets bytes of the Operational Dataset TLVs""" + hexstr = self.__parse_str(self.execute_command('dataset tlvs')) + return self.__hex_to_bytes(hexstr) + def dataset_set_buffer(self, active_timestamp: Optional[int] = None, channel: Optional[int] = None, @@ -2543,6 +2548,55 @@ def is_command_supported(self, command: str) -> bool: return True + # + # Network management utilities + # + def create_dataset(self, + active_timestamp: Optional[int] = None, + channel: Optional[int] = None, + channel_mask: Optional[int] = None, + extpanid: Optional[str] = None, + mesh_local_prefix: Optional[str] = None, + network_key: Optional[str] = None, + network_name: Optional[str] = None, + panid: Optional[int] = None, + pskc: Optional[str] = None, + security_policy: Optional[tuple] = None, + pending_timestamp: Optional[int] = None) -> bytes: + """Creates a new Operational Dataset with given parameters.""" + self.dataset_clear_buffer() + self.dataset_init_buffer() + self.dataset_set_buffer(active_timestamp, channel, channel_mask, extpanid, mesh_local_prefix, network_key, + network_name, panid, pskc, security_policy, pending_timestamp) + return self.get_dataset_tlvs_bytes() + + def join(self, dataset: bytes) -> None: + """Joins to a Thread network with given Active Operational Dataset.""" + self.set_dataset_bytes('active', dataset) + self.ifconfig_up() + self.thread_start() + + def leave(self) -> None: + """Leaves from the Thread network.""" + self.thread_stop() + self.ifconfig_down() + + def wait_for(self, command: str, expect_line: Optional[Union[str, Pattern, Collection[Any]]], timeout: float = 60): + """Wait for the expected output by periodically executing the given command.""" + success = False + + while timeout > 0: + output = self.execute_command(command) + if any(match_line(line, expect_line) for line in output): + success = True + break + + self.__otcmd.wait(1) + timeout -= 1 + + if not success: + raise ExpectLineTimeoutError(expect_line) + # # Other TODOs # From 528784caa5dccce5c3cd6ae7ed94491fa75f63c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:01:28 -0700 Subject: [PATCH 028/160] github-actions: bump actions/setup-go from 5.0.0 to 5.0.1 (#10489) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/0c52d547c9bc32b1aa3301fd7a9cb496313a4491...cdcb36043654635271a94b9a6d1392de5bb323a7) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/otns.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml index 23ddc0f90..346671d11 100644 --- a/.github/workflows/otns.yml +++ b/.github/workflows/otns.yml @@ -63,7 +63,7 @@ jobs: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version: "1.20" - name: Set up Python @@ -103,7 +103,7 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version: "1.20" - name: Set up Python @@ -165,7 +165,7 @@ jobs: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version: "1.20" - name: Set up Python From 9ac67520f284ac748da4a7f8ea55627f1bc20fc8 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 8 Jul 2024 15:04:37 -0700 Subject: [PATCH 029/160] [mesh-forwarder] track parsed IPv6 header in `RxInfo` (#10467) This commit adds an `Ip6::Header` field to `RxInfo`, along with the `ParseIp6Headers()` method to decompress and parse the IPv6 headers from the received frame. `RxInfo` now tracks whether the headers have been parsed before. The IPv6 headers may be parsed from different code paths as the received frame is processed. For example, `UpdateRoutes()`, `GetFramePriority()`, and `CheckReachability()` may parse the IPv6 headers. By having `RxInfo` cache the parsed IPv6 headers, duplicate parsing is avoided. --- src/core/thread/mesh_forwarder.cpp | 38 +++++++++++++++++--------- src/core/thread/mesh_forwarder.hpp | 21 ++++++++++---- src/core/thread/mesh_forwarder_ftd.cpp | 28 +++++++++---------- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 2452c94f9..3eb324339 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -1371,10 +1371,22 @@ bool MeshForwarder::RemoveMessageIfNoPendingTx(Message &aMessage) return didRemove; } +Error MeshForwarder::RxInfo::ParseIp6Headers(void) +{ + Error error = kErrorNone; + + VerifyOrExit(!mParsedIp6Headers); + SuccessOrExit(error = mIp6Headers.DecompressFrom(mFrameData, mMacAddrs, GetInstance())); + mParsedIp6Headers = true; + +exit: + return error; +} + void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame) { Error error = kErrorNone; - RxInfo rxInfo; + RxInfo rxInfo(GetInstance()); VerifyOrExit(mEnabled, error = kErrorInvalidState); @@ -1610,7 +1622,7 @@ bool MeshForwarder::UpdateReassemblyList(void) return mReassemblyList.GetHead() != nullptr; } -Error MeshForwarder::FrameToMessage(const RxInfo &aRxInfo, uint16_t aDatagramSize, Message *&aMessage) +Error MeshForwarder::FrameToMessage(RxInfo &aRxInfo, uint16_t aDatagramSize, Message *&aMessage) { Error error = kErrorNone; FrameData frameData = aRxInfo.mFrameData; @@ -1630,7 +1642,7 @@ Error MeshForwarder::FrameToMessage(const RxInfo &aRxInfo, uint16_t aDatagramSiz return error; } -void MeshForwarder::HandleLowpanHc(const RxInfo &aRxInfo) +void MeshForwarder::HandleLowpanHc(RxInfo &aRxInfo) { Error error = kErrorNone; Message *message = nullptr; @@ -1681,32 +1693,32 @@ Error MeshForwarder::HandleDatagram(Message &aMessage, const Mac::Address &aMacS return Get().HandleDatagram(OwnedPtr(&aMessage)); } -Error MeshForwarder::GetFramePriority(const RxInfo &aRxInfo, Message::Priority &aPriority) +Error MeshForwarder::GetFramePriority(RxInfo &aRxInfo, Message::Priority &aPriority) { - Error error = kErrorNone; - Ip6::Headers headers; + Error error = kErrorNone; - SuccessOrExit(error = headers.DecompressFrom(aRxInfo.mFrameData, aRxInfo.mMacAddrs, GetInstance())); + SuccessOrExit(error = aRxInfo.ParseIp6Headers()); - aPriority = Ip6::Ip6::DscpToPriority(headers.GetIp6Header().GetDscp()); + aPriority = Ip6::Ip6::DscpToPriority(aRxInfo.mIp6Headers.GetIp6Header().GetDscp()); // Only ICMPv6 error messages are prioritized. - if (headers.IsIcmp6() && headers.GetIcmpHeader().IsError()) + if (aRxInfo.mIp6Headers.IsIcmp6() && aRxInfo.mIp6Headers.GetIcmpHeader().IsError()) { aPriority = Message::kPriorityNet; } - if (headers.IsUdp()) + if (aRxInfo.mIp6Headers.IsUdp()) { - uint16_t destPort = headers.GetUdpHeader().GetDestinationPort(); + uint16_t destPort = aRxInfo.mIp6Headers.GetUdpHeader().GetDestinationPort(); if (destPort == Mle::kUdpPort) { aPriority = Message::kPriorityNet; } - else if (Get().IsTmfMessage(headers.GetSourceAddress(), headers.GetDestinationAddress(), destPort)) + else if (Get().IsTmfMessage(aRxInfo.mIp6Headers.GetSourceAddress(), + aRxInfo.mIp6Headers.GetDestinationAddress(), destPort)) { - aPriority = Tmf::Agent::DscpToPriority(headers.GetIp6Header().GetDscp()); + aPriority = Tmf::Agent::DscpToPriority(aRxInfo.mIp6Headers.GetIp6Header().GetDscp()); } } diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index d337dfdb6..63a216985 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -411,20 +411,29 @@ class MeshForwarder : public InstanceLocator, private NonCopyable kAnycastService, }; - struct RxInfo + struct RxInfo : public InstanceLocator { static constexpr uint16_t kInfoStringSize = 70; typedef String InfoString; + explicit RxInfo(Instance &aInstance) + : InstanceLocator(aInstance) + , mParsedIp6Headers(false) + { + } + const Mac::Address &GetSrcAddr(void) const { return mMacAddrs.mSource; } const Mac::Address &GetDstAddr(void) const { return mMacAddrs.mDestination; } bool IsLinkSecurityEnabled(void) const { return mLinkInfo.IsLinkSecurityEnabled(); } + Error ParseIp6Headers(void); InfoString ToString(void) const; FrameData mFrameData; ThreadLinkInfo mLinkInfo; Mac::Addresses mMacAddrs; + Ip6::Headers mIp6Headers; + bool mParsedIp6Headers; }; #if OPENTHREAD_FTD @@ -507,17 +516,17 @@ class MeshForwarder : public InstanceLocator, private NonCopyable #endif void SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac::Addresses &aMacAddrs); - Error CheckReachability(const RxInfo &aRxInfo); + Error CheckReachability(RxInfo &aRxInfo); Error CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header); - void UpdateRoutes(const RxInfo &aRxInfo); - Error FrameToMessage(const RxInfo &aRxInfo, uint16_t aDatagramSize, Message *&aMessage); + void UpdateRoutes(RxInfo &aRxInfo); + Error FrameToMessage(RxInfo &aRxInfo, uint16_t aDatagramSize, Message *&aMessage); void GetMacDestinationAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); void GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); Message *PrepareNextDirectTransmission(void); void HandleMesh(RxInfo &aRxInfo); void ResolveRoutingLoops(uint16_t aSourceRloc16, uint16_t aDestRloc16); void HandleFragment(RxInfo &aRxInfo); - void HandleLowpanHc(const RxInfo &aRxInfo); + void HandleLowpanHc(RxInfo &aRxInfo); void PrepareMacHeaders(Mac::TxFrame &aFrame, Mac::Frame::Type aFrameType, @@ -576,7 +585,7 @@ class MeshForwarder : public InstanceLocator, private NonCopyable void HandleTimeTick(void); void ScheduleTransmissionTask(void); - Error GetFramePriority(const RxInfo &aRxInfo, Message::Priority &aPriority); + Error GetFramePriority(RxInfo &aRxInfo, Message::Priority &aPriority); Error GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader, uint16_t aSrcRloc16, Message::Priority &aPriority); diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 0d84847d4..740132b9a 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -641,12 +641,11 @@ void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac return; } -Error MeshForwarder::CheckReachability(const RxInfo &aRxInfo) +Error MeshForwarder::CheckReachability(RxInfo &aRxInfo) { - Error error; - Ip6::Headers ip6Headers; + Error error; - error = ip6Headers.DecompressFrom(aRxInfo.mFrameData, aRxInfo.mMacAddrs, GetInstance()); + error = aRxInfo.ParseIp6Headers(); switch (error) { @@ -660,11 +659,11 @@ Error MeshForwarder::CheckReachability(const RxInfo &aRxInfo) ExitNow(); } - error = CheckReachability(aRxInfo.GetDstAddr().GetShort(), ip6Headers.GetIp6Header()); + error = CheckReachability(aRxInfo.GetDstAddr().GetShort(), aRxInfo.mIp6Headers.GetIp6Header()); if (error == kErrorNoRoute) { - SendDestinationUnreachable(aRxInfo.GetSrcAddr().GetShort(), ip6Headers); + SendDestinationUnreachable(aRxInfo.GetSrcAddr().GetShort(), aRxInfo.mIp6Headers); } exit: @@ -819,27 +818,26 @@ void MeshForwarder::ResolveRoutingLoops(uint16_t aSourceRloc16, uint16_t aDestRl return; } -void MeshForwarder::UpdateRoutes(const RxInfo &aRxInfo) +void MeshForwarder::UpdateRoutes(RxInfo &aRxInfo) { - Ip6::Headers ip6Headers; - Neighbor *neighbor; + Neighbor *neighbor; VerifyOrExit(!aRxInfo.GetDstAddr().IsBroadcast() && aRxInfo.GetSrcAddr().IsShort()); - SuccessOrExit(ip6Headers.DecompressFrom(aRxInfo.mFrameData, aRxInfo.mMacAddrs, GetInstance())); + SuccessOrExit(aRxInfo.ParseIp6Headers()); - if (!ip6Headers.GetSourceAddress().GetIid().IsLocator() && - Get().IsOnMesh(ip6Headers.GetSourceAddress())) + if (!aRxInfo.mIp6Headers.GetSourceAddress().GetIid().IsLocator() && + Get().IsOnMesh(aRxInfo.mIp6Headers.GetSourceAddress())) { // FTDs MAY add/update EID-to-RLOC Map Cache entries by // inspecting packets being received only for on mesh // addresses. - Get().UpdateSnoopedCacheEntry(ip6Headers.GetSourceAddress(), aRxInfo.GetSrcAddr().GetShort(), - aRxInfo.GetDstAddr().GetShort()); + Get().UpdateSnoopedCacheEntry( + aRxInfo.mIp6Headers.GetSourceAddress(), aRxInfo.GetSrcAddr().GetShort(), aRxInfo.GetDstAddr().GetShort()); } - neighbor = Get().FindNeighbor(ip6Headers.GetSourceAddress()); + neighbor = Get().FindNeighbor(aRxInfo.mIp6Headers.GetSourceAddress()); VerifyOrExit(neighbor != nullptr && !neighbor->IsFullThreadDevice()); if (!Get().HasMatchingRouterIdWith(aRxInfo.GetSrcAddr().GetShort())) From 91e5a798f8bb8434ddd1a5ad2a77a7b1ac5cf4e9 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 8 Jul 2024 15:05:58 -0700 Subject: [PATCH 030/160] [cli] simplify parsing dataset TLVs (#10476) This commit adds the `ParseTlvs()` helper method in the `Cli::Dataset` class, which parses TLVs (as hex strings) from an input argument. This is used in multiple methods to simplify the code. The `Process()` method is updated to avoid extra conversion and use of core-internal types. --- src/cli/cli_dataset.cpp | 89 +++++++++++++++++------------------------ src/cli/cli_dataset.hpp | 2 + 2 files changed, 39 insertions(+), 52 deletions(-) diff --git a/src/cli/cli_dataset.cpp b/src/cli/cli_dataset.cpp index 86399c3fa..327a2411d 100644 --- a/src/cli/cli_dataset.cpp +++ b/src/cli/cli_dataset.cpp @@ -508,6 +508,19 @@ otError Dataset::ParseSecurityPolicy(Arg *&aArgs, otOperationalDataset &aDataset return ParseSecurityPolicy(aDataset.mSecurityPolicy, aArgs); } +otError Dataset::ParseTlvs(Arg &aArg, otOperationalDatasetTlvs &aDatasetTlvs) +{ + otError error; + uint16_t length; + + length = sizeof(aDatasetTlvs.mTlvs); + SuccessOrExit(error = aArg.ParseAsHexString(length, aDatasetTlvs.mTlvs)); + aDatasetTlvs.mLength = static_cast(length); + +exit: + return error; +} + //--------------------------------------------------------------------------------------------------------------------- otError Dataset::ProcessCommand(const ComponentMapper &aMapper, Arg aArgs[]) @@ -617,10 +630,7 @@ template <> otError Dataset::Process(Arg aArgs[]) #endif else if (aArgs[0] == "tlvs") { - uint16_t size = sizeof(sDatasetTlvs.mTlvs); - - SuccessOrExit(error = aArgs[1].ParseAsHexString(size, sDatasetTlvs.mTlvs)); - sDatasetTlvs.mLength = static_cast(size); + ExitNow(error = ParseTlvs(aArgs[1], sDatasetTlvs)); } exit: @@ -760,12 +770,12 @@ template <> otError Dataset::Process(Arg aArgs[]) template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; - otOperationalDataset dataset; - uint8_t tlvs[128]; - uint8_t tlvsLength = 0; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; + otOperationalDatasetTlvs tlvs; ClearAllBytes(dataset); + ClearAllBytes(tlvs); for (Arg *arg = &aArgs[1]; !arg->IsEmpty();) { @@ -779,12 +789,8 @@ template <> otError Dataset::Process(Arg aArgs[]) } else if (*arg == "-x") { - uint16_t length; - arg++; - length = sizeof(tlvs); - SuccessOrExit(error = arg->ParseAsHexString(length, tlvs)); - tlvsLength = static_cast(length); + SuccessOrExit(error = ParseTlvs(*arg, tlvs)); arg++; } else @@ -811,8 +817,9 @@ template <> otError Dataset::Process(Arg aArgs[]) */ if (aArgs[0] == "active") { - error = otDatasetSendMgmtActiveSet(GetInstancePtr(), &dataset, tlvs, tlvsLength, /* aCallback */ nullptr, - /* aContext */ nullptr); + error = + otDatasetSendMgmtActiveSet(GetInstancePtr(), &dataset, tlvs.mTlvs, tlvs.mLength, /* aCallback */ nullptr, + /* aContext */ nullptr); } /** * @cli dataset mgmtsetcommand pending @@ -832,8 +839,9 @@ template <> otError Dataset::Process(Arg aArgs[]) */ else if (aArgs[0] == "pending") { - error = otDatasetSendMgmtPendingSet(GetInstancePtr(), &dataset, tlvs, tlvsLength, /* aCallback */ nullptr, - /* aContext */ nullptr); + error = + otDatasetSendMgmtPendingSet(GetInstancePtr(), &dataset, tlvs.mTlvs, tlvs.mLength, /* aCallback */ nullptr, + /* aContext */ nullptr); } else { @@ -848,12 +856,12 @@ template <> otError Dataset::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; otOperationalDatasetComponents datasetComponents; - uint8_t tlvs[32]; - uint8_t tlvsLength = 0; + otOperationalDatasetTlvs tlvs; bool destAddrSpecified = false; otIp6Address address; ClearAllBytes(datasetComponents); + ClearAllBytes(tlvs); for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++) { @@ -865,12 +873,8 @@ template <> otError Dataset::Process(Arg aArgs[]) } else if (*arg == "-x") { - uint16_t length; - arg++; - length = sizeof(tlvs); - SuccessOrExit(error = arg->ParseAsHexString(length, tlvs)); - tlvsLength = static_cast(length); + SuccessOrExit(error = ParseTlvs(*arg, tlvs)); } else if (*arg == "address") { @@ -911,7 +915,7 @@ template <> otError Dataset::Process(Arg aArgs[]) */ if (aArgs[0] == "active") { - error = otDatasetSendMgmtActiveGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength, + error = otDatasetSendMgmtActiveGet(GetInstancePtr(), &datasetComponents, tlvs.mTlvs, tlvs.mLength, destAddrSpecified ? &address : nullptr); } /** @@ -936,7 +940,7 @@ template <> otError Dataset::Process(Arg aArgs[]) */ else if (aArgs[0] == "pending") { - error = otDatasetSendMgmtPendingGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength, + error = otDatasetSendMgmtPendingGet(GetInstancePtr(), &datasetComponents, tlvs.mTlvs, tlvs.mLength, destAddrSpecified ? &address : nullptr); } else @@ -1086,41 +1090,22 @@ otError Dataset::ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, Arg *&aA */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; - MeshCoP::Dataset::Type datasetType; + otError error = OT_ERROR_NONE; + otOperationalDatasetTlvs datasetTlvs; + + SuccessOrExit(error = ParseTlvs(aArgs[1], datasetTlvs)); if (aArgs[0] == "active") { - datasetType = MeshCoP::Dataset::Type::kActive; + error = otDatasetSetActiveTlvs(GetInstancePtr(), &datasetTlvs); } else if (aArgs[0] == "pending") { - datasetType = MeshCoP::Dataset::Type::kPending; + error = otDatasetSetPendingTlvs(GetInstancePtr(), &datasetTlvs); } else { - ExitNow(error = OT_ERROR_INVALID_ARGS); - } - - { - otOperationalDataset dataset; - otOperationalDatasetTlvs datasetTlvs; - uint16_t tlvsLength = OT_OPERATIONAL_DATASET_MAX_LENGTH; - - SuccessOrExit(error = aArgs[1].ParseAsHexString(tlvsLength, datasetTlvs.mTlvs)); - datasetTlvs.mLength = static_cast(tlvsLength); - - SuccessOrExit(error = otDatasetParseTlvs(&datasetTlvs, &dataset)); - - switch (datasetType) - { - case MeshCoP::Dataset::Type::kActive: - SuccessOrExit(error = otDatasetSetActiveTlvs(GetInstancePtr(), &datasetTlvs)); - break; - case MeshCoP::Dataset::Type::kPending: - SuccessOrExit(error = otDatasetSetPendingTlvs(GetInstancePtr(), &datasetTlvs)); - break; - } + error = OT_ERROR_INVALID_ARGS; } exit: diff --git a/src/cli/cli_dataset.hpp b/src/cli/cli_dataset.hpp index 06055e34a..708e4790e 100644 --- a/src/cli/cli_dataset.hpp +++ b/src/cli/cli_dataset.hpp @@ -118,6 +118,8 @@ class Dataset : private Utils otError ParsePskc(Arg *&aArgs, otOperationalDataset &aDataset); otError ParseSecurityPolicy(Arg *&aArgs, otOperationalDataset &aDataset); + otError ParseTlvs(Arg &aArg, otOperationalDatasetTlvs &aDatasetTlvs); + otError ProcessCommand(const ComponentMapper &aMapper, Arg aArgs[]); template otError Process(Arg aArgs[]); From 821f2415e0fc4f3ebf74e3d66f890dacf5b318b9 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 8 Jul 2024 15:44:53 -0700 Subject: [PATCH 031/160] [test] harden `test_srp_server` (#10486) This commit updates `test_srp_server` to wait longer after the start of the SRP client before performing the test steps. This makes the test more robust against random jitter which may be applied by the client when registering. --- tests/unit/test_srp_server.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_srp_server.cpp b/tests/unit/test_srp_server.cpp index 2f655f2bf..cab62b23d 100644 --- a/tests/unit/test_srp_server.cpp +++ b/tests/unit/test_srp_server.cpp @@ -533,7 +533,7 @@ void TestSrpServerReject(void) srpClient->EnableAutoStartMode(nullptr, nullptr); VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); - AdvanceTime(2000); + AdvanceTime(15 * 1000); VerifyOrQuit(srpClient->IsRunning()); SuccessOrQuit(srpClient->SetHostName(kHostName)); @@ -645,7 +645,7 @@ void TestSrpServerIgnore(void) srpClient->EnableAutoStartMode(nullptr, nullptr); VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); - AdvanceTime(2000); + AdvanceTime(15 * 1000); VerifyOrQuit(srpClient->IsRunning()); SuccessOrQuit(srpClient->SetHostName(kHostName)); @@ -759,7 +759,7 @@ void TestSrpServerClientRemove(bool aShouldRemoveKeyLease) srpClient->EnableAutoStartMode(nullptr, nullptr); VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); - AdvanceTime(2000); + AdvanceTime(15 * 1000); VerifyOrQuit(srpClient->IsRunning()); SuccessOrQuit(srpClient->SetHostName(kHostName)); From 8d4119c9140ca188748f4dec2907d2748593b050 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 9 Jul 2024 09:57:57 -0700 Subject: [PATCH 032/160] [routing-manager] track router age (#10453) This commit adds a new mechanism in `RoutingManager` to track the duration since a router is first discovered. This information is now provided in `otBorderRoutingRouterEntry`, and the CLI `br routers` command is updated to include this information for each router. To enable tracking of longer durations, `Uptime` is used, which tracks milliseconds since the start of `ot::Instance` as a `uint64_t` value. `TimerMilli::GetNow()` is not suitable for this purpose because it utilizes `uint32_t` intervals, which would limit the maximum trackable time to roughly 49 days due to potential overflow. --- include/openthread/border_routing.h | 2 ++ include/openthread/instance.h | 2 +- script/check-scan-build | 1 + src/cli/README_BR.md | 3 ++- src/cli/cli_br.cpp | 13 ++++++++++--- src/core/border_router/routing_manager.cpp | 15 +++++++++------ src/core/border_router/routing_manager.hpp | 21 +++++++++++++++++++-- 7 files changed, 44 insertions(+), 13 deletions(-) diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h index 55af0131a..95e50a116 100644 --- a/include/openthread/border_routing.h +++ b/include/openthread/border_routing.h @@ -85,6 +85,7 @@ typedef struct otBorderRoutingPrefixTableIterator { const void *mPtr1; const void *mPtr2; + uint32_t mData0; uint32_t mData1; uint8_t mData2; uint8_t mData3; @@ -98,6 +99,7 @@ typedef struct otBorderRoutingRouterEntry { otIp6Address mAddress; ///< IPv6 address of the router. uint32_t mMsecSinceLastUpdate; ///< Milliseconds since last update (any message rx) from this router. + uint32_t mAge; ///< The router's age in seconds (duration since its first discovery). bool mManagedAddressConfigFlag : 1; ///< The router's Managed Address Config flag (`M` flag). bool mOtherConfigFlag : 1; ///< The router's Other Config flag (`O` flag). bool mStubRouterFlag : 1; ///< The router's Stub Router flag. diff --git a/include/openthread/instance.h b/include/openthread/instance.h index b1079aab7..f533e4c11 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (424) +#define OPENTHREAD_API_VERSION (425) /** * @addtogroup api-instance diff --git a/script/check-scan-build b/script/check-scan-build index 2d855fb16..142468f9c 100755 --- a/script/check-scan-build +++ b/script/check-scan-build @@ -80,6 +80,7 @@ OT_BUILD_OPTIONS=( "-DOT_SRP_ADV_PROXY=ON" "-DOT_SRP_CLIENT=ON" "-DOT_SRP_SERVER=ON" + "-DOT_UPTIME=ON" "-DOT_VENDOR_NAME=OpenThread" "-DOT_VENDOR_MODEL=Scan-build" "-DOT_VENDOR_SW_VERSION=OT" diff --git a/src/cli/README_BR.md b/src/cli/README_BR.md index ed704ee32..cd7783fd9 100644 --- a/src/cli/README_BR.md +++ b/src/cli/README_BR.md @@ -353,10 +353,11 @@ Info per router: - Stub: Stub Router flag (indicates whether the router is a stub router) - Milliseconds since last received message from this router - Reachability flag: A router is marked as unreachable if it fails to respond to multiple Neighbor Solicitation probes. +- Age: Duration interval since this router was first discovered. It is formatted as `{hh}:{mm}:{ss}` for hours, minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the format is `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds. - `(this BR)` is appended when the router is the local device itself. ```bash > br routers -ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) ms-since-rx:1505 reachable:yes +ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) ms-since-rx:1505 reachable:yes age:00:18:13 Done ``` diff --git a/src/cli/cli_br.cpp b/src/cli/cli_br.cpp index cee063d01..7839823bb 100644 --- a/src/cli/cli_br.cpp +++ b/src/cli/cli_br.cpp @@ -518,7 +518,7 @@ template <> otError Br::Process(Arg aArgs[]) * @cli br routers * @code * br routers - * ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) ms-since-rx:1505 reachable:yes + * ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) ms-since-rx:1505 reachable:yes age:00:18:13 * Done * @endcode * @par @@ -532,6 +532,9 @@ template <> otError Br::Process(Arg aArgs[]) * - Milliseconds since last received message from this router * - Reachability flag: A router is marked as unreachable if it fails to respond to multiple Neighbor Solicitation * probes. + * - Age: Duration interval since this router was first discovered. It is formatted as `{hh}:{mm}:{ss}` for hours, + * minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the format is + * `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds. * - `(this BR)` is appended when the router is the local device itself. * @sa otBorderRoutingGetNextRouterEntry */ @@ -562,8 +565,12 @@ void Br::OutputRouterInfo(const otBorderRoutingRouterEntry &aEntry, RouterOutput if (aMode == kLongVersion) { - OutputFormat(" ms-since-rx:%lu reachable:%s", ToUlong(aEntry.mMsecSinceLastUpdate), - aEntry.mIsReachable ? "yes" : "no"); + char ageString[OT_DURATION_STRING_SIZE]; + + otConvertDurationInSecondsToString(aEntry.mAge, ageString, sizeof(ageString)); + + OutputFormat(" ms-since-rx:%lu reachable:%s age:%s", ToUlong(aEntry.mMsecSinceLastUpdate), + aEntry.mIsReachable ? "yes" : "no", ageString); if (aEntry.mIsLocalDevice) { diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index f0cb6a24c..86fc96d3d 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1032,7 +1032,8 @@ void RoutingManager::RxRaTracker::ProcessRouterAdvertMessage(const RouterAdvert: router = newEntry; router->Clear(); - router->mAddress = aSrcAddress; + router->mDiscoverTime = Uptime::MsecToSec(Get().GetUptime()); + router->mAddress = aSrcAddress; mRouters.Push(*newEntry); } @@ -1713,7 +1714,7 @@ void RoutingManager::RxRaTracker::SetHeaderFlagsOn(RouterAdvert::Header &aHeader void RoutingManager::RxRaTracker::InitIterator(PrefixTableIterator &aIterator) const { - static_cast(aIterator).Init(mRouters.GetHead()); + static_cast(aIterator).Init(mRouters.GetHead(), Uptime::MsecToSec(Get().GetUptime())); } Error RoutingManager::RxRaTracker::GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const @@ -1725,7 +1726,7 @@ Error RoutingManager::RxRaTracker::GetNextEntry(PrefixTableIterator &aIterator, SuccessOrExit(error = iterator.AdvanceToNextEntry()); - iterator.GetRouter()->CopyInfoTo(aEntry.mRouter, iterator.GetInitTime()); + iterator.GetRouter()->CopyInfoTo(aEntry.mRouter, iterator.GetInitTime(), iterator.GetInitUptime()); switch (iterator.GetEntryType()) { @@ -1749,7 +1750,7 @@ Error RoutingManager::RxRaTracker::GetNextRouter(PrefixTableIterator &aIterator, ClearAllBytes(aEntry); SuccessOrExit(error = iterator.AdvanceToNextRouter(Iterator::kRouterIterator)); - iterator.GetRouter()->CopyInfoTo(aEntry, iterator.GetInitTime()); + iterator.GetRouter()->CopyInfoTo(aEntry, iterator.GetInitTime(), iterator.GetInitUptime()); exit: return error; @@ -1758,8 +1759,9 @@ Error RoutingManager::RxRaTracker::GetNextRouter(PrefixTableIterator &aIterator, //--------------------------------------------------------------------------------------------------------------------- // RxRaTracker::Iterator -void RoutingManager::RxRaTracker::Iterator::Init(const Entry *aRoutersHead) +void RoutingManager::RxRaTracker::Iterator::Init(const Entry *aRoutersHead, uint32_t aUptime) { + SetInitUptime(aUptime); SetInitTime(); SetType(kUnspecified); SetRouter(aRoutersHead); @@ -1914,10 +1916,11 @@ bool RoutingManager::RxRaTracker::Router::Matches(const EmptyChecker &aChecker) return !hasFlags && mOnLinkPrefixes.IsEmpty() && mRoutePrefixes.IsEmpty(); } -void RoutingManager::RxRaTracker::Router::CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow) const +void RoutingManager::RxRaTracker::Router::CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow, uint32_t aUptime) const { aEntry.mAddress = mAddress; aEntry.mMsecSinceLastUpdate = aNow - mLastUpdateTime; + aEntry.mAge = aUptime - mDiscoverTime; aEntry.mManagedAddressConfigFlag = mManagedAddressConfigFlag; aEntry.mOtherConfigFlag = mOtherConfigFlag; aEntry.mStubRouterFlag = mStubRouterFlag; diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 17bcbd4b2..058974ba0 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -47,6 +47,10 @@ #error "OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE is required for OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE." #endif +#if !OPENTHREAD_CONFIG_UPTIME_ENABLE +#error "OPENTHREAD_CONFIG_UPTIME_ENABLE is required for OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE" +#endif + #include #include #include @@ -837,14 +841,25 @@ class RoutingManager : public InstanceLocator void DetermineReachabilityTimeout(void); bool Matches(const Ip6::Address &aAddress) const { return aAddress == mAddress; } bool Matches(const EmptyChecker &aChecker); - void CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow) const; + void CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow, uint32_t aUptime) const; using OnLinkPrefixList = OwningList>; using RoutePrefixList = OwningList>; + // `mDiscoverTime` tracks the initial discovery time of + // this router. To accommodate longer durations, the + // `Uptime` is used, as `TimeMilli` (which uses `uint32_t` + // intervals) would be limited to tracking ~ 49 days. + // + // `mLastUpdateTime` tracks the most recent time an RA or + // NA was received from this router. It is bounded due to + // the frequency of reachability checks, so we can safely + // use `TimeMilli` for it. + Ip6::Address mAddress; OnLinkPrefixList mOnLinkPrefixes; RoutePrefixList mRoutePrefixes; + uint32_t mDiscoverTime; TimeMilli mLastUpdateTime; TimeMilli mTimeoutTime; uint8_t mNsProbeCount; @@ -873,9 +888,10 @@ class RoutingManager : public InstanceLocator kRoutePrefix, }; - void Init(const Entry *aRoutersHead); + void Init(const Entry *aRoutersHead, uint32_t aUptime); Error AdvanceToNextRouter(Type aType); Error AdvanceToNextEntry(void); + uint32_t GetInitUptime(void) const { return mData0; } TimeMilli GetInitTime(void) const { return TimeMilli(mData1); } Type GetType(void) const { return static_cast(mData2); } const Entry *GetRouter(void) const { return static_cast *>(mPtr1); } @@ -888,6 +904,7 @@ class RoutingManager : public InstanceLocator private: void SetRouter(const Entry *aRouter) { mPtr1 = aRouter; } + void SetInitUptime(uint32_t aUptime) { mData0 = aUptime; } void SetInitTime(void) { mData1 = TimerMilli::GetNow().GetValue(); } void SetEntry(const void *aEntry) { mPtr2 = aEntry; } bool HasEntry(void) const { return mPtr2 != nullptr; } From e4c50bc5dfcbdbee6cbcf4949d2d05beefb9a4f1 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 9 Jul 2024 10:01:57 -0700 Subject: [PATCH 033/160] [cli] harmonize header include style (#10492) This commit harmonizes header file include style in CLI source files. Most `#include` statements do not have any `#if` guard checks before them, but some do. This commit removes the extra `#if` guard checks. For CLI sub-modules, the `cli_.hpp` header itself will have the proper `#if` guard checks. --- src/cli/cli.cpp | 60 +++++++++++++------------------------------------ src/cli/cli.hpp | 10 ++------- 2 files changed, 18 insertions(+), 52 deletions(-) diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 7680a4f58..b9ec129bb 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -37,59 +37,31 @@ #include #include +#include + +#include +#include +#include +#include +#include #include +#include #include #include #include -#include -#include +#include #include -#include -#include -#include "common/num_utils.hpp" -#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE #include -#endif -#if OPENTHREAD_FTD -#include -#include -#endif -#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE -#include -#endif -#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE +#include #include -#endif -#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE -#include -#endif -#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) -#include -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE -#include -#endif -#endif -#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \ - (OPENTHREAD_FTD || \ - (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)) -#include -#endif -#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE -#include -#endif -#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX -#include -#endif -#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +#include +#include #include -#endif -#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE -#include -#endif -#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD) -#include -#endif +#include +#include + #include "common/new.hpp" +#include "common/num_utils.hpp" #include "common/numeric_limits.hpp" #include "common/string.hpp" #include "mac/channel_mask.hpp" diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 65af2e839..c8e3e4d87 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -51,15 +51,15 @@ #include #include #include -#if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE #include -#endif #include #include #include #include "cli/cli_bbr.hpp" #include "cli/cli_br.hpp" +#include "cli/cli_coap.hpp" +#include "cli/cli_coap_secure.hpp" #include "cli/cli_commissioner.hpp" #include "cli/cli_config.h" #include "cli/cli_dataset.hpp" @@ -77,12 +77,6 @@ #include "cli/cli_tcp.hpp" #include "cli/cli_udp.hpp" #include "cli/cli_utils.hpp" -#if OPENTHREAD_CONFIG_COAP_API_ENABLE -#include "cli/cli_coap.hpp" -#endif -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE -#include "cli/cli_coap_secure.hpp" -#endif #include "common/array.hpp" #include "common/code_utils.hpp" From bf5ddb908ef645c552dddd87c65da64608937398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Bida?= Date: Tue, 9 Jul 2024 19:11:39 +0200 Subject: [PATCH 034/160] [tcat] implementation of tcat Ping command (#10484) Commit implements Tcat ping command `kTlvPing`. Signed-off-by: Przemyslaw Bida --- src/core/meshcop/tcat_agent.cpp | 52 ++++++++++++++++++---- src/core/meshcop/tcat_agent.hpp | 8 ++-- tests/scripts/expect/cli-tcat.exp | 12 +++++ tools/tcat_ble_client/cli/base_commands.py | 33 ++++++++++++++ tools/tcat_ble_client/cli/cli.py | 5 ++- tools/tcat_ble_client/tlv/tcat_tlv.py | 1 + tools/tcat_ble_client/tlv/tlv.py | 13 ++++-- 7 files changed, 107 insertions(+), 17 deletions(-) diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index 11c185922..49470828d 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -355,21 +355,21 @@ bool TcatAgent::CanProcessTlv(uint8_t aTlvType) const return IsCommandClassAuthorized(tlvCommandClass); } -Error TcatAgent::HandleSingleTlv(const Message &aIncommingMessage, Message &aOutgoingMessage) +Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutgoingMessage) { Error error = kErrorParse; ot::Tlv tlv; - uint16_t offset = aIncommingMessage.GetOffset(); + uint16_t offset = aIncomingMessage.GetOffset(); uint16_t length; bool response = false; VerifyOrExit(IsConnected(), error = kErrorInvalidState); - SuccessOrExit(error = aIncommingMessage.Read(offset, tlv)); + SuccessOrExit(error = aIncomingMessage.Read(offset, tlv)); if (tlv.IsExtended()) { ot::ExtendedTlv extTlv; - SuccessOrExit(error = aIncommingMessage.Read(offset, extTlv)); + SuccessOrExit(error = aIncomingMessage.Read(offset, extTlv)); length = extTlv.GetLength(); offset += sizeof(ot::ExtendedTlv); } @@ -392,7 +392,7 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncommingMessage, Message &aOut break; case kTlvSetActiveOperationalDataset: - error = HandleSetActiveOperationalDataset(aIncommingMessage, offset, length); + error = HandleSetActiveOperationalDataset(aIncomingMessage, offset, length); break; case kTlvStartThreadInterface: @@ -405,7 +405,7 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncommingMessage, Message &aOut case kTlvSendApplicationData: LogInfo("Application data len:%d, offset:%d", length, offset); - mAppDataReceiveCallback.InvokeIfSet(&GetInstance(), &aIncommingMessage, offset, + mAppDataReceiveCallback.InvokeIfSet(&GetInstance(), &aIncomingMessage, offset, MapEnum(mCurrentApplicationProtocol), mCurrentServiceName); response = true; error = kErrorNone; @@ -413,6 +413,13 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncommingMessage, Message &aOut case kTlvDecommission: error = HandleDecomission(); break; + case kTlvPing: + error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length); + if (error == kErrorNone) + { + response = true; + } + break; default: error = kErrorInvalidCommand; } @@ -460,14 +467,14 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncommingMessage, Message &aOut return error; } -Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncommingMessage, uint16_t aOffset, uint16_t aLength) +Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength) { Dataset dataset; OffsetRange offsetRange; Error error; offsetRange.Init(aOffset, aLength); - SuccessOrExit(error = dataset.SetFrom(aIncommingMessage, offsetRange)); + SuccessOrExit(error = dataset.SetFrom(aIncomingMessage, offsetRange)); SuccessOrExit(error = dataset.ValidateTlvs()); if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags, @@ -504,6 +511,35 @@ Error TcatAgent::HandleDecomission(void) return error; } +Error TcatAgent::HandlePing(const Message &aIncomingMessage, + Message &aOutgoingMessage, + uint16_t aOffset, + uint16_t aLength) +{ + Error error = kErrorNone; + ot::ExtendedTlv extTlv; + ot::Tlv tlv; + + VerifyOrExit(aLength <= kPingPayloadMaxLength, error = kErrorParse); + if (aLength > ot::Tlv::kBaseTlvMaxLength) + { + extTlv.SetType(kTlvResponseWithPayload); + extTlv.SetLength(aLength); + SuccessOrExit(error = aOutgoingMessage.Append(extTlv)); + } + else + { + tlv.SetType(kTlvResponseWithPayload); + tlv.SetLength(static_cast(aLength)); + SuccessOrExit(error = aOutgoingMessage.Append(tlv)); + } + + SuccessOrExit(error = aOutgoingMessage.AppendBytesFromMessage(aIncomingMessage, aOffset, aLength)); + +exit: + return error; +} + Error TcatAgent::HandleStartThreadInterface(void) { Error error; diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp index a95b42cfd..5d6d0dd97 100644 --- a/src/core/meshcop/tcat_agent.hpp +++ b/src/core/meshcop/tcat_agent.hpp @@ -350,9 +350,10 @@ class TcatAgent : public InstanceLocator, private NonCopyable Error Connected(MeshCoP::SecureTransport &aTlsContext); void Disconnected(void); - Error HandleSingleTlv(const Message &aIncommingMessage, Message &aOutgoingMessage); - Error HandleSetActiveOperationalDataset(const Message &aIncommingMessage, uint16_t aOffset, uint16_t aLength); + Error HandleSingleTlv(const Message &aIncomingMessage, Message &aOutgoingMessage); + Error HandleSetActiveOperationalDataset(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength); Error HandleDecomission(void); + Error HandlePing(const Message &aIncomingMessage, Message &aOutgoingMessage, uint16_t aOffset, uint16_t aLength); Error HandleStartThreadInterface(void); bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags, @@ -361,7 +362,8 @@ class TcatAgent : public InstanceLocator, private NonCopyable bool CanProcessTlv(uint8_t aTlvType) const; CommandClass GetCommandClass(uint8_t aTlvType) const; - static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; + static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; + static constexpr uint16_t kPingPayloadMaxLength = 512; JoinerPskd mJoinerPskd; const VendorInfo *mVendorInfo; diff --git a/tests/scripts/expect/cli-tcat.exp b/tests/scripts/expect/cli-tcat.exp index 00ce08710..9617cb39a 100755 --- a/tests/scripts/expect/cli-tcat.exp +++ b/tests/scripts/expect/cli-tcat.exp @@ -46,6 +46,18 @@ send "thread start\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" expect_line "\tVALUE:\t0x00" +send "ping\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t10" + +send "ping 255\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t255" + +send "ping 512\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t512" + send "exit\n" expect eof diff --git a/tools/tcat_ble_client/cli/base_commands.py b/tools/tcat_ble_client/cli/base_commands.py index 20d48dcb3..bb0258ee2 100644 --- a/tools/tcat_ble_client/cli/base_commands.py +++ b/tools/tcat_ble_client/cli/base_commands.py @@ -37,6 +37,8 @@ from dataset.dataset import ThreadDataset from utils import select_device_by_user_input from os import path +from time import time +from secrets import token_bytes class HelpCommand(Command): @@ -103,6 +105,37 @@ async def execute_default(self, args, context): return CommandResultTLV(tlv_response) +class PingCommand(Command): + + def get_help_string(self) -> str: + return 'Send echo request to TCAT device.' + + async def execute_default(self, args, context): + bless: BleStreamSecure = context['ble_sstream'] + payload_size = 10 + max_payload = 512 + if len(args) > 0: + payload_size = int(args[0]) + if payload_size > max_payload: + print(f'Payload size too large. Maximum supported value is {max_payload}') + return + to_send = token_bytes(payload_size) + data = TLV(TcatTLVType.PING.value, to_send).to_bytes() + elapsed_time = time() + response = await bless.send_with_resp(data) + elapsed_time = time() - elapsed_time + if not response: + return + + tlv_response = TLV.from_bytes(response) + if tlv_response.value != to_send: + print("Received malformed response.") + + print(f"Roundtrip time {elapsed_time} s.") + + return CommandResultTLV(tlv_response) + + class ThreadStartCommand(Command): def get_help_string(self) -> str: diff --git a/tools/tcat_ble_client/cli/cli.py b/tools/tcat_ble_client/cli/cli.py index 838295805..28c7fb9a1 100644 --- a/tools/tcat_ble_client/cli/cli.py +++ b/tools/tcat_ble_client/cli/cli.py @@ -29,8 +29,8 @@ import readline import shlex from ble.ble_stream_secure import BleStreamSecure -from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, ThreadStateCommand, - ScanCommand) +from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, PingCommand, + ThreadStateCommand, ScanCommand) from cli.dataset_commands import (DatasetCommand) from dataset.dataset import ThreadDataset from typing import Optional @@ -44,6 +44,7 @@ def __init__(self, dataset: ThreadDataset, ble_sstream: Optional[BleStreamSecure 'hello': HelloCommand(), 'commission': CommissionCommand(), 'decommission': DecommissionCommand(), + 'ping': PingCommand(), 'dataset': DatasetCommand(), 'thread': ThreadStateCommand(), 'scan': ScanCommand(), diff --git a/tools/tcat_ble_client/tlv/tcat_tlv.py b/tools/tcat_ble_client/tlv/tcat_tlv.py index 30ca543bf..a276cafb2 100644 --- a/tools/tcat_ble_client/tlv/tcat_tlv.py +++ b/tools/tcat_ble_client/tlv/tcat_tlv.py @@ -32,6 +32,7 @@ class TcatTLVType(Enum): RESPONSE_W_STATUS = 0x01 RESPONSE_W_PAYLOAD = 0x02 DISCONNECT = 0x09 + PING = 0x0A ACTIVE_DATASET = 0x20 DECOMMISSION = 0x60 APPLICATION = 0x82 diff --git a/tools/tcat_ble_client/tlv/tlv.py b/tools/tcat_ble_client/tlv/tlv.py index 75657c41a..f8904fc4e 100644 --- a/tools/tcat_ble_client/tlv/tlv.py +++ b/tools/tcat_ble_client/tlv/tlv.py @@ -58,14 +58,19 @@ def from_bytes(data: bytes) -> TLV: def set_from_bytes(self, data: bytes): self.type = data[0] header_len = 2 + size_offset = 1 if data[1] == 0xFF: header_len = 4 - length = int.from_bytes(data[1:header_len], byteorder='big') + size_offset = 2 + length = int.from_bytes(data[size_offset:header_len], byteorder='big') self.value = data[header_len:header_len + length] def to_bytes(self) -> bytes: - has_long_header = len(self.value) >= 255 + has_long_header = len(self.value) >= 254 header_len = 4 if has_long_header else 2 - len_bytes = len(self.value).to_bytes(header_len - 1, byteorder='big') - header = bytes([self.type]) + len_bytes + len_bytes = len(self.value).to_bytes(header_len // 2, byteorder='big') + type = self.type + if has_long_header: + type = type << 8 | 255 + header = type.to_bytes(header_len // 2, byteorder='big') + len_bytes return header + self.value From 695e7a50a315d8496af9ddd78a2a11e3f3dc2bb3 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 9 Jul 2024 11:24:37 -0700 Subject: [PATCH 035/160] [network-data] refactor anycast dest lookup to `NetworkData::Leader` (#10493) This commit refactors the code and methods responsible for looking up anycast destination, moving them from the `MeshForwarder` class to the more appropriate `NetworkData::Leader` class. This better aligns the responsibilities of each module (e.g. `RouteLookup()` is provided by `NetworkData::Leader). This is a pure refactor with no changes or enhancements to the existing implementation. --- src/core/thread/mesh_forwarder.hpp | 9 -- src/core/thread/mesh_forwarder_ftd.cpp | 133 +----------------- src/core/thread/network_data_leader.hpp | 23 ++++ src/core/thread/network_data_leader_ftd.cpp | 142 ++++++++++++++++++++ 4 files changed, 166 insertions(+), 141 deletions(-) diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index 63a216985..99b622fbd 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -404,13 +404,6 @@ class MeshForwarder : public InstanceLocator, private NonCopyable #endif }; - enum AnycastType : uint8_t - { - kAnycastDhcp6Agent, - kAnycastNeighborDiscoveryAgent, - kAnycastService, - }; - struct RxInfo : public InstanceLocator { static constexpr uint16_t kInfoStringSize = 70; @@ -557,8 +550,6 @@ class MeshForwarder : public InstanceLocator, private NonCopyable void SendDestinationUnreachable(uint16_t aMeshSource, const Ip6::Headers &aIp6Headers); Error UpdateIp6Route(Message &aMessage); Error UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &aMessage); - void EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const; - Error AnycastRouteLookup(uint8_t aServiceId, AnycastType aType, uint16_t &aMeshDest) const; Error UpdateMeshRoute(Message &aMessage); bool UpdateReassemblyList(void); void UpdateFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader, diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 740132b9a..6f7039640 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -408,102 +408,6 @@ Error MeshForwarder::UpdateMeshRoute(Message &aMessage) return error; } -void MeshForwarder::EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const -{ - uint8_t cost = Get().GetPathCost(aDest); - - if ((aBestDest == Mle::kInvalidRloc16) || (cost < aBestCost)) - { - aBestDest = aDest; - aBestCost = cost; - } -} - -Error MeshForwarder::AnycastRouteLookup(uint8_t aServiceId, AnycastType aType, uint16_t &aMeshDest) const -{ - NetworkData::Iterator iterator = NetworkData::kIteratorInit; - uint8_t bestCost = Mle::kMaxRouteCost; - uint16_t bestDest = Mle::kInvalidRloc16; - - switch (aType) - { - case kAnycastDhcp6Agent: - case kAnycastNeighborDiscoveryAgent: - { - NetworkData::OnMeshPrefixConfig config; - Lowpan::Context context; - - SuccessOrExit(Get().GetContext(aServiceId, context)); - - while (Get().GetNextOnMeshPrefix(iterator, config) == kErrorNone) - { - if (config.GetPrefix() != context.mPrefix) - { - continue; - } - - switch (aType) - { - case kAnycastDhcp6Agent: - if (!(config.mDhcp || config.mConfigure)) - { - continue; - } - break; - case kAnycastNeighborDiscoveryAgent: - if (!config.mNdDns) - { - continue; - } - break; - default: - OT_ASSERT(false); - break; - } - - EvaluateRoutingCost(config.mRloc16, bestCost, bestDest); - } - - break; - } - case kAnycastService: - { - NetworkData::ServiceConfig config; - - while (Get().GetNextService(iterator, config) == kErrorNone) - { - if (config.mServiceId != aServiceId) - { - continue; - } - - EvaluateRoutingCost(config.mServerConfig.mRloc16, bestCost, bestDest); - } - - break; - } - } - - if (Mle::IsChildRloc16(bestDest)) - { - // If the selected destination is a child, we use its parent - // as the destination unless the device itself is the - // parent of the `bestDest`. - - uint16_t bestDestParent = Mle::ParentRloc16ForRloc16(bestDest); - - if (!Get().HasRloc16(bestDestParent)) - { - bestDest = bestDestParent; - } - } - - aMeshDest = bestDest; - -exit: - return (bestDest != Mle::kInvalidRloc16) ? kErrorNone : kErrorNoRoute; -} - Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &aMessage) { Mle::MleRouter &mle = Get(); @@ -524,42 +428,7 @@ Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &a { uint16_t aloc16 = aIp6Header.GetDestination().GetIid().GetLocator(); - if (aloc16 == Mle::kAloc16Leader) - { - mMeshDest = mle.GetLeaderRloc16(); - } - else if (aloc16 <= Mle::kAloc16DhcpAgentEnd) - { - uint8_t contextId = static_cast(aloc16 - Mle::kAloc16DhcpAgentStart + 1); - SuccessOrExit(error = AnycastRouteLookup(contextId, kAnycastDhcp6Agent, mMeshDest)); - } - else if (aloc16 <= Mle::kAloc16ServiceEnd) - { - uint8_t serviceId = static_cast(aloc16 - Mle::kAloc16ServiceStart); - SuccessOrExit(error = AnycastRouteLookup(serviceId, kAnycastService, mMeshDest)); - } - else if (aloc16 <= Mle::kAloc16CommissionerEnd) - { - SuccessOrExit(error = Get().FindBorderAgentRloc(mMeshDest)); - } - -#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) - else if (aloc16 == Mle::kAloc16BackboneRouterPrimary) - { - VerifyOrExit(Get().HasPrimary(), error = kErrorDrop); - mMeshDest = Get().GetServer16(); - } -#endif - else if ((aloc16 >= Mle::kAloc16NeighborDiscoveryAgentStart) && - (aloc16 <= Mle::kAloc16NeighborDiscoveryAgentEnd)) - { - uint8_t contextId = static_cast(aloc16 - Mle::kAloc16NeighborDiscoveryAgentStart + 1); - SuccessOrExit(error = AnycastRouteLookup(contextId, kAnycastNeighborDiscoveryAgent, mMeshDest)); - } - else - { - ExitNow(error = kErrorDrop); - } + SuccessOrExit(error = Get().AnycastLookup(aloc16, mMeshDest)); // If the selected ALOC destination, `mMeshDest`, is a sleepy // child of this device, prepare the message for indirect tx diff --git a/src/core/thread/network_data_leader.hpp b/src/core/thread/network_data_leader.hpp index 0c13a2f67..f7ba0c138 100644 --- a/src/core/thread/network_data_leader.hpp +++ b/src/core/thread/network_data_leader.hpp @@ -363,6 +363,19 @@ class Leader : public MutableNetworkData, private NonCopyable */ void IncrementVersionAndStableVersion(void); + /** + * Performs anycast ALOC route lookup using the Network Data. + * + * @param[in] aAloc16 The ALOC16 destination to lookup. + * @param[out] aRloc16 A reference to return the RLOC16 for the selected route. + * + * @retval kErrorNone Successfully lookup best option for @p aAloc16. @p aRloc16 is updated. + * @retval kErrorNoRoute No valid route was found. + * @retval kErrorDrop The @p aAloc16 is not valid. + * + */ + Error AnycastLookup(uint16_t aAloc16, uint16_t &aRloc16) const; + /** * Returns CONTEXT_ID_RESUSE_DELAY value. * @@ -467,6 +480,13 @@ class Leader : public MutableNetworkData, private NonCopyable static constexpr uint8_t kMinServiceId = 0x00; static constexpr uint8_t kMaxServiceId = 0x0f; + enum AnycastType : uint8_t + { + kAnycastDhcp6Agent, + kAnycastNdAgent, + kAnycastService, + }; + class ChangedFlags { public: @@ -551,6 +571,9 @@ class Leader : public MutableNetworkData, private NonCopyable void HandleTimer(void); + Error AnycastLookup(uint8_t aServiceId, AnycastType aType, uint16_t &aRloc16) const; + void EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const; + void RegisterNetworkData(uint16_t aRloc16, const NetworkData &aNetworkData); Error AddPrefix(const PrefixTlv &aPrefix, ChangedFlags &aChangedFlags); diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp index a174ba7c7..684d907cb 100644 --- a/src/core/thread/network_data_leader_ftd.cpp +++ b/src/core/thread/network_data_leader_ftd.cpp @@ -115,6 +115,148 @@ void Leader::IncrementVersions(bool aIncludeStable) return; } +Error Leader::AnycastLookup(uint16_t aAloc16, uint16_t &aRloc16) const +{ + Error error = kErrorNone; + + if (aAloc16 == Mle::kAloc16Leader) + { + aRloc16 = Get().GetLeaderRloc16(); + } + else if (aAloc16 <= Mle::kAloc16DhcpAgentEnd) + { + uint8_t contextId = static_cast(aAloc16 - Mle::kAloc16DhcpAgentStart + 1); + + error = AnycastLookup(contextId, kAnycastDhcp6Agent, aRloc16); + } + else if (aAloc16 <= Mle::kAloc16ServiceEnd) + { + uint8_t serviceId = static_cast(aAloc16 - Mle::kAloc16ServiceStart); + + error = AnycastLookup(serviceId, kAnycastService, aRloc16); + } + else if (aAloc16 <= Mle::kAloc16CommissionerEnd) + { + error = FindBorderAgentRloc(aRloc16); + } +#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) + else if (aAloc16 == Mle::kAloc16BackboneRouterPrimary) + { + VerifyOrExit(Get().HasPrimary(), error = kErrorDrop); + aRloc16 = Get().GetServer16(); + } +#endif + else if ((aAloc16 >= Mle::kAloc16NeighborDiscoveryAgentStart) && (aAloc16 <= Mle::kAloc16NeighborDiscoveryAgentEnd)) + { + uint8_t contextId = static_cast(aAloc16 - Mle::kAloc16NeighborDiscoveryAgentStart + 1); + + error = AnycastLookup(contextId, kAnycastNdAgent, aRloc16); + } + else + { + ExitNow(error = kErrorDrop); + } + +exit: + return error; +} + +Error Leader::AnycastLookup(uint8_t aServiceId, AnycastType aType, uint16_t &aRloc16) const +{ + Iterator iterator = kIteratorInit; + uint8_t bestCost = Mle::kMaxRouteCost; + uint16_t bestDest = Mle::kInvalidRloc16; + + switch (aType) + { + case kAnycastDhcp6Agent: + case kAnycastNdAgent: + { + OnMeshPrefixConfig config; + Lowpan::Context context; + + SuccessOrExit(GetContext(aServiceId, context)); + + while (GetNextOnMeshPrefix(iterator, config) == kErrorNone) + { + if (config.GetPrefix() != context.mPrefix) + { + continue; + } + + switch (aType) + { + case kAnycastDhcp6Agent: + if (!(config.mDhcp || config.mConfigure)) + { + continue; + } + break; + case kAnycastNdAgent: + if (!config.mNdDns) + { + continue; + } + break; + default: + OT_ASSERT(false); + break; + } + + EvaluateRoutingCost(config.mRloc16, bestCost, bestDest); + } + + break; + } + case kAnycastService: + { + ServiceConfig config; + + while (GetNextService(iterator, config) == kErrorNone) + { + if (config.mServiceId != aServiceId) + { + continue; + } + + EvaluateRoutingCost(config.mServerConfig.mRloc16, bestCost, bestDest); + } + + break; + } + } + + if (Mle::IsChildRloc16(bestDest)) + { + // If the selected destination is a child, we use its parent + // as the destination unless the device itself is the + // parent of the `bestDest`. + + uint16_t bestDestParent = Mle::ParentRloc16ForRloc16(bestDest); + + if (!Get().HasRloc16(bestDestParent)) + { + bestDest = bestDestParent; + } + } + + aRloc16 = bestDest; + +exit: + return (bestDest != Mle::kInvalidRloc16) ? kErrorNone : kErrorNoRoute; +} + +void Leader::EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const +{ + uint8_t cost = Get().GetPathCost(aDest); + + if ((aBestDest == Mle::kInvalidRloc16) || (cost < aBestCost)) + { + aBestDest = aDest; + aBestCost = cost; + } +} + void Leader::RemoveBorderRouter(uint16_t aRloc16, MatchMode aMatchMode) { ChangedFlags flags; From cdbb9e5342bd8e3fa88d3b9bde7e7c23e5504ac0 Mon Sep 17 00:00:00 2001 From: TimL Date: Thu, 11 Jul 2024 03:11:38 +1000 Subject: [PATCH 036/160] [posix] deassert DTR and RTS if flow control is disabled (#10454) Many USB radio devices (particularly those based on TI CC2652) have reset and bootloader activation directly connected to DTR/RTS lines. These devices will fail to start, in the default state where both DTR/RTS are asserted on connection. This patch ensures flow control is disabled and both DTR and RTS are deasserted on startup while configuring the terminal. --- src/posix/platform/hdlc_interface.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp index 12726fc72..a1a6c8c9a 100644 --- a/src/posix/platform/hdlc_interface.cpp +++ b/src/posix/platform/hdlc_interface.cpp @@ -600,6 +600,20 @@ int HdlcInterface::OpenFile(const Url::Url &aRadioUrl) { tios.c_cflag |= CRTSCTS; } + else + { +#ifndef __APPLE__ + int flags; +#endif + + tios.c_cflag &= ~(CRTSCTS); + +#ifndef __APPLE__ + // Deassert DTR and RTS + flags = TIOCM_DTR | TIOCM_RTS; + VerifyOrExit(ioctl(fd, TIOCMBIC, &flags) != -1, perror("tiocmbic")); +#endif + } VerifyOrExit((rval = cfsetspeed(&tios, static_cast(speed))) == 0, perror("cfsetspeed")); rval = tcsetattr(fd, TCSANOW, &tios); From b85d4eed7147001f23aecc8668e1bf8ddbf48ac2 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Thu, 11 Jul 2024 01:20:01 +0800 Subject: [PATCH 037/160] [platform] add per-frame tx power capability (#10481) This commit adds a new radio capability to indicates that the platform supports specifying tx power per frame. --- include/openthread/instance.h | 2 +- include/openthread/platform/radio.h | 40 +++++++++++++++++++++-------- src/core/mac/mac_frame.hpp | 8 ++++++ src/core/mac/mac_links.hpp | 1 + src/core/radio/radio.hpp | 2 ++ 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index f533e4c11..e725ecfec 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (425) +#define OPENTHREAD_API_VERSION (426) /** * @addtogroup api-instance diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index d06510383..b1afdaa83 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -127,16 +127,17 @@ typedef uint16_t otRadioCaps; */ enum { - OT_RADIO_CAPS_NONE = 0, ///< Radio supports no capability. - OT_RADIO_CAPS_ACK_TIMEOUT = 1 << 0, ///< Radio supports AckTime event. - OT_RADIO_CAPS_ENERGY_SCAN = 1 << 1, ///< Radio supports Energy Scans. - OT_RADIO_CAPS_TRANSMIT_RETRIES = 1 << 2, ///< Radio supports tx retry logic with collision avoidance (CSMA). - OT_RADIO_CAPS_CSMA_BACKOFF = 1 << 3, ///< Radio supports CSMA backoff for frame transmission (but no retry). - OT_RADIO_CAPS_SLEEP_TO_TX = 1 << 4, ///< Radio supports direct transition from sleep to TX with CSMA. - OT_RADIO_CAPS_TRANSMIT_SEC = 1 << 5, ///< Radio supports tx security. - OT_RADIO_CAPS_TRANSMIT_TIMING = 1 << 6, ///< Radio supports tx at specific time. - OT_RADIO_CAPS_RECEIVE_TIMING = 1 << 7, ///< Radio supports rx at specific time. - OT_RADIO_CAPS_RX_ON_WHEN_IDLE = 1 << 8, ///< Radio supports RxOnWhenIdle handling. + OT_RADIO_CAPS_NONE = 0, ///< Radio supports no capability. + OT_RADIO_CAPS_ACK_TIMEOUT = 1 << 0, ///< Radio supports AckTime event. + OT_RADIO_CAPS_ENERGY_SCAN = 1 << 1, ///< Radio supports Energy Scans. + OT_RADIO_CAPS_TRANSMIT_RETRIES = 1 << 2, ///< Radio supports tx retry logic with collision avoidance (CSMA). + OT_RADIO_CAPS_CSMA_BACKOFF = 1 << 3, ///< Radio supports CSMA backoff for frame transmission (but no retry). + OT_RADIO_CAPS_SLEEP_TO_TX = 1 << 4, ///< Radio supports direct transition from sleep to TX with CSMA. + OT_RADIO_CAPS_TRANSMIT_SEC = 1 << 5, ///< Radio supports tx security. + OT_RADIO_CAPS_TRANSMIT_TIMING = 1 << 6, ///< Radio supports tx at specific time. + OT_RADIO_CAPS_RECEIVE_TIMING = 1 << 7, ///< Radio supports rx at specific time. + OT_RADIO_CAPS_RX_ON_WHEN_IDLE = 1 << 8, ///< Radio supports RxOnWhenIdle handling. + OT_RADIO_CAPS_TRANSMIT_FRAME_POWER = 1 << 9, ///< Radio supports setting per-frame transmit power. }; #define OT_PANID_BROADCAST 0xffff ///< IEEE 802.15.4 Broadcast PAN ID @@ -315,6 +316,25 @@ typedef struct otRadioFrame */ uint8_t mRxChannelAfterTxDone; + /** + * The transmit power in dBm. + * + * If the platform layer does not provide `OT_RADIO_CAPS_TRANSMIT_FRAME_POWER` capability, it can ignore + * this value. + * + * If the value is OT_RADIO_POWER_INVALID, then the platform should ignore this value and transmit the frame + * with its default transmit power. + * + * Otherwise, the platform should transmit this frame with the maximum power no larger than minimal of the + * following values: + * 1. mTxPower, + * 2. The power limit set by otPlatRadioSetChannelTargetPower(), + * 3. The power limit set by otPlatRadioSetChannelMaxTransmitPower(), + * 4. The power limit set by otPlatRadioSetRegion(). + * + */ + int8_t mTxPower; + /** * Indicates whether frame counter and CSL IEs are properly updated in the header. * diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index ccf8a500e..a515ddc13 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -1276,6 +1276,14 @@ class TxFrame : public Frame SetRxChannelAfterTxDone(aChannel); } + /** + * Sets TX power to send the frame. + * + * @param[in] aTxPower The tx power used for transmission. + * + */ + void SetTxPower(int8_t aTxPower) { mInfo.mTxInfo.mTxPower = aTxPower; } + /** * Gets the RX channel after frame TX is done. * diff --git a/src/core/mac/mac_links.hpp b/src/core/mac/mac_links.hpp index 5d9a8f09a..a9ab39d8f 100644 --- a/src/core/mac/mac_links.hpp +++ b/src/core/mac/mac_links.hpp @@ -186,6 +186,7 @@ class TxFrames : InstanceLocator mTxFrame802154.SetTxDelay(0); mTxFrame802154.SetTxDelayBaseTime(0); #endif + mTxFrame802154.SetTxPower(kRadioPowerInvalid); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE mTxFrame802154.SetCslIePresent(false); #endif diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index c8bb938a3..b76763083 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -52,6 +52,8 @@ static constexpr uint32_t kUsPerTenSymbols = OT_US_PER_TEN_SYMBOLS; ///< Time fo static constexpr uint32_t kRadioHeaderShrDuration = 160; ///< Duration of SHR in us static constexpr uint32_t kRadioHeaderPhrDuration = 32; ///< Duration of PHR in us +static constexpr int8_t kRadioPowerInvalid = OT_RADIO_POWER_INVALID; ///< Invalid TX power value + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE /** * Minimum CSL period supported in units of 10 symbols. From 32fe4c7cdd869d960b8fd9793780c6c5390cff4c Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 10 Jul 2024 10:21:17 -0700 Subject: [PATCH 038/160] [dataset-manager] apply partial dataset config on channel set failure (#10490) This commit updates `DatasetManager::ApplyConfiguration()` to apply the rest of the parameters in the Dataset even if setting the channel fails. The channel error is now logged as `LogCrit()`. --- src/core/meshcop/dataset_manager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/meshcop/dataset_manager.cpp b/src/core/meshcop/dataset_manager.cpp index de2c1d886..3ebd90d13 100644 --- a/src/core/meshcop/dataset_manager.cpp +++ b/src/core/meshcop/dataset_manager.cpp @@ -199,8 +199,7 @@ Error DatasetManager::ApplyConfiguration(const Dataset &aDataset) const if (error != kErrorNone) { - LogWarn("ApplyConfiguration() Failed to set channel to %d (%s)", channel, ErrorToString(error)); - ExitNow(); + LogCrit("Failed to set channel to %u when applying dataset: %s", channel, ErrorToString(error)); } break; From 4c84e4d64265022b5d252dd586247e598b99f199 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 10 Jul 2024 14:47:18 -0700 Subject: [PATCH 039/160] [routing-manager] indicate peer BRs in discovered infra-if routers (#10448) This commit updates the public API `otBorderRoutingGetNextRouterEntry` and `otBorderRoutingRouterEntry` structure to indicate whether a discovered router on an infrastructure link is likely a peer Thread Border Router (BR) connected to the same Thread mesh. The related CLI commands are also updated. Additionally, this commit adds new tests to validate the discovery and tracking of peer BRs. --- include/openthread/border_routing.h | 6 + include/openthread/instance.h | 2 +- src/cli/README_BR.md | 1 + src/cli/cli_br.cpp | 9 ++ src/core/border_router/routing_manager.cpp | 12 ++ src/core/border_router/routing_manager.hpp | 1 + .../cli/test-500-two-brs-two-networks.py | 15 ++- .../toranj/cli/test-503-peer-tbr-discovery.py | 118 ++++++++++++++++++ 8 files changed, 158 insertions(+), 6 deletions(-) create mode 100755 tests/toranj/cli/test-503-peer-tbr-discovery.py diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h index 95e50a116..19923aa6b 100644 --- a/include/openthread/border_routing.h +++ b/include/openthread/border_routing.h @@ -94,6 +94,11 @@ typedef struct otBorderRoutingPrefixTableIterator /** * Represents a discovered router on the infrastructure link. * + * The `mIsPeerBr` field requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`. Routing Manager + * determines whether the router is a peer BR (connected to the same Thread mesh network) by comparing its advertised + * PIO/RIO prefixes with the entries in the Thread Network Data. While this method is generally effective, it may not + * be 100% accurate in all scenarios, so the `mIsPeerBr` flag should be used with caution. + * */ typedef struct otBorderRoutingRouterEntry { @@ -105,6 +110,7 @@ typedef struct otBorderRoutingRouterEntry bool mStubRouterFlag : 1; ///< The router's Stub Router flag. bool mIsLocalDevice : 1; ///< This router is the local device (this BR). bool mIsReachable : 1; ///< This router is reachable. + bool mIsPeerBr : 1; ///< This router is (likely) a peer BR. } otBorderRoutingRouterEntry; /** diff --git a/include/openthread/instance.h b/include/openthread/instance.h index e725ecfec..7f18df902 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (426) +#define OPENTHREAD_API_VERSION (427) /** * @addtogroup api-instance diff --git a/src/cli/README_BR.md b/src/cli/README_BR.md index cd7783fd9..821dc269d 100644 --- a/src/cli/README_BR.md +++ b/src/cli/README_BR.md @@ -355,6 +355,7 @@ Info per router: - Reachability flag: A router is marked as unreachable if it fails to respond to multiple Neighbor Solicitation probes. - Age: Duration interval since this router was first discovered. It is formatted as `{hh}:{mm}:{ss}` for hours, minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the format is `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds. - `(this BR)` is appended when the router is the local device itself. +- `(peer BR)` is appended when the router is likely a peer BR connected to the same Thread mesh. This requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`. ```bash > br routers diff --git a/src/cli/cli_br.cpp b/src/cli/cli_br.cpp index 7839823bb..cab216d28 100644 --- a/src/cli/cli_br.cpp +++ b/src/cli/cli_br.cpp @@ -536,6 +536,8 @@ template <> otError Br::Process(Arg aArgs[]) * minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the format is * `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds. * - `(this BR)` is appended when the router is the local device itself. + * - `(peer BR)` is appended when the router is likely a peer BR connected to the same Thread mesh. This requires + * `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`. * @sa otBorderRoutingGetNextRouterEntry */ template <> otError Br::Process(Arg aArgs[]) @@ -576,6 +578,13 @@ void Br::OutputRouterInfo(const otBorderRoutingRouterEntry &aEntry, RouterOutput { OutputFormat(" (this BR)"); } + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + if (aEntry.mIsPeerBr) + { + OutputFormat(" (peer BR)"); + } +#endif } OutputNewLine(); diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 86fc96d3d..86d35cc8a 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1916,6 +1916,17 @@ bool RoutingManager::RxRaTracker::Router::Matches(const EmptyChecker &aChecker) return !hasFlags && mOnLinkPrefixes.IsEmpty() && mRoutePrefixes.IsEmpty(); } +bool RoutingManager::RxRaTracker::Router::IsPeerBr(void) const +{ + // Determines whether the router is a peer BR (connected to the + // same Thread mesh network). It must have at least one entry + // (on-link or route) and all entries should be marked to be + // disregarded. While this model is generally effective to detect + // peer BRs, it may not be 100% accurate in all scenarios. + + return mAllEntriesDisregarded && !(mOnLinkPrefixes.IsEmpty() && mRoutePrefixes.IsEmpty()); +} + void RoutingManager::RxRaTracker::Router::CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow, uint32_t aUptime) const { aEntry.mAddress = mAddress; @@ -1926,6 +1937,7 @@ void RoutingManager::RxRaTracker::Router::CopyInfoTo(RouterEntry &aEntry, TimeMi aEntry.mStubRouterFlag = mStubRouterFlag; aEntry.mIsLocalDevice = mIsLocalDevice; aEntry.mIsReachable = IsReachable(); + aEntry.mIsPeerBr = IsPeerBr(); } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 058974ba0..c9753b88a 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -841,6 +841,7 @@ class RoutingManager : public InstanceLocator void DetermineReachabilityTimeout(void); bool Matches(const Ip6::Address &aAddress) const { return aAddress == mAddress; } bool Matches(const EmptyChecker &aChecker); + bool IsPeerBr(void) const; void CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow, uint32_t aUptime) const; using OnLinkPrefixList = OwningList>; diff --git a/tests/toranj/cli/test-500-two-brs-two-networks.py b/tests/toranj/cli/test-500-two-brs-two-networks.py index 12c61a7e7..c14b5930d 100755 --- a/tests/toranj/cli/test-500-two-brs-two-networks.py +++ b/tests/toranj/cli/test-500-two-brs-two-networks.py @@ -97,11 +97,16 @@ verify(br1_favored_onlink == br2_favored_onlink) -br1_routers = br1.br_get_routers() -br2_routers = br2.br_get_routers() - -verify(len(br1_routers) > 0) -verify(len(br2_routers) > 0) +# Check that the two BRs discover and track each other (not as peer BR since +# connected to different networks). + +for br in [br1, br1]: + routers = br.br_get_routers() + verify(len(routers) > 0) + for router in routers: + verify('reachable:yes' in router) + verify('Stub:1' in router) + verify(not router.endswith('(peer BR)')) # ----------------------------------------------------------------------------------------------------------------------- # Test finished diff --git a/tests/toranj/cli/test-503-peer-tbr-discovery.py b/tests/toranj/cli/test-503-peer-tbr-discovery.py new file mode 100755 index 000000000..d6816a4bf --- /dev/null +++ b/tests/toranj/cli/test-503-peer-tbr-discovery.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024, 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: +# +# Check tracking of peer BR (connected to same Thread mesh). +# +# ______________ +# / \ +# br1 --- br2 --- br3 +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 60 +cli.Node.set_time_speedup_factor(speedup) + +br1 = cli.Node() +br2 = cli.Node() +br3 = cli.Node() + +IF_INDEX = 1 + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +br1.form("peer-brs") +br2.join(br1) +br3.join(br2) + +verify(br1.get_state() == 'leader') +verify(br2.get_state() == 'router') +verify(br3.get_state() == 'router') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test implementation + +# Start first border router `br1` + +br1.srp_server_set_addr_mode('unicast') +br1.srp_server_auto_enable() + +br1.br_init(IF_INDEX, 1) +br1.br_enable() + +time.sleep(1) +verify(br1.br_get_state() == 'running') + +br1_local_omr = br1.br_get_local_omrprefix() +br1_favored_omr = br1.br_get_favored_omrprefix().split()[0] +verify(br1_local_omr == br1_favored_omr) + +br1_local_onlink = br1.br_get_local_onlinkprefix() +br1_favored_onlink = br1.br_get_favored_onlinkprefix().split()[0] +verify(br1_local_onlink == br1_favored_onlink) + +# Start `br2` and `br3` together + +br2.br_init(IF_INDEX, 1) +br2.br_enable() + +br3.br_init(IF_INDEX, 1) +br3.br_enable() + +time.sleep(1) +verify(br2.br_get_state() == 'running') +verify(br3.br_get_state() == 'running') + +# Validate that all BRs discovered the other ones as peer BR + +for br in [br1, br2, br3]: + routers = br.br_get_routers() + verify(len(routers) == 2) + for router in routers: + verify(router.endswith('(peer BR)')) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) From 78ecafb0d75aa7d72060a1163e553b9dde44cb3f Mon Sep 17 00:00:00 2001 From: Li Cao Date: Thu, 11 Jul 2024 13:56:52 +0800 Subject: [PATCH 040/160] [radio] move required radio caps from radio spinel into posix platform (#10502) This commit moves the definition of the 'RequiredRadioCaps' from `RadioSpinel` to posix `Radio` class. Because this definition depends on `OPENTHREAD_CONFIG_THREAD_VERSION`. And we want to remove lib's dependency on OT core. And lib as a tool should be able to decide what caps to check at runtime. --- src/lib/spinel/radio_spinel.cpp | 21 +++++++++------------ src/lib/spinel/radio_spinel.hpp | 9 +++++++-- src/posix/platform/radio.cpp | 2 +- src/posix/platform/radio.hpp | 6 ++++++ 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index ae2622e69..8b3e2b5ea 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -115,7 +115,10 @@ RadioSpinel::RadioSpinel(void) memset(&mCallbacks, 0, sizeof(mCallbacks)); } -void RadioSpinel::Init(bool aSkipRcpCompatibilityCheck, bool aSoftwareReset, SpinelDriver *aSpinelDriver) +void RadioSpinel::Init(bool aSkipRcpCompatibilityCheck, + bool aSoftwareReset, + SpinelDriver *aSpinelDriver, + otRadioCaps aRequiredRadioCaps) { otError error = OT_ERROR_NONE; bool supportsRcpApiVersion; @@ -147,7 +150,7 @@ void RadioSpinel::Init(bool aSkipRcpCompatibilityCheck, bool aSoftwareReset, Spi if (!aSkipRcpCompatibilityCheck) { SuccessOrDie(CheckRcpApiVersion(supportsRcpApiVersion, supportsRcpMinHostApiVersion)); - SuccessOrDie(CheckRadioCapabilities()); + SuccessOrDie(CheckRadioCapabilities(aRequiredRadioCaps)); } mRxRadioFrame.mPsdu = mRxPsdu; @@ -215,23 +218,17 @@ void RadioSpinel::InitializeCaps(bool &aSupportsRcpApiVersion, bool &aSupportsRc sSupportsLogCrashDump = GetSpinelDriver().CoprocessorHasCap(SPINEL_CAP_RCP_LOG_CRASH_DUMP); } -otError RadioSpinel::CheckRadioCapabilities(void) +otError RadioSpinel::CheckRadioCapabilities(otRadioCaps aRequiredRadioCaps) { - const otRadioCaps kRequiredRadioCaps = -#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 - OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING | -#endif - OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_CSMA_BACKOFF; - otError error = OT_ERROR_NONE; unsigned int radioCaps; EXPECT_NO_ERROR(error = Get(SPINEL_PROP_RADIO_CAPS, SPINEL_DATATYPE_UINT_PACKED_S, &radioCaps)); sRadioCaps = static_cast(radioCaps); - if ((sRadioCaps & kRequiredRadioCaps) != kRequiredRadioCaps) + if ((sRadioCaps & aRequiredRadioCaps) != aRequiredRadioCaps) { - otRadioCaps missingCaps = (sRadioCaps & kRequiredRadioCaps) ^ kRequiredRadioCaps; + otRadioCaps missingCaps = (sRadioCaps & aRequiredRadioCaps) ^ aRequiredRadioCaps; // missingCaps may be an unused variable when LogCrit is blank // avoid compiler warning in that case @@ -1796,7 +1793,7 @@ void RadioSpinel::PlatDiagOutput(const char *aFormat, ...) va_end(args); } -#endif +#endif // OPENTHREAD_CONFIG_DIAG_ENABLE uint32_t RadioSpinel::GetRadioChannelMask(bool aPreferred) { diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 62027c2d8..bfee96176 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -163,9 +163,14 @@ class RadioSpinel : private Logger * @param[in] aSoftwareReset When doing RCP recovery, TRUE to try software reset first, FALSE to * directly do a hardware reset. * @param[in] aSpinelDriver A pointer to the spinel driver instance that this object depends on. + * @param[in] aRequiredRadioCaps The required radio capabilities. RadioSpinel will check if RCP has + * the required capabilities during initiailization. * */ - void Init(bool aSkipRcpCompatibilityCheck, bool aSoftwareReset, SpinelDriver *aSpinelDriver); + void Init(bool aSkipRcpCompatibilityCheck, + bool aSoftwareReset, + SpinelDriver *aSpinelDriver, + otRadioCaps aRequiredRadioCaps); /** * This method sets the notification callbacks. @@ -1115,7 +1120,7 @@ class RadioSpinel : private Logger SpinelDriver &GetSpinelDriver(void) const; otError CheckSpinelVersion(void); - otError CheckRadioCapabilities(void); + otError CheckRadioCapabilities(otRadioCaps aRequiredRadioCaps); otError CheckRcpApiVersion(bool aSupportsRcpApiVersion, bool aSupportsRcpMinHostApiVersion); void InitializeCaps(bool &aSupportsRcpApiVersion, bool &aSupportsRcpMinHostApiVersion); diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index ba63d1b8d..c82b07ebf 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -93,7 +93,7 @@ void Radio::Init(const char *aUrl) skipCompatibilityCheck = mRadioUrl.HasParam("skip-rcp-compatibility-check"); mRadioSpinel.SetCallbacks(callbacks); - mRadioSpinel.Init(skipCompatibilityCheck, resetRadio, &GetSpinelDriver()); + mRadioSpinel.Init(skipCompatibilityCheck, resetRadio, &GetSpinelDriver(), kRequiredRadioCaps); ProcessRadioUrl(mRadioUrl); } diff --git a/src/posix/platform/radio.hpp b/src/posix/platform/radio.hpp index 4a5bea408..06f302b7a 100644 --- a/src/posix/platform/radio.hpp +++ b/src/posix/platform/radio.hpp @@ -115,6 +115,12 @@ class Radio : public Logger #error "No Spinel interface is specified!" #endif + static constexpr otRadioCaps kRequiredRadioCaps = +#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 + OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING | +#endif + OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_CSMA_BACKOFF; + RadioUrl mRadioUrl; #if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE Spinel::VendorRadioSpinel mRadioSpinel; From 77ca3c54d17a7aab5befce32dda9cd7c2680dbd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Feh=C3=A9r?= <82935051+feherdave@users.noreply.github.com> Date: Thu, 11 Jul 2024 18:34:02 +0200 Subject: [PATCH 041/160] [cli] add support for CoAP URI queries in CLI requests (#10003) New feature: Added CoAP URI query processing functionality to CLI. Solution: URI string is checked for a '?' character. If found, the URI query related part is moved to another string. Afterwards, the modified `coapUri` string will contain only the path-related parts, and a second string (`coapUriQuery`) will hold only the URI query related parts. Then `coapUri` is passed to the original `otCoapMessageAppendUriPathOptions()` method and `coapUriQuery` is passed to a new method called `otCoapMessageAppendUriQueryOptions()`, which - similarly to the other - splits the URI query part into more, smaller pieces by the delimiter '&' and adds them as separate options to the CoAP message making the request generated by this CLI function RFC compliant. If '?' is not found, everything is processed the old way. --- include/openthread/coap.h | 13 +++++++++++++ include/openthread/instance.h | 2 +- src/cli/cli_coap.cpp | 18 ++++++++++++++++-- src/core/api/coap_api.cpp | 5 +++++ src/core/coap/coap_message.cpp | 18 ++++++++++++++++++ src/core/coap/coap_message.hpp | 12 ++++++++++++ 6 files changed, 65 insertions(+), 3 deletions(-) diff --git a/include/openthread/coap.h b/include/openthread/coap.h index d5d02060b..a19fb16a6 100644 --- a/include/openthread/coap.h +++ b/include/openthread/coap.h @@ -608,6 +608,19 @@ otError otCoapMessageAppendObserveOption(otMessage *aMessage, uint32_t aObserve) */ otError otCoapMessageAppendUriPathOptions(otMessage *aMessage, const char *aUriPath); +/** + * Appends a Uri-Query option. + * + * @param[in,out] aMessage A pointer to the CoAP message. + * @param[in] aUriQuery A pointer to a NULL-terminated string. + * + * @retval OT_ERROR_NONE Successfully appended the option. + * @retval OT_ERROR_INVALID_ARGS The option type is not equal or greater than the last option type. + * @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size. + * + */ +otError otCoapMessageAppendUriQueryOptions(otMessage *aMessage, const char *aUriQuery); + /** * Converts a CoAP Block option SZX field to the actual block size * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 7f18df902..ef6e2d4f8 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (427) +#define OPENTHREAD_API_VERSION (428) /** * @addtogroup api-instance diff --git a/src/cli/cli_coap.cpp b/src/cli/cli_coap.cpp index 5edbe00c6..e91439109 100644 --- a/src/cli/cli_coap.cpp +++ b/src/cli/cli_coap.cpp @@ -583,7 +583,8 @@ otError Coap::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode) otError error = OT_ERROR_NONE; otMessage *message = nullptr; otMessageInfo messageInfo; - uint16_t payloadLength = 0; + uint16_t payloadLength = 0; + char *uriQueryStartPtr = nullptr; // Default parameters char coapUri[kMaxUriLength] = "test"; @@ -682,7 +683,20 @@ otError Coap::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode) } #endif - SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri)); + uriQueryStartPtr = const_cast(StringFind(coapUri, '?')); + + if (uriQueryStartPtr == nullptr) + { + // "?" doesn't present in URI --> contains only URI path parts + SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri)); + } + else + { + // "?" presents in URI --> contains URI path AND URI query parts + *uriQueryStartPtr++ = '\0'; + SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri)); + SuccessOrExit(error = otCoapMessageAppendUriQueryOptions(message, uriQueryStartPtr)); + } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE if (coapBlock) diff --git a/src/core/api/coap_api.cpp b/src/core/api/coap_api.cpp index 25f3208e7..7539eba75 100644 --- a/src/core/api/coap_api.cpp +++ b/src/core/api/coap_api.cpp @@ -99,6 +99,11 @@ otError otCoapMessageAppendUriPathOptions(otMessage *aMessage, const char *aUriP return AsCoapMessage(aMessage).AppendUriPathOptions(aUriPath); } +otError otCoapMessageAppendUriQueryOptions(otMessage *aMessage, const char *aUriQuery) +{ + return AsCoapMessage(aMessage).AppendUriQueryOptions(aUriQuery); +} + uint16_t otCoapBlockSizeFromExponent(otCoapBlockSzx aSize) { return static_cast(1 << (static_cast(aSize) + Coap::Message::kBlockSzxBase)); diff --git a/src/core/coap/coap_message.cpp b/src/core/coap/coap_message.cpp index 6743f2055..cc82145e8 100644 --- a/src/core/coap/coap_message.cpp +++ b/src/core/coap/coap_message.cpp @@ -275,6 +275,24 @@ Error Message::ReadUriPathOptions(char (&aUriPath)[kMaxReceivedUriPath + 1]) con return error; } +Error Message::AppendUriQueryOptions(const char *aUriQuery) +{ + Error error = kErrorNone; + const char *cur = aUriQuery; + const char *end; + + while ((end = StringFind(cur, '&')) != nullptr) + { + SuccessOrExit(error = AppendOption(kOptionUriQuery, static_cast(end - cur), cur)); + cur = end + 1; + } + + SuccessOrExit(error = AppendStringOption(kOptionUriQuery, cur)); + +exit: + return error; +} + Error Message::AppendBlockOption(Message::BlockType aType, uint32_t aNum, bool aMore, otCoapBlockSzx aSize) { Error error = kErrorNone; diff --git a/src/core/coap/coap_message.hpp b/src/core/coap/coap_message.hpp index 62128b6a3..6156df8ce 100644 --- a/src/core/coap/coap_message.hpp +++ b/src/core/coap/coap_message.hpp @@ -479,6 +479,18 @@ class Message : public ot::Message */ Error ReadUriPathOptions(char (&aUriPath)[kMaxReceivedUriPath + 1]) const; + /** + * Appends a Uri-Query option. + * + * @param[in] aUriQuery A pointer to a null-terminated string. + * + * @retval kErrorNone Successfully appended the option. + * @retval kErrorInvalidArgs The option type is not equal or greater than the last option type. + * @retval kErrorNoBufs The option length exceeds the buffer size. + * + */ + Error AppendUriQueryOptions(const char *aUriQuery); + /** * Appends a Block option * From fbb715baa747af72a05dd9f104559773d7bbe479 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Fri, 12 Jul 2024 01:37:15 +0800 Subject: [PATCH 042/160] [test] test whether the RCP support CSL transmitter (#10495) This commit adds a test case to `cp-tests` to test whether the RCP supports the CSL transmitter. --- tools/cp-caps/README.md | 14 +++++++++-- tools/cp-caps/rcp_caps_test.py | 46 ++++++++++++++++++++++++++++++++++ tools/otci/otci/otci.py | 14 ++++++++++- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/tools/cp-caps/README.md b/tools/cp-caps/README.md index 6eab16592..2a4909647 100644 --- a/tools/cp-caps/README.md +++ b/tools/cp-caps/README.md @@ -41,7 +41,7 @@ $ git clone git@github.com:openthread/ot-nrf528xx.git $ cd ot-nrf528xx/ $ git submodule update --init $ ./script/bootstrap -$ ./script/build nrf52840 UART_trans -DOT_DIAGNOSTIC=ON +$ ./script/build nrf52840 UART_trans -DOT_DIAGNOSTIC=ON -DOT_CSL_RECEIVER=ON $ arm-none-eabi-objcopy -O ihex build/bin/ot-cli-ftd ot-cli-ftd.hex $ nrfjprog -f nrf52 --chiperase --program ot-cli-ftd.hex --reset ``` @@ -54,12 +54,13 @@ Show help info. ```bash $ python3 ./tools/cp-caps/rcp_caps_test.py -h -usage: rcp_caps_test.py [-h] [-d] [-v] +usage: rcp_caps_test.py [-h] [-c] [-d] [-v] This script is used for testing RCP capabilities. options: -h, --help show this help message and exit + -c, --csl test whether the RCP supports CSL transmitter -d, --diag-commands test whether the RCP supports all diag commands -v, --verbose output verbose information @@ -123,3 +124,12 @@ diag gpio get 2 ------------------------------------------ NotSupported diag gpio set 2 0 ---------------------------------------- NotSupported diag gpio set 2 1 ---------------------------------------- NotSupported ``` + +### Test CSL Transmitter + +The parameter `-c` or `--csl` starts to test whether the RCP supports the CSL transmitter. + +```bash +$ DUT_ADB_USB=TW69UCKFZTGM95OR REF_CLI_SERIAL=/dev/ttyACM0 python3 ./tools/cp-caps/rcp_caps_test.py -c +CSL Transmitter ------------------------------------------ OK +``` diff --git a/tools/cp-caps/rcp_caps_test.py b/tools/cp-caps/rcp_caps_test.py index 755fb58dd..adaa86a80 100644 --- a/tools/cp-caps/rcp_caps_test.py +++ b/tools/cp-caps/rcp_caps_test.py @@ -79,9 +79,44 @@ def test_diag_commands(self): self.__ref.diag_stop() self.__dut.diag_stop() + def test_csl(self): + self.__dataset = self.__get_default_dataset() + self.__test_csl_transmitter() + # # Private methods # + def __get_default_dataset(self): + return self.__dut.create_dataset(channel=20, network_key='00112233445566778899aabbccddcafe') + + def __test_csl_transmitter(self): + packets = 10 + + self.__dut.factory_reset() + self.__ref.factory_reset() + + self.__dut.join(self.__dataset) + self.__dut.wait_for('state', 'leader') + + # Set the reference device as an SSED + self.__ref.set_mode('-') + self.__ref.config_csl(channel=15, period=320000, timeout=100) + self.__ref.join(self.__dataset) + self.__ref.wait_for('state', 'child') + + child_table = self.__dut.get_child_table() + ret = len(child_table) == 1 and child_table[1]['csl'] + + if ret: + ref_mleid = self.__ref.get_ipaddr_mleid() + result = self.__dut.ping(ref_mleid, count=packets, interval=1) + ret = result['transmitted_packets'] == result['received_packets'] == packets + + self.__dut.leave() + self.__ref.leave() + + self.__output_format_bool('CSL Transmitter', ret) + def __test_diag_channel(self): channel = 20 commands = ['diag channel', f'diag channel {channel}'] @@ -378,6 +413,14 @@ def parse_arguments(): description=description_msg, epilog=epilog_msg) + parser.add_argument( + '-c', + '--csl', + action='store_true', + default=False, + help='test whether the RCP supports CSL transmitter', + ) + parser.add_argument( '-d', '--diag-commands', @@ -409,6 +452,9 @@ def main(): if arguments.diag_commands is True: rcp_caps.test_diag_commands() + if arguments.csl is True: + rcp_caps.test_csl() + if __name__ == '__main__': main() diff --git a/tools/otci/otci/otci.py b/tools/otci/otci/otci.py index e8e54c049..e3fc57ca3 100644 --- a/tools/otci/otci/otci.py +++ b/tools/otci/otci/otci.py @@ -746,7 +746,16 @@ def get_child_table(self) -> Dict[ChildId, Dict[str, Any]]: id = int(col("ID")) r, d, n = int(col("R")), int(col("D")), int(col("N")) - mode = DeviceMode(f'{"r" if r else ""}{"d" if d else ""}{"n" if n else ""}') + + # + # Device mode flags: + # + # r: rx-on-when-idle + # d: Full Thread Device + # n: Full Network Data + # -: no flags set (rx-off-when-idle, minimal Thread device, stable network data) + mode = DeviceMode( + f'{"r" if r else ""}{"d" if d else ""}{"n" if n else ""}{"-" if r == d == n == 0 else ""}') child = { 'id': ChildId(id), @@ -768,6 +777,9 @@ def get_child_table(self) -> Dict[ChildId, Dict[str, Any]]: if 'QMsgCnt' in headers: child['qmsgcnt'] = int(col('QMsgCnt')) + if 'Suprvsn' in headers: + child['suprvsn'] = int(col('Suprvsn')) + table[ChildId(id)] = child return table From 2d2e8cf80a0619351cfdad363a5dcc3135b60354 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 11 Jul 2024 14:16:26 -0700 Subject: [PATCH 043/160] [channel-manager] require CSL to enabled with `CSL_CHANNEL_SELECT_ENABLE` (#10266) This commit requires `MAC_CSL_RECEIVER_ENABLE` to be enabled when `CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` is also enabled. This is checked and enforced in the `channel_manager.hpp`, emitting an `#error` if it fails. This simplifies the `#if` checks related to this in the `ChannelManager` class and CLI module. --- .github/workflows/simulation-1.2.yml | 6 ------ src/cli/cli.cpp | 14 ++++++------- src/core/api/channel_manager_api.cpp | 5 ++--- src/core/instance/instance.cpp | 3 +-- src/core/instance/instance.hpp | 6 ++---- src/core/utils/channel_manager.cpp | 30 +++++++++++++--------------- src/core/utils/channel_manager.hpp | 23 ++++++++++++--------- 7 files changed, 39 insertions(+), 48 deletions(-) diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml index 1941300f7..515e2ab3e 100644 --- a/.github/workflows/simulation-1.2.yml +++ b/.github/workflows/simulation-1.2.yml @@ -245,9 +245,6 @@ jobs: COVERAGE: 1 THREAD_VERSION: 1.3 VIRTUAL_TIME: 1 - INTER_OP: 1 - INTER_OP_BBR: 1 - ADDON_FEAT_1_2: 1 steps: - name: Harden Runner uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 @@ -268,9 +265,6 @@ jobs: - name: Run run: | ulimit -c unlimited - ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py - ./script/test cert_suite ./tests/scripts/thread-cert/test_*.py - ./script/test cert_suite ./tests/scripts/thread-cert/v1_2_*.py ./script/test cert_suite ./tests/scripts/thread-cert/addon_test_channel_manager_autocsl*.py - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 if: ${{ failure() }} diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index b9ec129bb..5d32f8f31 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -1320,8 +1320,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \ - (OPENTHREAD_FTD || \ - (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)) + (OPENTHREAD_FTD || OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) else if (aArgs[0] == "manager") { /** @@ -1348,17 +1347,16 @@ template <> otError Interpreter::Process(Arg aArgs[]) #if OPENTHREAD_FTD OutputLine("auto: %d", otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr())); #endif -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE OutputLine("autocsl: %u", otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr())); #endif -#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \ - OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()) || otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr())) #elif OPENTHREAD_FTD if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr())) -#elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#elif OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE if (otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr())) #endif { @@ -1440,7 +1438,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) otChannelManagerSetAutoChannelSelectionEnabled(GetInstancePtr(), enable); } #endif // OPENTHREAD_FTD -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE /** * @cli channel manager autocsl * @code @@ -1463,7 +1461,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) SuccessOrExit(error = aArgs[2].ParseAsBool(enable)); otChannelManagerSetAutoCslChannelSelectionEnabled(GetInstancePtr(), enable); } -#endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#endif // OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE #if OPENTHREAD_FTD /** * @cli channel manager delay diff --git a/src/core/api/channel_manager_api.cpp b/src/core/api/channel_manager_api.cpp index 3aa5d36a7..b4b053aae 100644 --- a/src/core/api/channel_manager_api.cpp +++ b/src/core/api/channel_manager_api.cpp @@ -34,8 +34,7 @@ #include "openthread-core-config.h" #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \ - (OPENTHREAD_FTD || \ - (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)) + (OPENTHREAD_FTD || OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) #include @@ -86,7 +85,7 @@ bool otChannelManagerGetAutoChannelSelectionEnabled(otInstance *aInstance) } #endif // OPENTHREAD_FTD -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE otError otChannelManagerRequestCslChannelSelect(otInstance *aInstance, bool aSkipQualityCheck) { diff --git a/src/core/instance/instance.cpp b/src/core/instance/instance.cpp index 902209622..e208eebdc 100644 --- a/src/core/instance/instance.cpp +++ b/src/core/instance/instance.cpp @@ -234,8 +234,7 @@ Instance::Instance(void) , mChannelMonitor(*this) #endif #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \ - (OPENTHREAD_FTD || \ - (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)) + (OPENTHREAD_FTD || OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) , mChannelManager(*this) #endif #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD diff --git a/src/core/instance/instance.hpp b/src/core/instance/instance.hpp index 2b462b467..cb9c3d340 100644 --- a/src/core/instance/instance.hpp +++ b/src/core/instance/instance.hpp @@ -671,8 +671,7 @@ class Instance : public otInstance, private NonCopyable #endif #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \ - (OPENTHREAD_FTD || \ - (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)) + (OPENTHREAD_FTD || OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) Utils::ChannelManager mChannelManager; #endif @@ -978,8 +977,7 @@ template <> inline Utils::ChannelMonitor &Instance::Get(void) { return mChannelM #endif #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \ - (OPENTHREAD_FTD || \ - (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)) + (OPENTHREAD_FTD || OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) template <> inline Utils::ChannelManager &Instance::Get(void) { return mChannelManager; } #endif diff --git a/src/core/utils/channel_manager.cpp b/src/core/utils/channel_manager.cpp index c8b685948..1e5c76252 100644 --- a/src/core/utils/channel_manager.cpp +++ b/src/core/utils/channel_manager.cpp @@ -34,9 +34,8 @@ #include "channel_manager.hpp" -#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \ - (OPENTHREAD_FTD || \ - (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE +#if (OPENTHREAD_FTD || OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) #include "common/code_utils.hpp" #include "common/locator_getters.hpp" @@ -67,7 +66,7 @@ ChannelManager::ChannelManager(Instance &aInstance) #if OPENTHREAD_FTD , mAutoSelectEnabled(false) #endif -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE , mAutoSelectCslEnabled(false) #endif , mCcaFailureRateThreshold(kCcaFailureRateThreshold) @@ -82,7 +81,7 @@ void ChannelManager::RequestChannelChange(uint8_t aChannel) RequestNetworkChannelChange(aChannel); } #endif -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE if (mAutoSelectCslEnabled) { ChangeCslChannel(aChannel); @@ -118,7 +117,7 @@ void ChannelManager::RequestNetworkChannelChange(uint8_t aChannel) } #endif -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE void ChannelManager::ChangeCslChannel(uint8_t aChannel) { if (!(!Get().IsRxOnWhenIdle() && Get().IsCslEnabled())) @@ -143,7 +142,7 @@ void ChannelManager::ChangeCslChannel(uint8_t aChannel) exit: return; } -#endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#endif // OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE #if OPENTHREAD_FTD Error ChannelManager::SetDelay(uint16_t aDelay) @@ -315,7 +314,7 @@ Error ChannelManager::RequestNetworkChannelSelect(bool aSkipQualityCheck) } #endif -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE Error ChannelManager::RequestCslChannelSelect(bool aSkipQualityCheck) { Error error = kErrorNone; @@ -358,7 +357,7 @@ Error ChannelManager::RequestChannelSelect(bool aSkipQualityCheck) SuccessOrExit(error = FindBetterChannel(newChannel, newOccupancy)); -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE if (Get().IsCslEnabled() && (Get().GetCslChannel() != 0)) { curChannel = Get().GetCslChannel(); @@ -402,12 +401,11 @@ void ChannelManager::StartAutoSelectTimer(void) { VerifyOrExit(mState == kStateIdle); -#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \ - OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) if (mAutoSelectEnabled || mAutoSelectCslEnabled) #elif OPENTHREAD_FTD if (mAutoSelectEnabled) -#elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#elif OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE if (mAutoSelectCslEnabled) #endif { @@ -436,7 +434,7 @@ void ChannelManager::SetAutoNetworkChannelSelectionEnabled(bool aEnabled) } #endif -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE void ChannelManager::SetAutoCslChannelSelectionEnabled(bool aEnabled) { if (aEnabled != mAutoSelectCslEnabled) @@ -457,12 +455,11 @@ Error ChannelManager::SetAutoChannelSelectionInterval(uint32_t aInterval) mAutoSelectInterval = aInterval; -#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \ - OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) if (mAutoSelectEnabled || mAutoSelectCslEnabled) #elif OPENTHREAD_FTD if (mAutoSelectEnabled) -#elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#elif OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE if (mAutoSelectCslEnabled) #endif { @@ -500,4 +497,5 @@ void ChannelManager::SetCcaFailureRateThreshold(uint16_t aThreshold) } // namespace Utils } // namespace ot +#endif // #if (OPENTHREAD_FTD || OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) #endif // #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE diff --git a/src/core/utils/channel_manager.hpp b/src/core/utils/channel_manager.hpp index 02d93053b..d4841bce3 100644 --- a/src/core/utils/channel_manager.hpp +++ b/src/core/utils/channel_manager.hpp @@ -36,9 +36,13 @@ #include "openthread-core-config.h" -#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \ - (OPENTHREAD_FTD || \ - (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE + +#if (OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE && !OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE) +#error "CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE requires OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE"; +#endif + +#if (OPENTHREAD_FTD || OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) #include @@ -164,7 +168,7 @@ class ChannelManager : public InstanceLocator, private NonCopyable Error RequestNetworkChannelSelect(bool aSkipQualityCheck); #endif // OPENTHREAD_FTD -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE /** * Requests that `ChannelManager` checks and selects a new Csl channel and starts a channel change. * @@ -192,7 +196,7 @@ class ChannelManager : public InstanceLocator, private NonCopyable * */ Error RequestCslChannelSelect(bool aSkipQualityCheck); -#endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#endif // OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE #if OPENTHREAD_FTD /** @@ -215,7 +219,7 @@ class ChannelManager : public InstanceLocator, private NonCopyable bool GetAutoNetworkChannelSelectionEnabled(void) const { return mAutoSelectEnabled; } #endif -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE /** * Enables/disables the auto-channel-selection functionality. * @@ -352,7 +356,7 @@ class ChannelManager : public InstanceLocator, private NonCopyable bool ShouldAttemptChannelChange(void); #endif -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE void ChangeCslChannel(uint8_t aChannel); #endif @@ -371,7 +375,7 @@ class ChannelManager : public InstanceLocator, private NonCopyable #if OPENTHREAD_FTD bool mAutoSelectEnabled; #endif -#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE bool mAutoSelectCslEnabled; #endif uint16_t mCcaFailureRateThreshold; @@ -385,6 +389,7 @@ class ChannelManager : public InstanceLocator, private NonCopyable } // namespace Utils } // namespace ot -#endif // OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD +#endif // #if (OPENTHREAD_FTD || OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE) +#endif // #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE #endif // CHANNEL_MANAGER_HPP_ From f32c18bc0840f400182456e58ae3900fc2fb4af7 Mon Sep 17 00:00:00 2001 From: Li Cao Date: Fri, 12 Jul 2024 07:00:55 +0800 Subject: [PATCH 044/160] [spinel] extract SpinelStatusToOtError method from RadioSpinel into a helper module (#10506) This commit moves `RadioSpinel::SpinelStatusToOtError` to a seperate header `spinel_header.hpp` so that this method can be used externally. (For example, in `ot-br-posix`. --- src/lib/spinel/BUILD.gn | 2 + src/lib/spinel/CMakeLists.txt | 1 + src/lib/spinel/radio_spinel.cpp | 78 +-------------------- src/lib/spinel/radio_spinel.hpp | 22 ------ src/lib/spinel/spinel_helper.cpp | 117 +++++++++++++++++++++++++++++++ src/lib/spinel/spinel_helper.hpp | 69 ++++++++++++++++++ 6 files changed, 190 insertions(+), 99 deletions(-) create mode 100644 src/lib/spinel/spinel_helper.cpp create mode 100644 src/lib/spinel/spinel_helper.hpp diff --git a/src/lib/spinel/BUILD.gn b/src/lib/spinel/BUILD.gn index 09b2d39d9..c83cc08ea 100644 --- a/src/lib/spinel/BUILD.gn +++ b/src/lib/spinel/BUILD.gn @@ -49,6 +49,8 @@ spinel_sources = [ "spinel_driver.hpp", "spinel_encoder.cpp", "spinel_encoder.hpp", + "spinel_helper.cpp", + "spinel_helper.hpp", "spinel_platform.h", ] diff --git a/src/lib/spinel/CMakeLists.txt b/src/lib/spinel/CMakeLists.txt index 74c3a95f4..11f83683a 100644 --- a/src/lib/spinel/CMakeLists.txt +++ b/src/lib/spinel/CMakeLists.txt @@ -72,6 +72,7 @@ set(COMMON_SOURCES spinel_buffer.cpp spinel_decoder.cpp spinel_encoder.cpp + spinel_helper.cpp ) set(OT_SPINEL_VENDOR_HOOK_SOURCE "" CACHE STRING "set vendor hook source file for Spinel") diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 8b3e2b5ea..da71ecf38 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -46,6 +46,7 @@ #include "lib/spinel/logger.hpp" #include "lib/spinel/spinel_decoder.hpp" #include "lib/spinel/spinel_driver.hpp" +#include "lib/spinel/spinel_helper.hpp" #include "lib/utils/utils.hpp" namespace ot { @@ -2335,82 +2336,5 @@ otError RadioSpinel::SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPowe } #endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE -otError RadioSpinel::SpinelStatusToOtError(spinel_status_t aStatus) -{ - otError ret; - - switch (aStatus) - { - case SPINEL_STATUS_OK: - ret = OT_ERROR_NONE; - break; - - case SPINEL_STATUS_FAILURE: - ret = OT_ERROR_FAILED; - break; - - case SPINEL_STATUS_DROPPED: - ret = OT_ERROR_DROP; - break; - - case SPINEL_STATUS_NOMEM: - ret = OT_ERROR_NO_BUFS; - break; - - case SPINEL_STATUS_BUSY: - ret = OT_ERROR_BUSY; - break; - - case SPINEL_STATUS_PARSE_ERROR: - ret = OT_ERROR_PARSE; - break; - - case SPINEL_STATUS_INVALID_ARGUMENT: - ret = OT_ERROR_INVALID_ARGS; - break; - - case SPINEL_STATUS_UNIMPLEMENTED: - ret = OT_ERROR_NOT_IMPLEMENTED; - break; - - case SPINEL_STATUS_INVALID_STATE: - ret = OT_ERROR_INVALID_STATE; - break; - - case SPINEL_STATUS_NO_ACK: - ret = OT_ERROR_NO_ACK; - break; - - case SPINEL_STATUS_CCA_FAILURE: - ret = OT_ERROR_CHANNEL_ACCESS_FAILURE; - break; - - case SPINEL_STATUS_ALREADY: - ret = OT_ERROR_ALREADY; - break; - - case SPINEL_STATUS_PROP_NOT_FOUND: - ret = OT_ERROR_NOT_IMPLEMENTED; - break; - - case SPINEL_STATUS_ITEM_NOT_FOUND: - ret = OT_ERROR_NOT_FOUND; - break; - - default: - if (aStatus >= SPINEL_STATUS_STACK_NATIVE__BEGIN && aStatus <= SPINEL_STATUS_STACK_NATIVE__END) - { - ret = static_cast(aStatus - SPINEL_STATUS_STACK_NATIVE__BEGIN); - } - else - { - ret = OT_ERROR_FAILED; - } - break; - } - - return ret; -} - } // namespace Spinel } // namespace ot diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index bfee96176..5abc8de43 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -1022,28 +1022,6 @@ class RadioSpinel : private Logger otError SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower); #endif - /** - * Convert the Spinel status code to OpenThread error code. - * - * @param[in] aStatus The Spinel status code. - * - * @retval OT_ERROR_NONE The operation has completed successfully. - * @retval OT_ERROR_DROP The packet was dropped. - * @retval OT_ERROR_NO_BUFS The operation has been prevented due to memory pressure. - * @retval OT_ERROR_BUSY The device is currently performing a mutuallyexclusive operation. - * @retval OT_ERROR_PARSE An error has occurred while parsing the command. - * @retval OT_ERROR_INVALID_ARGS An argument to the given operation is invalid. - * @retval OT_ERROR_NOT_IMPLEMENTED The given operation has not been implemented. - * @retval OT_ERROR_INVALID_STATE The given operation is invalid for the current state of the device. - * @retval OT_ERROR_NO_ACK The packet was not acknowledged. - * @retval OT_ERROR_NOT_FOUND The given property is not recognized. - * @retval OT_ERROR_FAILED The given operation has failed for some undefined reason. - * @retval OT_ERROR_CHANNEL_ACCESS_FAILURE The packet was not sent due to a CCA failure. - * @retval OT_ERROR_ALREADY The operation is already in progress or the property was already set - * to the given value. - */ - static otError SpinelStatusToOtError(spinel_status_t aStatus); - #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 /** * Restore the properties of Radio Co-processor (RCP). diff --git a/src/lib/spinel/spinel_helper.cpp b/src/lib/spinel/spinel_helper.cpp new file mode 100644 index 000000000..42b0bcfbc --- /dev/null +++ b/src/lib/spinel/spinel_helper.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024, 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. + */ + +#include "lib/spinel/spinel_helper.hpp" + +/** + * @file This file implements the spinel helper methods. + * + */ + +namespace ot { +namespace Spinel { + +otError SpinelStatusToOtError(spinel_status_t aStatus) +{ + otError ret; + + switch (aStatus) + { + case SPINEL_STATUS_OK: + ret = OT_ERROR_NONE; + break; + + case SPINEL_STATUS_FAILURE: + ret = OT_ERROR_FAILED; + break; + + case SPINEL_STATUS_DROPPED: + ret = OT_ERROR_DROP; + break; + + case SPINEL_STATUS_NOMEM: + ret = OT_ERROR_NO_BUFS; + break; + + case SPINEL_STATUS_BUSY: + ret = OT_ERROR_BUSY; + break; + + case SPINEL_STATUS_PARSE_ERROR: + ret = OT_ERROR_PARSE; + break; + + case SPINEL_STATUS_INVALID_ARGUMENT: + ret = OT_ERROR_INVALID_ARGS; + break; + + case SPINEL_STATUS_UNIMPLEMENTED: + ret = OT_ERROR_NOT_IMPLEMENTED; + break; + + case SPINEL_STATUS_INVALID_STATE: + ret = OT_ERROR_INVALID_STATE; + break; + + case SPINEL_STATUS_NO_ACK: + ret = OT_ERROR_NO_ACK; + break; + + case SPINEL_STATUS_CCA_FAILURE: + ret = OT_ERROR_CHANNEL_ACCESS_FAILURE; + break; + + case SPINEL_STATUS_ALREADY: + ret = OT_ERROR_ALREADY; + break; + + case SPINEL_STATUS_PROP_NOT_FOUND: + ret = OT_ERROR_NOT_IMPLEMENTED; + break; + + case SPINEL_STATUS_ITEM_NOT_FOUND: + ret = OT_ERROR_NOT_FOUND; + break; + + default: + if (aStatus >= SPINEL_STATUS_STACK_NATIVE__BEGIN && aStatus <= SPINEL_STATUS_STACK_NATIVE__END) + { + ret = static_cast(aStatus - SPINEL_STATUS_STACK_NATIVE__BEGIN); + } + else + { + ret = OT_ERROR_FAILED; + } + break; + } + + return ret; +} + +} // namespace Spinel +} // namespace ot diff --git a/src/lib/spinel/spinel_helper.hpp b/src/lib/spinel/spinel_helper.hpp new file mode 100644 index 000000000..62f55fae6 --- /dev/null +++ b/src/lib/spinel/spinel_helper.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, 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 spinel helper methods. + * + */ + +#ifndef SPINEL_HELPER_HPP_ +#define SPINEL_HELPER_HPP_ + +#include + +#include "lib/spinel/spinel.h" + +namespace ot { +namespace Spinel { + +/** + * Convert the Spinel status code to OpenThread error code. + * + * @param[in] aStatus The Spinel status code. + * + * @retval OT_ERROR_NONE The operation has completed successfully. + * @retval OT_ERROR_DROP The packet was dropped. + * @retval OT_ERROR_NO_BUFS The operation has been prevented due to memory pressure. + * @retval OT_ERROR_BUSY The device is currently performing a mutuallyexclusive operation. + * @retval OT_ERROR_PARSE An error has occurred while parsing the command. + * @retval OT_ERROR_INVALID_ARGS An argument to the given operation is invalid. + * @retval OT_ERROR_NOT_IMPLEMENTED The given operation has not been implemented. + * @retval OT_ERROR_INVALID_STATE The given operation is invalid for the current state of the device. + * @retval OT_ERROR_NO_ACK The packet was not acknowledged. + * @retval OT_ERROR_NOT_FOUND The given property is not recognized. + * @retval OT_ERROR_FAILED The given operation has failed for some undefined reason. + * @retval OT_ERROR_CHANNEL_ACCESS_FAILURE The packet was not sent due to a CCA failure. + * @retval OT_ERROR_ALREADY The operation is already in progress or the property was already set + * to the given value. + */ +otError SpinelStatusToOtError(spinel_status_t aStatus); + +} // namespace Spinel +} // namespace ot + +#endif // SPINEL_HELPER_HPP_ From 20c9bcbc4142fd74e490bd50faa7def9e1b3a823 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Fri, 12 Jul 2024 23:59:28 +0800 Subject: [PATCH 045/160] [test] test whether the RCP supports data poll (#10494) This commit adds a test case to the `cp-tests` to test whether the RCP supports data poll transmitter and receiver. --- tools/cp-caps/README.md | 13 ++++++- tools/cp-caps/rcp_caps_test.py | 64 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/tools/cp-caps/README.md b/tools/cp-caps/README.md index 2a4909647..1614f3e1f 100644 --- a/tools/cp-caps/README.md +++ b/tools/cp-caps/README.md @@ -54,7 +54,7 @@ Show help info. ```bash $ python3 ./tools/cp-caps/rcp_caps_test.py -h -usage: rcp_caps_test.py [-h] [-c] [-d] [-v] +usage: rcp_caps_test.py [-h] [-c] [-d] [-p] [-v] This script is used for testing RCP capabilities. @@ -62,6 +62,7 @@ options: -h, --help show this help message and exit -c, --csl test whether the RCP supports CSL transmitter -d, --diag-commands test whether the RCP supports all diag commands + -p, --data-poll test whether the RCP supports data poll -v, --verbose output verbose information Device Interfaces: @@ -133,3 +134,13 @@ The parameter `-c` or `--csl` starts to test whether the RCP supports the CSL tr $ DUT_ADB_USB=TW69UCKFZTGM95OR REF_CLI_SERIAL=/dev/ttyACM0 python3 ./tools/cp-caps/rcp_caps_test.py -c CSL Transmitter ------------------------------------------ OK ``` + +### Test Data Poll + +The parameter `-p` or `--data-poll` starts to test whether the RCP supports data poll. + +```bash +$ DUT_ADB_USB=1269UCKFZTAM95OR REF_CLI_SERIAL=/dev/ttyACM0 python3 ./tools/cp-caps/rcp_caps_test.py -p +Data Poll Parent ----------------------------------------- OK +Data Poll Child ------------------------------------------ OK +``` diff --git a/tools/cp-caps/rcp_caps_test.py b/tools/cp-caps/rcp_caps_test.py index adaa86a80..f40e7e2c7 100644 --- a/tools/cp-caps/rcp_caps_test.py +++ b/tools/cp-caps/rcp_caps_test.py @@ -83,6 +83,11 @@ def test_csl(self): self.__dataset = self.__get_default_dataset() self.__test_csl_transmitter() + def test_data_poll(self): + self.__dataset = self.__get_default_dataset() + self.__test_data_poll_parent() + self.__test_data_poll_child() + # # Private methods # @@ -117,6 +122,54 @@ def __test_csl_transmitter(self): self.__output_format_bool('CSL Transmitter', ret) + def __test_data_poll_parent(self): + packets = 10 + + self.__dut.factory_reset() + self.__ref.factory_reset() + + self.__dut.join(self.__dataset) + self.__dut.wait_for('state', 'leader') + + # Set the reference device as an SED + self.__ref.set_mode('-') + self.__ref.set_poll_period(500) + self.__ref.join(self.__dataset) + self.__ref.wait_for('state', 'child') + + dut_mleid = self.__dut.get_ipaddr_mleid() + result = self.__ref.ping(dut_mleid, count=packets, interval=1) + + self.__dut.leave() + self.__ref.leave() + + ret = result['transmitted_packets'] == result['received_packets'] == packets + self.__output_format_bool('Data Poll Parent', ret) + + def __test_data_poll_child(self): + packets = 10 + + self.__dut.factory_reset() + self.__ref.factory_reset() + + self.__ref.join(self.__dataset) + self.__ref.wait_for('state', 'leader') + + # Set the DUT as an SED + self.__dut.set_mode('-') + self.__dut.set_poll_period(500) + self.__dut.join(self.__dataset) + self.__dut.wait_for('state', 'child') + + dut_mleid = self.__dut.get_ipaddr_mleid() + result = self.__ref.ping(dut_mleid, count=packets, interval=1) + + self.__dut.leave() + self.__ref.leave() + + ret = result['transmitted_packets'] == result['received_packets'] == packets + self.__output_format_bool('Data Poll Child', ret) + def __test_diag_channel(self): channel = 20 commands = ['diag channel', f'diag channel {channel}'] @@ -429,6 +482,14 @@ def parse_arguments(): help='test whether the RCP supports all diag commands', ) + parser.add_argument( + '-p', + '--data-poll', + action='store_true', + default=False, + help='test whether the RCP supports data poll', + ) + parser.add_argument( '-v', '--verbose', @@ -455,6 +516,9 @@ def main(): if arguments.csl is True: rcp_caps.test_csl() + if arguments.data_poll is True: + rcp_caps.test_data_poll() + if __name__ == '__main__': main() From b0790b3daae6bec31589be002b606419d6ab7bf1 Mon Sep 17 00:00:00 2001 From: Li Cao Date: Mon, 15 Jul 2024 23:50:43 +0800 Subject: [PATCH 046/160] [spinel] fix cmake source list for spinel_helper.cpp (#10515) --- src/lib/spinel/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/spinel/CMakeLists.txt b/src/lib/spinel/CMakeLists.txt index 11f83683a..70f6c677a 100644 --- a/src/lib/spinel/CMakeLists.txt +++ b/src/lib/spinel/CMakeLists.txt @@ -95,6 +95,7 @@ target_sources(openthread-radio-spinel logger.cpp radio_spinel.cpp spinel_driver.cpp + spinel_helper.cpp ) target_sources(openthread-spinel-ncp PRIVATE ${COMMON_SOURCES}) target_sources(openthread-spinel-rcp PRIVATE ${COMMON_SOURCES}) From f331472b73293e34cc0d37e0255716703f3061a7 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 15 Jul 2024 09:49:08 -0700 Subject: [PATCH 047/160] [routing-manager] track peer BRs and their age from Network Data (#10485) This commit adds a new mechanism in `RoutingManager` to discover and track peer BRs found in Network Data. The `NetDataPeerBrTracker` class implements this functionality, tracking a list of peer BR RLOC16s and their age (time since appearance in the Network Data). This commit also implements public OT APIs to iterate over the peer BR list or get the total count, along with corresponding CLI commands (`br peers`), and a test case validating the behavior. --- include/openthread/border_routing.h | 62 +++++++++ src/cli/README_BR.md | 44 +++++++ src/cli/cli_br.cpp | 81 ++++++++++++ src/core/api/border_routing_api.cpp | 22 ++++ src/core/border_router/routing_manager.cpp | 118 ++++++++++++++++++ src/core/border_router/routing_manager.hpp | 97 +++++++++++++- tests/toranj/cli/cli.py | 6 + .../toranj/cli/test-503-peer-tbr-discovery.py | 24 +++- 8 files changed, 452 insertions(+), 2 deletions(-) diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h index 19923aa6b..d45903249 100644 --- a/include/openthread/border_routing.h +++ b/include/openthread/border_routing.h @@ -131,6 +131,16 @@ typedef struct otBorderRoutingPrefixTableEntry uint32_t mPreferredLifetime; ///< Preferred lifetime of the on-link prefix when `mIsOnLink`. } otBorderRoutingPrefixTableEntry; +/** + * Represents information about a peer Border Router found in the Network Data. + * + */ +typedef struct otBorderRoutingPeerBorderRouterEntry +{ + uint16_t mRloc16; ///< The RLOC16 of BR. + uint32_t mAge; ///< Seconds since the BR appeared in the Network Data. +} otBorderRoutingPeerBorderRouterEntry; + /** * Represents a group of data of platform-generated RA messages processed. * @@ -488,6 +498,58 @@ otError otBorderRoutingGetNextRouterEntry(otInstance *aI otBorderRoutingPrefixTableIterator *aIterator, otBorderRoutingRouterEntry *aEntry); +/** + * Iterates over the peer BRs found in the Network Data. + * + * Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`. + * + * Peer BRs are other devices within the Thread mesh that provide external IP connectivity. A device is considered + * to provide external IP connectivity if at least one of the following conditions is met regarding its Network Data + * entries: + * + * - It has added at least one external route entry. + * - It has added at least one prefix entry with both the default-route and on-mesh flags set. + * - It has added at least one domain prefix (with both the domain and on-mesh flags set). + * + * The list of peer BRs specifically excludes the current device, even if it is itself acting as a BR. + * + * @param[in] aInstance The OpenThread instance. + * @param[in,out] aIterator A pointer to the iterator. + * @param[out] aEntry A pointer to the entry to populate. + * + * @retval OT_ERROR_NONE Iterated to the next entry, @p aEntry and @p aIterator are updated. + * @retval OT_ERROR_NOT_FOUND No more entries. + * + */ +otError otBorderRoutingGetNextPeerBrEntry(otInstance *aInstance, + otBorderRoutingPrefixTableIterator *aIterator, + otBorderRoutingPeerBorderRouterEntry *aEntry); + +/** + * Returns the number of peer BRs found in the Network Data. + * + * Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`. + * + * Peer BRs are other devices within the Thread mesh that provide external IP connectivity. A device is considered + * to provide external IP connectivity if at least one of the following conditions is met regarding its Network Data + * entries: + * + * - It has added at least one external route entry. + * - It has added at least one prefix entry with both the default-route and on-mesh flags set. + * - It has added at least one domain prefix (with both the domain and on-mesh flags set). + * + * The list of peer BRs specifically excludes the current device, even if it is itself acting as a BR. + * + * @param[in] aInstance The OpenThread instance. + * @param[out] aMinAge Pointer to an `uint32_t` to return the minimum age among all peer BRs. + * Can be NULL if the caller does not need this information. + * Age is represented as seconds since appearance of the BR entry in the Network Data. + * + * @returns The number of peer BRs. + * + */ +uint16_t otBorderRoutingCountPeerBrs(otInstance *aInstance, uint32_t *aMinAge); + /** * Enables / Disables DHCPv6 Prefix Delegation. * diff --git a/src/cli/README_BR.md b/src/cli/README_BR.md index 821dc269d..27e8b0864 100644 --- a/src/cli/README_BR.md +++ b/src/cli/README_BR.md @@ -13,6 +13,7 @@ Usage : `br [command] ...` - [omrprefix](#omrprefix) - [onlinkprefix](#onlinkprefix) - [pd](#pd) +- [peers](#peers) - [prefixtable](#prefixtable) - [rioprf](#rioprf) - [routeprf](#routeprf) @@ -35,6 +36,7 @@ enable omrprefix onlinkprefix pd +peers prefixtable raoptions rioprf @@ -221,6 +223,48 @@ Get the DHCPv6 Prefix Delegation (PD) provided off-mesh-routable (OMR) prefix. Done ``` +### peers + +Usage: `br peers` + +Get the list of peer BRs found in the Network Data. + +`OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE` is required. + +Peer BRs are other devices within the Thread mesh that provide external IP connectivity. A device is considered to provide external IP connectivity if at least one of the following conditions is met regarding its Network Data entries: + +- It has added at least one external route entry. +- It has added at least one prefix entry with both the default-route and on-mesh flags set. +- It has added at least one domain prefix (with both the domain and on-mesh flags set). + +The list of peer BRs specifically excludes the current device, even if it is itself acting as a BR. + +Info per BR entry: + +- RLOC16 of the BR +- Age as the duration interval since this BR appeared in Network Data. It is formatted as `{hh}:{mm}:{ss}` for hours, minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the format is `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds. + +```bash +> br peers +rloc16:0x5c00 age:00:00:49 +rloc16:0xf800 age:00:01:51 +Done +``` + +Usage: `br peers count` + +Gets the number of peer BRs found in the Network Data. + +The count does not include the current device, even if it is itself acting as a BR. + +The output indicates the minimum age among all peer BRs. Age is formatted as `{hh}:{mm}:{ss}` for hours, minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the format is `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds. + +```bash +> br peer count +2 min-age:00:00:49 +Done +``` + ### prefixtable Usage: `br prefixtable` diff --git a/src/cli/cli_br.cpp b/src/cli/cli_br.cpp index cab216d28..b49c774ce 100644 --- a/src/cli/cli_br.cpp +++ b/src/cli/cli_br.cpp @@ -374,6 +374,84 @@ template <> otError Br::Process(Arg aArgs[]) #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + +template <> otError Br::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + /** + * @cli br peers + * @code + * br peers + * rloc16:0x5c00 age:00:00:49 + * rloc16:0xf800 age:00:01:51 + * Done + * @endcode + * @par + * Get the list of peer BRs found in Network Data entries. + * `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE` is required. + * Peer BRs are other devices within the Thread mesh that provide external IP connectivity. A device is considered + * to provide external IP connectivity if at least one of the following conditions is met regarding its Network + * Data entries: + * - It has added at least one external route entry. + * - It has added at least one prefix entry with both the default-route and on-mesh flags set. + * - It has added at least one domain prefix (with both the domain and on-mesh flags set). + * The list of peer BRs specifically excludes the current device, even if its is itself acting as a BR. + * Info per BR entry: + * - RLOC16 of the BR + * - Age as the duration interval since this BR appeared in Network Data. It is formatted as `{hh}:{mm}:{ss}` for + * hours, minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the + * format is `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds. + * @sa otBorderRoutingGetNextPrefixTableEntry + */ + if (aArgs[0].IsEmpty()) + { + otBorderRoutingPrefixTableIterator iterator; + otBorderRoutingPeerBorderRouterEntry peerBrEntry; + char ageString[OT_DURATION_STRING_SIZE]; + + otBorderRoutingPrefixTableInitIterator(GetInstancePtr(), &iterator); + + while (otBorderRoutingGetNextPeerBrEntry(GetInstancePtr(), &iterator, &peerBrEntry) == OT_ERROR_NONE) + { + otConvertDurationInSecondsToString(peerBrEntry.mAge, ageString, sizeof(ageString)); + OutputLine("rloc16:0x%04x age:%s", peerBrEntry.mRloc16, ageString); + } + } + /** + * @cli br peers count + * @code + * br peers count + * 2 min-age:00:00:47 + * Done + * @endcode + * @par api_copy + * #otBorderRoutingCountPeerBrs + */ + else if (aArgs[0] == "count") + { + uint32_t minAge; + uint16_t count; + char ageString[OT_DURATION_STRING_SIZE]; + + VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + count = otBorderRoutingCountPeerBrs(GetInstancePtr(), &minAge); + otConvertDurationInSecondsToString(minAge, ageString, sizeof(ageString)); + OutputLine("%u min-age:%s", count, ageString); + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + /** * @cli br prefixtable * @code @@ -791,6 +869,9 @@ otError Br::Process(Arg aArgs[]) CmdEntry("onlinkprefix"), #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE CmdEntry("pd"), +#endif +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + CmdEntry("peers"), #endif CmdEntry("prefixtable"), CmdEntry("raoptions"), diff --git a/src/core/api/border_routing_api.cpp b/src/core/api/border_routing_api.cpp index 8bc2b7af0..a7e477f8c 100644 --- a/src/core/api/border_routing_api.cpp +++ b/src/core/api/border_routing_api.cpp @@ -194,6 +194,28 @@ otError otBorderRoutingGetNextRouterEntry(otInstance *aI return AsCoreType(aInstance).Get().GetNextRouterEntry(*aIterator, *aEntry); } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + +otError otBorderRoutingGetNextPeerBrEntry(otInstance *aInstance, + otBorderRoutingPrefixTableIterator *aIterator, + otBorderRoutingPeerBorderRouterEntry *aEntry) +{ + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aEntry); + + return AsCoreType(aInstance).Get().GetNextPeerBrEntry(*aIterator, *aEntry); +} + +uint16_t otBorderRoutingCountPeerBrs(otInstance *aInstance, uint32_t *aMinAge) +{ + uint32_t minAge; + + return AsCoreType(aInstance).Get().CountPeerBrs((aMinAge != nullptr) ? *aMinAge + : minAge); +} + +#endif + #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE void otBorderRoutingDhcp6PdSetEnabled(otInstance *aInstance, bool aEnabled) { diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 86d35cc8a..02c930ae2 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -75,6 +75,9 @@ RoutingManager::RoutingManager(Instance &aInstance) , mOmrPrefixManager(aInstance) , mRioAdvertiser(aInstance) , mOnLinkPrefixManager(aInstance) +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + , mNetDataPeerBrTracker(aInstance) +#endif , mRxRaTracker(aInstance) , mRoutePublisher(aInstance) #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE @@ -438,6 +441,10 @@ void RoutingManager::HandleNotifierEvents(Events aEvents) mRoutePublisher.HandleNotifierEvents(aEvents); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + mNetDataPeerBrTracker.HandleNotifierEvents(aEvents); +#endif + VerifyOrExit(IsInitialized() && IsEnabled()); if (aEvents.Contains(kEventThreadRoleChanged)) @@ -982,6 +989,91 @@ void RoutingManager::RoutePrefix::CopyInfoTo(PrefixTableEntry &aEntry, TimeMilli aEntry.mRoutePreference = static_cast(GetRoutePreference()); } +//--------------------------------------------------------------------------------------------------------------------- +// NetDataPeerBrTracker + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE +RoutingManager::NetDataPeerBrTracker::NetDataPeerBrTracker(Instance &aInstance) + : InstanceLocator(aInstance) +{ +} + +uint16_t RoutingManager::NetDataPeerBrTracker::CountPeerBrs(uint32_t &aMinAge) const +{ + uint32_t uptime = Uptime::MsecToSec(Get().GetUptime()); + uint16_t count = 0; + + aMinAge = NumericLimits::kMax; + + for (const PeerBr &peerBr : mPeerBrs) + { + count++; + aMinAge = Min(aMinAge, peerBr.GetAge(uptime)); + } + + if (count == 0) + { + aMinAge = 0; + } + + return count; +} + +Error RoutingManager::NetDataPeerBrTracker::GetNext(PrefixTableIterator &aIterator, PeerBrEntry &aEntry) const +{ + using Iterator = RxRaTracker::Iterator; + + Iterator &iterator = static_cast(aIterator); + Error error; + + SuccessOrExit(error = iterator.AdvanceToNextPeerBr(mPeerBrs.GetHead())); + + aEntry.mRloc16 = iterator.GetPeerBrEntry()->mRloc16; + aEntry.mAge = iterator.GetPeerBrEntry()->GetAge(iterator.GetInitUptime()); + +exit: + return error; +} + +void RoutingManager::NetDataPeerBrTracker::HandleNotifierEvents(Events aEvents) +{ + NetworkData::Rlocs rlocs; + + VerifyOrExit(aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged)); + + Get().FindRlocs(NetworkData::kBrProvidingExternalIpConn, NetworkData::kAnyRole, rlocs); + + // Remove `PeerBr` entries no longer found in Network Data, + // or they match the device RLOC16. Then allocate and add + // entries for newly discovered peers. + + mPeerBrs.RemoveAndFreeAllMatching(PeerBr::Filter(rlocs)); + mPeerBrs.RemoveAndFreeAllMatching(Get().GetRloc16()); + + for (uint16_t rloc16 : rlocs) + { + PeerBr *newEntry; + + if (Get().HasRloc16(rloc16) || mPeerBrs.ContainsMatching(rloc16)) + { + continue; + } + + newEntry = PeerBr::Allocate(); + VerifyOrExit(newEntry != nullptr, LogWarn("Failed to allocate `PeerBr` entry")); + + newEntry->mRloc16 = rloc16; + newEntry->mDiscoverTime = Uptime::MsecToSec(Get().GetUptime()); + + mPeerBrs.Push(*newEntry); + } + +exit: + return; +} + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + //--------------------------------------------------------------------------------------------------------------------- // RxRaTracker @@ -1850,6 +1942,32 @@ Error RoutingManager::RxRaTracker::Iterator::AdvanceToNextEntry(void) return error; } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + +Error RoutingManager::RxRaTracker::Iterator::AdvanceToNextPeerBr(const PeerBr *aPeerBrsHead) +{ + Error error = kErrorNone; + + if (GetType() == kUnspecified) + { + SetType(kPeerBrIterator); + SetEntry(aPeerBrsHead); + } + else + { + VerifyOrExit(GetType() == kPeerBrIterator, error = kErrorInvalidArgs); + VerifyOrExit(GetPeerBrEntry() != nullptr, error = kErrorNotFound); + SetEntry(GetPeerBrEntry()->GetNext()); + } + + VerifyOrExit(GetPeerBrEntry() != nullptr, error = kErrorNotFound); + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + //--------------------------------------------------------------------------------------------------------------------- // RxRaTracker::Router diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index c9753b88a..c6e4c8dff 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -51,6 +51,10 @@ #error "OPENTHREAD_CONFIG_UPTIME_ENABLE is required for OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE" #endif +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE && !OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE +#error "TRACK_PEER_BR_INFO_ENABLE feature requires OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE" +#endif + #include #include #include @@ -78,7 +82,6 @@ #include "thread/network_data.hpp" namespace ot { - namespace BorderRouter { extern "C" void otPlatBorderRoutingProcessIcmp6Ra(otInstance *aInstance, const uint8_t *aMessage, uint16_t aLength); @@ -108,6 +111,7 @@ class RoutingManager : public InstanceLocator typedef otBorderRoutingPrefixTableIterator PrefixTableIterator; ///< Prefix Table Iterator. typedef otBorderRoutingPrefixTableEntry PrefixTableEntry; ///< Prefix Table Entry. typedef otBorderRoutingRouterEntry RouterEntry; ///< Router Entry. + typedef otBorderRoutingPeerBorderRouterEntry PeerBrEntry; ///< Peer Border Router Entry. typedef otPdProcessedRaInfo PdProcessedRaInfo; ///< Data of PdProcessedRaInfo. typedef otBorderRoutingRequestDhcp6PdCallback PdCallback; ///< DHCPv6 PD callback. @@ -501,6 +505,38 @@ class RoutingManager : public InstanceLocator return mRxRaTracker.GetNextRouter(aIterator, aEntry); } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + + /** + * Iterates over the peer BRs found in the Network Data. + * + * @param[in,out] aIterator An iterator. + * @param[out] aEntry A reference to the entry to populate. + * + * @retval kErrorNone Got the next peer BR info, @p aEntry is updated and @p aIterator is advanced. + * @retval kErrorNotFound No more PR beers in the list. + * + */ + Error GetNextPeerBrEntry(PrefixTableIterator &aIterator, PeerBrEntry &aEntry) const + { + return mNetDataPeerBrTracker.GetNext(aIterator, aEntry); + } + + /** + * Returns the number of peer BRs found in the Network Data. + * + * The count does not include this device itself (when it itself is acting as a BR). + * + * @param[out] aMinAge Reference to an `uint32_t` to return the minimum age among all peer BRs. + * Age is represented as seconds since appearance of the BR entry in the Network Data. + * + * @returns The number of peer BRs. + * + */ + uint16_t CountPeerBrs(uint32_t &aMinAge) const { return mNetDataPeerBrTracker.CountPeerBrs(aMinAge); } + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE /** * Determines whether to enable/disable SRP server when the auto-enable mode is changed on SRP server. @@ -736,6 +772,51 @@ class RoutingManager : public InstanceLocator //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + + class RxRaTracker; + + class NetDataPeerBrTracker : public InstanceLocator + { + friend class RxRaTracker; + + public: + explicit NetDataPeerBrTracker(Instance &aInstance); + + uint16_t CountPeerBrs(uint32_t &aMinAge) const; + Error GetNext(PrefixTableIterator &aIterator, PeerBrEntry &aEntry) const; + + void HandleNotifierEvents(Events aEvents); + + private: + struct PeerBr : LinkedListEntry, Heap::Allocatable + { + struct Filter + { + Filter(const NetworkData::Rlocs &aRlocs) + : mExcludeRlocs(aRlocs) + { + } + + const NetworkData::Rlocs &mExcludeRlocs; + }; + + uint32_t GetAge(uint32_t aUptime) const { return aUptime - mDiscoverTime; } + bool Matches(uint16_t aRloc16) const { return mRloc16 == aRloc16; } + bool Matches(const Filter &aFilter) const { return !aFilter.mExcludeRlocs.Contains(mRloc16); } + + PeerBr *mNext; + uint16_t mRloc16; + uint32_t mDiscoverTime; + }; + + OwningList mPeerBrs; + }; + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + void HandleRxRaTrackerSignalTask(void) { mRxRaTracker.HandleSignalTask(); } void HandleRxRaTrackerExpirationTimer(void) { mRxRaTracker.HandleExpirationTimer(); } void HandleRxRaTrackerStaleTimer(void) { mRxRaTracker.HandleStaleTimer(); } @@ -755,6 +836,8 @@ class RoutingManager : public InstanceLocator // the same flow of execution, the callback is invoked after all the // changes are processed. + friend class NetDataPeerBrTracker; + public: explicit RxRaTracker(Instance &aInstance); @@ -881,6 +964,7 @@ class RoutingManager : public InstanceLocator kUnspecified, kRouterIterator, kPrefixIterator, + kPeerBrIterator, }; enum EntryType : uint8_t @@ -903,6 +987,13 @@ class RoutingManager : public InstanceLocator return static_cast *>(mPtr2); } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + using PeerBr = NetDataPeerBrTracker::PeerBr; + + Error AdvanceToNextPeerBr(const PeerBr *aPeerBrsHead); + const PeerBr *GetPeerBrEntry(void) const { return static_cast(mPtr2); } +#endif + private: void SetRouter(const Entry *aRouter) { mPtr1 = aRouter; } void SetInitUptime(uint32_t aUptime) { mData0 = aUptime; } @@ -1487,6 +1578,10 @@ class RoutingManager : public InstanceLocator OnLinkPrefixManager mOnLinkPrefixManager; +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + NetDataPeerBrTracker mNetDataPeerBrTracker; +#endif + RxRaTracker mRxRaTracker; RoutePublisher mRoutePublisher; diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index 8f278f437..8e02b65b3 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -805,6 +805,12 @@ def br_clear_routeprf(self): def br_get_routers(self): return self.cli('br routers') + def br_get_peer_brs(self): + return self.cli('br peers') + + def br_count_peers(self): + return self._cli_single_output('br peers count') + # ------------------------------------------------------------------------------------------------------------------ # Helper methods diff --git a/tests/toranj/cli/test-503-peer-tbr-discovery.py b/tests/toranj/cli/test-503-peer-tbr-discovery.py index d6816a4bf..2cc58d42f 100755 --- a/tests/toranj/cli/test-503-peer-tbr-discovery.py +++ b/tests/toranj/cli/test-503-peer-tbr-discovery.py @@ -104,11 +104,33 @@ # Validate that all BRs discovered the other ones as peer BR -for br in [br1, br2, br3]: +all_brs = [br1, br2, br3] + +for br in all_brs: routers = br.br_get_routers() verify(len(routers) == 2) for router in routers: verify(router.endswith('(peer BR)')) + verify(int(br.br_count_peers().split()[0]) == 2) + peers = br.br_get_peer_brs() + verify(len(peers) == 2) + other_brs = all_brs + other_brs.remove(br) + for other_br in other_brs: + rloc16 = other_br.get_rloc16() + verify(any([rloc16 in peer for peer in peers])) + +# Disable BR3 and validate that BR1 and BR2 detect this. +# BR3 itself should continue to detect BR1 and BR2 + +br3.br_disable() + +time.sleep(0.5) + +for br in [br1, br2]: + verify(len(br.br_get_peer_brs()) == 1) + +verify(len(br3.br_get_peer_brs()) == 2) # ----------------------------------------------------------------------------------------------------------------------- # Test finished From 7887cc75740ccc16257e62f8272f24778faaf5e5 Mon Sep 17 00:00:00 2001 From: Li Cao Date: Tue, 16 Jul 2024 00:50:25 +0800 Subject: [PATCH 048/160] [spinel] fix hints of missing radio caps (#10507) --- src/lib/spinel/radio_spinel.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index da71ecf38..016dd95d2 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -221,6 +221,10 @@ void RadioSpinel::InitializeCaps(bool &aSupportsRcpApiVersion, bool &aSupportsRc otError RadioSpinel::CheckRadioCapabilities(otRadioCaps aRequiredRadioCaps) { + static const char *const kAllRadioCapsStr[] = {"ack-timeout", "energy-scan", "tx-retries", "CSMA-backoff", + "sleep-to-tx", "tx-security", "tx-timing", "rx-timing", + "rx-on-when-idle", "tx-frame-power"}; + otError error = OT_ERROR_NONE; unsigned int radioCaps; @@ -230,17 +234,15 @@ otError RadioSpinel::CheckRadioCapabilities(otRadioCaps aRequiredRadioCaps) if ((sRadioCaps & aRequiredRadioCaps) != aRequiredRadioCaps) { otRadioCaps missingCaps = (sRadioCaps & aRequiredRadioCaps) ^ aRequiredRadioCaps; + LogCrit("RCP is missing required capabilities: "); - // missingCaps may be an unused variable when LogCrit is blank - // avoid compiler warning in that case - OT_UNUSED_VARIABLE(missingCaps); - - LogCrit("RCP is missing required capabilities: %s%s%s%s%s", - (missingCaps & OT_RADIO_CAPS_ACK_TIMEOUT) ? "ack-timeout " : "", - (missingCaps & OT_RADIO_CAPS_TRANSMIT_RETRIES) ? "tx-retries " : "", - (missingCaps & OT_RADIO_CAPS_CSMA_BACKOFF) ? "CSMA-backoff " : "", - (missingCaps & OT_RADIO_CAPS_TRANSMIT_SEC) ? "tx-security " : "", - (missingCaps & OT_RADIO_CAPS_TRANSMIT_TIMING) ? "tx-timing " : ""); + for (unsigned long i = 0; i < sizeof(kAllRadioCapsStr) / sizeof(kAllRadioCapsStr[0]); i++) + { + if (missingCaps & (1 << i)) + { + LogCrit(" %s", kAllRadioCapsStr[i]); + } + } DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE); } From 9b57797c0a2377b2287545aaf8a191f8219d92a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 10:26:28 -0700 Subject: [PATCH 049/160] github-actions: bump actions/checkout from 4.1.6 to 4.1.7 (#10517) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.6 to 4.1.7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/a5ac7e51b41094c92402da3b24376905380afc29...692973e3d937129bcbf40652eb9f2f61becf3332) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 30 ++++++++++++++-------------- .github/workflows/codeql.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/makefile-check.yml | 2 +- .github/workflows/otbr.yml | 6 +++--- .github/workflows/otci.yml | 2 +- .github/workflows/otns.yml | 8 ++++---- .github/workflows/posix.yml | 12 +++++------ .github/workflows/scorecards.yml | 2 +- .github/workflows/simulation-1.1.yml | 18 ++++++++--------- .github/workflows/simulation-1.2.yml | 14 ++++++------- .github/workflows/size.yml | 2 +- .github/workflows/toranj.yml | 10 +++++----- .github/workflows/unit.yml | 6 +++--- .github/workflows/version.yml | 2 +- 15 files changed, 59 insertions(+), 59 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index acfdd5d52..34ba41831 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,7 +53,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -75,7 +75,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # v1 with: use-verbose-mode: 'yes' @@ -89,7 +89,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -108,7 +108,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -148,7 +148,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -167,7 +167,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -186,14 +186,14 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y ninja-build libreadline-dev libncurses-dev rm -rf third_party/mbedtls/repo - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: repository: ARMmbed/mbedtls ref: v2.28.8 @@ -242,7 +242,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -283,7 +283,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -316,7 +316,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -354,7 +354,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -382,7 +382,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -418,7 +418,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -442,7 +442,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Install unzip diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b12e5d817..164b09f6f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -59,7 +59,7 @@ jobs: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - name: Checkout repository - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Bootstrap run: | diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c8d37141e..ffbc2b7c3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -59,7 +59,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true diff --git a/.github/workflows/makefile-check.yml b/.github/workflows/makefile-check.yml index e264d5fd8..2101e9517 100644 --- a/.github/workflows/makefile-check.yml +++ b/.github/workflows/makefile-check.yml @@ -52,7 +52,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Check diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml index 0d4d5c46f..214334923 100644 --- a/.github/workflows/otbr.yml +++ b/.github/workflows/otbr.yml @@ -62,7 +62,7 @@ jobs: # of OMR prefix and Domain prefix is not deterministic. BORDER_ROUTING: 0 steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Build OTBR Docker @@ -181,7 +181,7 @@ jobs: NAT64: ${{ matrix.nat64 }} MAX_JOBS: 3 steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set firewall environment variables if: ${{ matrix.use_core_firewall }} run: | @@ -238,7 +238,7 @@ jobs: - thread-border-router runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml index dea78b709..ad862253a 100644 --- a/.github/workflows/otci.yml +++ b/.github/workflows/otci.yml @@ -61,7 +61,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml index 346671d11..e085662dc 100644 --- a/.github/workflows/otns.yml +++ b/.github/workflows/otns.yml @@ -62,7 +62,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version: "1.20" @@ -102,7 +102,7 @@ jobs: name: Examples runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version: "1.20" @@ -164,7 +164,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version: "1.20" @@ -212,7 +212,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y lcov diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index 7f3e3cb65..f07d532e4 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -56,7 +56,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat @@ -142,7 +142,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -186,7 +186,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update @@ -236,7 +236,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Bootstrap run: | rm -f /usr/local/bin/2to3 @@ -266,7 +266,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Bootstrap env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" @@ -300,7 +300,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index f2ea58d95..b9dc91dc2 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -60,7 +60,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: persist-credentials: false diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml index f7a8eed31..366f09303 100644 --- a/.github/workflows/simulation-1.1.yml +++ b/.github/workflows/simulation-1.1.yml @@ -59,7 +59,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -108,7 +108,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -159,7 +159,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -203,7 +203,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -243,7 +243,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat @@ -284,7 +284,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -337,7 +337,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -374,7 +374,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -408,7 +408,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml index 515e2ab3e..d6870933f 100644 --- a/.github/workflows/simulation-1.2.yml +++ b/.github/workflows/simulation-1.2.yml @@ -70,7 +70,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -132,7 +132,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -203,7 +203,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -251,7 +251,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -292,7 +292,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -342,7 +342,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -403,7 +403,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml index 5ba9f3d37..2217c7273 100644 --- a/.github/workflows/size.yml +++ b/.github/workflows/size.yml @@ -53,7 +53,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Run env: OT_BASE_BRANCH: "${{ github.base_ref }}" diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml index 9b3983af6..120b8b820 100644 --- a/.github/workflows/toranj.yml +++ b/.github/workflows/toranj.yml @@ -63,7 +63,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -94,7 +94,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -127,7 +127,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -168,7 +168,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -191,7 +191,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 9a8a66f71..1c8bdcb22 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -53,7 +53,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Build @@ -71,7 +71,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap @@ -108,7 +108,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Bootstrap diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 054c887fa..cadf03116 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -49,7 +49,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Check From 6f2d8b6b2298697239e103bcd7d3c794e17f479b Mon Sep 17 00:00:00 2001 From: Li Cao Date: Tue, 16 Jul 2024 01:43:40 +0800 Subject: [PATCH 050/160] [spinel] add get iid method (#10516) This commit adds a method to get spinel interface Id in `SpinelDriver`. This is to enable the user class of `SpinelDriver` can compose the spinel header itself. --- src/lib/spinel/spinel_driver.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/spinel/spinel_driver.hpp b/src/lib/spinel/spinel_driver.hpp index 5a3090c83..0c3657f6f 100644 --- a/src/lib/spinel/spinel_driver.hpp +++ b/src/lib/spinel/spinel_driver.hpp @@ -210,6 +210,14 @@ class SpinelDriver : public otSpinelDriver, public Logger */ bool CoprocessorHasCap(unsigned int aCapability) { return mCoprocessorCaps.Contains(aCapability); } + /** + * Returns the spinel interface id. + * + * @returns the spinel interface id. + * + */ + spinel_iid_t GetIid(void) { return mIid; } + private: static constexpr uint16_t kMaxSpinelFrame = SPINEL_FRAME_MAX_SIZE; static constexpr uint16_t kVersionStringSize = 128; From b301a4c43f19c984e9e7232d964dc6212d35e0d6 Mon Sep 17 00:00:00 2001 From: sarveshkumarv3 <86755931+sarveshkumarv3@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:17:09 -0700 Subject: [PATCH 051/160] [settings] reset the platform settings file descriptor once closed (#10512) On platform settings deinit, the file descriptor is closed but the variable is not reset, which can lead to calling close on a stale fd leading to bad file descriptor error in case the API is invoked multiple times. --- src/posix/platform/settings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/posix/platform/settings.cpp b/src/posix/platform/settings.cpp index 3b4c2fd56..8ef12ffab 100644 --- a/src/posix/platform/settings.cpp +++ b/src/posix/platform/settings.cpp @@ -232,6 +232,7 @@ void otPlatSettingsDeinit(otInstance *aInstance) VerifyOrExit(sSettingsFd != -1); VerifyOrDie(close(sSettingsFd) == 0, OT_EXIT_ERROR_ERRNO); + sSettingsFd = -1; exit: return; From be6b198730df0d700229b6d0a250cf070abfdff1 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 16 Jul 2024 08:57:49 -0700 Subject: [PATCH 052/160] [mesh-forwarder] use `RemoveMessageIfNoPendingTx()` (#10513) This commit replaces direct calls to `DequeueAndFree()` with `RemoveMessageIfNoPendingTx()` in `EvictMessage()`, `HandleResolved()`, and `RemoveDataResponseMessages()`. This promotes consistency in message removal logic and eliminates the need to check if the removed message is `mSendMessage`, as this is already handled by `RemoveMessageIfNoPendingTx()`. --- src/core/thread/mesh_forwarder.cpp | 15 +++++++-------- src/core/thread/mesh_forwarder_ftd.cpp | 9 ++------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 3eb324339..004bbe834 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -208,6 +208,8 @@ void MeshForwarder::EvictMessage(Message &aMessage) OT_ASSERT(queue != nullptr); + LogMessage(kMessageEvict, aMessage, kErrorNoBufs); + if (queue == &mSendQueue) { #if OPENTHREAD_FTD @@ -218,15 +220,12 @@ void MeshForwarder::EvictMessage(Message &aMessage) #endif FinalizeMessageDirectTx(aMessage, kErrorNoBufs); - - if (mSendMessage == &aMessage) - { - mSendMessage = nullptr; - } + RemoveMessageIfNoPendingTx(aMessage); + } + else + { + queue->DequeueAndFree(aMessage); } - - LogMessage(kMessageEvict, aMessage, kErrorNoBufs); - queue->DequeueAndFree(aMessage); } void MeshForwarder::ResumeMessageTransmissions(void) diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 6f7039640..603c7ed14 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -166,7 +166,7 @@ void MeshForwarder::HandleResolved(const Ip6::Address &aEid, Error aError) { LogMessage(kMessageDrop, message, kErrorAddressQuery); FinalizeMessageDirectTx(message, kErrorAddressQuery); - mSendQueue.DequeueAndFree(message); + RemoveMessageIfNoPendingTx(message); continue; } @@ -337,14 +337,9 @@ void MeshForwarder::RemoveDataResponseMessages(void) } } - if (mSendMessage == &message) - { - mSendMessage = nullptr; - } - LogMessage(kMessageDrop, message); FinalizeMessageDirectTx(message, kErrorDrop); - mSendQueue.DequeueAndFree(message); + RemoveMessageIfNoPendingTx(message); } } From cf5637f3cabf70b39d153cc4ba61be6eb1b6461b Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 16 Jul 2024 09:11:10 -0700 Subject: [PATCH 053/160] [mesh-forwarder] `RemoveMessagesForChild()` to use a predicate fn (#10514) This commit updates `RemoveMessagesForChild()` to use a predicate function to determine which messages should be removed. This replaces the `Message::SubType` filtering and allows messages matching multiple sub-types to be removed together. --- src/core/thread/mesh_forwarder.hpp | 26 +++++++++++++++----- src/core/thread/mesh_forwarder_ftd.cpp | 34 +++++++++----------------- src/core/thread/mle_router.cpp | 31 +++++++++++++++++++---- src/core/thread/mle_router.hpp | 3 +++ 4 files changed, 60 insertions(+), 34 deletions(-) diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index 99b622fbd..2790be9ba 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -241,16 +241,30 @@ class MeshForwarder : public InstanceLocator, private NonCopyable void SetRxOnWhenIdle(bool aRxOnWhenIdle); #if OPENTHREAD_FTD + /** - * Frees any messages queued for an existing child. + * Represents a predicate function for checking if a given `Message` meets specific criteria. + * + * @param[in] aMessage The message to evaluate. * - * @param[in] aChild A reference to the child. - * @param[in] aSubType The message sub-type to remove. - * Use Message::kSubTypeNone remove all messages for @p aChild. + * @return TRUE If the @p aMessage satisfies the predicate condition. + * @return FALSE If the @p aMessage does not satisfy the predicate condition. * */ - void RemoveMessages(Child &aChild, Message::SubType aSubType); -#endif + typedef bool (&MessageChecker)(const Message &aMessage); + + /** + * Removes and frees messages queued for a child, based on a given predicate. + * + * The `aChild` can be either sleepy or non-sleepy. + * + * @param[in] aChild The child whose messages are to be evaluated. + * @param[in] aMessageChecker The predicate function to filter messages. + * + */ + void RemoveMessagesForChild(Child &aChild, MessageChecker aMessageChecker); + +#endif // OPENTHREAD_FTD /** * Frees unicast/multicast MLE Data Responses from Send Message Queue if any. diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 603c7ed14..904286439 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -266,49 +266,37 @@ Error MeshForwarder::EvictMessage(Message::Priority aPriority) return error; } -void MeshForwarder::RemoveMessages(Child &aChild, Message::SubType aSubType) +void MeshForwarder::RemoveMessagesForChild(Child &aChild, MessageChecker &aMessageChecker) { for (Message &message : mSendQueue) { - if ((aSubType != Message::kSubTypeNone) && (aSubType != message.GetSubType())) + if (!aMessageChecker(message)) { continue; } if (mIndirectSender.RemoveMessageFromSleepyChild(message, aChild) != kErrorNone) { - switch (message.GetType()) - { - case Message::kTypeIp6: + const Neighbor *neighbor = nullptr; + + if (message.GetType() == Message::kTypeIp6) { Ip6::Header ip6header; IgnoreError(message.Read(0, ip6header)); - - if (&aChild == Get().FindNeighbor(ip6header.GetDestination())) - { - message.ClearDirectTransmission(); - } - - break; + neighbor = Get().FindNeighbor(ip6header.GetDestination()); } - - case Message::kType6lowpan: + else if (message.GetType() == Message::kType6lowpan) { Lowpan::MeshHeader meshHeader; IgnoreError(meshHeader.ParseFrom(message)); - - if (&aChild == Get().FindNeighbor(meshHeader.GetDestination())) - { - message.ClearDirectTransmission(); - } - - break; + neighbor = Get().FindNeighbor(meshHeader.GetDestination()); } - default: - break; + if (&aChild == neighbor) + { + message.ClearDirectTransmission(); } } diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index a776e533e..d4d1878d5 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -1960,6 +1960,30 @@ void MleRouter::SignalDuaAddressEvent(const Child &aChild, const Ip6::Address &a } #endif // OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE +bool MleRouter::IsMessageMleSubType(const Message &aMessage) +{ + bool isMle = false; + + switch (aMessage.GetSubType()) + { + case Message::kSubTypeMleGeneral: + case Message::kSubTypeMleChildIdRequest: + case Message::kSubTypeMleChildUpdateRequest: + case Message::kSubTypeMleDataResponse: + isMle = true; + break; + default: + break; + } + + return isMle; +} + +bool MleRouter::IsMessageChildUpdateRequest(const Message &aMessage) +{ + return aMessage.GetSubType() == Message::kSubTypeMleChildUpdateRequest; +} + void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) { Error error = kErrorNone; @@ -1990,10 +2014,7 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) SuccessOrExit(error = aRxInfo.mMessage.ReadAndMatchResponseTlvWith(child->GetChallenge())); - Get().RemoveMessages(*child, Message::kSubTypeMleGeneral); - Get().RemoveMessages(*child, Message::kSubTypeMleChildIdRequest); - Get().RemoveMessages(*child, Message::kSubTypeMleChildUpdateRequest); - Get().RemoveMessages(*child, Message::kSubTypeMleDataResponse); + Get().RemoveMessagesForChild(*child, IsMessageMleSubType); SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter)); @@ -2883,7 +2904,7 @@ Error MleRouter::SendChildUpdateRequest(Child &aChild) // Remove queued outdated "Child Update Request" when // there is newer Network Data is to send. - Get().RemoveMessages(aChild, Message::kSubTypeMleChildUpdateRequest); + Get().RemoveMessagesForChild(aChild, IsMessageChildUpdateRequest); break; } } diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index ac39177cf..8a43b1941 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -608,6 +608,9 @@ class MleRouter : public Mle void HandleNetworkDataUpdateRouter(void); void HandleDiscoveryRequest(RxInfo &aRxInfo); + static bool IsMessageMleSubType(const Message &aMessage); + static bool IsMessageChildUpdateRequest(const Message &aMessage); + Error ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo); Error ReadAndProcessRouteTlvOnFed(RxInfo &aRxInfo, uint8_t aParentId); From aba7aede1476339abe3032a250349d67a4ec1605 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 16 Jul 2024 13:06:14 -0700 Subject: [PATCH 054/160] [network-data] enhance route lookup for ALOC destination (#10509) This commit updates `NetworkData::Leader` to enhance route lookup for ALOC destinations. It contains the following changes: - Adds `LookupRouteForServiceAloc()` to look up the route for a service ALOC. This method uses the common `CompareRouteEntries()`, which applies the same rules as in external route lookup. - Adds `LookupRouteForAgentAloc()` to look up the route for DHCPv6 or ND agent ALOC. - Adds `FindPrefixTlvForContextId()` to find a Prefix TLV matching a given Context ID (used in `GetContext()` and `LookupRouteForAgentAloc()`) - Adds `LookupRouteIn()` to perform route lookup among all entries of a given `PrefixTlv` that match a given `EntryChecker` function (used for `DefaultRouteLookup()` or `LookupRouteForAgentAloc()`) - Adds `test-031-service-aloc-route-lookup.py`. --- src/core/thread/network_data_leader.cpp | 83 ++++++--- src/core/thread/network_data_leader.hpp | 44 +++-- src/core/thread/network_data_leader_ftd.cpp | 131 ++++++-------- tests/toranj/cli/cli.py | 3 + .../cli/test-031-service-aloc-route-lookup.py | 168 ++++++++++++++++++ tests/toranj/start.sh | 1 + 6 files changed, 300 insertions(+), 130 deletions(-) create mode 100755 tests/toranj/cli/test-031-service-aloc-route-lookup.py diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp index f032f4c0b..d6742a923 100644 --- a/src/core/thread/network_data_leader.cpp +++ b/src/core/thread/network_data_leader.cpp @@ -187,34 +187,46 @@ Error Leader::GetContext(const Ip6::Address &aAddress, Lowpan::Context &aContext return (aContext.mPrefix.GetLength() > 0) ? kErrorNone : kErrorNotFound; } -Error Leader::GetContext(uint8_t aContextId, Lowpan::Context &aContext) const +const PrefixTlv *Leader::FindPrefixTlvForContextId(uint8_t aContextId, const ContextTlv *&aContextTlv) const { - Error error = kErrorNotFound; TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd()); const PrefixTlv *prefixTlv; - if (aContextId == Mle::kMeshLocalPrefixContextId) - { - GetContextForMeshLocalPrefix(aContext); - ExitNow(error = kErrorNone); - } - while ((prefixTlv = tlvIterator.Iterate()) != nullptr) { const ContextTlv *contextTlv = prefixTlv->FindSubTlv(); - if ((contextTlv == nullptr) || (contextTlv->GetContextId() != aContextId)) + if ((contextTlv != nullptr) && (contextTlv->GetContextId() == aContextId)) { - continue; + aContextTlv = contextTlv; + break; } + } - prefixTlv->CopyPrefixTo(aContext.mPrefix); - aContext.mContextId = contextTlv->GetContextId(); - aContext.mCompressFlag = contextTlv->IsCompress(); - aContext.mIsValid = true; - ExitNow(error = kErrorNone); + return prefixTlv; +} + +Error Leader::GetContext(uint8_t aContextId, Lowpan::Context &aContext) const +{ + Error error = kErrorNone; + TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd()); + const PrefixTlv *prefixTlv; + const ContextTlv *contextTlv; + + if (aContextId == Mle::kMeshLocalPrefixContextId) + { + GetContextForMeshLocalPrefix(aContext); + ExitNow(); } + prefixTlv = FindPrefixTlvForContextId(aContextId, contextTlv); + VerifyOrExit(prefixTlv != nullptr, error = kErrorNotFound); + + prefixTlv->CopyPrefixTo(aContext.mPrefix); + aContext.mContextId = contextTlv->GetContextId(); + aContext.mCompressFlag = contextTlv->IsCompress(); + aContext.mIsValid = true; + exit: return error; } @@ -299,13 +311,22 @@ Error Leader::RouteLookup(const Ip6::Address &aSource, const Ip6::Address &aDest return error; } -template int Leader::CompareRouteEntries(const EntryType &aFirst, const EntryType &aSecond) const +int Leader::CompareRouteEntries(const BorderRouterEntry &aFirst, const BorderRouterEntry &aSecond) const { - // `EntryType` can be `HasRouteEntry` or `BorderRouterEntry`. + return CompareRouteEntries(aFirst.GetPreference(), aFirst.GetRloc(), aSecond.GetPreference(), aSecond.GetRloc()); +} +int Leader::CompareRouteEntries(const HasRouteEntry &aFirst, const HasRouteEntry &aSecond) const +{ return CompareRouteEntries(aFirst.GetPreference(), aFirst.GetRloc(), aSecond.GetPreference(), aSecond.GetRloc()); } +int Leader::CompareRouteEntries(const ServerTlv &aFirst, const ServerTlv &aSecond) const +{ + return CompareRouteEntries(/* aFirstPreference */ 0, aFirst.GetServer16(), /* aSecondPreference */ 0, + aSecond.GetServer16()); +} + int Leader::CompareRouteEntries(int8_t aFirstPreference, uint16_t aFirstRloc, int8_t aSecondPreference, @@ -393,39 +414,51 @@ Error Leader::ExternalRouteLookup(uint8_t aDomainId, const Ip6::Address &aDestin return error; } -Error Leader::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) const +Error Leader::LookupRouteIn(const PrefixTlv &aPrefixTlv, EntryChecker aEntryChecker, uint16_t &aRloc16) const { + // Iterates over all `BorderRouterEntry` associated with + // `aPrefixTlv` which also match `aEntryChecker` and determine the + // best route. For example, this is used from `DefaultRouteLookup()` + // to look up best default route. + Error error = kErrorNoRoute; - TlvIterator subTlvIterator(aPrefix); + TlvIterator subTlvIterator(aPrefixTlv); const BorderRouterTlv *brTlv; - const BorderRouterEntry *route = nullptr; + const BorderRouterEntry *bestEntry = nullptr; while ((brTlv = subTlvIterator.Iterate()) != nullptr) { for (const BorderRouterEntry *entry = brTlv->GetFirstEntry(); entry <= brTlv->GetLastEntry(); entry = entry->GetNext()) { - if (!entry->IsDefaultRoute()) + if (!aEntryChecker(*entry)) { continue; } - if (route == nullptr || CompareRouteEntries(*entry, *route) > 0) + if ((bestEntry == nullptr) || CompareRouteEntries(*entry, *bestEntry) > 0) { - route = entry; + bestEntry = entry; } } } - if (route != nullptr) + if (bestEntry != nullptr) { - aRloc16 = route->GetRloc(); + aRloc16 = bestEntry->GetRloc(); error = kErrorNone; } return error; } +bool Leader::IsEntryDefaultRoute(const BorderRouterEntry &aEntry) { return aEntry.IsDefaultRoute(); } + +Error Leader::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) const +{ + return LookupRouteIn(aPrefix, IsEntryDefaultRoute, aRloc16); +} + Error Leader::SetNetworkData(uint8_t aVersion, uint8_t aStableVersion, Type aType, diff --git a/src/core/thread/network_data_leader.hpp b/src/core/thread/network_data_leader.hpp index f7ba0c138..22566d531 100644 --- a/src/core/thread/network_data_leader.hpp +++ b/src/core/thread/network_data_leader.hpp @@ -423,15 +423,6 @@ class Leader : public MutableNetworkData, private NonCopyable */ void HandleNetworkDataRestoredAfterReset(void); - /** - * Scans network data for given Service ID and returns pointer to the respective TLV, if present. - * - * @param aServiceId Service ID to look for. - * @return Pointer to the Service TLV for given Service ID, or nullptr if not present. - * - */ - const ServiceTlv *FindServiceById(uint8_t aServiceId) const; - #endif // OPENTHREAD_FTD #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE @@ -453,16 +444,24 @@ class Leader : public MutableNetworkData, private NonCopyable private: using FilterIndexes = MeshCoP::SteeringData::HashBitIndexes; + typedef bool (&EntryChecker)(const BorderRouterEntry &aEntry); + const PrefixTlv *FindNextMatchingPrefixTlv(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const; + const PrefixTlv *FindPrefixTlvForContextId(uint8_t aContextId, const ContextTlv *&aContextTlv) const; - template int CompareRouteEntries(const EntryType &aFirst, const EntryType &aSecond) const; - int CompareRouteEntries(int8_t aFirstPreference, - uint16_t aFirstRloc, - int8_t aSecondPreference, - uint16_t aSecondRloc) const; + int CompareRouteEntries(const BorderRouterEntry &aFirst, const BorderRouterEntry &aSecond) const; + int CompareRouteEntries(const HasRouteEntry &aFirst, const HasRouteEntry &aSecond) const; + int CompareRouteEntries(const ServerTlv &aFirst, const ServerTlv &aSecond) const; + int CompareRouteEntries(int8_t aFirstPreference, + uint16_t aFirstRloc, + int8_t aSecondPreference, + uint16_t aSecondRloc) const; + + static bool IsEntryDefaultRoute(const BorderRouterEntry &aEntry); Error ExternalRouteLookup(uint8_t aDomainId, const Ip6::Address &aDestination, uint16_t &aRloc16) const; Error DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) const; + Error LookupRouteIn(const PrefixTlv &aPrefixTlv, EntryChecker aEntryChecker, uint16_t &aRloc16) const; Error SteeringDataCheck(const FilterIndexes &aFilterIndexes) const; void GetContextForMeshLocalPrefix(Lowpan::Context &aContext) const; Error ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::Type aType, uint16_t &aValue) const; @@ -480,13 +479,6 @@ class Leader : public MutableNetworkData, private NonCopyable static constexpr uint8_t kMinServiceId = 0x00; static constexpr uint8_t kMaxServiceId = 0x0f; - enum AnycastType : uint8_t - { - kAnycastDhcp6Agent, - kAnycastNdAgent, - kAnycastService, - }; - class ChangedFlags { public: @@ -571,8 +563,11 @@ class Leader : public MutableNetworkData, private NonCopyable void HandleTimer(void); - Error AnycastLookup(uint8_t aServiceId, AnycastType aType, uint16_t &aRloc16) const; - void EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const; + static bool IsEntryForDhcp6Agent(const BorderRouterEntry &aEntry); + static bool IsEntryForNdAgent(const BorderRouterEntry &aEntry); + + Error LookupRouteForServiceAloc(uint16_t aAloc16, uint16_t &aRloc16) const; + Error LookupRouteForAgentAloc(uint8_t aContextId, EntryChecker aEntryChecker, uint16_t &aRloc16) const; void RegisterNetworkData(uint16_t aRloc16, const NetworkData &aNetworkData); @@ -582,7 +577,8 @@ class Leader : public MutableNetworkData, private NonCopyable Error AddService(const ServiceTlv &aService, ChangedFlags &aChangedFlags); Error AddServer(const ServerTlv &aServer, ServiceTlv &aDstService, ChangedFlags &aChangedFlags); - Error AllocateServiceId(uint8_t &aServiceId) const; + Error AllocateServiceId(uint8_t &aServiceId) const; + const ServiceTlv *FindServiceById(uint8_t aServiceId) const; void RemoveContext(uint8_t aContextId); void RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId); diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp index 684d907cb..3768d385d 100644 --- a/src/core/thread/network_data_leader_ftd.cpp +++ b/src/core/thread/network_data_leader_ftd.cpp @@ -119,6 +119,8 @@ Error Leader::AnycastLookup(uint16_t aAloc16, uint16_t &aRloc16) const { Error error = kErrorNone; + aRloc16 = Mle::kInvalidRloc16; + if (aAloc16 == Mle::kAloc16Leader) { aRloc16 = Get().GetLeaderRloc16(); @@ -127,13 +129,11 @@ Error Leader::AnycastLookup(uint16_t aAloc16, uint16_t &aRloc16) const { uint8_t contextId = static_cast(aAloc16 - Mle::kAloc16DhcpAgentStart + 1); - error = AnycastLookup(contextId, kAnycastDhcp6Agent, aRloc16); + error = LookupRouteForAgentAloc(contextId, IsEntryForDhcp6Agent, aRloc16); } else if (aAloc16 <= Mle::kAloc16ServiceEnd) { - uint8_t serviceId = static_cast(aAloc16 - Mle::kAloc16ServiceStart); - - error = AnycastLookup(serviceId, kAnycastService, aRloc16); + error = LookupRouteForServiceAloc(aAloc16, aRloc16); } else if (aAloc16 <= Mle::kAloc16CommissionerEnd) { @@ -150,111 +150,80 @@ Error Leader::AnycastLookup(uint16_t aAloc16, uint16_t &aRloc16) const { uint8_t contextId = static_cast(aAloc16 - Mle::kAloc16NeighborDiscoveryAgentStart + 1); - error = AnycastLookup(contextId, kAnycastNdAgent, aRloc16); + error = LookupRouteForAgentAloc(contextId, IsEntryForNdAgent, aRloc16); } else { - ExitNow(error = kErrorDrop); + error = kErrorDrop; } -exit: - return error; -} - -Error Leader::AnycastLookup(uint8_t aServiceId, AnycastType aType, uint16_t &aRloc16) const -{ - Iterator iterator = kIteratorInit; - uint8_t bestCost = Mle::kMaxRouteCost; - uint16_t bestDest = Mle::kInvalidRloc16; + SuccessOrExit(error); + VerifyOrExit(aRloc16 != Mle::kInvalidRloc16, error = kErrorNoRoute); - switch (aType) + if (Mle::IsChildRloc16(aRloc16)) { - case kAnycastDhcp6Agent: - case kAnycastNdAgent: - { - OnMeshPrefixConfig config; - Lowpan::Context context; + // If the selected destination is a child, we use its parent + // as the destination unless the device itself is the + // parent of the `aRloc16`. - SuccessOrExit(GetContext(aServiceId, context)); + uint16_t parentRloc16 = Mle::ParentRloc16ForRloc16(aRloc16); - while (GetNextOnMeshPrefix(iterator, config) == kErrorNone) + if (!Get().HasRloc16(parentRloc16)) { - if (config.GetPrefix() != context.mPrefix) - { - continue; - } + aRloc16 = parentRloc16; + } + } - switch (aType) - { - case kAnycastDhcp6Agent: - if (!(config.mDhcp || config.mConfigure)) - { - continue; - } - break; - case kAnycastNdAgent: - if (!config.mNdDns) - { - continue; - } - break; - default: - OT_ASSERT(false); - break; - } +exit: + return error; +} - EvaluateRoutingCost(config.mRloc16, bestCost, bestDest); - } +Error Leader::LookupRouteForServiceAloc(uint16_t aAloc16, uint16_t &aRloc16) const +{ + Error error = kErrorNoRoute; + const ServiceTlv *serviceTlv = FindServiceById(Mle::ServiceIdFromAloc(aAloc16)); - break; - } - case kAnycastService: + if (serviceTlv != nullptr) { - ServiceConfig config; + TlvIterator subTlvIterator(*serviceTlv); + const ServerTlv *bestServerTlv = nullptr; + const ServerTlv *serverTlv; - while (GetNextService(iterator, config) == kErrorNone) + while ((serverTlv = subTlvIterator.Iterate()) != nullptr) { - if (config.mServiceId != aServiceId) + if ((bestServerTlv == nullptr) || CompareRouteEntries(*serverTlv, *bestServerTlv) > 0) { - continue; + bestServerTlv = serverTlv; } - - EvaluateRoutingCost(config.mServerConfig.mRloc16, bestCost, bestDest); } - break; - } - } - - if (Mle::IsChildRloc16(bestDest)) - { - // If the selected destination is a child, we use its parent - // as the destination unless the device itself is the - // parent of the `bestDest`. - - uint16_t bestDestParent = Mle::ParentRloc16ForRloc16(bestDest); - - if (!Get().HasRloc16(bestDestParent)) + if (bestServerTlv != nullptr) { - bestDest = bestDestParent; + aRloc16 = bestServerTlv->GetServer16(); + error = kErrorNone; } } - aRloc16 = bestDest; - -exit: - return (bestDest != Mle::kInvalidRloc16) ? kErrorNone : kErrorNoRoute; + return error; } -void Leader::EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const +bool Leader::IsEntryForDhcp6Agent(const BorderRouterEntry &aEntry) { return aEntry.IsDhcp() || aEntry.IsConfigure(); } + +bool Leader::IsEntryForNdAgent(const BorderRouterEntry &aEntry) { return aEntry.IsNdDns(); } + +Error Leader::LookupRouteForAgentAloc(uint8_t aContextId, EntryChecker aEntryChecker, uint16_t &aRloc16) const { - uint8_t cost = Get().GetPathCost(aDest); + Error error = kErrorNoRoute; + const PrefixTlv *prefixTlv; + const ContextTlv *contextTlv; - if ((aBestDest == Mle::kInvalidRloc16) || (cost < aBestCost)) - { - aBestDest = aDest; - aBestCost = cost; - } + prefixTlv = FindPrefixTlvForContextId(aContextId, contextTlv); + VerifyOrExit(prefixTlv != nullptr); + + error = LookupRouteIn(*prefixTlv, aEntryChecker, aRloc16); + +exit: + return error; } void Leader::RemoveBorderRouter(uint16_t aRloc16, MatchMode aMatchMode) diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index 8e02b65b3..308a39867 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -469,6 +469,9 @@ def ping(self, address, size=0, count=1, verify_success=True): def get_mle_counter(self): return self.cli('counters mle') + def get_ip_counters(self): + return Node.parse_list(self.cli('counters ip')) + def get_br_counter_unicast_outbound_packets(self): outputs = self.cli('counters br') for line in outputs: diff --git a/tests/toranj/cli/test-031-service-aloc-route-lookup.py b/tests/toranj/cli/test-031-service-aloc-route-lookup.py new file mode 100755 index 000000000..dcc6f5e77 --- /dev/null +++ b/tests/toranj/cli/test-031-service-aloc-route-lookup.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024, 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Service ALOC destination route lookup +# +# Test topology: +# +# r1---- r2 ---- r3 ---- r4 +# | +# | +# fed1 +# +# The same service is added on `r4` and `fed1`. + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 40 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +r4 = cli.Node() +fed1 = cli.Node() + +nodes = [r1, r2, r3, r4, fed1] + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) +r1.allowlist_node(fed1) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) + +r3.allowlist_node(r2) +r3.allowlist_node(r4) + +r4.allowlist_node(r3) + +fed1.allowlist_node(r1) + +r1.form("srv-aloc") +r2.join(r1) +r3.join(r2) +r4.join(r3) +fed1.join(r1, cli.JOIN_TYPE_REED) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') +verify(r4.get_state() == 'router') +verify(fed1.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# Wait till first router has either established a link or +# has a valid "next hop" towards all other routers. + +r1_rloc16 = int(r1.get_rloc16(), 16) + + +def check_r1_router_table(): + table = r1.get_router_table() + verify(len(table) == 4) + for entry in table: + verify(int(entry['RLOC16'], 0) == r1_rloc16 or int(entry['Link']) == 1 or int(entry['Next Hop']) != 63) + + +verify_within(check_r1_router_table, 120) + +# Add the same service on `r4` and `fed1` + +r4.cli('service add 44970 11 00') +r4.register_netdata() + +fed1.cli('service add 44970 11 00') +fed1.register_netdata() + + +def check_netdata_services(expected_num_services): + # Check that all nodes see the `expected_num_services` service + # entries in network data. + for node in nodes: + verify(len(node.get_netdata_services()) == expected_num_services) + + +verify_within(check_netdata_services, 100, 2) + +# Determine the ALOC address of the added service. + +aloc = r4.get_mesh_local_prefix().split('/')[0] + 'ff:fe00:fc10' + +# Ping ALOC address from `r3` and verify that `r4` responds. +# `r4` should be chosen due to its shorter path cost from `r3`. + +old_counters = r4.get_ip_counters() +r3.ping(aloc) +new_counters = r4.get_ip_counters() + +verify(int(new_counters['RxSuccess']) > int(old_counters['RxSuccess'])) +verify(int(new_counters['TxSuccess']) > int(old_counters['TxSuccess'])) + +# Ping ALOC address from `r1` and verify that `fed1` responds. +# `fed1` should be chosen due to its shorter path cost from `r1`. + +old_counters = fed1.get_ip_counters() +r1.ping(aloc) +new_counters = fed1.get_ip_counters() + +verify(int(new_counters['RxSuccess']) > int(old_counters['RxSuccess'])) +verify(int(new_counters['TxSuccess']) > int(old_counters['TxSuccess'])) + +# Ping ALOC address from `r2` and verify that `r4` responds. +# Both `r4` and `fed1` have the same path cost, but `r4` is +# preferred because it is acting as a router. + +old_counters = r4.get_ip_counters() +r2.ping(aloc) +new_counters = r4.get_ip_counters() + +verify(int(new_counters['RxSuccess']) > int(old_counters['RxSuccess'])) +verify(int(new_counters['TxSuccess']) > int(old_counters['TxSuccess'])) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh index ef471b00e..495af398c 100755 --- a/tests/toranj/start.sh +++ b/tests/toranj/start.sh @@ -195,6 +195,7 @@ if [ "$TORANJ_CLI" = 1 ]; then run cli/test-028-border-agent-ephemeral-key.py run cli/test-029-pending-dataset-key-change.py run cli/test-030-anycast-forwarding.py + run cli/./test-031-service-aloc-route-lookup.py run cli/test-400-srp-client-server.py run cli/test-401-srp-server-address-cache-snoop.py run cli/test-500-two-brs-two-networks.py From 0897b50a27267db4ba4b2627b946d6016210349c Mon Sep 17 00:00:00 2001 From: Li Cao Date: Thu, 18 Jul 2024 01:10:12 +0800 Subject: [PATCH 055/160] [spinel] fix little endian in lib (#10522) --- src/lib/spinel/multi_frame_buffer.hpp | 6 +++--- src/lib/spinel/spi_frame.hpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/spinel/multi_frame_buffer.hpp b/src/lib/spinel/multi_frame_buffer.hpp index 0ff30aa23..921181f3d 100644 --- a/src/lib/spinel/multi_frame_buffer.hpp +++ b/src/lib/spinel/multi_frame_buffer.hpp @@ -464,12 +464,12 @@ template class MultiFrameBuffer : public FrameWritePointer public: static uint16_t ReadUint16(const uint8_t *aBuffer) { - return static_cast((aBuffer[0] << 8) | aBuffer[1]); + return static_cast((aBuffer[0]) | aBuffer[1] << 8); } static void WriteUint16(uint16_t aValue, uint8_t *aBuffer) { - aBuffer[0] = (aValue >> 8) & 0xff; - aBuffer[1] = (aValue >> 0) & 0xff; + aBuffer[0] = (aValue >> 0) & 0xff; + aBuffer[1] = (aValue >> 8) & 0xff; } }; diff --git a/src/lib/spinel/spi_frame.hpp b/src/lib/spinel/spi_frame.hpp index 72578ae93..43843bb0f 100644 --- a/src/lib/spinel/spi_frame.hpp +++ b/src/lib/spinel/spi_frame.hpp @@ -238,12 +238,12 @@ class SpiFrame public: static uint16_t ReadUint16(const uint8_t *aBuffer) { - return static_cast((aBuffer[0] << 8) | aBuffer[1]); + return static_cast((aBuffer[0]) | aBuffer[1] << 8); } static void WriteUint16(uint16_t aValue, uint8_t *aBuffer) { - aBuffer[0] = (aValue >> 8) & 0xff; - aBuffer[1] = (aValue >> 0) & 0xff; + aBuffer[0] = (aValue >> 0) & 0xff; + aBuffer[1] = (aValue >> 8) & 0xff; } }; From 6f52d781954f8e30c99e441b26619d877fdd9ee1 Mon Sep 17 00:00:00 2001 From: Li Cao Date: Thu, 18 Jul 2024 01:44:38 +0800 Subject: [PATCH 056/160] [radio] make host-rcp time sync be controlled by the posix platform (#10504) This commit makes `CalcRcpTimeOffset` of `RadioSpinel` into a public method and call it from system `Process` method instead of making it default behavior of `RadioSpinel`. This is also to avoid `RadioSpinel` has a fixed behavior which is decided by `OPENTHREAD_CONFIG_THREAD_VERSION`. --- src/lib/spinel/radio_spinel.cpp | 19 ++++++++++++++----- src/lib/spinel/radio_spinel.hpp | 6 +++++- src/posix/platform/radio.cpp | 11 ++++++++--- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 016dd95d2..ff4819045 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -110,6 +110,7 @@ RadioSpinel::RadioSpinel(void) , mVendorRestorePropertiesCallback(nullptr) , mVendorRestorePropertiesContext(nullptr) #endif + , mEnableRcpTimeSync(false) , mSpinelDriver(nullptr) { memset(&mRadioSpinelMetrics, 0, sizeof(mRadioSpinelMetrics)); @@ -119,7 +120,8 @@ RadioSpinel::RadioSpinel(void) void RadioSpinel::Init(bool aSkipRcpCompatibilityCheck, bool aSoftwareReset, SpinelDriver *aSpinelDriver, - otRadioCaps aRequiredRadioCaps) + otRadioCaps aRequiredRadioCaps, + bool aEnableRcpTimeSync) { otError error = OT_ERROR_NONE; bool supportsRcpApiVersion; @@ -131,6 +133,8 @@ void RadioSpinel::Init(bool aSkipRcpCompatibilityCheck, mResetRadioOnStartup = aSoftwareReset; #endif + mEnableRcpTimeSync = aEnableRcpTimeSync; + mSpinelDriver = aSpinelDriver; mSpinelDriver->SetFrameHandler(&HandleReceivedFrame, &HandleSavedFrame, this); @@ -792,7 +796,11 @@ void RadioSpinel::Process(const void *aContext) ProcessRadioStateMachine(); RecoverFromRcpFailure(); - CalcRcpTimeOffset(); + + if (mEnableRcpTimeSync) + { + CalcRcpTimeOffset(); + } } otError RadioSpinel::SetPromiscuous(bool aEnable) @@ -1843,7 +1851,6 @@ otRadioState RadioSpinel::GetState(void) const void RadioSpinel::CalcRcpTimeOffset(void) { -#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 otError error = OT_ERROR_NONE; uint64_t localTxTimestamp; uint64_t localRxTimestamp; @@ -1899,7 +1906,6 @@ void RadioSpinel::CalcRcpTimeOffset(void) exit: LogIfFail("Error calculating RCP time offset: %s", error); -#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 } uint64_t RadioSpinel::GetNow(void) { return (mIsTimeSynced) ? (otPlatTimeGet() + mRadioTimeOffset) : UINT64_MAX; } @@ -2177,7 +2183,10 @@ void RadioSpinel::RestoreProperties(void) } #endif - CalcRcpTimeOffset(); + if (mEnableRcpTimeSync) + { + CalcRcpTimeOffset(); + } } #endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 5abc8de43..eae94b06f 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -165,12 +165,14 @@ class RadioSpinel : private Logger * @param[in] aSpinelDriver A pointer to the spinel driver instance that this object depends on. * @param[in] aRequiredRadioCaps The required radio capabilities. RadioSpinel will check if RCP has * the required capabilities during initiailization. + * @param[in] aEnableRcpTimeSync TRUE to enable RCP time sync, FALSE to not enable. * */ void Init(bool aSkipRcpCompatibilityCheck, bool aSoftwareReset, SpinelDriver *aSpinelDriver, - otRadioCaps aRequiredRadioCaps); + otRadioCaps aRequiredRadioCaps, + bool aEnableRcpTimeSync); /** * This method sets the notification callbacks. @@ -1303,6 +1305,8 @@ class RadioSpinel : private Logger void *mVendorRestorePropertiesContext; #endif + bool mEnableRcpTimeSync; + SpinelDriver *mSpinelDriver; }; diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index c82b07ebf..82551e311 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -72,8 +72,13 @@ Radio::Radio(void) void Radio::Init(const char *aUrl) { - bool resetRadio; - bool skipCompatibilityCheck; + bool resetRadio; + bool skipCompatibilityCheck; +#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + bool aEnableRcpTimeSync = true; +#else + bool aEnableRcpTimeSync = false; +#endif struct ot::Spinel::RadioSpinelCallbacks callbacks; mRadioUrl.Init(aUrl); @@ -93,7 +98,7 @@ void Radio::Init(const char *aUrl) skipCompatibilityCheck = mRadioUrl.HasParam("skip-rcp-compatibility-check"); mRadioSpinel.SetCallbacks(callbacks); - mRadioSpinel.Init(skipCompatibilityCheck, resetRadio, &GetSpinelDriver(), kRequiredRadioCaps); + mRadioSpinel.Init(skipCompatibilityCheck, resetRadio, &GetSpinelDriver(), kRequiredRadioCaps, aEnableRcpTimeSync); ProcessRadioUrl(mRadioUrl); } From 171f94c307132a0ae5152d3e4e869a5ecc7bd4c9 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 17 Jul 2024 11:14:09 -0700 Subject: [PATCH 057/160] [srp-client] defer SRP update on SLAAC address deprecation (#10505) This commit updates SRP client's `AutoHostAddress` behavior to defer SRP updates on SLAAC address deprecation events. Under `AutoHostAddressMode`, all preferred addresses on Thread Netif, excluding link-local and mesh-local addresses, are registered. If no eligible address is available, then the ML-EID will be registered. This commit adds a new mechanism where if a previously registered address starts being deprecated (e.g., due to an OMR prefix removal from Network Data), the SRP update is deferred. The client will re-register after the deprecation time has elapsed and the address is removed. In the meantime, if any other event triggers the client to send an SRP update, the updated address list will be included in that update. This commit also updates `test_srp_auto_host_address` to validate the newly added behavior. --- src/core/net/srp_client.cpp | 42 +++++++-- .../thread-cert/test_srp_auto_host_address.py | 91 +++++++++++++++++-- 2 files changed, 113 insertions(+), 20 deletions(-) diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index 29ae91a99..debc43749 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -656,21 +656,28 @@ void Client::HandleUnicastAddressEvent(Ip6::Netif::AddressEvent aEvent, const Ip bool Client::ShouldUpdateHostAutoAddresses(void) const { + // Determine if any changes to the addresses on `ThreadNetif` + // require registration with the server when `AutoHostAddress` is + // enabled. This includes registering all preferred addresses, + // excluding link-local and mesh-local addresses. If no eligible + // address is available, the ML-EID will be registered. + bool shouldUpdate = false; uint16_t registeredCount = 0; Ip6::Netif::UnicastAddress &mlEid = Get().GetMeshLocalEidUnicastAddress(); VerifyOrExit(mHostInfo.IsAutoAddressEnabled()); - // Check all addresses on `ThreadNetif` excluding the mesh local - // EID (`mlEid`). If any address should be registered but is not, - // or if any address was registered earlier but no longer should - // be, the host information needs to be re-registered to update - // the addresses. If there is no eligible address, then `mlEid` - // should be registered, so its status is checked. Finally, the - // number of addresses that should be registered is verified - // against the previous value `mAutoHostAddressCount` to handle - // the case where an earlier registered address is now removed. + // We iterate through all eligible addresses on the `ThreadNetif`. + // If we encounter a new address that should be registered but + // isn't, or a previously registered address has been removed, we + // trigger an SRP update to reflect these changes. However, if a + // previously registered address is being deprecated (e.g., due + // to an OMR prefix removal from Network Data), we defer the SRP + // update. The client will re-register after the deprecation + // time has elapsed and the address is removed. In the meantime, + // if any other event triggers the client to send an SRP update, + // the updated address list will be included in that update. for (const Ip6::Netif::UnicastAddress &unicastAddress : Get().GetUnicastAddresses()) { @@ -681,7 +688,17 @@ bool Client::ShouldUpdateHostAutoAddresses(void) const if (ShouldHostAutoAddressRegister(unicastAddress) != unicastAddress.mSrpRegistered) { - ExitNow(shouldUpdate = true); + // If this address was previously registered but is no + // longer eligible, we skip sending an immediate update + // only if the address is currently being deprecated + // (it's still valid but no longer preferred). + + bool skip = unicastAddress.mSrpRegistered && unicastAddress.mValid && !unicastAddress.mPreferred; + + if (!skip) + { + ExitNow(shouldUpdate = true); + } } if (unicastAddress.mSrpRegistered) @@ -695,6 +712,11 @@ bool Client::ShouldUpdateHostAutoAddresses(void) const ExitNow(shouldUpdate = !mlEid.mSrpRegistered); } + // Compare the current number of addresses that are marked as + // registered with the previous value `mAutoHostAddressCount`. + // This check handles the case where a previously registered address + // has been removed. + shouldUpdate = (registeredCount != mAutoHostAddressCount); exit: diff --git a/tests/scripts/thread-cert/test_srp_auto_host_address.py b/tests/scripts/thread-cert/test_srp_auto_host_address.py index 2f86db58f..b672730c2 100755 --- a/tests/scripts/thread-cert/test_srp_auto_host_address.py +++ b/tests/scripts/thread-cert/test_srp_auto_host_address.py @@ -67,6 +67,9 @@ def test(self): client = self.nodes[CLIENT] server = self.nodes[SERVER] + # Deprecation interval of an SLAAC address before removal. + deprecate_time = 300 + #------------------------------------------------------------------- # Form the network. @@ -158,7 +161,7 @@ def test(self): #------------------------------------------------------------------- # Add a non-preferred SLAAC on-mesh prefix and check that the # set of registered addresses remains unchanged and that the - # non-preferred address is not registered by SRP client. + # non-preferred address is not registered by SRP client. client.add_prefix('fd00:a:b:c::/64', 'aos') client.register_netdata() @@ -169,27 +172,93 @@ def test(self): self.check_registered_addresses(client, server) #------------------------------------------------------------------- - # Remove the on-mesh prefix and check that the SRP client - # re-registered and updated server with the remaining address. + # Remove the on-mesh prefix. This should trigger the + # associated SLAAC address to be deprecated, but it should + # not yet cause the client to re-register. Verify that the + # registered addresses on server remain unchanged. + + old_registered_addresses = self.get_registered_host_addresses_from_server(server) client.remove_prefix('fd00:abba:cafe:bee::/64') client.register_netdata() self.simulator.go(15) + self.assertEqual(old_registered_addresses, self.get_registered_host_addresses_from_server(server)) + + # Wait until the SLAAC address deprecation time has elapsed + # and the address is removed. Verify that the SRP client + # re-registers and updates the server with the remaining + # address. + + self.simulator.go(deprecate_time) self.check_registered_addresses(client, server) #------------------------------------------------------------------- - # Remove the next on-mesh prefix. Check that SRP client re-registered - # now with only ML-EID. + # Remove the next on-mesh prefix. Verify that the client does + # not re-register while the address is deprecating. After the + # address is removed, confirm that the SRP client + # re-registers using only the ML-EID. + + old_registered_addresses = self.get_registered_host_addresses_from_server(server) client.remove_prefix('fd00:9:8:7::/64') client.register_netdata() self.simulator.go(15) + self.assertEqual(old_registered_addresses, self.get_registered_host_addresses_from_server(server)) + + self.simulator.go(deprecate_time) self.check_registered_addresses(client, server) + #------------------------------------------------------------------- + # Add and remove the on-mesh prefix again. However, before the + # address deprecation time elapses and the address is removed, + # restart the server. This should trigger the client to + # re-register. Verify that the client re-registers with the + # most up-to-date addresses and does not register the deprecating + # address. + + client.add_prefix('fd00:9:8:7::/64', 'paos') + client.register_netdata() + self.simulator.go(15) + + slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:9:8:7:')] + self.assertEqual(len(slaac_addr), 1) + self.check_registered_addresses(client, server) + + # Remove the prefix and verify that client does not + # register while the SLAAC address is deprecating. + + old_registered_addresses = self.get_registered_host_addresses_from_server(server) + + client.remove_prefix('fd00:9:8:7::/64') + client.register_netdata() + + self.simulator.go(15) + self.assertEqual(old_registered_addresses, self.get_registered_host_addresses_from_server(server)) + + # Disable and re-enable the server. This should trigger the + # client to re-register. Verify that the ML-EID address is + # now registered. + + server.srp_server_set_enabled(False) + server.srp_server_set_enabled(True) + + self.simulator.go(20) + + self.check_registered_addresses(client, server) + + registered_addresses = self.get_registered_host_addresses_from_server(server) + self.assertEqual(len(registered_addresses), 1) + self.assertEqual(registered_addresses[0], client.get_mleid()) + + # Check that SLAAC address is still deprecating. + + slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:9:8:7:')] + self.assertEqual(len(slaac_addr), 1) + #------------------------------------------------------------------- # Explicitly set the host addresses (which disables the auto host # address mode) and check that only the new addresses are registered. @@ -215,20 +284,22 @@ def test(self): self.simulator.go(5) self.check_registered_addresses(client, server) - def check_registered_addresses(self, client, server): - # Ensure client has registered successfully. - self.assertEqual(client.srp_client_get_host_state(), 'Registered') - + def get_registered_host_addresses_from_server(self, server): # Check the host info on server. server_hosts = server.srp_server_get_hosts() self.assertEqual(len(server_hosts), 1) server_host = server_hosts[0] self.assertEqual(server_host['deleted'], 'false') self.assertEqual(server_host['fullname'], 'host.default.service.arpa.') + return [addr.strip() for addr in server_host['addresses']] + + def check_registered_addresses(self, client, server): + # Ensure client has registered successfully. + self.assertEqual(client.srp_client_get_host_state(), 'Registered') # Check the host addresses on server to match client. - host_addresses = [addr.strip() for addr in server_host['addresses']] + host_addresses = self.get_registered_host_addresses_from_server(server) client_mleid = client.get_mleid() client_addresses = [addr.split(' ')[0] for addr in client.get_addrs(verbose=True) if 'preferred:1' in addr] From 6f30a3f913685d64ec9064f3cbc7b21137d962c0 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 17 Jul 2024 19:43:09 -0700 Subject: [PATCH 058/160] [mle] update `otThreadBecomeLeader()` to allow leader take over (#9186) This commit updates the `otThreadBecomeLeader()` API (and its related core `MleRouter::BecomeLeader()` method) to allow an already attached device to take over as leader, creating a new partition. For this to work, the local leader weight (`otThreadGetLocalLeaderWeight()`) must be greater than the weight of the current leader (which can be retrieved using `otThreadGetLeaderWeight()`). If it is not, error code `OT_ERROR_NOT_CAPABLE` is returned to indicate to the caller that they need to adjust the local weight. This commit also updates the related CLI command and adds a new test, `test-032-leader-take-over.py`, to validate the newly added mechanism. --- include/openthread/instance.h | 2 +- include/openthread/thread_ftd.h | 17 ++- src/cli/README.md | 37 +++++- src/core/api/thread_ftd_api.cpp | 2 +- src/core/thread/mle.cpp | 2 +- src/core/thread/mle_router.cpp | 7 +- src/core/thread/mle_router.hpp | 16 ++- tests/toranj/cli/cli.py | 6 + tests/toranj/cli/test-032-leader-take-over.py | 120 ++++++++++++++++++ tests/toranj/start.sh | 3 +- 10 files changed, 198 insertions(+), 14 deletions(-) create mode 100755 tests/toranj/cli/test-032-leader-take-over.py diff --git a/include/openthread/instance.h b/include/openthread/instance.h index ef6e2d4f8..2444f50b8 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (428) +#define OPENTHREAD_API_VERSION (429) /** * @addtogroup api-instance diff --git a/include/openthread/thread_ftd.h b/include/openthread/thread_ftd.h index 14aab1307..8c5491c76 100644 --- a/include/openthread/thread_ftd.h +++ b/include/openthread/thread_ftd.h @@ -484,13 +484,24 @@ otError otThreadBecomeRouter(otInstance *aInstance); /** * Become a leader and start a new partition. * - * @note This API is reserved for testing and demo purposes only. Changing settings with - * this API will render a production application non-compliant with the Thread Specification. + * If the device is not attached, this API will force the device to start as the leader of the network. This use case + * is only intended for testing and demo purposes, and using the API while the device is detached can make a production + * application non-compliant with the Thread Specification. + * + * If the device is already attached, this API can be used to try to take over as the leader, creating a new partition. + * For this to work, the local leader weight (`otThreadGetLocalLeaderWeight()`) must be larger than the weight of the + * current leader (`otThreadGetLeaderWeight()`). If it is not, `OT_ERROR_NOT_CAPABLE` is returned to indicate to the + * caller that they need to adjust the weight. + * + * Taking over the leader role in this way is only allowed when triggered by an explicit user action. Using this API + * without such user action can make a production application non-compliant with the Thread Specification. * * @param[in] aInstance A pointer to an OpenThread instance. * - * @retval OT_ERROR_NONE Successfully became a leader and started a new partition. + * @retval OT_ERROR_NONE Successfully became a leader and started a new partition, or was leader already. * @retval OT_ERROR_INVALID_STATE Thread is disabled. + * @retval OT_ERROR_NOT_CAPABLE Device cannot override the current leader due to its local leader weight being same + * or smaller than current leader's weight, or device is not router eligible. */ otError otThreadBecomeLeader(otInstance *aInstance); diff --git a/src/cli/README.md b/src/cli/README.md index deb90c3d7..ee440b134 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -3573,12 +3573,45 @@ offline, disabled, detached, child, router or leader Done ``` -### state +### state leader + +Become a leader and start a new partition + +If the device is not attached, this command will force the device to start as the leader of the network. This use case is only intended for testing and demo purposes, and using the API while the device is detached can make a production application non-compliant with the Thread Specification. -Try to switch to state `detached`, `child`, `router` or `leader`. +If the device is already attached, this API can be used to try to take over as the leader, creating a new partition. For this to work, the local leader weight (`leaderweight`) must be larger than the weight of the current leader (from `leaderdata`). If it is not, error `NotCapable` is outputted to indicate to the caller that they need to adjust the weight. + +Taking over the leader role in this way is only allowed when triggered by an explicit user action. Using this API without such user action can make a production application non-compliant with the Thread Specification. ```bash +> leaderdata +Partition ID: 1886755069 +Weighting: 65 +Data Version: 178 +Stable Data Version: 48 +Leader Router ID: 59 +Done + +> leaderweight +64 +Done + > state leader +Error 27: NotCapable + +> leaderweight 66 +Done + +> state leader +Done +``` + +### state + +Try to switch to state `detached`, `child`, `router`. + +```bash +> state detached Done ``` diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp index 6b3f554ee..6f6d266cb 100644 --- a/src/core/api/thread_ftd_api.cpp +++ b/src/core/api/thread_ftd_api.cpp @@ -202,7 +202,7 @@ otError otThreadBecomeRouter(otInstance *aInstance) otError otThreadBecomeLeader(otInstance *aInstance) { - return AsCoreType(aInstance).Get().BecomeLeader(); + return AsCoreType(aInstance).Get().BecomeLeader(/* aCheckWeight */ true); } uint8_t otThreadGetRouterDowngradeThreshold(otInstance *aInstance) diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index c614ed060..efe9e593b 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -1548,7 +1548,7 @@ uint32_t Mle::Reattach(void) IgnoreError(BecomeDetached()); } #if OPENTHREAD_FTD - else if (IsFullThreadDevice() && Get().BecomeLeader() == kErrorNone) + else if (IsFullThreadDevice() && Get().BecomeLeader(/* aCheckWeight */ false) == kErrorNone) { // do nothing } diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index d4d1878d5..fcb4595de 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -267,7 +267,7 @@ Error MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus) return error; } -Error MleRouter::BecomeLeader(void) +Error MleRouter::BecomeLeader(bool aCheckWeight) { Error error = kErrorNone; Router *router; @@ -283,6 +283,11 @@ Error MleRouter::BecomeLeader(void) VerifyOrExit(!IsLeader(), error = kErrorNone); VerifyOrExit(IsRouterEligible(), error = kErrorNotCapable); + if (aCheckWeight && IsAttached()) + { + VerifyOrExit(mLeaderWeight > mLeaderData.GetWeighting(), error = kErrorNotCapable); + } + mRouterTable.Clear(); #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 8a43b1941..2e52cff3d 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -135,14 +135,22 @@ class MleRouter : public Mle Error BecomeRouter(ThreadStatusTlv::Status aStatus); /** - * Causes the Thread interface to become a Leader and start a new partition. + * Becomes a leader and starts a new partition. + * + * If the device is already attached, this method can be used to attempt to take over as the leader, creating a new + * partition. For this to work, the local leader weight must be greater than the weight of the current leader. The + * @p aCheckWeight can be used to ensure that this check is performed. + * + * @param[in] aCheckWeight Check that the local leader weight is larger than the weight of the current leader. * * @retval kErrorNone Successfully become a Leader and started a new partition. - * @retval kErrorNotCapable Device is not capable of becoming a leader - * @retval kErrorInvalidState Thread is not enabled + * @retval kErrorInvalidState Thread is not enabled. + * @retval kErrorNotCapable Device is not capable of becoming a leader (not router eligible), or + * @p aCheckWeight is true and cannot override the current leader due to its local + * leader weight being same or smaller than current leader's weight. * */ - Error BecomeLeader(void); + Error BecomeLeader(bool aCheckWeight); #if OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE /** diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index 308a39867..06f7bd3c9 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -339,6 +339,12 @@ def get_ip_maddrs(self): def add_ip_maddr(self, maddr): return self._cli_no_output('ipmaddr add', maddr) + def get_leader_weight(self): + return self._cli_single_output('leaderweight') + + def set_leader_weight(self, weight): + self._cli_no_output('leaderweight', weight) + def get_pollperiod(self): return self._cli_single_output('pollperiod') diff --git a/tests/toranj/cli/test-032-leader-take-over.py b/tests/toranj/cli/test-032-leader-take-over.py new file mode 100755 index 000000000..f59301227 --- /dev/null +++ b/tests/toranj/cli/test-032-leader-take-over.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023, 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: + +# This test covers behavior of leader take over (an already attached device +# trying to form their own partition and taking over the leader role). +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 25 +cli.Node.set_time_speedup_factor(speedup) + +node1 = cli.Node() +node2 = cli.Node() +node3 = cli.Node() +child2 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +node1.form('lto') +node2.join(node1) +node3.join(node1) + +child2.allowlist_node(node2) +child2.join(node2, cli.JOIN_TYPE_REED) + +verify(node1.get_state() == 'leader') +verify(node2.get_state() == 'router') +verify(node3.get_state() == 'router') +verify(child2.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +node1.set_router_selection_jitter(1) + +n1_weight = int(node1.get_leader_weight()) + +node2.set_leader_weight(n1_weight) + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Make sure we get `NonCapable` if local leader weight same as current leader's weight + +error = None +try: + node2.cli('state leader') +except cli.CliError as e: + error = e + +verify(error.message == 'NotCapable') + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Update local leader weight and try to take over the leader role on `node2`. + +node2.set_leader_weight(n1_weight + 1) + +old_partition_id = int(node2.get_partition_id()) + +node2.cli('state leader') + +new_partition_id = int(node2.get_partition_id()) +verify(new_partition_id != old_partition_id) + + +def check_leader_switch(): + for node in [node1, node2, node3, child2]: + verify(int(node.get_partition_id()) == new_partition_id) + verify(node1.get_state() == 'router') + verify(node2.get_state() == 'leader') + verify(node3.get_state() == 'router') + verify(child2.get_state() == 'child') + + +verify_within(check_leader_switch, 30) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh index 495af398c..d242752b9 100755 --- a/tests/toranj/start.sh +++ b/tests/toranj/start.sh @@ -195,7 +195,8 @@ if [ "$TORANJ_CLI" = 1 ]; then run cli/test-028-border-agent-ephemeral-key.py run cli/test-029-pending-dataset-key-change.py run cli/test-030-anycast-forwarding.py - run cli/./test-031-service-aloc-route-lookup.py + run cli/test-031-service-aloc-route-lookup.py + run cli/test-032-leader-take-over.py run cli/test-400-srp-client-server.py run cli/test-401-srp-server-address-cache-snoop.py run cli/test-500-two-brs-two-networks.py From b73114c860c0b15ab05dac1cafd3060d5d7212d0 Mon Sep 17 00:00:00 2001 From: Handa Wang <7058128+superwhd@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:45:48 +0800 Subject: [PATCH 059/160] [continuous-integration] fix the path for uploading artifacts when the BR fails (#10521) The OTBR testing artifacts used to be at the root directory but they are now at `ot_testing/`. --- .github/workflows/otbr.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml index 214334923..c3e58c13b 100644 --- a/.github/workflows/otbr.yml +++ b/.github/workflows/otbr.yml @@ -85,7 +85,7 @@ jobs: run: | export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE" echo "CI_ENV=${CI_ENV}" - sudo -E ./script/test cert_suite ./tests/scripts/thread-cert/backbone/*.py || (sudo chmod a+r *.log *.json *.pcap && false) + sudo -E ./script/test cert_suite ./tests/scripts/thread-cert/backbone/*.py || (sudo chmod a+r ot_testing/* && false) - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 with: name: cov-thread-1-3-backbone-docker @@ -96,11 +96,11 @@ jobs: with: name: thread-1-3-backbone-results path: | - *.pcap - *.json - *.log - coredump_* - otbr-agent_* + ot_testing/*.pcap + ot_testing/*.json + ot_testing/*.log + ot_testing/coredump_* + ot_testing/otbr-agent_* - name: Generate Coverage run: | ./script/test generate_coverage gcc @@ -207,7 +207,7 @@ jobs: run: | export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE" echo "CI_ENV=${CI_ENV}" - sudo -E ./script/test cert_suite ${{ matrix.cert_scripts }} || (sudo chmod a+r *.log *.json *.pcap && false) + sudo -E ./script/test cert_suite ${{ matrix.cert_scripts }} || (sudo chmod a+r ot_testing/* && false) - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 with: name: cov-br-docker-${{ matrix.description }}-${{ matrix.otbr_mdns }}-${{matrix.otbr_trel}} @@ -218,11 +218,11 @@ jobs: with: name: br-results-${{ matrix.description }}-${{ matrix.otbr_mdns }}-${{matrix.otbr_trel}} path: | - *.pcap - *.json - *.log - coredump_* - otbr-agent_* + ot_testing/*.pcap + ot_testing/*.json + ot_testing/*.log + ot_testing/coredump_* + ot_testing/otbr-agent_* - name: Generate Coverage run: | ./script/test generate_coverage gcc From 602167f94ad4318a26d73bb7ecb639cbf54df633 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Fri, 19 Jul 2024 01:36:29 +0800 Subject: [PATCH 060/160] [sub-mac] unify the timer usage (#10520) The `SubMac` uses `MilliTimer` or `MicroTimer` based on the configuration. When the `SubMac` uses the timer, it has to distinguish the type of the timer. This commit optimizes the code to provide unified time related functions to `SubMac` to use time. This commit doesn't change the code logic. --- src/core/mac/sub_mac.cpp | 51 +++++++++++++++------------ src/core/mac/sub_mac.hpp | 25 +++++++------ src/core/mac/sub_mac_csl_receiver.cpp | 26 ++++++++------ 3 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 036fddd60..dcd0f444b 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -394,13 +394,14 @@ void SubMac::StartCsmaBackoff(void) if (ShouldHandleTransmitTargetTime()) { - if (Time(static_cast(otPlatRadioGetNow(&GetInstance()))) < - Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime) + mTransmitFrame.mInfo.mTxInfo.mTxDelay - - kCcaSampleInterval - kCslTransmitTimeAhead - kRadioHeaderShrDuration) + static constexpr uint32_t kAheadTime = kCcaSampleInterval + kCslTransmitTimeAhead + kRadioHeaderShrDuration; + Time txStartTime = Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime); + + txStartTime += (mTransmitFrame.mInfo.mTxInfo.mTxDelay - kAheadTime); + + if (Time(static_cast(otPlatRadioGetNow(&GetInstance()))) < txStartTime) { - mTimer.StartAt(Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime) - kCcaSampleInterval - - kCslTransmitTimeAhead - kRadioHeaderShrDuration, - mTransmitFrame.mInfo.mTxInfo.mTxDelay); + mTimer.FireAt(txStartTime); } else // Transmit without delay { @@ -444,11 +445,7 @@ void SubMac::StartTimerForBackoff(uint8_t aBackoffExponent) IgnoreError(Get().Sleep()); } -#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE - mTimer.Start(backoff); -#else - mTimer.Start(backoff / 1000UL); -#endif + StartTimer(backoff); #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY if (mState == kStateDelayBeforeRetx) @@ -501,11 +498,7 @@ void SubMac::HandleTransmitStarted(TxFrame &aFrame) { if (ShouldHandleAckTimeout() && aFrame.GetAckRequest()) { -#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE - mTimer.Start(kAckTimeout * 1000UL); -#else - mTimer.Start(kAckTimeout); -#endif + StartTimer(kAckTimeout); } } @@ -706,7 +699,7 @@ Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration) SetState(kStateEnergyScan); mEnergyScanMaxRssi = Radio::kInvalidRssi; mEnergyScanEndTime = TimerMilli::GetNow() + static_cast(aScanDuration); - mTimer.Start(0); + StartTimer(0); } else { @@ -733,11 +726,7 @@ void SubMac::SampleRssi(void) if (TimerMilli::GetNow() < mEnergyScanEndTime) { -#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE - mTimer.StartAt(mTimer.GetFireTime(), kEnergyScanRssiSampleInterval * 1000UL); -#else - mTimer.StartAt(mTimer.GetFireTime(), kEnergyScanRssiSampleInterval); -#endif + StartTimerAt(mTimer.GetFireTime(), kEnergyScanRssiSampleInterval); } else { @@ -978,6 +967,24 @@ void SubMac::SetFrameCounter(uint32_t aFrameCounter, bool aSetIfLarger) return; } +void SubMac::StartTimer(uint32_t aDelayUs) +{ +#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE + mTimer.Start(aDelayUs); +#else + mTimer.Start(aDelayUs / Time::kOneMsecInUsec); +#endif +} + +void SubMac::StartTimerAt(Time aStartTime, uint32_t aDelayUs) +{ +#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE + mTimer.StartAt(aStartTime, aDelayUs); +#else + mTimer.StartAt(aStartTime, aDelayUs / Time::kOneMsecInUsec); +#endif +} + // LCOV_EXCL_START const char *SubMac::StateToString(State aState) diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 5bd43c25b..e334091da 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -532,16 +532,17 @@ class SubMac : public InstanceLocator, private NonCopyable static void HandleCslTimer(Timer &aTimer); void HandleCslTimer(void); void GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter); + uint32_t GetLocalTime(void); #if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE void LogReceived(RxFrame *aFrame); #endif #endif - static constexpr uint8_t kCsmaMinBe = 3; // macMinBE (IEEE 802.15.4-2006). - static constexpr uint8_t kCsmaMaxBe = 5; // macMaxBE (IEEE 802.15.4-2006). - static constexpr uint32_t kUnitBackoffPeriod = 20; // Number of symbols (IEEE 802.15.4-2006). - static constexpr uint32_t kAckTimeout = 16; // Timeout for waiting on an ACK (in msec). - static constexpr uint32_t kCcaSampleInterval = 128; // CCA sample interval, 128 usec. + static constexpr uint8_t kCsmaMinBe = 3; // macMinBE (IEEE 802.15.4-2006). + static constexpr uint8_t kCsmaMaxBe = 5; // macMaxBE (IEEE 802.15.4-2006). + static constexpr uint32_t kUnitBackoffPeriod = 20; // Number of symbols (IEEE 802.15.4-2006). + static constexpr uint32_t kAckTimeout = 16 * Time::kOneMsecInUsec; // Timeout for waiting on an ACK (in usec). + static constexpr uint32_t kCcaSampleInterval = 128; // CCA sample interval, 128 usec. #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY static constexpr uint8_t kRetxDelayMinBackoffExponent = OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT; @@ -549,9 +550,9 @@ class SubMac : public InstanceLocator, private NonCopyable #endif #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE - static constexpr uint32_t kEnergyScanRssiSampleInterval = 128; // RSSI sample interval for energy scan, 128 usec + static constexpr uint32_t kEnergyScanRssiSampleInterval = 128; // RSSI sample interval for energy scan, in usec #else - static constexpr uint32_t kEnergyScanRssiSampleInterval = 1; // RSSI sample interval during energy scan, 1 msec + static constexpr uint32_t kEnergyScanRssiSampleInterval = 1000; // RSSI sample interval for energy scan, in usec #endif enum State : uint8_t @@ -623,6 +624,8 @@ class SubMac : public InstanceLocator, private NonCopyable void StartTimerForBackoff(uint8_t aBackoffExponent); void BeginTransmit(void); void SampleRssi(void); + void StartTimer(uint32_t aDelayUs); + void StartTimerAt(Time aStartTime, uint32_t aDelayUs); void HandleReceiveDone(RxFrame *aFrame, Error aError); void HandleTransmitStarted(TxFrame &aFrame); @@ -667,10 +670,10 @@ class SubMac : public InstanceLocator, private NonCopyable SubMacTimer mTimer; #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - uint16_t mCslPeriod; // The CSL sample period, in units of 10 symbols (160 microseconds). - uint8_t mCslChannel : 7; // The CSL sample channel. - bool mIsCslSampling : 1; // Indicates that the radio is receiving in CSL state for platforms not supporting delayed - // reception. + uint16_t mCslPeriod; // The CSL sample period, in units of 10 symbols (160 microseconds). + uint8_t mCslChannel : 7; // The CSL sample channel. + bool mIsCslSampling : 1; // Indicates that the radio is receiving in CSL state for platforms not supporting + // delayed reception. uint16_t mCslPeerShort; // The CSL peer short address. TimeMicro mCslSampleTime; // The CSL sample time of the current period relative to the local radio clock. TimeMicro mCslLastSync; // The timestamp of the last successful CSL synchronization. diff --git a/src/core/mac/sub_mac_csl_receiver.cpp b/src/core/mac/sub_mac_csl_receiver.cpp index 74f15e2b3..85611f95b 100644 --- a/src/core/mac/sub_mac_csl_receiver.cpp +++ b/src/core/mac/sub_mac_csl_receiver.cpp @@ -62,11 +62,7 @@ void SubMac::UpdateCslLastSyncTimestamp(TxFrame &aFrame, RxFrame *aAckFrame) // Assuming the error here since it is bounded and has very small effect on the final window duration. if (aAckFrame != nullptr && aFrame.GetHeaderIe(CslIe::kHeaderIeId) != nullptr) { -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC - mCslLastSync = TimerMicro::GetNow(); -#else - mCslLastSync = TimeMicro(static_cast(otPlatRadioGetNow(&GetInstance()))); -#endif + mCslLastSync = TimeMicro(GetLocalTime()); } } @@ -237,12 +233,7 @@ void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter) uint32_t semiPeriod = mCslPeriod * kUsPerTenSymbols / 2; uint32_t curTime, elapsed, semiWindow; -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC - curTime = TimerMicro::GetNow().GetValue(); -#else - curTime = static_cast(otPlatRadioGetNow(&GetInstance())); -#endif - + curTime = GetLocalTime(); elapsed = curTime - mCslLastSync.GetValue(); semiWindow = @@ -254,6 +245,19 @@ void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter) aAfter = Min(semiPeriod, semiWindow + kMinReceiveOnAfter); } +uint32_t SubMac::GetLocalTime(void) +{ + uint32_t now; + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC + now = TimerMicro::GetNow().GetValue(); +#else + now = static_cast(otPlatRadioGetNow(&GetInstance())); +#endif + + return now; +} + #if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE void SubMac::LogReceived(RxFrame *aFrame) { From 4e3483c75124a469c539d059ecd194005fbd97db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Baczma=C5=84ski?= Date: Sat, 20 Jul 2024 06:39:32 +0200 Subject: [PATCH 061/160] [low-power] enhance `mCslFrameRequestAheadUs` calculation for RCP (#10488) Implement `otPlatRadioGetBusLatency` API, add optional `bus-latency` argument to ot-daemon and `diag radiospinel buslatency`diagnostic commands. Add callback to notify that the bus latency has been updated and update frame request ahead from runtime, by recalculating the `mCslFrameRequestAheadUs` value. Changes allow setting a bus latency while starting a new session between host and RCP device. This way, one host can be connected to different devices and vice versa, ensuring that the latency will be added to `mCslFrameRequestAheadUs` calculations and CSL tx requests will not be sent too late. Signed-off-by: Maciej Baczmanski --- include/openthread/instance.h | 2 +- include/openthread/platform/radio.h | 19 +++++++++ src/core/radio/radio.hpp | 6 +++ src/core/radio/radio_callbacks.cpp | 7 ++++ src/core/radio/radio_platform.cpp | 20 +++++++++ src/core/thread/csl_tx_scheduler.cpp | 11 +++-- src/core/thread/csl_tx_scheduler.hpp | 8 +++- src/lib/spinel/README_RADIO_SPINEL_DIAG.md | 30 ++++++++++++++ src/lib/spinel/radio_spinel.cpp | 48 ++++++++++++++++++++++ src/lib/spinel/radio_spinel.hpp | 37 +++++++++++++++++ src/posix/platform/radio.cpp | 45 +++++++++++++++----- src/posix/platform/radio_url.cpp | 1 + 12 files changed, 219 insertions(+), 15 deletions(-) create mode 100644 src/lib/spinel/README_RADIO_SPINEL_DIAG.md diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 2444f50b8..d00925393 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (429) +#define OPENTHREAD_API_VERSION (430) /** * @addtogroup api-instance diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index b1afdaa83..fc11416c6 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -757,6 +757,17 @@ uint64_t otPlatRadioGetNow(otInstance *aInstance); */ uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance); +/** + * Get the bus latency in microseconds between the host and the radio chip. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The bus latency in microseconds between the host and the radio chip. + * Return 0 when the MAC and above layer and Radio layer resides on the same chip. + * + */ +uint32_t otPlatRadioGetBusLatency(otInstance *aInstance); + /** * @} * @@ -1003,6 +1014,14 @@ otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint1 */ extern void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi); +/** + * The radio driver calls this method to notify OpenThread that the spinel bus latency has been changed. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +extern void otPlatRadioBusLatencyChanged(otInstance *aInstance); + /** * Enable/Disable source address match feature. * diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index b76763083..72d9f4157 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -229,6 +229,12 @@ class Radio : public InstanceLocator, private NonCopyable */ void HandleEnergyScanDone(int8_t aMaxRssi); + /** + * This callback method handles "Bus Latency Changed" event from radio platform. + * + */ + void HandleBusLatencyChanged(void); + #if OPENTHREAD_CONFIG_DIAG_ENABLE /** * This callback method handles a "Receive Done" event from radio platform when diagnostics mode is enabled. diff --git a/src/core/radio/radio_callbacks.cpp b/src/core/radio/radio_callbacks.cpp index 6a6131c19..c0ad0a12f 100644 --- a/src/core/radio/radio_callbacks.cpp +++ b/src/core/radio/radio_callbacks.cpp @@ -58,6 +58,13 @@ void Radio::Callbacks::HandleTransmitDone(Mac::TxFrame &aFrame, Mac::RxFrame *aA void Radio::Callbacks::HandleEnergyScanDone(int8_t aMaxRssi) { Get().HandleEnergyScanDone(aMaxRssi); } +void Radio::Callbacks::HandleBusLatencyChanged(void) +{ +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + Get().UpdateFrameRequestAhead(); +#endif +} + #if OPENTHREAD_CONFIG_DIAG_ENABLE void Radio::Callbacks::HandleDiagsReceiveDone(Mac::RxFrame *aFrame, Error aError) { diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp index 0f6001246..3556d2629 100644 --- a/src/core/radio/radio_platform.cpp +++ b/src/core/radio/radio_platform.cpp @@ -116,6 +116,17 @@ extern "C" void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyS return; } +extern "C" void otPlatRadioBusLatencyChanged(otInstance *aInstance) +{ + Instance &instance = AsCoreType(aInstance); + + VerifyOrExit(instance.IsInitialized()); + instance.Get().HandleBusLatencyChanged(); + +exit: + return; +} + #if OPENTHREAD_CONFIG_DIAG_ENABLE extern "C" void otPlatDiagRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) { @@ -153,6 +164,8 @@ extern "C" void otPlatRadioTxDone(otInstance *, otRadioFrame *, otRadioFrame *, extern "C" void otPlatRadioEnergyScanDone(otInstance *, int8_t) {} +extern "C" void otPlatRadioBusLatencyChanged(otInstance *) {} + #if OPENTHREAD_CONFIG_DIAG_ENABLE extern "C" void otPlatDiagRadioReceiveDone(otInstance *, otRadioFrame *, otError) {} @@ -250,6 +263,13 @@ OT_TOOL_WEAK uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance) return 0; } +OT_TOOL_WEAK uint32_t otPlatRadioGetBusLatency(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + return 0; +} + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE OT_TOOL_WEAK otError otPlatRadioResetCsl(otInstance *aInstance) { diff --git a/src/core/thread/csl_tx_scheduler.cpp b/src/core/thread/csl_tx_scheduler.cpp index 35a070c1e..0f28c43a0 100644 --- a/src/core/thread/csl_tx_scheduler.cpp +++ b/src/core/thread/csl_tx_scheduler.cpp @@ -32,6 +32,7 @@ #include "common/locator_getters.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" #include "common/time.hpp" #include "mac/mac.hpp" @@ -68,16 +69,20 @@ CslTxScheduler::CslTxScheduler(Instance &aInstance) , mFrameContext() , mCallbacks(aInstance) { - InitFrameRequestAhead(); + UpdateFrameRequestAhead(); } -void CslTxScheduler::InitFrameRequestAhead(void) +void CslTxScheduler::UpdateFrameRequestAhead(void) { uint32_t busSpeedHz = otPlatRadioGetBusSpeed(&GetInstance()); + uint32_t busLatency = otPlatRadioGetBusLatency(&GetInstance()); + // longest frame on bus is 127 bytes with some metadata, use 150 bytes for bus Tx time estimation uint32_t busTxTimeUs = ((busSpeedHz == 0) ? 0 : (150 * 8 * 1000000 + busSpeedHz - 1) / busSpeedHz); - mCslFrameRequestAheadUs = OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US + busTxTimeUs; + mCslFrameRequestAheadUs = OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US + busTxTimeUs + busLatency; + LogInfo("Bus TX Time: %lu usec, Latency: %lu usec. Calculated CSL Frame Request Ahead: %lu usec", + ToUlong(busTxTimeUs), ToUlong(busLatency), ToUlong(mCslFrameRequestAheadUs)); } void CslTxScheduler::Update(void) diff --git a/src/core/thread/csl_tx_scheduler.hpp b/src/core/thread/csl_tx_scheduler.hpp index 7b3d12940..5073706c7 100644 --- a/src/core/thread/csl_tx_scheduler.hpp +++ b/src/core/thread/csl_tx_scheduler.hpp @@ -231,11 +231,17 @@ class CslTxScheduler : public InstanceLocator, private NonCopyable */ void Clear(void); + /** + * Updates the value of `mCslFrameRequestAheadUs`, based on bus speed, bus latency + * and `OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US`. + * + */ + void UpdateFrameRequestAhead(void); + private: // Guard time in usec to add when checking delay while preparing the CSL frame for tx. static constexpr uint32_t kFramePreparationGuardInterval = 1500; - void InitFrameRequestAhead(void); void RescheduleCslTx(void); uint32_t GetNextCslTransmissionDelay(const Child &aChild, uint32_t &aDelayFromLastRx, uint32_t aAheadUs) const; diff --git a/src/lib/spinel/README_RADIO_SPINEL_DIAG.md b/src/lib/spinel/README_RADIO_SPINEL_DIAG.md new file mode 100644 index 000000000..f2d4d7e92 --- /dev/null +++ b/src/lib/spinel/README_RADIO_SPINEL_DIAG.md @@ -0,0 +1,30 @@ +# OpenThread Diagnostics - Radio Spinel diagnostic commands + +This module provides Spinel based radio transceiver diagnostic commands. + +`OPENTHREAD_CONFIG_DIAG_ENABLE` is required. + +## Command List + +- [buslatency](#buslatency) + +## Command Details + +### buslatency + +Get the bus latency in microseconds between the host and the radio chip. + +```bash +> diag radiospinel buslatency +0 +Done +``` + +#### buslatency \ + +Set the bus latency in microseconds between the host and the radio chip. + +```bash +> diag radiospinel buslatency 1000 +Done +``` diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index ff4819045..5244bcefc 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -79,6 +79,7 @@ RadioSpinel::RadioSpinel(void) , mPanId(0xffff) , mChannel(0) , mRxSensitivity(0) + , mBusLatency(0) , mState(kStateDisabled) , mIsPromiscuous(false) , mRxOnWhenIdle(true) @@ -1785,6 +1786,41 @@ void RadioSpinel::GetDiagOutputCallback(otPlatDiagOutputCallback &aCallback, voi aContext = mOutputContext; } +otError RadioSpinel::RadioSpinelDiagProcess(char *aArgs[], uint8_t aArgsLength) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aArgsLength > 1, error = OT_ERROR_INVALID_ARGS); + + aArgs++; + aArgsLength--; + + if (strcmp(aArgs[0], "buslatency") == 0) + { + if (aArgsLength == 1) + { + PlatDiagOutput("%lu\n", ToUlong(GetBusLatency())); + } + else if (aArgsLength == 2) + { + uint32_t busLatency; + char *endptr; + + busLatency = static_cast(strtoul(aArgs[1], &endptr, 0)); + VerifyOrExit(*endptr == '\0', error = OT_ERROR_INVALID_ARGS); + + SetBusLatency(busLatency); + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + } + +exit: + return error; +} + otError RadioSpinel::PlatDiagProcess(const char *aString) { return Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString); @@ -1912,6 +1948,18 @@ uint64_t RadioSpinel::GetNow(void) { return (mIsTimeSynced) ? (otPlatTimeGet() + uint32_t RadioSpinel::GetBusSpeed(void) const { return GetSpinelDriver().GetSpinelInterface()->GetBusSpeed(); } +uint32_t RadioSpinel::GetBusLatency(void) const { return mBusLatency; } + +void RadioSpinel::SetBusLatency(uint32_t aBusLatency) +{ + mBusLatency = aBusLatency; + + if (IsEnabled() && mCallbacks.mBusLatencyChanged != nullptr) + { + mCallbacks.mBusLatencyChanged(mInstance); + } +} + void RadioSpinel::HandleRcpUnexpectedReset(spinel_status_t aStatus) { OT_UNUSED_VARIABLE(aStatus); diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index eae94b06f..0eb49108b 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -87,6 +87,14 @@ struct RadioSpinelCallbacks */ void (*mEnergyScanDone)(otInstance *aInstance, int8_t aMaxRssi); + /** + * This callback notifies user of `RadioSpinel` that the bus latency has been changed. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ + void (*mBusLatencyChanged)(otInstance *aInstance); + /** * This callback notifies user of `RadioSpinel` that the transmission has started. * @@ -682,6 +690,18 @@ class RadioSpinel : private Logger */ bool IsDiagEnabled(void) const { return mDiagMode; } + /** + * Processes RadioSpinel - specific diagnostics commands. + * + * @param[in] aArgsLength The number of arguments in @p aArgs. + * @param[in] aArgs The arguments of diagnostics command line. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_INVALID_ARGS Failed due to invalid arguments provided. + * + */ + otError RadioSpinelDiagProcess(char *aArgs[], uint8_t aArgsLength); + /** * Processes platform diagnostics commands. * @@ -859,6 +879,22 @@ class RadioSpinel : private Logger */ uint32_t GetBusSpeed(void) const; + /** + * Returns the bus latency between the host and the radio. + * + * @returns Bus latency in microseconds. + * + */ + uint32_t GetBusLatency(void) const; + + /** + * Sets the bus latency between the host and the radio. + * + * @param[in] aBusLatency Bus latency in microseconds. + * + */ + void SetBusLatency(uint32_t aBusLatency); + /** * Returns the co-processor sw version string. * @@ -1233,6 +1269,7 @@ class RadioSpinel : private Logger otError mTxError; static otExtAddress sIeeeEui64; static otRadioCaps sRadioCaps; + uint32_t mBusLatency; State mState; bool mIsPromiscuous : 1; ///< Promiscuous mode. diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index 82551e311..46c3040b2 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -89,10 +89,11 @@ void Radio::Init(const char *aUrl) callbacks.mDiagReceiveDone = otPlatDiagRadioReceiveDone; callbacks.mDiagTransmitDone = otPlatDiagRadioTransmitDone; #endif // OPENTHREAD_CONFIG_DIAG_ENABLE - callbacks.mEnergyScanDone = otPlatRadioEnergyScanDone; - callbacks.mReceiveDone = otPlatRadioReceiveDone; - callbacks.mTransmitDone = otPlatRadioTxDone; - callbacks.mTxStarted = otPlatRadioTxStarted; + callbacks.mEnergyScanDone = otPlatRadioEnergyScanDone; + callbacks.mBusLatencyChanged = otPlatRadioBusLatencyChanged; + callbacks.mReceiveDone = otPlatRadioReceiveDone; + callbacks.mTransmitDone = otPlatRadioTxDone; + callbacks.mTxStarted = otPlatRadioTxStarted; resetRadio = !mRadioUrl.HasParam("no-reset"); skipCompatibilityCheck = mRadioUrl.HasParam("skip-rcp-compatibility-check"); @@ -135,6 +136,13 @@ void Radio::ProcessRadioUrl(const RadioUrl &aRadioUrl) SuccessOrDie(otPlatRadioSetRegion(gInstance, regionCode)); } + if (aRadioUrl.HasParam("bus-latency")) + { + uint32_t busLatency; + SuccessOrDie(aRadioUrl.ParseUint32("bus-latency", busLatency)); + mRadioSpinel.SetBusLatency(busLatency); + } + ProcessMaxPowerTable(aRadioUrl); #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE @@ -553,25 +561,36 @@ void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { - // deliver the platform specific diags commands to radio only ncp. OT_UNUSED_VARIABLE(aInstance); - char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {'\0'}; - char *cur = cmd; - char *end = cmd + sizeof(cmd); + otError error = OT_ERROR_NONE; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {'\0'}; + char *cur = cmd; + char *end = cmd + sizeof(cmd); #if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE if (strcmp(aArgs[0], "rcpcaps") == 0) { - return GetRcpCapsDiag().DiagProcess(aArgs, aArgsLength); + error = GetRcpCapsDiag().DiagProcess(aArgs, aArgsLength); + ExitNow(); } #endif + if (strcmp(aArgs[0], "radiospinel") == 0) + { + error = GetRadioSpinel().RadioSpinelDiagProcess(aArgs, aArgsLength); + ExitNow(); + } + for (uint8_t index = 0; (index < aArgsLength) && (cur < end); index++) { cur += snprintf(cur, static_cast(end - cur), "%s ", aArgs[index]); } - return GetRadioSpinel().PlatDiagProcess(cmd); + // deliver the platform specific diags commands to radio only ncp. + error = GetRadioSpinel().PlatDiagProcess(cmd); + +exit: + return error; } void otPlatDiagModeSet(bool aMode) @@ -905,6 +924,12 @@ uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance) return GetRadioSpinel().GetBusSpeed(); } +uint32_t otPlatRadioGetBusLatency(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return GetRadioSpinel().GetBusLatency(); +} + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance) { diff --git a/src/posix/platform/radio_url.cpp b/src/posix/platform/radio_url.cpp index db0c13461..57e1df0c2 100644 --- a/src/posix/platform/radio_url.cpp +++ b/src/posix/platform/radio_url.cpp @@ -121,6 +121,7 @@ const char *otSysGetRadioUrlHelpString(void) " fem-lnagain[=dbm] Set the Rx LNA gain in dBm of the external FEM.\n" " no-reset Do not send Spinel reset command to RCP on initialization.\n" " skip-rcp-compatibility-check Skip checking RCP API version and capabilities during initialization.\n" + " bus-latency[=usec] Communication latency in usec, default is 0.\n" #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE " iid Set the Spinel Interface ID for this process. Valid values are 0-3.\n" " iid-list List of IIDs a host can subscribe to receive spinel frames other than \n" From 625b18630c3405b23d1f964c7acbb72f3066ebf2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 09:21:21 -0700 Subject: [PATCH 062/160] github-actions: bump docker/setup-buildx-action from 3.3.0 to 3.4.0 (#10531) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/d70bba72b1f3fd22344832f00baa16ece964efeb...4fd812986e6c8c2a69e18311145f9371337f27d4) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ffbc2b7c3..ea42cfcb2 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -83,7 +83,7 @@ jobs: ${TAGS} --file ${DOCKER_FILE} ." >> $GITHUB_OUTPUT - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 + uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3.4.0 - name: Docker Buildx (build) run: | From d0fbfb8c768eae85d6bf5099bd8129db1c0a94b1 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Tue, 23 Jul 2024 00:56:54 +0800 Subject: [PATCH 063/160] [sub-mac] use relative time when starting CSL TX delay (#10525) The original code uses the absolute time when staring the CSL TX delay in `SubMac`. The timer is a `MicroTimer` and the absolute time comes from the `mTxDelayBaseTime`. The `mTxDelayBaseTime` comes from the radio time `otPlatRadioGetNow ()`, the time of `MicroTimer` should come from `otPlatAlarmMicroGetNow()`. If the `MicroTimer` and the radio are using different timer sources, it will cause the unexpected issue. This commit use the relative time when starting CSL TX delay to fix this issue. --- src/core/mac/sub_mac.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index dcd0f444b..94916f6d7 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -396,12 +396,13 @@ void SubMac::StartCsmaBackoff(void) { static constexpr uint32_t kAheadTime = kCcaSampleInterval + kCslTransmitTimeAhead + kRadioHeaderShrDuration; Time txStartTime = Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime); + Time radioNow = Time(static_cast(otPlatRadioGetNow(&GetInstance()))); txStartTime += (mTransmitFrame.mInfo.mTxInfo.mTxDelay - kAheadTime); - if (Time(static_cast(otPlatRadioGetNow(&GetInstance()))) < txStartTime) + if (radioNow < txStartTime) { - mTimer.FireAt(txStartTime); + StartTimer(txStartTime - radioNow); } else // Transmit without delay { From af18582b4e5488e9d708f2d5bb74282ac6a60ba9 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Wed, 24 Jul 2024 21:50:17 +0800 Subject: [PATCH 064/160] [test] set default paths of expect scripts (#10530) This commit adds default paths for expect scripts to make it easier for running expect scripts. With this change, we can run expect tests as follows: ```bash ./script/cmake-build simulation ./script/cmake-build posix ./tests/scripts/expect/cli-ping.exp OT_NODE_TYPE=rcp ./tests/scripts/expect/cli-ping.exp ``` This commit also updates an existing test to cover the change. --- script/check-simulation-local-host | 21 ++++++++++++--------- tests/scripts/expect/_common.exp | 26 +++++++++++++++++++++----- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/script/check-simulation-local-host b/script/check-simulation-local-host index 7c375685d..29f2182c6 100755 --- a/script/check-simulation-local-host +++ b/script/check-simulation-local-host @@ -30,7 +30,7 @@ set -euxo pipefail IFACE_NAME="dummy116" -EXPECT_TEST=tests/scripts/expect/cli-ping.exp +EXPECT_TEST=./tests/scripts/expect/cli-ping.exp cleanup() { @@ -59,24 +59,27 @@ setup_dummy116() test_ipv6() { setup_dummy116 - OT_SIMULATION_LOCAL_HOST=$IFACE_NAME ./script/test build expect $EXPECT_TEST - OT_SIMULATION_LOCAL_HOST=$IP6ADDR ./script/test build expect $EXPECT_TEST + OT_SIMULATION_LOCAL_HOST=$IFACE_NAME $EXPECT_TEST + OT_SIMULATION_LOCAL_HOST=$IP6ADDR $EXPECT_TEST - OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=$IFACE_NAME ./script/test build expect $EXPECT_TEST - OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=$IP6ADDR ./script/test build expect $EXPECT_TEST + OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=$IFACE_NAME $EXPECT_TEST + OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=$IP6ADDR $EXPECT_TEST } test_ipv4() { - OT_SIMULATION_LOCAL_HOST=lo ./script/test build expect $EXPECT_TEST - OT_SIMULATION_LOCAL_HOST=127.0.0.1 ./script/test build expect $EXPECT_TEST + OT_SIMULATION_LOCAL_HOST=lo $EXPECT_TEST + OT_SIMULATION_LOCAL_HOST=127.0.0.1 $EXPECT_TEST - OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=lo ./script/test build expect $EXPECT_TEST - OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=127.0.0.1 ./script/test build expect $EXPECT_TEST + OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=lo $EXPECT_TEST + OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=127.0.0.1 $EXPECT_TEST } main() { + ./script/cmake-build posix + ./script/cmake-build simulation + test_ipv4 test_ipv6 } diff --git a/tests/scripts/expect/_common.exp b/tests/scripts/expect/_common.exp index a94799e2d..b94a48583 100644 --- a/tests/scripts/expect/_common.exp +++ b/tests/scripts/expect/_common.exp @@ -69,11 +69,27 @@ proc spawn_node {id {type ""} {radio_url ""}} { global argv0 if {${type} == ""} { - set type $::env(OT_NODE_TYPE) + if {[info exists ::env(OT_NODE_TYPE)]} { + set type $::env(OT_NODE_TYPE) + } else { + set type "cli" + } + } + + if {[info exists ::env(OT_POSIX_APPS)]} { + set ot_posix_apps $::env(OT_POSIX_APPS) + } else { + set ot_posix_apps "build/posix/src/posix" + } + + if {[info exists ::env(OT_SIMULATION_APPS)]} { + set ot_simulation_apps $::env(OT_SIMULATION_APPS) + } else { + set ot_simulation_apps "build/simulation/examples/apps" } if {${radio_url} == ""} { - set radio_url "spinel+hdlc+uart://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=$id" + set radio_url "spinel+hdlc+uart://$ot_simulation_apps/ncp/ot-rcp?forkpty-arg=$id" } send_user "\n# ${id} ${type}\n" @@ -96,7 +112,7 @@ proc spawn_node {id {type ""} {radio_url ""}} { # Sleep 0.2 seconds to ensure that the ot-rcp in the previous test has exited to # avoid the error: "bind(sTxFd): Address already in use" sleep 0.2 - spawn /usr/bin/env GCOV_PREFIX=$gcov_prefix $::env(OT_POSIX_APPS)/ot-cli $radio_url + spawn /usr/bin/env GCOV_PREFIX=$gcov_prefix $ot_posix_apps/ot-cli $radio_url send "factoryreset\n" wait_for "state" "disabled" expect_line "Done" @@ -104,7 +120,7 @@ proc spawn_node {id {type ""} {radio_url ""}} { expect_line "Done" } cli { - spawn /usr/bin/env GCOV_PREFIX=$gcov_prefix $::env(OT_SIMULATION_APPS)/cli/ot-cli-ftd \ + spawn /usr/bin/env GCOV_PREFIX=$gcov_prefix $ot_simulation_apps/cli/ot-cli-ftd \ -L$ot_simulation_local_host $id send "factoryreset\n" wait_for "state" "disabled" @@ -113,7 +129,7 @@ proc spawn_node {id {type ""} {radio_url ""}} { expect_line "Done" } mtd { - spawn /usr/bin/env GCOV_PREFIX=$gcov_prefix $::env(OT_SIMULATION_APPS)/cli/ot-cli-mtd \ + spawn /usr/bin/env GCOV_PREFIX=$gcov_prefix $ot_simulation_apps/cli/ot-cli-mtd \ -L$ot_simulation_local_host $id send "factoryreset\n" wait_for "state" "disabled" From 2cc0798a971164dded05c491d994f7715a477e93 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 25 Jul 2024 11:04:37 -0700 Subject: [PATCH 065/160] [border-agent] directly respond to MGMT_GET from non-active commissioner (#10524) This commit updates the `BorderAgent` to directly respond to `MGMT_ACTIVE_GET` and `MGMT_PENDING_GET` requests from a non-active commissioner. Requests from an active commissioner are still forwarded to the leader. This aligns the implementation with Thread 1.4 requirements (ephemeral PSKc use case). To achieve this, the following changes are made: - New `State` values are added to distinguish between when a commissioner candidate is connected and when its petition to become the active commissioner is accepted. This determines whether the `MGMT_GET` request should be handled directly or forwarded to the leader. - This state is tracked locally by `BorderAgent` instead of monitoring Network Data to determine whether an active commissioner exists. This ensures correct behavior even when Network Data updates are delayed. - The `DatasetManager` is updated to provide `ProcessGetRequest()` to process an `MGMT_GET` request and prepare the response. This is then used by `DatasetManager` itself and `BorderAgent`. --- src/core/api/border_agent_api.cpp | 17 +++++- src/core/meshcop/border_agent.cpp | 49 ++++++++++++++-- src/core/meshcop/border_agent.hpp | 10 ++-- src/core/meshcop/dataset_manager.cpp | 84 ++++++++++++++++------------ src/core/meshcop/dataset_manager.hpp | 26 ++++++++- 5 files changed, 138 insertions(+), 48 deletions(-) diff --git a/src/core/api/border_agent_api.cpp b/src/core/api/border_agent_api.cpp index 6a9f42f50..c6d321ee9 100644 --- a/src/core/api/border_agent_api.cpp +++ b/src/core/api/border_agent_api.cpp @@ -56,7 +56,22 @@ otError otBorderAgentSetId(otInstance *aInstance, const otBorderAgentId *aId) otBorderAgentState otBorderAgentGetState(otInstance *aInstance) { - return MapEnum(AsCoreType(aInstance).Get().GetState()); + otBorderAgentState state = OT_BORDER_AGENT_STATE_STOPPED; + + switch (AsCoreType(aInstance).Get().GetState()) + { + case MeshCoP::BorderAgent::kStateStopped: + break; + case MeshCoP::BorderAgent::kStateStarted: + state = OT_BORDER_AGENT_STATE_STARTED; + break; + case MeshCoP::BorderAgent::kStateConnected: + case MeshCoP::BorderAgent::kStateAccepted: + state = OT_BORDER_AGENT_STATE_ACTIVE; + break; + } + + return state; } uint16_t otBorderAgentGetUdpPort(otInstance *aInstance) diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index ae3cc10e1..e084fa63c 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -203,6 +203,7 @@ void BorderAgent::HandleCoapResponse(const ForwardContext &aForwardContext, Get().GetCommissionerAloc(sessionId, mCommissionerAloc.GetAddress()); Get().AddUnicastAddress(mCommissionerAloc); IgnoreError(Get().AddReceiver(mUdpReceiver)); + mState = kStateAccepted; LogInfo("Commissioner accepted - SessionId:%u ALOC:%s", sessionId, mCommissionerAloc.GetAddress().ToString().AsCString()); @@ -462,7 +463,7 @@ void BorderAgent::HandleTmf(Coap::Message &aMessage, const template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriActiveGet)); + HandleTmfDatasetGet(aMessage, aMessageInfo, Dataset::kActive); } template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) @@ -472,7 +473,7 @@ template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriPendingGet)); + HandleTmfDatasetGet(aMessage, aMessageInfo, Dataset::kPending); } template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) @@ -591,6 +592,45 @@ Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::Mes return error; } +void BorderAgent::HandleTmfDatasetGet(Coap::Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, + Dataset::Type aType) +{ + Error error = kErrorNone; + Coap::Message *response = nullptr; + + if (mState == kStateAccepted) + { + Uri uri = (aType == Dataset::kActive) ? kUriActiveGet : kUriPendingGet; + + IgnoreError(ForwardToLeader(aMessage, aMessageInfo, uri)); + ExitNow(); + } + + // When processing `MGMT_GET` request directly on Border Agent, + // the Security Policy flags (O-bit) should be ignore to allow + // the commissioner candidate to get the full Operational Dataset. + + if (aType == Dataset::kActive) + { + response = Get().ProcessGetRequest(aMessage, DatasetManager::kIgnoreSecurityPolicyFlags); + } + else + { + response = Get().ProcessGetRequest(aMessage, DatasetManager::kIgnoreSecurityPolicyFlags); + } + + VerifyOrExit(response != nullptr, error = kErrorParse); + + SuccessOrExit(error = Get().SendMessage(*response, aMessageInfo)); + + LogInfo("Sent %sGet response to non-active commissioner", Dataset::TypeToString(aType)); + +exit: + LogWarnOnError(error, "send Active/PendingGet response"); + FreeMessageOnError(response, error); +} + void BorderAgent::HandleConnected(bool aConnected, void *aContext) { static_cast(aContext)->HandleConnected(aConnected); @@ -601,7 +641,7 @@ void BorderAgent::HandleConnected(bool aConnected) if (aConnected) { LogInfo("Commissioner connected"); - mState = kStateActive; + mState = kStateConnected; mTimer.Start(kKeepAliveTimeout); } else @@ -766,7 +806,8 @@ void BorderAgent::ClearEphemeralKey(void) break; case kStateStopped: - case kStateActive: + case kStateConnected: + case kStateAccepted: // If there is an active commissioner connection, we wait till // it gets disconnected before removing ephemeral key and // restarting the agent. diff --git a/src/core/meshcop/border_agent.hpp b/src/core/meshcop/border_agent.hpp index 933a95c76..4e52ad179 100644 --- a/src/core/meshcop/border_agent.hpp +++ b/src/core/meshcop/border_agent.hpp @@ -46,6 +46,7 @@ #include "common/non_copyable.hpp" #include "common/notifier.hpp" #include "common/tasklet.hpp" +#include "meshcop/dataset.hpp" #include "meshcop/secure_transport.hpp" #include "net/udp6.hpp" #include "thread/tmf.hpp" @@ -94,9 +95,10 @@ class BorderAgent : public InstanceLocator, private NonCopyable */ enum State : uint8_t { - kStateStopped = OT_BORDER_AGENT_STATE_STOPPED, ///< Border agent is stopped/disabled. - kStateStarted = OT_BORDER_AGENT_STATE_STARTED, ///< Border agent is started. - kStateActive = OT_BORDER_AGENT_STATE_ACTIVE, ///< Border agent is connected with external commissioner. + kStateStopped, ///< Stopped/disabled. + kStateStarted, ///< Started and listening for connections. + kStateConnected, ///< Connected to an external commissioner candidate, petition pending. + kStateAccepted, ///< Connected to and accepted an external commissioner. }; /** @@ -288,6 +290,7 @@ class BorderAgent : public InstanceLocator, private NonCopyable template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void HandleTmfDatasetGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Dataset::Type aType); void HandleTimeout(void); #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE @@ -346,7 +349,6 @@ DeclareTmfHandler(BorderAgent, kUriProxyTx); } // namespace MeshCoP -DefineMapEnum(otBorderAgentState, MeshCoP::BorderAgent::State); DefineCoreType(otBorderAgentId, MeshCoP::BorderAgent::Id); } // namespace ot diff --git a/src/core/meshcop/dataset_manager.cpp b/src/core/meshcop/dataset_manager.cpp index 3ebd90d13..a90ae2ef7 100644 --- a/src/core/meshcop/dataset_manager.cpp +++ b/src/core/meshcop/dataset_manager.cpp @@ -531,71 +531,83 @@ void DatasetManager::HandleMgmtSetResponse(Coap::Message *aMessage, const Ip6::M void DatasetManager::HandleGet(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const { - TlvList tlvList; - uint8_t tlvType; - OffsetRange offsetRange; + Error error = kErrorNone; + Coap::Message *response = ProcessGetRequest(aMessage, kCheckSecurityPolicyFlags); - SuccessOrExit(Tlv::FindTlvValueOffsetRange(aMessage, Tlv::kGet, offsetRange)); + VerifyOrExit(response != nullptr); + SuccessOrExit(error = Get().SendMessage(*response, aMessageInfo)); - while (!offsetRange.IsEmpty()) - { - IgnoreError(aMessage.Read(offsetRange, tlvType)); - tlvList.Add(tlvType); - offsetRange.AdvanceOffset(sizeof(uint8_t)); - } - - // MGMT_PENDING_GET.rsp must include Delay Timer TLV (Thread 1.1.1 - // Section 8.7.5.4). - - if (!tlvList.IsEmpty() && IsPendingDataset()) - { - tlvList.Add(Tlv::kDelayTimer); - } + LogInfo("sent %s dataset get response to %s", IsActiveDataset() ? "active" : "pending", + aMessageInfo.GetPeerAddr().ToString().AsCString()); exit: - SendGetResponse(aMessage, aMessageInfo, tlvList); + FreeMessageOnError(response, error); } -void DatasetManager::SendGetResponse(const Coap::Message &aRequest, - const Ip6::MessageInfo &aMessageInfo, - const TlvList &aTlvList) const +Coap::Message *DatasetManager::ProcessGetRequest(const Coap::Message &aRequest, + SecurityPolicyCheckMode aCheckMode) const { - Error error = kErrorNone; - Coap::Message *message; + // Processes a MGMT_ACTIVE_GET or MGMT_PENDING_GET request + // and prepares the response. + + Error error = kErrorNone; + Coap::Message *response = nullptr; Dataset dataset; + TlvList tlvList; + OffsetRange offsetRange; + + if (Tlv::FindTlvValueOffsetRange(aRequest, Tlv::kGet, offsetRange) == kErrorNone) + { + while (!offsetRange.IsEmpty()) + { + uint8_t tlvType; + + IgnoreError(aRequest.Read(offsetRange, tlvType)); + tlvList.Add(tlvType); + offsetRange.AdvanceOffset(sizeof(uint8_t)); + } + + // MGMT_PENDING_GET.rsp must include Delay Timer TLV (Thread 1.1.1 + // Section 8.7.5.4). + + if (!tlvList.IsEmpty() && IsPendingDataset()) + { + tlvList.Add(Tlv::kDelayTimer); + } + } + + // Ignore `Read()` error, since even if no Dataset is saved, we should + // respond with an empty one. IgnoreError(Read(dataset)); - message = Get().NewPriorityResponseMessage(aRequest); - VerifyOrExit(message != nullptr, error = kErrorNoBufs); + response = Get().NewPriorityResponseMessage(aRequest); + VerifyOrExit(response != nullptr, error = kErrorNoBufs); for (const Tlv *tlv = dataset.GetTlvsStart(); tlv < dataset.GetTlvsEnd(); tlv = tlv->GetNext()) { bool shouldAppend = true; - if (!aTlvList.IsEmpty()) + if (!tlvList.IsEmpty()) { - shouldAppend = aTlvList.Contains(tlv->GetType()); + shouldAppend = tlvList.Contains(tlv->GetType()); } - if ((tlv->GetType() == Tlv::kNetworkKey) && !Get().GetSecurityPolicy().mObtainNetworkKeyEnabled) + if ((aCheckMode == kCheckSecurityPolicyFlags) && (tlv->GetType() == Tlv::kNetworkKey) && + !Get().GetSecurityPolicy().mObtainNetworkKeyEnabled) { shouldAppend = false; } if (shouldAppend) { - SuccessOrExit(error = tlv->AppendTo(*message)); + SuccessOrExit(error = tlv->AppendTo(*response)); } } - SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo)); - - LogInfo("sent %s dataset get response to %s", IsActiveDataset() ? "active" : "pending", - aMessageInfo.GetPeerAddr().ToString().AsCString()); - exit: - FreeMessageOnError(message, error); + FreeAndNullMessageOnError(response, error); + return response; } Error DatasetManager::SendSetRequest(const Dataset::Info &aDatasetInfo, diff --git a/src/core/meshcop/dataset_manager.hpp b/src/core/meshcop/dataset_manager.hpp index 270903b8d..9c76df517 100644 --- a/src/core/meshcop/dataset_manager.hpp +++ b/src/core/meshcop/dataset_manager.hpp @@ -65,6 +65,18 @@ class DatasetManager : public InstanceLocator */ typedef otDatasetMgmtSetCallback MgmtSetCallback; + /** + * Indicates whether to check or ignore Security Policy flag when processing an MGMT_GET request message. + * + * This is used as input in `ProcessGetRequest(). + * + */ + enum SecurityPolicyCheckMode : uint8_t + { + kCheckSecurityPolicyFlags, ///< Check Security Policy flags. + kIgnoreSecurityPolicyFlags, ///< Ignore Security Policy flags. + }; + /** * Returns the network Timestamp. * @@ -219,6 +231,17 @@ class DatasetManager : public InstanceLocator uint8_t aLength, const otIp6Address *aAddress) const; + /** + * Processes a MGMT_GET request message and prepares the response. + * + * @param[in] aRequest The MGMT_GET request message. + * @param[in] aCheckMode Indicates whether to check or ignore the Security Policy flags. + * + * @returns The prepared response, or `nullptr` if fails to parse the request or cannot allocate message. + * + */ + Coap::Message *ProcessGetRequest(const Coap::Message &aRequest, SecurityPolicyCheckMode aCheckMode) const; + private: static constexpr uint8_t kMaxGetTypes = 64; // Max number of types in MGMT_GET.req static constexpr uint32_t kSendSetDelay = 5000; // in msec. @@ -279,9 +302,6 @@ class DatasetManager : public InstanceLocator void SignalDatasetChange(void) const; void SyncLocalWithLeader(const Dataset &aDataset); Error SendSetRequest(const Dataset &aDataset); - void SendGetResponse(const Coap::Message &aRequest, - const Ip6::MessageInfo &aMessageInfo, - const TlvList &aTlvList) const; void HandleMgmtSetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aError); static void HandleMgmtSetResponse(void *aContext, From 70969285f93baa5e2fe21ffb5952fdcff3e8816b Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Sat, 27 Jul 2024 01:44:22 +0800 Subject: [PATCH 066/160] [otci] add iperf3 commands support to otci (#10519) This commit adds the iperf3 commands to `otci` for testing Thread network throughput. --- tools/otci/otci/command_handlers.py | 23 +++- tools/otci/otci/otci.py | 158 ++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 1 deletion(-) diff --git a/tools/otci/otci/command_handlers.py b/tools/otci/otci/command_handlers.py index ed08584c6..3d5479a86 100644 --- a/tools/otci/otci/command_handlers.py +++ b/tools/otci/otci/command_handlers.py @@ -51,6 +51,14 @@ def execute_command(self, cmd: str, timeout: float) -> List[str]: 'Error : ' following OT CLI conventions. """ + @abstractmethod + def execute_platform_command(self, cmd: str, timeout: float) -> List[str]: + """Method execute_platform_command should execute the platform command within a timeout (in seconds) and + return the command output as a list of lines. + + Note: each line of the command output MUST NOT contain '\r\n' at the end. + """ + @abstractmethod def close(self): """Method close should close the OT Command Handler.""" @@ -129,6 +137,9 @@ def execute_command(self, cmd, timeout=10) -> List[str]: asynchronous=cmd.split()[0] in OtCliCommandRunner.__ASYNC_COMMANDS) return output + def execute_platform_command(self, cmd, timeout=10) -> List[str]: + raise NotImplementedError(f'Platform command is not supported on {self.__class__.__name__}') + def wait(self, duration: float) -> List[str]: self.__otcli.wait(duration) @@ -261,6 +272,12 @@ def execute_command(self, cmd: str, timeout: float) -> List[str]: return output + def execute_platform_command(self, cmd, timeout=10) -> List[str]: + if self.__sudo: + cmd = 'sudo ' + cmd + + return self.shell(cmd, timeout=timeout) + def shell(self, cmd: str, timeout: float) -> List[str]: cmd_in, cmd_out, cmd_err = self.__ssh.exec_command(cmd, timeout=int(timeout), bufsize=1024) errput = [l.rstrip('\r\n') for l in cmd_err.readlines()] @@ -305,8 +322,12 @@ def execute_command(self, cmd: str, timeout: float) -> List[str]: return output + def execute_platform_command(self, cmd: str, timeout: float = 10) -> List[str]: + return self.shell(cmd, timeout=timeout) + def shell(self, cmd: str, timeout: float) -> List[str]: - return self.__adb.shell(cmd, timeout_s=timeout).splitlines() + return self.__adb.shell(cmd, transport_timeout_s=timeout, read_timeout_s=timeout, + timeout_s=timeout).splitlines() def close(self): self.__adb.close() diff --git a/tools/otci/otci/otci.py b/tools/otci/otci/otci.py index e3fc57ca3..46a21eb3f 100644 --- a/tools/otci/otci/otci.py +++ b/tools/otci/otci/otci.py @@ -136,6 +136,25 @@ def __execute_command(self, else: raise CommandError(cmd, output) + def execute_platform_command(self, cmd: str, timeout: float = 10, silent: bool = False) -> List[str]: + """Execute the platform command. + + :param cmd: The command to execute. + :param timeout: The command timeout. + :param silent: Whether to run the command silent without logging. + :returns: The command output as a list of lines. + """ + if not silent: + self.log('info', '> %s', cmd) + + output = self.__otcmd.execute_platform_command(cmd, timeout) + + if not silent: + for line in output: + self.log('info', '%s', line) + + return output + def set_execute_command_retry(self, n: int): assert n >= 0 self.__exec_command_retry = n @@ -2619,6 +2638,145 @@ def wait_for(self, command: str, expect_line: Optional[Union[str, Pattern, Colle # TODO: pskc [-p] | # + # + # Platform Commands Utilities + # + def support_iperf3(self) -> bool: + """Check whether the platform supports iperf3.""" + # + # Command example: + # + # $ command -v iperf3 + # /usr/bin/iperf3 + # + ret = False + output = self.execute_platform_command('command -v iperf3') + if len(output) > 0 and 'iperf3' in output[0]: + ret = True + + return ret + + def iperf3_client(self, + host: Union[str, Ip6Addr], + ipv6: bool = True, + udp: bool = True, + bind_address: Optional[Union[str, Ip6Addr]] = None, + bitrate: int = 10000, + interval: int = 10, + transmit_time: int = 10, + length: Optional[int] = None) -> Dict[str, Dict[str, Any]]: + """Run iperf3 in client mode. + + :param host: The host IPv6 address to send iperf3 traffic. + :param ipv6: True to use IPv6, False to use IPv4 (default IPv6). + :param udp: True to use UDP, False to use TCP (default UDP). + :param bind_address: The local address to be bound. + :param bitrate: The target bitrate in bits/sec (default 10000 bit/sec). + :param interval: Seconds between periodic throughput reports (default 10 sec). + :param transmit_time: Time in seconds to transmit for (default 10 secs) + :param length: Length of buffer to read or write (default None). + """ + # + # Iperf3 client example: + # + # $ iperf3 -6 -c fdd6:f5cf:d32d:8d88:a98b:cf7c:2ed2:691a -u -b 90000 -i 20 -t 10 -l 1232 -f k + # Connecting to host fdd6:f5cf:d32d:8d88:a98b:cf7c:2ed2:691a, port 5201 + # [ 5] local fdd6:f5cf:d32d:8d88:0:ff:fe00:fc00 port 59495 connected to fdd6:f5cf:d32d:8d88:a98b:cf7c:2ed2:691a port 5201 + # [ ID] Interval Transfer Bitrate Total Datagrams + # [ 5] 0.00-10.00 sec 111 KBytes 90.7 Kbits/sec 92 + # - - - - - - - - - - - - - - - - - - - - - - - - - + # [ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams + # [ 5] 0.00-10.00 sec 111 KBytes 90.7 Kbits/sec 0.000 ms 0/92 (0%) sender + # [ 5] 0.00-10.96 sec 99.9 KBytes 74.7 Kbits/sec 30.157 ms 9/92 (9.8%) receiver + # + # iperf Done. + # + + wait_time = 10 + client_option = f'-c {host}' + version_option = "-6" if ipv6 else "-4" + udp_option = '-u' if udp else '' + bind_option = f'-B {bind_address}' if bind_address else '' + bitrate_option = f'-b {bitrate}' + interval_option = f'-i {interval}' + time_option = f'-t {transmit_time}' + length_option = f'-l {length}' if length else '' + format_option = '-f k' + + cmd = f'iperf3 {version_option} {client_option} {udp_option} {bitrate_option} {interval_option} {time_option} {length_option} {format_option}' + output = self.execute_platform_command(cmd, timeout=transmit_time + wait_time) + + results = {} + for line in output: + fields = line.split() + if len(fields) != 13: + continue + + if fields[-1] == 'sender': + results['sender'] = self.__parse_iperf3_report(line) + elif fields[-1] == 'receiver': + results['receiver'] = self.__parse_iperf3_report(line) + + return results + + def iperf3_server(self, + bind_address: Optional[Union[str, Ip6Addr]] = None, + interval: int = 10, + timeout: int = 60) -> Dict[str, Any]: + """Run iperf3 in server mode. + + :param bind_address: The local address to be bound. + :param interval: Seconds between periodic throughput reports (default 10 sec). + :param timeout: Timeout in seconds to abort the program (default 60 secs) + """ + # + # Iperf3 server example: + # + # $ iperf3 -s -1 -B fdd6:f5cf:d32d:8d88:a98b:cf7c:2ed2:691a -i 50 -f k + # ----------------------------------------------------------- + # Server listening on 5201 + # ----------------------------------------------------------- + # Accepted connection from fdd6:f5cf:d32d:8d88:0:ff:fe00:fc00, port 44080 + # [ 5] local fdd6:f5cf:d32d:8d88:a98b:cf7c:2ed2:691a port 5201 connected to fdd6:f5cf:d32d:8d88:0:ff:fe00:fc00 port 59495 + # [ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams + # [ 5] 0.00-10.96 sec 99.9 KBytes 74.7 Kbits/sec 30.157 ms 9/92 (9.8%) + # - - - - - - - - - - - - - - - - - - - - - - - - - + # [ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams + # [ 5] 0.00-10.96 sec 99.9 KBytes 74.7 Kbits/sec 30.157 ms 9/92 (9.8%) receiver + # + + bind_option = f'-B {bind_address}' if bind_address else '' + interval_option = f'-i {interval}' + format_option = '-f k' + + cmd = f'iperf3 -s -1 {bind_option} {interval_option} {format_option}' + output = self.execute_platform_command(cmd, timeout) + + results = {} + for line in output: + fields = line.split() + if len(fields) == 13 and fields[-1] == 'receiver': + results = self.__parse_iperf3_report(line) + + return results + + def __parse_iperf3_report(self, line: str) -> Dict[str, Any]: + results = {} + fields = line.split() + format_unit = 1000 + + if len(fields) == 13 and (fields[-1] == 'sender' or fields[-1] == 'receiver'): + results['id'] = int(fields[1].replace(']', '')) + results['interval_start'] = float(fields[2].split('-')[0]) + results['interval_end'] = float(fields[2].split('-')[1]) + results['transfer'] = int(float(fields[4]) * format_unit) + results['bitrate'] = int(float(fields[6]) * format_unit) + results['jitter'] = float(fields[8]) + results['lossrate'] = float(fields[11].replace('(', '').replace(')', '').replace('%', '')) / 100 + results['datagrams'] = fields[12] + + return results + # # Private methods # From e913c7d235c855b486c7276620c503f5669e2db4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 10:41:43 -0700 Subject: [PATCH 067/160] github-actions: bump actions/setup-go from 5.0.1 to 5.0.2 (#10554) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.0.1 to 5.0.2. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/cdcb36043654635271a94b9a6d1392de5bb323a7...0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/otns.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml index e085662dc..64208a3a0 100644 --- a/.github/workflows/otns.yml +++ b/.github/workflows/otns.yml @@ -63,7 +63,7 @@ jobs: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: "1.20" - name: Set up Python @@ -103,7 +103,7 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: "1.20" - name: Set up Python @@ -165,7 +165,7 @@ jobs: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: "1.20" - name: Set up Python From 0df96d8dad8ad50ff5eff19e933a76f6894d3244 Mon Sep 17 00:00:00 2001 From: Mia Yang <145632982+mia1yang@users.noreply.github.com> Date: Wed, 31 Jul 2024 01:43:54 +0800 Subject: [PATCH 068/160] [lib] add little endian methods in utils (#10534) --- src/lib/spinel/multi_frame_buffer.hpp | 30 ++++------- src/lib/spinel/spi_frame.hpp | 30 +++++------ src/lib/utils/endian.hpp | 72 +++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 37 deletions(-) create mode 100644 src/lib/utils/endian.hpp diff --git a/src/lib/spinel/multi_frame_buffer.hpp b/src/lib/spinel/multi_frame_buffer.hpp index 921181f3d..af920fabf 100644 --- a/src/lib/spinel/multi_frame_buffer.hpp +++ b/src/lib/spinel/multi_frame_buffer.hpp @@ -40,6 +40,8 @@ #include +#include "lib/utils/endian.hpp" + namespace ot { namespace Spinel { @@ -263,7 +265,7 @@ template class MultiFrameBuffer : public FrameWritePointer if (mWriteFrameStart + kHeaderSize + aSkipLength <= GetArrayEnd(mBuffer)) { - LittleEndian::WriteUint16(aSkipLength, mWriteFrameStart + kHeaderSkipLengthOffset); + Lib::Utils::LittleEndian::WriteUint16(aSkipLength, mWriteFrameStart + kHeaderSkipLengthOffset); mWritePointer = GetFrame(); mRemainingLength = static_cast(mBuffer + kSize - mWritePointer); error = OT_ERROR_NONE; @@ -278,7 +280,10 @@ template class MultiFrameBuffer : public FrameWritePointer * @returns The length (number of bytes) of the reserved buffer. * */ - uint16_t GetSkipLength(void) const { return LittleEndian::ReadUint16(mWriteFrameStart + kHeaderSkipLengthOffset); } + uint16_t GetSkipLength(void) const + { + return Lib::Utils::LittleEndian::ReadUint16(mWriteFrameStart + kHeaderSkipLengthOffset); + } /** * Gets a pointer to the start of the current frame. @@ -316,7 +321,8 @@ template class MultiFrameBuffer : public FrameWritePointer } else { - LittleEndian::WriteUint16(GetSkipLength() + GetLength(), mWriteFrameStart + kHeaderTotalLengthOffset); + Lib::Utils::LittleEndian::WriteUint16(GetSkipLength() + GetLength(), + mWriteFrameStart + kHeaderTotalLengthOffset); mWriteFrameStart = mWritePointer; IgnoreError(SetSkipLength(0)); } @@ -369,8 +375,8 @@ template class MultiFrameBuffer : public FrameWritePointer if (HasSavedFrame() && (aFrame != mWriteFrameStart)) { - uint16_t totalLength = LittleEndian::ReadUint16(aFrame + kHeaderTotalLengthOffset); - uint16_t skipLength = LittleEndian::ReadUint16(aFrame + kHeaderSkipLengthOffset); + uint16_t totalLength = Lib::Utils::LittleEndian::ReadUint16(aFrame + kHeaderTotalLengthOffset); + uint16_t skipLength = Lib::Utils::LittleEndian::ReadUint16(aFrame + kHeaderSkipLengthOffset); aLength = totalLength - skipLength; aFrame += kHeaderSize + skipLength; @@ -459,20 +465,6 @@ template class MultiFrameBuffer : public FrameWritePointer static void IgnoreError(otError aError) { (void)(aError); } - class LittleEndian - { - public: - static uint16_t ReadUint16(const uint8_t *aBuffer) - { - return static_cast((aBuffer[0]) | aBuffer[1] << 8); - } - static void WriteUint16(uint16_t aValue, uint8_t *aBuffer) - { - aBuffer[0] = (aValue >> 0) & 0xff; - aBuffer[1] = (aValue >> 8) & 0xff; - } - }; - uint8_t mBuffer[kSize]; uint8_t *mWriteFrameStart; // Pointer to start of current frame being written. }; diff --git a/src/lib/spinel/spi_frame.hpp b/src/lib/spinel/spi_frame.hpp index 43843bb0f..0d41516d4 100644 --- a/src/lib/spinel/spi_frame.hpp +++ b/src/lib/spinel/spi_frame.hpp @@ -35,6 +35,8 @@ #include +#include "lib/utils/endian.hpp" + namespace ot { namespace Spinel { @@ -193,7 +195,10 @@ class SpiFrame * @param[in] aAcceptLen The accept length in bytes. * */ - void SetHeaderAcceptLen(uint16_t aAcceptLen) { LittleEndian::WriteUint16(aAcceptLen, mBuffer + kIndexAcceptLen); } + void SetHeaderAcceptLen(uint16_t aAcceptLen) + { + Lib::Utils::LittleEndian::WriteUint16(aAcceptLen, mBuffer + kIndexAcceptLen); + } /** * Gets the "accept len" field in the SPI frame header. @@ -201,7 +206,7 @@ class SpiFrame * @returns The accept length in bytes. * */ - uint16_t GetHeaderAcceptLen(void) const { return LittleEndian::ReadUint16(mBuffer + kIndexAcceptLen); } + uint16_t GetHeaderAcceptLen(void) const { return Lib::Utils::LittleEndian::ReadUint16(mBuffer + kIndexAcceptLen); } /** * Sets the "data len" field in the SPI frame header. @@ -211,7 +216,10 @@ class SpiFrame * @param[in] aDataLen The data length in bytes. * */ - void SetHeaderDataLen(uint16_t aDataLen) { LittleEndian::WriteUint16(aDataLen, mBuffer + kIndexDataLen); } + void SetHeaderDataLen(uint16_t aDataLen) + { + Lib::Utils::LittleEndian::WriteUint16(aDataLen, mBuffer + kIndexDataLen); + } /** * Gets the "data len" field in the SPI frame header. @@ -219,7 +227,7 @@ class SpiFrame * @returns The data length in bytes. * */ - uint16_t GetHeaderDataLen(void) const { return LittleEndian::ReadUint16(mBuffer + kIndexDataLen); } + uint16_t GetHeaderDataLen(void) const { return Lib::Utils::LittleEndian::ReadUint16(mBuffer + kIndexDataLen); } private: enum @@ -233,20 +241,6 @@ class SpiFrame kFlagPatternMask = 0x03, // Flag byte PATTERN mask. }; - class LittleEndian - { - public: - static uint16_t ReadUint16(const uint8_t *aBuffer) - { - return static_cast((aBuffer[0]) | aBuffer[1] << 8); - } - static void WriteUint16(uint16_t aValue, uint8_t *aBuffer) - { - aBuffer[0] = (aValue >> 0) & 0xff; - aBuffer[1] = (aValue >> 8) & 0xff; - } - }; - uint8_t *mBuffer; }; diff --git a/src/lib/utils/endian.hpp b/src/lib/utils/endian.hpp new file mode 100644 index 000000000..3b990535d --- /dev/null +++ b/src/lib/utils/endian.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, 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 endianness utility functions. + */ + +#ifndef LIB_UTILS_ENDIAN_HPP_ +#define LIB_UTILS_ENDIAN_HPP_ + +#include + +namespace ot { +namespace Lib { +namespace Utils { + +namespace LittleEndian { +/** + * Reads a `uint16_t` value from a given buffer assuming little-endian encoding. + * + * @param[in] aBuffer Pointer to buffer to read from. + * + * @returns The `uint16_t` value read from buffer. + * + */ +inline uint16_t ReadUint16(const uint8_t *aBuffer) { return static_cast(aBuffer[0] | (aBuffer[1] << 8)); } + +/** + * Writes a `uint16_t` value to a given buffer using little-endian encoding. + * + * @param[in] aValue The value to write to buffer. + * @param[out] aBuffer Pointer to buffer where the value will be written. + * + */ +inline void WriteUint16(uint16_t aValue, uint8_t *aBuffer) +{ + aBuffer[0] = (aValue >> 0) & 0xff; + aBuffer[1] = (aValue >> 8) & 0xff; +} + +} // namespace LittleEndian +} // namespace Utils +} // namespace Lib +} // namespace ot + +#endif // LIB_UTILS_ENDIAN_HPP_ From c6da03c378b18e02294f57426ccc84c3b4a38c46 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 30 Jul 2024 10:53:49 -0700 Subject: [PATCH 069/160] [ip6] simplify `PassToHost()` to directly use `Ip6::Header` (#10542) This commit updates `PassToHost()` to directly use the `Ip6::Header` and its `GetSource()` and `GetDestination()` methods when applying filter rules. This replaces the previous model where a `MessageInfo` was constructed from the received IPv6 header and passed to `PassToHost()`. The previous model indirectly assumed that the message was received, with the `MessageInfo` sock/peer addresses mapped accordingly. However, `HandleDatagram()` can also process messages originating from the device itself, where the notion of peer and sock addresses would be reversed. Using `Ip6::Header` directly makes the rules clearer and simplifies the logic of `PassToHost()`. Additionally, this change simplifies the code by moving the construction of `MessageInfo` to the `Receive()` method, where the message is received. --- src/core/net/icmp6.cpp | 10 +++--- src/core/net/icmp6.hpp | 4 ++- src/core/net/ip6.cpp | 70 +++++++++++++++++++----------------------- src/core/net/ip6.hpp | 16 +++++----- 4 files changed, 46 insertions(+), 54 deletions(-) diff --git a/src/core/net/icmp6.cpp b/src/core/net/icmp6.cpp index 3dba7ca96..b47e21e87 100644 --- a/src/core/net/icmp6.cpp +++ b/src/core/net/icmp6.cpp @@ -153,7 +153,7 @@ Error Icmp::HandleMessage(Message &aMessage, MessageInfo &aMessageInfo) return error; } -bool Icmp::ShouldHandleEchoRequest(const MessageInfo &aMessageInfo) +bool Icmp::ShouldHandleEchoRequest(const Address &aAddress) { bool rval = false; @@ -163,16 +163,16 @@ bool Icmp::ShouldHandleEchoRequest(const MessageInfo &aMessageInfo) rval = false; break; case OT_ICMP6_ECHO_HANDLER_UNICAST_ONLY: - rval = !aMessageInfo.GetSockAddr().IsMulticast(); + rval = !aAddress.IsMulticast(); break; case OT_ICMP6_ECHO_HANDLER_MULTICAST_ONLY: - rval = aMessageInfo.GetSockAddr().IsMulticast(); + rval = aAddress.IsMulticast(); break; case OT_ICMP6_ECHO_HANDLER_ALL: rval = true; break; case OT_ICMP6_ECHO_HANDLER_RLOC_ALOC_ONLY: - rval = aMessageInfo.GetSockAddr().GetIid().IsLocator(); + rval = aAddress.GetIid().IsLocator(); break; } @@ -187,7 +187,7 @@ Error Icmp::HandleEchoRequest(Message &aRequestMessage, const MessageInfo &aMess MessageInfo replyMessageInfo; uint16_t dataOffset; - VerifyOrExit(ShouldHandleEchoRequest(aMessageInfo)); + VerifyOrExit(ShouldHandleEchoRequest(aMessageInfo.GetSockAddr())); LogInfo("Received Echo Request"); diff --git a/src/core/net/icmp6.hpp b/src/core/net/icmp6.hpp index ae5239a99..48dc6ccf9 100644 --- a/src/core/net/icmp6.hpp +++ b/src/core/net/icmp6.hpp @@ -331,11 +331,13 @@ class Icmp : public InstanceLocator, private NonCopyable /** * Indicates whether or not the ICMPv6 Echo Request should be handled. * + * @param[in] aAddress The ICMPv6 destination IPv6 address. + * * @retval TRUE if OpenThread should respond with an ICMPv6 Echo Reply. * @retval FALSE if OpenThread should not respond with an ICMPv6 Echo Reply. * */ - bool ShouldHandleEchoRequest(const MessageInfo &aMessageInfo); + bool ShouldHandleEchoRequest(const Address &aAddress); /** * Returns the ICMPv6 Echo sequence number. diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 2d3b1922b..4ac19ec76 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -526,7 +526,7 @@ Error Ip6::ReadHopByHopHeader(const Message &aMessage, OffsetRange &aOffsetRange return error; } -Error Ip6::HandleOptions(Message &aMessage, Header &aHeader, bool &aReceive) +Error Ip6::HandleOptions(Message &aMessage, const Header &aHeader, bool &aReceive) { Error error = kErrorNone; HopByHopHeader hbhHeader; @@ -824,8 +824,7 @@ Error Ip6::HandleFragment(Message &aMessage) #endif // OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE Error Ip6::HandleExtensionHeaders(OwnedPtr &aMessagePtr, - MessageInfo &aMessageInfo, - Header &aHeader, + const Header &aHeader, uint8_t &aNextHeader, bool &aReceive) { @@ -844,7 +843,7 @@ Error Ip6::HandleExtensionHeaders(OwnedPtr &aMessagePtr, break; case kProtoFragment: - IgnoreError(PassToHost(aMessagePtr, aMessageInfo, aNextHeader, + IgnoreError(PassToHost(aMessagePtr, aHeader, aNextHeader, /* aApplyFilter */ false, aReceive, Message::kCopyToUse)); SuccessOrExit(error = HandleFragment(*aMessagePtr)); break; @@ -889,18 +888,20 @@ Error Ip6::TakeOrCopyMessagePtr(OwnedPtr &aTargetPtr, return (aTargetPtr != nullptr) ? kErrorNone : kErrorNoBufs; } -Error Ip6::HandlePayload(Header &aIp6Header, - OwnedPtr &aMessagePtr, - MessageInfo &aMessageInfo, - uint8_t aIpProto, - Message::Ownership aMessageOwnership) +Error Ip6::Receive(Header &aIp6Header, + OwnedPtr &aMessagePtr, + uint8_t aIpProto, + Message::Ownership aMessageOwnership) { -#if !OPENTHREAD_CONFIG_TCP_ENABLE - OT_UNUSED_VARIABLE(aIp6Header); -#endif - Error error = kErrorNone; OwnedPtr messagePtr; + MessageInfo messageInfo; + + messageInfo.Clear(); + messageInfo.SetPeerAddr(aIp6Header.GetSource()); + messageInfo.SetSockAddr(aIp6Header.GetDestination()); + messageInfo.SetHopLimit(aIp6Header.GetHopLimit()); + messageInfo.SetEcn(aIp6Header.GetEcn()); switch (aIpProto) { @@ -921,15 +922,15 @@ Error Ip6::HandlePayload(Header &aIp6Header, { #if OPENTHREAD_CONFIG_TCP_ENABLE case kProtoTcp: - error = mTcp.HandleMessage(aIp6Header, *messagePtr, aMessageInfo); + error = mTcp.HandleMessage(aIp6Header, *messagePtr, messageInfo); break; #endif case kProtoUdp: - error = mUdp.HandleMessage(*messagePtr, aMessageInfo); + error = mUdp.HandleMessage(*messagePtr, messageInfo); break; case kProtoIcmp6: - error = mIcmp.HandleMessage(*messagePtr, aMessageInfo); + error = mIcmp.HandleMessage(*messagePtr, messageInfo); break; default: @@ -942,7 +943,7 @@ Error Ip6::HandlePayload(Header &aIp6Header, } Error Ip6::PassToHost(OwnedPtr &aMessagePtr, - const MessageInfo &aMessageInfo, + const Header &aHeader, uint8_t aIpProto, bool aApplyFilter, bool aReceive, @@ -965,7 +966,7 @@ Error Ip6::PassToHost(OwnedPtr &aMessagePtr, // If the sender used mesh-local address as source, do not pass to // host unless this message is intended for this device itself. - if (Get().IsMeshLocalAddress(aMessageInfo.GetPeerAddr())) + if (Get().IsMeshLocalAddress(aHeader.GetSource())) { VerifyOrExit(aReceive, error = kErrorDrop); } @@ -975,7 +976,7 @@ Error Ip6::PassToHost(OwnedPtr &aMessagePtr, switch (aIpProto) { case kProtoIcmp6: - if (mIcmp.ShouldHandleEchoRequest(aMessageInfo)) + if (mIcmp.ShouldHandleEchoRequest(aHeader.GetDestination())) { Icmp::Header icmp; @@ -1045,9 +1046,8 @@ Error Ip6::PassToHost(OwnedPtr &aMessagePtr, // For a multicast packet sent from link-local/mesh-local address to scope larger // than realm-local, set the hop limit to 1 before sending to host, so this packet // will not be forwarded by host. - if (aMessageInfo.GetSockAddr().IsMulticastLargerThanRealmLocal() && - (aMessageInfo.GetPeerAddr().IsLinkLocalUnicast() || - (Get().IsMeshLocalAddress(aMessageInfo.GetPeerAddr())))) + if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal() && + (aHeader.GetSource().IsLinkLocalUnicast() || (Get().IsMeshLocalAddress(aHeader.GetSource())))) { messagePtr->Write(Header::kHopLimitFieldOffset, 1); } @@ -1099,13 +1099,12 @@ Error Ip6::SendRaw(OwnedPtr aMessagePtr) Error Ip6::HandleDatagram(OwnedPtr aMessagePtr, bool aIsReassembled) { - Error error; - MessageInfo messageInfo; - Header header; - bool receive; - bool forwardThread; - bool forwardHost; - uint8_t nextHeader; + Error error; + Header header; + bool receive; + bool forwardThread; + bool forwardHost; + uint8_t nextHeader; receive = false; forwardThread = false; @@ -1113,12 +1112,6 @@ Error Ip6::HandleDatagram(OwnedPtr aMessagePtr, bool aIsReassembled) SuccessOrExit(error = header.ParseFrom(*aMessagePtr)); - messageInfo.Clear(); - messageInfo.SetPeerAddr(header.GetSource()); - messageInfo.SetSockAddr(header.GetDestination()); - messageInfo.SetHopLimit(header.GetHopLimit()); - messageInfo.SetEcn(header.GetEcn()); - // Determine `forwardThread`, `forwardHost` and `receive` // based on the destination address. @@ -1181,7 +1174,7 @@ Error Ip6::HandleDatagram(OwnedPtr aMessagePtr, bool aIsReassembled) // Process IPv6 Extension Headers nextHeader = static_cast(header.GetNextHeader()); - SuccessOrExit(error = HandleExtensionHeaders(aMessagePtr, messageInfo, header, nextHeader, receive)); + SuccessOrExit(error = HandleExtensionHeaders(aMessagePtr, header, nextHeader, receive)); if (receive && (nextHeader == kProtoIp6)) { @@ -1209,15 +1202,14 @@ Error Ip6::HandleDatagram(OwnedPtr aMessagePtr, bool aIsReassembled) if ((forwardHost || receive) && !aIsReassembled) { - error = PassToHost(aMessagePtr, messageInfo, nextHeader, + error = PassToHost(aMessagePtr, header, nextHeader, /* aApplyFilter */ !forwardHost, receive, (receive || forwardThread) ? Message::kCopyToUse : Message::kTakeCustody); } if (receive) { - error = HandlePayload(header, aMessagePtr, messageInfo, nextHeader, - forwardThread ? Message::kCopyToUse : Message::kTakeCustody); + error = Receive(header, aMessagePtr, nextHeader, forwardThread ? Message::kCopyToUse : Message::kTakeCustody); } if (forwardThread) diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp index 03414ec32..c63c49f20 100644 --- a/src/core/net/ip6.hpp +++ b/src/core/net/ip6.hpp @@ -384,14 +384,13 @@ class Ip6 : public InstanceLocator, private NonCopyable void EnqueueDatagram(Message &aMessage); void HandleSendQueue(void); Error PassToHost(OwnedPtr &aMessagePtr, - const MessageInfo &aMessageInfo, + const Header &aHeader, uint8_t aIpProto, bool aApplyFilter, bool aReceive, Message::Ownership aMessageOwnership); Error HandleExtensionHeaders(OwnedPtr &aMessagePtr, - MessageInfo &aMessageInfo, - Header &aHeader, + const Header &aHeader, uint8_t &aNextHeader, bool &aReceive); Error FragmentDatagram(Message &aMessage, uint8_t aIpProto); @@ -407,12 +406,11 @@ class Ip6 : public InstanceLocator, private NonCopyable Error PrepareMulticastToLargerThanRealmLocal(Message &aMessage, const Header &aHeader); Error InsertMplOption(Message &aMessage, Header &aHeader); Error RemoveMplOption(Message &aMessage); - Error HandleOptions(Message &aMessage, Header &aHeader, bool &aReceive); - Error HandlePayload(Header &aIp6Header, - OwnedPtr &aMessagePtr, - MessageInfo &aMessageInfo, - uint8_t aIpProto, - Message::Ownership aMessageOwnership); + Error HandleOptions(Message &aMessage, const Header &aHeader, bool &aReceive); + Error Receive(Header &aIp6Header, + OwnedPtr &aMessagePtr, + uint8_t aIpProto, + Message::Ownership aMessageOwnership); bool IsOnLink(const Address &aAddress) const; Error RouteLookup(const Address &aSource, const Address &aDestination) const; #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE From 736bd18652f220cdb634cc431132ec786b1fec14 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 30 Jul 2024 10:54:21 -0700 Subject: [PATCH 070/160] [ip6] remove UDP port check for messages with `HostTrusted` origin (#10546) This commit removes the checks previously performed on messages with the origin `HostTrusted` that are to be forwarded to the Thread mesh. This origin is used for messages generated by the OpenThread stack itself. These checks were unnecessarily restricting such messages from using Thread Control UDP port numbers (like TMF, MLE, etc.). The additional check `!IsLoopbackToHostAllowed()` (which is set to `true` by default on such messages) bypassed the entire block, preventing any functional impact. Recent related changes (in #9437) added similar guard checks for messages with `HostUntrusted` origins. --- src/core/net/ip6.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 4ac19ec76..0941a4abf 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -1263,22 +1263,6 @@ Error Ip6::HandleDatagram(OwnedPtr aMessagePtr, bool aIsReassembled) } } -#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE - if (aMessagePtr->IsOriginHostTrusted() && !aMessagePtr->IsLoopbackToHostAllowed() && (nextHeader == kProtoUdp)) - { - uint16_t destPort; - - SuccessOrExit( - error = aMessagePtr->Read(aMessagePtr->GetOffset() + Udp::Header::kDestPortFieldOffset, destPort)); - destPort = BigEndian::HostSwap16(destPort); - - if (nextHeader == kProtoUdp) - { - VerifyOrExit(Get().ShouldUsePlatformUdp(destPort), error = kErrorDrop); - } - } -#endif - #if OPENTHREAD_CONFIG_MULTI_RADIO // Since the message will be forwarded, we clear the radio // type on the message to allow the radio type for tx to be From 82cee34a3fb746ecae16cf01dba53804cc4dfe1f Mon Sep 17 00:00:00 2001 From: Li Cao Date: Wed, 31 Jul 2024 02:01:46 +0800 Subject: [PATCH 071/160] [api] fix dnssd platform API doc (#10556) --- include/openthread/instance.h | 2 +- include/openthread/platform/dnssd.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index d00925393..677ddc5f6 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (430) +#define OPENTHREAD_API_VERSION (431) /** * @addtogroup api-instance diff --git a/include/openthread/platform/dnssd.h b/include/openthread/platform/dnssd.h index 894dff561..d951db585 100644 --- a/include/openthread/platform/dnssd.h +++ b/include/openthread/platform/dnssd.h @@ -372,7 +372,7 @@ void otPlatDnssdUnregisterHost(otInstance *aInstance, * a result, these changes always need to be synced on the infrastructure DNS-SD module. * * @param[in] aInstance The OpenThread instance. - * @param[in] aHost Information about the key record to register. + * @param[in] aKey Information about the key record to register. * @param[in] aRequestId The ID associated with this request. * @param[in] aCallback The callback function pointer to report the outcome (may be NULL if no callback needed). * From 3c2d4487d8dce8720729d861e08f1ef0a3a0c931 Mon Sep 17 00:00:00 2001 From: Suvesh Pratapa <66088488+suveshpratapa@users.noreply.github.com> Date: Tue, 30 Jul 2024 15:03:43 -0400 Subject: [PATCH 072/160] [cmake] add option for `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE` (#10545) This makes it easier to configure this option for testing on OTBR/RCP. --- etc/cmake/options.cmake | 1 + src/core/config/network_diagnostic.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index c756e9ea7..8825fb14d 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -229,6 +229,7 @@ ot_option(OT_NAT64_TRANSLATOR OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE "NAT64 t ot_option(OT_NEIGHBOR_DISCOVERY_AGENT OPENTHREAD_CONFIG_NEIGHBOR_DISCOVERY_AGENT_ENABLE "neighbor discovery agent") ot_option(OT_NETDATA_PUBLISHER OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE "Network Data publisher") ot_option(OT_NETDIAG_CLIENT OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE "Network Diagnostic client") +ot_option(OT_NETDIAG_VENDOR_INFO OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE "Allow setting vendor info at runtime") ot_option(OT_OPERATIONAL_DATASET_AUTO_INIT OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT "operational dataset auto init") ot_option(OT_OTNS OPENTHREAD_CONFIG_OTNS_ENABLE "OTNS") ot_option(OT_PING_SENDER OPENTHREAD_CONFIG_PING_SENDER_ENABLE "ping sender" ${OT_APP_CLI}) diff --git a/src/core/config/network_diagnostic.h b/src/core/config/network_diagnostic.h index 6f670efc7..32f23c293 100644 --- a/src/core/config/network_diagnostic.h +++ b/src/core/config/network_diagnostic.h @@ -97,9 +97,11 @@ * config can be used to add API to change Vendor Name, Model, and SW Version at run-time. In this case, the strings in * `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_*` are treated as the default values (used when OT stack is initialized). * + * Enabled by default for reference devices, when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is defined. + * */ #ifndef OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE -#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE 0 +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE #endif /** From 5493815126897db1dc2ad493543aa992d1944178 Mon Sep 17 00:00:00 2001 From: Suvesh Pratapa <66088488+suveshpratapa@users.noreply.github.com> Date: Tue, 30 Jul 2024 16:48:14 -0400 Subject: [PATCH 073/160] [ncp] initialize diag output callback per instance in `NcpBase` (#10547) This fixes a bug where diag get output callback was not set properly if we used instance with iid > 1, leading to failure output. --- src/ncp/ncp_base.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index fc6b56529..a29da84e5 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -258,6 +258,9 @@ NcpBase::NcpBase(Instance **aInstances, uint8_t aCount) OT_ASSERT(i + skipped <= SPINEL_HEADER_IID_MAX); mInstances[i + skipped] = aInstances[i]; +#if OPENTHREAD_CONFIG_DIAG_ENABLE + otDiagSetOutputCallback(mInstances[i + skipped], &NcpBase::HandleDiagOutput_Jump, this); +#endif } } #endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO From 6a8d4ea2a518305cfbde4a68e8f10bb0a83d3703 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 31 Jul 2024 08:32:55 -0700 Subject: [PATCH 074/160] [ip6] avoid re-reading IPv6 header when updating BR counters (#10558) This commit updates the code to avoid re-reading the IPv6 header when updating BR-related counters in `Ip6::PassToHost()`. --- src/core/net/ip6.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 0941a4abf..1ca4f4b0f 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -1032,12 +1032,7 @@ Error Ip6::PassToHost(OwnedPtr &aMessagePtr, #endif #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE - { - Header header; - - IgnoreError(header.ParseFrom(*messagePtr)); - UpdateBorderRoutingCounters(header, messagePtr->GetLength(), /* aIsInbound */ false); - } + UpdateBorderRoutingCounters(aHeader, messagePtr->GetLength(), /* aIsInbound */ false); #endif #if OPENTHREAD_CONFIG_IP6_RESTRICT_FORWARDING_LARGER_SCOPE_MCAST_WITH_LOCAL_SRC From a759a4a09f9fe5bdf757c470aa86dffdc7a349f1 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 31 Jul 2024 14:54:01 -0700 Subject: [PATCH 075/160] [ip6] update `PassToHost` to use `aReceive` for applying RX filter (#10541) This commit updates `PassToHost()`, removing the `aApplyFilter` input and using `aReceive` to determine whether or not to apply the RX filter. The filter serves multiple purposes: - Ensure Thread control UDP traffic (e.g., MLE, TMF, etc.) is filtered out when delivering IPv6 datagrams to the host. - Apply ICMPv6 echo requests filtering, based on the configured mode (whether the OT stack should respond to pings for specific address types or they should be passed to the next layer). - Filter TCP traffic when the native OT TCP stack is used. In all cases, the filter should be applied only if the IPv6 message is supposed to be received by the device itself (i.e., the destination matches the device's address), which is indicated by the `aReceive` input. The `aApplyFilter` input was previously set to `!forwardHost`, indirectly assuming that `forwardHost` and `receive` would be exclusive. While this remains true for unicast destinations, recent changes updated the code so that all multicast traffic is forwarded to the host regardless of whether it is also marked for `receive`. This caused multicast MLE/TMF traffic to be passed to the host even though it should be filtered. This issue is now addressed by this change. This commit also ensures that RX filter rules are consistently applied to fragmented IPv6 messages. Previously, the filter would not apply when fragmented IPv6 messages were passed to the host, but with this change, the filter is applied when the message should be received by the device. --- src/core/net/ip6.cpp | 9 +++------ src/core/net/ip6.hpp | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 1ca4f4b0f..7cbdf059b 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -843,8 +843,7 @@ Error Ip6::HandleExtensionHeaders(OwnedPtr &aMessagePtr, break; case kProtoFragment: - IgnoreError(PassToHost(aMessagePtr, aHeader, aNextHeader, - /* aApplyFilter */ false, aReceive, Message::kCopyToUse)); + IgnoreError(PassToHost(aMessagePtr, aHeader, aNextHeader, aReceive, Message::kCopyToUse)); SuccessOrExit(error = HandleFragment(*aMessagePtr)); break; @@ -945,7 +944,6 @@ Error Ip6::Receive(Header &aIp6Header, Error Ip6::PassToHost(OwnedPtr &aMessagePtr, const Header &aHeader, uint8_t aIpProto, - bool aApplyFilter, bool aReceive, Message::Ownership aMessageOwnership) { @@ -971,7 +969,7 @@ Error Ip6::PassToHost(OwnedPtr &aMessagePtr, VerifyOrExit(aReceive, error = kErrorDrop); } - if (mIsReceiveIp6FilterEnabled && aApplyFilter) + if (mIsReceiveIp6FilterEnabled && aReceive) { switch (aIpProto) { @@ -1197,8 +1195,7 @@ Error Ip6::HandleDatagram(OwnedPtr aMessagePtr, bool aIsReassembled) if ((forwardHost || receive) && !aIsReassembled) { - error = PassToHost(aMessagePtr, header, nextHeader, - /* aApplyFilter */ !forwardHost, receive, + error = PassToHost(aMessagePtr, header, nextHeader, receive, (receive || forwardThread) ? Message::kCopyToUse : Message::kTakeCustody); } diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp index c63c49f20..48616ed5e 100644 --- a/src/core/net/ip6.hpp +++ b/src/core/net/ip6.hpp @@ -386,7 +386,6 @@ class Ip6 : public InstanceLocator, private NonCopyable Error PassToHost(OwnedPtr &aMessagePtr, const Header &aHeader, uint8_t aIpProto, - bool aApplyFilter, bool aReceive, Message::Ownership aMessageOwnership); Error HandleExtensionHeaders(OwnedPtr &aMessagePtr, From cbd35e34a7fa856aaf177629edc811d50408bfa8 Mon Sep 17 00:00:00 2001 From: Rongli Sun Date: Thu, 1 Aug 2024 23:15:10 +0800 Subject: [PATCH 076/160] [dua] resume registration when fails to become router (#10559) --- src/core/thread/dua_manager.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/thread/dua_manager.cpp b/src/core/thread/dua_manager.cpp index 808ed9250..d8dc2068a 100644 --- a/src/core/thread/dua_manager.cpp +++ b/src/core/thread/dua_manager.cpp @@ -445,7 +445,11 @@ void DuaManager::PerformNextRegistration(void) // Only send DUA.req when necessary #if OPENTHREAD_CONFIG_DUA_ENABLE #if OPENTHREAD_FTD - VerifyOrExit(mle.IsRouterOrLeader() || !mle.IsExpectedToBecomeRouterSoon(), error = kErrorInvalidState); + if (!mle.IsRouterOrLeader() && mle.IsExpectedToBecomeRouterSoon()) + { + UpdateRegistrationDelay(mle.GetRouterRoleTransitionTimeout() + kNewRouterRegistrationDelay + 1); + ExitNow(error = kErrorInvalidState); + } #endif VerifyOrExit(mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1(), error = kErrorInvalidState); #endif // OPENTHREAD_CONFIG_DUA_ENABLE From fba922ebe2b76ddac3f33454d284da4c6381db86 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Thu, 1 Aug 2024 23:27:35 +0800 Subject: [PATCH 077/160] [test] test the Thread network 1 hop throughput (#10552) This commit adds a test case to the `cp-tests` to test the Thread network 1 hop throughput. --- tools/cp-caps/README.md | 12 +++++- tools/cp-caps/rcp_caps_test.py | 72 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/tools/cp-caps/README.md b/tools/cp-caps/README.md index 1614f3e1f..48ec8883b 100644 --- a/tools/cp-caps/README.md +++ b/tools/cp-caps/README.md @@ -54,7 +54,7 @@ Show help info. ```bash $ python3 ./tools/cp-caps/rcp_caps_test.py -h -usage: rcp_caps_test.py [-h] [-c] [-d] [-p] [-v] +usage: rcp_caps_test.py [-h] [-c] [-d] [-p] [-t] [-v] This script is used for testing RCP capabilities. @@ -63,6 +63,7 @@ options: -c, --csl test whether the RCP supports CSL transmitter -d, --diag-commands test whether the RCP supports all diag commands -p, --data-poll test whether the RCP supports data poll + -t, --throughput test the Thread network 1-hop throughput -v, --verbose output verbose information Device Interfaces: @@ -144,3 +145,12 @@ $ DUT_ADB_USB=1269UCKFZTAM95OR REF_CLI_SERIAL=/dev/ttyACM0 python3 ./tools/cp-ca Data Poll Parent ----------------------------------------- OK Data Poll Child ------------------------------------------ OK ``` + +### Test Throughput + +The parameter `-t` or `--throughput` starts to test the Thread network 1-hop throughput of the DUT. + +```bash +$ DUT_ADB_USB=1269UCKFZTAM95OR REF_ADB_USB=44061HFAG01AQK python3 ./tools/cp-caps/rcp_caps_test.py -t +Throughput ----------------------------------------------- 75.6 Kbits/sec +``` diff --git a/tools/cp-caps/rcp_caps_test.py b/tools/cp-caps/rcp_caps_test.py index f40e7e2c7..432777859 100644 --- a/tools/cp-caps/rcp_caps_test.py +++ b/tools/cp-caps/rcp_caps_test.py @@ -32,6 +32,7 @@ import os import sys import textwrap +import threading from typing import List @@ -80,17 +81,77 @@ def test_diag_commands(self): self.__dut.diag_stop() def test_csl(self): + """Test whether the DUT supports CSL transmitter.""" self.__dataset = self.__get_default_dataset() self.__test_csl_transmitter() def test_data_poll(self): + """Test whether the DUT supports data poll parent and child.""" self.__dataset = self.__get_default_dataset() self.__test_data_poll_parent() self.__test_data_poll_child() + def test_throughput(self): + """Test Thread network 1 hop throughput.""" + if not self.__dut.support_iperf3(): + print("The DUT doesn't support the tool iperf3") + return + + if not self.__ref.support_iperf3(): + print("The reference device doesn't support the tool iperf3") + return + + bitrate = 90000 + length = 1232 + transmit_time = 30 + max_wait_time = 30 + timeout = transmit_time + max_wait_time + + self.__dut.factory_reset() + self.__ref.factory_reset() + + dataset = self.__get_default_dataset() + + self.__dut.join(dataset) + self.__dut.wait_for('state', 'leader') + + self.__ref.set_router_selection_jitter(1) + self.__ref.join(dataset) + self.__ref.wait_for('state', ['child', 'router']) + + ref_mleid = self.__ref.get_ipaddr_mleid() + + ref_iperf3_server = threading.Thread(target=self.__ref_iperf3_server_task, + args=(ref_mleid, timeout), + daemon=True) + ref_iperf3_server.start() + self.__dut.wait(1) + + results = self.__dut.iperf3_client(host=ref_mleid, bitrate=bitrate, transmit_time=transmit_time, length=length) + ref_iperf3_server.join() + + if not results: + print('Failed to run the iperf3') + return + + self.__output_format_string('Throughput', self.__bitrate_to_string(results['receiver']['bitrate'])) + # # Private methods # + def __ref_iperf3_server_task(self, bind_address: str, timeout: int): + self.__ref.iperf3_server(bind_address, timeout=timeout) + + def __bitrate_to_string(self, bitrate: float): + units = ['bits/sec', 'Kbits/sec', 'Mbits/sec', 'Gbits/sec', 'Tbits/sec'] + unit_index = 0 + + while bitrate >= 1000 and unit_index < len(units) - 1: + bitrate /= 1000 + unit_index += 1 + + return f'{bitrate:.2f} {units[unit_index]}' + def __get_default_dataset(self): return self.__dut.create_dataset(channel=20, network_key='00112233445566778899aabbccddcafe') @@ -490,6 +551,14 @@ def parse_arguments(): help='test whether the RCP supports data poll', ) + parser.add_argument( + '-t', + '--throughput', + action='store_true', + default=False, + help='test Thread network 1-hop throughput', + ) + parser.add_argument( '-v', '--verbose', @@ -519,6 +588,9 @@ def main(): if arguments.data_poll is True: rcp_caps.test_data_poll() + if arguments.throughput: + rcp_caps.test_throughput() + if __name__ == '__main__': main() From 03113e8502ab6153a5f320f00b6f60685fdfc6ef Mon Sep 17 00:00:00 2001 From: Handa Wang <7058128+superwhd@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:33:19 +0800 Subject: [PATCH 078/160] [nat64] enhancements for `Translator::UpdateState` (#10564) This commit applies following enhancements to the `Translator::UpdateState`: - Remove the `aAlwaysNotify` parameter to simplify its logic. - Fix an issue that `mState` may not be updated when setting a CIDR. --- src/core/net/nat64_translator.cpp | 17 ++++++----------- src/core/net/nat64_translator.hpp | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/core/net/nat64_translator.cpp b/src/core/net/nat64_translator.cpp index 5afe9bfb0..da1e6f870 100644 --- a/src/core/net/nat64_translator.cpp +++ b/src/core/net/nat64_translator.cpp @@ -509,8 +509,10 @@ Error Translator::SetIp4Cidr(const Ip4::Cidr &aCidr) ToUlong(numberOfHosts)); mIp4Cidr = aCidr; - // Always notify the platform when the CIDR is changed. - UpdateState(true /* aAlwaysNotify */); + UpdateState(); + + // Notify the platform when the CIDR is changed. + Get().Signal(kEventNat64TranslatorStateChanged); exit: return err; @@ -634,7 +636,7 @@ void Translator::ProtocolCounters::Count4To6Packet(uint8_t aProtocol, uint64_t a mTotal.m4To6Bytes += aPacketSize; } -void Translator::UpdateState(bool aAlwaysNotify) +void Translator::UpdateState(void) { State newState; @@ -654,14 +656,7 @@ void Translator::UpdateState(bool aAlwaysNotify) newState = kStateDisabled; } - if (aAlwaysNotify) - { - Get().Signal(kEventNat64TranslatorStateChanged); - } - else - { - SuccessOrExit(Get().Update(mState, newState, kEventNat64TranslatorStateChanged)); - } + SuccessOrExit(Get().Update(mState, newState, kEventNat64TranslatorStateChanged)); LogInfo("NAT64 translator is now %s", StateToString(mState)); exit: diff --git a/src/core/net/nat64_translator.hpp b/src/core/net/nat64_translator.hpp index bd29ff690..2335e7a9f 100644 --- a/src/core/net/nat64_translator.hpp +++ b/src/core/net/nat64_translator.hpp @@ -380,7 +380,7 @@ class Translator : public InstanceLocator, private NonCopyable using MappingTimer = TimerMilliIn; - void UpdateState(bool aAlwaysNotify = false); + void UpdateState(void); bool mEnabled; State mState; From e219fe92f15097b40f04e846ff4d56dea3643395 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Sat, 3 Aug 2024 00:50:45 +0800 Subject: [PATCH 079/160] [mac] support frames without sequence number (#10544) This commit adds the capability to support frames without sequence number. --- examples/platforms/simulation/radio.c | 6 ++- examples/platforms/utils/mac_frame.cpp | 16 ++++++- examples/platforms/utils/mac_frame.h | 6 ++- src/core/mac/mac_frame.cpp | 55 +++++++++++++++++++---- src/core/mac/mac_frame.hpp | 62 ++++++++++++++++++-------- tests/fuzz/fuzzer_platform.cpp | 21 +++++++-- tests/unit/test_mac_frame.cpp | 30 ++++++++++--- 7 files changed, 155 insertions(+), 41 deletions(-) diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index 960d8c66c..9e547e1c2 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -606,7 +606,11 @@ static void radioReceive(otInstance *aInstance) { if (otMacFrameIsAckRequested(&sTransmitFrame)) { - isTxDone = isAck && otMacFrameGetSequence(&sReceiveFrame) == otMacFrameGetSequence(&sTransmitFrame); + uint8_t rxSeq; + uint8_t txSeq; + + isTxDone = isAck && otMacFrameGetSequence(&sReceiveFrame, &rxSeq) == OT_ERROR_NONE && + otMacFrameGetSequence(&sTransmitFrame, &txSeq) == OT_ERROR_NONE && rxSeq == txSeq; } #if OPENTHREAD_SIMULATION_VIRTUAL_TIME // Simulate tx done when receiving the echo frame. diff --git a/examples/platforms/utils/mac_frame.cpp b/examples/platforms/utils/mac_frame.cpp index 36a1c975c..98ea21736 100644 --- a/examples/platforms/utils/mac_frame.cpp +++ b/examples/platforms/utils/mac_frame.cpp @@ -139,9 +139,21 @@ otError otMacFrameGetDstAddr(const otRadioFrame *aFrame, otMacAddress *aMacAddre return error; } -uint8_t otMacFrameGetSequence(const otRadioFrame *aFrame) +otError otMacFrameGetSequence(const otRadioFrame *aFrame, uint8_t *aSequence) { - return static_cast(aFrame)->GetSequence(); + otError error; + + if (static_cast(aFrame)->IsSequencePresent()) + { + *aSequence = static_cast(aFrame)->GetSequence(); + error = kErrorNone; + } + else + { + error = kErrorParse; + } + + return error; } void otMacFrameProcessTransmitAesCcm(otRadioFrame *aFrame, const otExtAddress *aExtAddress) diff --git a/examples/platforms/utils/mac_frame.h b/examples/platforms/utils/mac_frame.h index 5fba6ecea..adbed43f2 100644 --- a/examples/platforms/utils/mac_frame.h +++ b/examples/platforms/utils/mac_frame.h @@ -169,11 +169,13 @@ otError otMacFrameGetDstAddr(const otRadioFrame *aFrame, otMacAddress *aMacAddre * Get the sequence of @p aFrame. * * @param[in] aFrame A pointer to the frame. + * @param[out] aSequence A pointer to the sequence. * - * @returns The sequence of the frame. + * @retval OT_ERROR_NONE Successfully got the sequence. + * @retval OT_ERROR_PARSE Failed to parse the sequence. * */ -uint8_t otMacFrameGetSequence(const otRadioFrame *aFrame); +otError otMacFrameGetSequence(const otRadioFrame *aFrame, uint8_t *aSequence); /** * Performs AES CCM on the frame which is going to be sent. diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 9946bb2c7..035319b27 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -59,7 +59,8 @@ void Frame::InitMacHeader(Type aType, const Addresses &aAddrs, const PanIds &aPanIds, SecurityLevel aSecurityLevel, - KeyIdMode aKeyIdMode) + KeyIdMode aKeyIdMode, + bool aSuppressSequence) { uint16_t fcf; FrameBuilder builder; @@ -124,6 +125,9 @@ void Frame::InitMacHeader(Type aType, { fcf |= kFcfPanidCompression; } + + // Sequence Number Suppression bit was reserved, and must not be set on initialization. + OT_ASSERT(!aSuppressSequence); break; case kVersion2015: @@ -194,9 +198,18 @@ void Frame::InitMacHeader(Type aType, break; } + if (aSuppressSequence) + { + fcf |= kFcfSequenceSupression; + } + builder.Init(mPsdu, GetMtu()); IgnoreError(builder.AppendLittleEndianUint16(fcf)); - IgnoreError(builder.AppendUint8(0)); // Seq number + + if (!IsSequenceSuppressed(fcf)) + { + IgnoreError(builder.AppendUint8(0)); // Seq number + } if (IsDstPanIdPresent(fcf)) { @@ -294,7 +307,7 @@ uint8_t Frame::FindDstPanIdIndex(void) const VerifyOrExit(IsDstPanIdPresent(), index = kInvalidIndex); - index = kFcfSize + kDsnSize; + index = kFcfSize + GetSeqNumSize(); exit: return index; @@ -373,7 +386,22 @@ void Frame::SetDstPanId(PanId aPanId) LittleEndian::WriteUint16(aPanId, &mPsdu[index]); } -uint8_t Frame::FindDstAddrIndex(void) const { return kFcfSize + kDsnSize + (IsDstPanIdPresent() ? sizeof(PanId) : 0); } +uint8_t Frame::GetSequence(void) const +{ + OT_ASSERT(IsSequencePresent()); + return GetPsdu()[kSequenceIndex]; +} + +void Frame::SetSequence(uint8_t aSequence) +{ + OT_ASSERT(IsSequencePresent()); + GetPsdu()[kSequenceIndex] = aSequence; +} + +uint8_t Frame::FindDstAddrIndex(void) const +{ + return kFcfSize + GetSeqNumSize() + (IsDstPanIdPresent() ? sizeof(PanId) : 0); +} Error Frame::GetDstAddr(Address &aAddress) const { @@ -442,7 +470,7 @@ uint8_t Frame::FindSrcPanIdIndex(void) const VerifyOrExit(IsSrcPanIdPresent(), index = kInvalidIndex); - index += kFcfSize + kDsnSize; + index += kFcfSize + GetSeqNumSize(); if (IsDstPanIdPresent(fcf)) { @@ -533,7 +561,7 @@ uint8_t Frame::FindSrcAddrIndex(void) const uint8_t index = 0; uint16_t fcf = GetFrameControlField(); - index += kFcfSize + kDsnSize; + index += kFcfSize + GetSeqNumSize(); if (IsDstPanIdPresent(fcf)) { @@ -941,7 +969,9 @@ uint8_t Frame::SkipAddrFieldIndex(void) const { uint8_t index; - VerifyOrExit(kFcfSize + kDsnSize + GetFcsSize() <= mLength, index = kInvalidIndex); + VerifyOrExit(kFcfSize + GetFcsSize() <= mLength, index = kInvalidIndex); + + VerifyOrExit(!IsSequencePresent() || kFcfSize + kDsnSize + GetFcsSize() <= mLength, index = kInvalidIndex); index = CalculateAddrFieldSize(GetFrameControlField()); @@ -951,7 +981,7 @@ uint8_t Frame::SkipAddrFieldIndex(void) const uint8_t Frame::CalculateAddrFieldSize(uint16_t aFcf) { - uint8_t size = kFcfSize + kDsnSize; + uint8_t size = kFcfSize + GetSeqNumSize(aFcf); // This static method calculates the size (number of bytes) of // Address header field for a given Frame Control `aFcf` value. @@ -1538,7 +1568,14 @@ Frame::InfoString Frame::ToInfoString(void) const uint8_t commandId, type; Address src, dst; - string.Append("len:%d, seqnum:%d, type:", mLength, GetSequence()); + if (IsSequencePresent()) + { + string.Append("len:%d, seqnum:%d, type:", mLength, GetSequence()); + } + else + { + string.Append("len:%d, type:", mLength); + } type = GetType(); diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index a515ddc13..eef63aae4 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -386,6 +386,7 @@ class Frame : public otRadioFrame * @param[in] aPanIds Source and destination PAN IDs. * @param[in] aSecurityLevel Frame security level. * @param[in] aKeyIdMode Frame security key ID mode. + * @param[in] aSuppressSequence Whether to suppress sequence number. * */ void InitMacHeader(Type aType, @@ -393,7 +394,8 @@ class Frame : public otRadioFrame const Addresses &aAddrs, const PanIds &aPanIds, SecurityLevel aSecurityLevel, - KeyIdMode aKeyIdMode = kKeyIdMode0); + KeyIdMode aKeyIdMode = kKeyIdMode0, + bool aSuppressSequence = false); /** * Validates the frame. @@ -512,7 +514,7 @@ class Frame : public otRadioFrame * @returns The Sequence Number value. * */ - uint8_t GetSequence(void) const { return GetPsdu()[kSequenceIndex]; } + uint8_t GetSequence(void) const; /** * Sets the Sequence Number value. @@ -520,7 +522,24 @@ class Frame : public otRadioFrame * @param[in] aSequence The Sequence Number value. * */ - void SetSequence(uint8_t aSequence) { GetPsdu()[kSequenceIndex] = aSequence; } + void SetSequence(uint8_t aSequence); + + /** + * Indicates whether or not the Sequence Number is present. + * + * @returns TRUE if the Sequence Number is present, FALSE otherwise. + * + */ + uint8_t IsSequencePresent(void) const { return !IsSequenceSuppressed(GetFrameControlField()); } + + /** + * Get the size of the sequence number. + * + * @retval 0 The size of sequence number is 0, indicating it's not present. + * @retval 1 The size of sequence number is 1, indicating it's present. + * + */ + uint8_t GetSeqNumSize(void) const { return GetSeqNumSize(GetFrameControlField()); } /** * Indicates whether or not the Destination PAN ID is present. @@ -1089,21 +1108,22 @@ class Frame : public otRadioFrame static constexpr uint8_t kCommandIdSize = sizeof(uint8_t); static constexpr uint8_t kKeyIndexSize = sizeof(uint8_t); - static constexpr uint16_t kFcfFrameTypeMask = 7 << 0; - static constexpr uint16_t kFcfSecurityEnabled = 1 << 3; - static constexpr uint16_t kFcfFramePending = 1 << 4; - static constexpr uint16_t kFcfAckRequest = 1 << 5; - static constexpr uint16_t kFcfPanidCompression = 1 << 6; - static constexpr uint16_t kFcfIePresent = 1 << 9; - static constexpr uint16_t kFcfDstAddrNone = 0 << 10; - static constexpr uint16_t kFcfDstAddrShort = 2 << 10; - static constexpr uint16_t kFcfDstAddrExt = 3 << 10; - static constexpr uint16_t kFcfDstAddrMask = 3 << 10; - static constexpr uint16_t kFcfFrameVersionMask = 3 << 12; - static constexpr uint16_t kFcfSrcAddrNone = 0 << 14; - static constexpr uint16_t kFcfSrcAddrShort = 2 << 14; - static constexpr uint16_t kFcfSrcAddrExt = 3 << 14; - static constexpr uint16_t kFcfSrcAddrMask = 3 << 14; + static constexpr uint16_t kFcfFrameTypeMask = 7 << 0; + static constexpr uint16_t kFcfSecurityEnabled = 1 << 3; + static constexpr uint16_t kFcfFramePending = 1 << 4; + static constexpr uint16_t kFcfAckRequest = 1 << 5; + static constexpr uint16_t kFcfPanidCompression = 1 << 6; + static constexpr uint16_t kFcfSequenceSupression = 1 << 8; + static constexpr uint16_t kFcfIePresent = 1 << 9; + static constexpr uint16_t kFcfDstAddrNone = 0 << 10; + static constexpr uint16_t kFcfDstAddrShort = 2 << 10; + static constexpr uint16_t kFcfDstAddrExt = 3 << 10; + static constexpr uint16_t kFcfDstAddrMask = 3 << 10; + static constexpr uint16_t kFcfFrameVersionMask = 3 << 12; + static constexpr uint16_t kFcfSrcAddrNone = 0 << 14; + static constexpr uint16_t kFcfSrcAddrShort = 2 << 14; + static constexpr uint16_t kFcfSrcAddrExt = 3 << 14; + static constexpr uint16_t kFcfSrcAddrMask = 3 << 14; static constexpr uint8_t kSecLevelMask = 7 << 0; static constexpr uint8_t kKeyIdModeMask = 3 << 3; @@ -1144,6 +1164,12 @@ class Frame : public otRadioFrame static bool IsDstAddrPresent(uint16_t aFcf) { return (aFcf & kFcfDstAddrMask) != kFcfDstAddrNone; } static bool IsDstPanIdPresent(uint16_t aFcf); + static bool IsSequenceSuppressed(uint16_t aFcf) + { + return (aFcf & (kFcfSequenceSupression | kFcfFrameVersionMask)) == (kFcfSequenceSupression | kVersion2015); + } + static uint8_t GetSeqNumSize(uint16_t aFcf) { return !IsSequenceSuppressed(aFcf) ? kDsnSize : 0; } + static bool IsSrcAddrPresent(uint16_t aFcf) { return (aFcf & kFcfSrcAddrMask) != kFcfSrcAddrNone; } static bool IsSrcPanIdPresent(uint16_t aFcf); static bool IsVersion2015(uint16_t aFcf) { return (aFcf & kFcfFrameVersionMask) == kVersion2015; } diff --git a/tests/fuzz/fuzzer_platform.cpp b/tests/fuzz/fuzzer_platform.cpp index 524760954..906f86b09 100644 --- a/tests/fuzz/fuzzer_platform.cpp +++ b/tests/fuzz/fuzzer_platform.cpp @@ -77,9 +77,21 @@ bool otMacFrameIsAckRequested(const otRadioFrame *aFrame) return static_cast(aFrame)->GetAckRequest(); } -uint8_t otMacFrameGetSequence(const otRadioFrame *aFrame) +otError otMacFrameGetSequence(const otRadioFrame *aFrame, uint8_t *aSequence) { - return static_cast(aFrame)->GetSequence(); + otError error; + + if (static_cast(aFrame)->IsSequencePresent()) + { + *aSequence = static_cast(aFrame)->GetSequence(); + error = kErrorNone; + } + else + { + error = kErrorParse; + } + + return error; } void FuzzerPlatformInit(void) @@ -101,10 +113,13 @@ void FuzzerPlatformProcess(otInstance *aInstance) if (otMacFrameIsAckRequested(&sRadioTransmitFrame)) { + otError error; + sRadioAckFrame.mLength = IEEE802154_ACK_LENGTH; sRadioAckFrame.mPsdu[0] = IEEE802154_FRAME_TYPE_ACK; sRadioAckFrame.mPsdu[1] = 0; - sRadioAckFrame.mPsdu[2] = otMacFrameGetSequence(&sRadioTransmitFrame); + error = otMacFrameGetSequence(&sRadioTransmitFrame, &sRadioAckFrame.mPsdu[2]); + OT_ASSERT(error == OT_ERROR_NONE); sRadioAckFrame.mChannel = sRadioTransmitFrame.mChannel; otPlatRadioTxDone(aInstance, &sRadioTransmitFrame, &sRadioAckFrame, OT_ERROR_NONE); diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp index 722b41aa4..cf7e29603 100644 --- a/tests/unit/test_mac_frame.cpp +++ b/tests/unit/test_mac_frame.cpp @@ -205,6 +205,7 @@ void TestMacHeader(void) Mac::Frame::KeyIdMode mKeyIdMode; uint8_t mHeaderLength; uint8_t mFooterLength; + bool mSuppressSequence; }; static constexpr Mac::Frame::Version kVer2006 = Mac::Frame::kVersion2006; @@ -265,6 +266,7 @@ void TestMacHeader(void) {kVer2015, kExtdAddr, kNoPanId, kNoneAddr, kNoPanId, kMic32, kModeId1, 17, 6}, {kVer2015, kExtdAddr, kNoPanId, kExtdAddr, kNoPanId, kNoSec, kModeId1, 19, 2}, {kVer2015, kExtdAddr, kNoPanId, kExtdAddr, kNoPanId, kMic32, kModeId1, 25, 6}, + {kVer2015, kExtdAddr, kNoPanId, kExtdAddr, kNoPanId, kMic32, kModeId1, 24, 6, true}, }; const uint16_t kPanId1 = 0xbaba; @@ -285,7 +287,9 @@ void TestMacHeader(void) for (const TestCase &testCase : kTestCases) { - uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE]; + uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE]; + uint8_t offset; + Mac::TxFrame frame; Mac::Addresses addresses; Mac::Address address; @@ -352,7 +356,7 @@ void TestMacHeader(void) } frame.InitMacHeader(Mac::Frame::kTypeData, testCase.mVersion, addresses, panIds, testCase.mSecurity, - testCase.mKeyIdMode); + testCase.mKeyIdMode, testCase.mSuppressSequence); VerifyOrQuit(frame.GetHeaderLength() == testCase.mHeaderLength); VerifyOrQuit(frame.GetFooterLength() == testCase.mFooterLength); @@ -401,11 +405,20 @@ void TestMacHeader(void) VerifyOrQuit(keyIdMode == testCase.mKeyIdMode); } - snprintf(string, sizeof(string), "\nver:%s, src[addr:%s, pan:%s], dst[addr:%s, pan:%s], sec:%s", - (testCase.mVersion == kVer2006) ? "2006" : "2015", kAddrTypeStrings[testCase.mSrcAddrType], - kPanIdModeStrings[testCase.mSrcPanIdMode], kAddrTypeStrings[testCase.mDstAddrType], - kPanIdModeStrings[testCase.mDstPanIdMode], testCase.mSecurity == kNoSec ? "no" : "mic32"); + offset = snprintf(string, sizeof(string), "\nver:%s, src[addr:%s, pan:%s], dst[addr:%s, pan:%s], sec:%s", + (testCase.mVersion == kVer2006) ? "2006" : "2015", kAddrTypeStrings[testCase.mSrcAddrType], + kPanIdModeStrings[testCase.mSrcPanIdMode], kAddrTypeStrings[testCase.mDstAddrType], + kPanIdModeStrings[testCase.mDstPanIdMode], testCase.mSecurity == kNoSec ? "no" : "mic32"); + if (!testCase.mSuppressSequence) + { + VerifyOrQuit(frame.IsSequencePresent()); + offset += snprintf(string + offset, sizeof(string) - offset, ", seq:%u", frame.GetSequence()); + } + else + { + VerifyOrQuit(!frame.IsSequencePresent()); + } DumpBuffer(string, frame.GetPsdu(), frame.GetLength()); } } @@ -590,6 +603,7 @@ void TestMacFrameApi(void) VerifyOrQuit(!frame.IsDstAddrPresent()); VerifyOrQuit(frame.GetVersion() == Mac::Frame::kVersion2006); VerifyOrQuit(!frame.IsSrcAddrPresent()); + VerifyOrQuit(frame.IsSequencePresent()); VerifyOrQuit(frame.GetSequence() == 94); #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) @@ -619,6 +633,7 @@ void TestMacFrameApi(void) uint8_t commandId; frame.mPsdu = mac_cmd_psdu1; frame.mLength = sizeof(mac_cmd_psdu1); + VerifyOrQuit(frame.IsSequencePresent()); VerifyOrQuit(frame.GetSequence() == 133); VerifyOrQuit(frame.GetVersion() == Mac::Frame::kVersion2006); VerifyOrQuit(frame.GetType() == Mac::Frame::kTypeMacCmd); @@ -637,6 +652,7 @@ void TestMacFrameApi(void) // Command Identifier: Data Request (0x04) frame.mPsdu = mac_cmd_psdu2; frame.mLength = sizeof(mac_cmd_psdu2); + VerifyOrQuit(frame.IsSequencePresent()); VerifyOrQuit(frame.GetSequence() == 141); VerifyOrQuit(frame.IsVersion2015()); VerifyOrQuit(frame.GetType() == Mac::Frame::kTypeMacCmd); @@ -700,6 +716,7 @@ void TestMacFrameAckGeneration(void) VerifyOrQuit(!ackFrame.IsDstAddrPresent()); VerifyOrQuit(!ackFrame.IsSrcAddrPresent()); VerifyOrQuit(ackFrame.GetVersion() == Mac::Frame::kVersion2006); + VerifyOrQuit(ackFrame.IsSequencePresent()); VerifyOrQuit(ackFrame.GetSequence() == 189); #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) @@ -758,6 +775,7 @@ void TestMacFrameAckGeneration(void) VerifyOrQuit(ackFrame.IsDstAddrPresent()); VerifyOrQuit(!ackFrame.IsSrcAddrPresent()); VerifyOrQuit(ackFrame.GetVersion() == Mac::Frame::kVersion2015); + VerifyOrQuit(ackFrame.IsSequencePresent()); VerifyOrQuit(ackFrame.GetSequence() == 142); VerifyOrQuit(csl->GetPeriod() == 3125 && csl->GetPhase() == 3105); From c5ad131028e4d23e937d64206c3ffc333cc8aa22 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Sat, 3 Aug 2024 04:37:18 +0800 Subject: [PATCH 080/160] [cli] add the sync option to the linkmetrics cli commands (#10518) The cli command `linkmetrics` only provides an asynchronous output method. It is difficult for scripts to call the `ot-ctl` to capture the results of asynchronous output. This commit replaces the command `linkmetrics mgmt` and `linkmetrics query` with commands `linkmetrics config` and `linkmetrics request`. Both the commands `linkmetrics config` and `linkmetrics request` are set to the `sync` mode by default, and an option `async` is added to these commands to support `async` mode. --- src/cli/README.md | 69 +++++-- src/cli/cli_link_metrics.cpp | 189 ++++++++++++------ src/cli/cli_link_metrics.hpp | 17 +- tests/scripts/thread-cert/node.py | 31 +-- ..._SingleProbeLinkMetricsWithEnhancedAcks.py | 14 +- ...ingleProbeLinkMetricsWithoutEnhancedAck.py | 10 +- ...2_LowPower_7_2_01_ForwardTrackingSeries.py | 14 +- ...2_LowPower_test_forward_tracking_series.py | 17 +- .../thread-cert/v1_2_test_single_probe.py | 10 +- 9 files changed, 250 insertions(+), 121 deletions(-) diff --git a/src/cli/README.md b/src/cli/README.md index ee440b134..856d57f1e 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -66,7 +66,7 @@ Done - [keysequence](#keysequence-counter) - [leaderdata](#leaderdata) - [leaderweight](#leaderweight) -- [linkmetrics](#linkmetrics-mgmt-ipaddr-enhanced-ack-clear) +- [linkmetrics](#linkmetrics-config-async-ipaddr-enhanced-ack-clear) - [linkmetricsmgr](#linkmetricsmgr-disable) - [locate](#locate) - [log](#log-filename-filename) @@ -1861,23 +1861,30 @@ Set the Thread Leader Weight. Done ``` -### linkmetrics mgmt \ enhanced-ack clear +### linkmetrics config \[async\] \ enhanced-ack clear Send a Link Metrics Management Request to clear an Enhanced-ACK Based Probing. +- async: Use the non-blocking mode. - ipaddr: Peer address (SHOULD be link local address of the neighboring device). ```bash -> linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack clear +> linkmetrics config fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack clear +Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 +Status: Success +Done + +> linkmetrics config async fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack clear Done > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 Status: Success ``` -### linkmetrics mgmt \ enhanced-ack register [qmr][r] +### linkmetrics config \[async\] \ enhanced-ack register \ \[r\] Send a Link Metrics Management Request to register an Enhanced-ACK Based Probing. +- async: Use the non-blocking mode. - ipaddr: Peer address. - qmr: This specifies what metrics to query. At most two options are allowed to select (per spec 4.11.3.4.4.6). - q: Layer 2 LQI. @@ -1886,21 +1893,27 @@ Send a Link Metrics Management Request to register an Enhanced-ACK Based Probing - r: This is optional and only used for reference devices. When this option is specified, Type/Average Enum of each Type Id Flags would be set to `reserved`. This is used to verify the Probing Subject correctly handles invalid Type Id Flags. This is only available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. ```bash -> linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm +> linkmetrics config fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm +Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 +Status: Success +Done + +> linkmetrics config async fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm Done > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 Status: Success -> linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm r +> linkmetrics config async fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm r Done > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 Status: Cannot support new series ``` -### linkmetrics mgmt \ forward \ [ldraX][pqmr] +### linkmetrics config \[async\] \ forward \ \ \ Send a Link Metrics Management Request to configure a Forward Tracking Series. +- async: Use the non-blocking mode. - ipaddr: Peer address. - seriesid: The Series ID. - ldraX: This specifies which frames are to be accounted. @@ -1916,7 +1929,12 @@ Send a Link Metrics Management Request to configure a Forward Tracking Series. - r: RSSI. ```bash -> linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 forward 1 dra pqmr +> linkmetrics config fe80:0:0:0:3092:f334:1455:1ad2 forward 1 dra pqmr +Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 +Status: SUCCESS +Done + +> linkmetrics config async fe80:0:0:0:3092:f334:1455:1ad2 forward 1 dra pqmr Done > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 Status: SUCCESS @@ -1935,19 +1953,28 @@ Send a MLE Link Probe message to the peer. Done ``` -### linkmetrics query \ single [pqmr] +### linkmetrics request \[async\] \ single \ Perform a Link Metrics query (Single Probe). +- async: Use the non-blocking mode. - ipaddr: Peer address. - pqmr: This specifies what metrics to query. -- p: Layer 2 Number of PDUs received. -- q: Layer 2 LQI. -- m: Link Margin. -- r: RSSI. + - p: Layer 2 Number of PDUs received. + - q: Layer 2 LQI. + - m: Link Margin. + - r: RSSI. ```bash -> linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 single qmr +> linkmetrics request fe80:0:0:0:3092:f334:1455:1ad2 single qmr +Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 + + - LQI: 76 (Exponential Moving Average) + - Margin: 82 (dB) (Exponential Moving Average) + - RSSI: -18 (dBm) (Exponential Moving Average) +Done + +> linkmetrics request async fe80:0:0:0:3092:f334:1455:1ad2 single qmr Done > Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 @@ -1956,15 +1983,25 @@ Done - RSSI: -18 (dBm) (Exponential Moving Average) ``` -### linkmetrics query \ forward \ +### linkmetrics request \[async\] \ forward \ Perform a Link Metrics query (Forward Tracking Series). +- sync: Use the blocking mode. - ipaddr: Peer address. - seriesid: The Series ID. ```bash -> linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 forward 1 +> linkmetrics request fe80:0:0:0:3092:f334:1455:1ad2 forward 1 +Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 + + - PDU Counter: 2 (Count/Summation) + - LQI: 76 (Exponential Moving Average) + - Margin: 82 (dB) (Exponential Moving Average) + - RSSI: -18 (dBm) (Exponential Moving Average) +Done + +> linkmetrics request async fe80:0:0:0:3092:f334:1455:1ad2 forward 1 Done > Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 diff --git a/src/cli/cli_link_metrics.cpp b/src/cli/cli_link_metrics.cpp index a12515d1b..69ad781c8 100644 --- a/src/cli/cli_link_metrics.cpp +++ b/src/cli/cli_link_metrics.cpp @@ -46,32 +46,48 @@ namespace Cli { LinkMetrics::LinkMetrics(otInstance *aInstance, OutputImplementer &aOutputImplementer) : Utils(aInstance, aOutputImplementer) - , mLinkMetricsQueryInProgress(false) + , mQuerySync(false) + , mConfigForwardTrackingSeriesSync(false) + , mConfigEnhAckProbingSync(false) { } template <> otError LinkMetrics::Process(Arg aArgs[]) +{ + OT_UNUSED_VARIABLE(aArgs); + OutputLine("The command \"linkmetrics query\" has been replaced by the command \"linkmetrics request\"."); + return OT_ERROR_INVALID_COMMAND; +} + +template <> otError LinkMetrics::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; otIp6Address address; + bool sync = true; bool isSingle; - bool blocking; uint8_t seriesId; otLinkMetrics linkMetrics; + if (aArgs[0] == "async") + { + sync = false; + aArgs++; + } + SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address)); /** - * @cli linkmetrics query single + * @cli linkmetrics request single * @code - * linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 single qmr - * Done - * > Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 + * linkmetrics request fe80:0:0:0:3092:f334:1455:1ad2 single qmr + * Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 * - LQI: 76 (Exponential Moving Average) * - Margin: 82 (dB) (Exponential Moving Average) * - RSSI: -18 (dBm) (Exponential Moving Average) + * Done * @endcode - * @cparam linkmetrics query @ca{peer-ipaddr} single [@ca{pqmr}] + * @cparam linkmetrics request [@ca{async}] @ca{peer-ipaddr} single [@ca{pqmr}] + * - `async`: Use the non-blocking mode. * - `peer-ipaddr`: Peer address. * - [`p`, `q`, `m`, and `r`] map to #otLinkMetrics. * - `p`: Layer 2 Number of PDUs received. @@ -88,17 +104,18 @@ template <> otError LinkMetrics::Process(Arg aArgs[]) SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[2])); } /** - * @cli linkmetrics query forward + * @cli linkmetrics request forward * @code - * linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 forward 1 - * Done - * > Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 + * linkmetrics request fe80:0:0:0:3092:f334:1455:1ad2 forward 1 + * Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 * - PDU Counter: 2 (Count/Summation) * - LQI: 76 (Exponential Moving Average) * - Margin: 82 (dB) (Exponential Moving Average) * - RSSI: -18 (dBm) (Exponential Moving Average) + * Done * @endcode - * @cparam linkmetrics query @ca{peer-ipaddr} forward @ca{series-id} + * @cparam linkmetrics query [@ca{async}] @ca{peer-ipaddr} forward @ca{series-id} + * - `async`: Use the non-blocking mode. * - `peer-ipaddr`: Peer address. * - `series-id`: The Series ID. * @par @@ -115,40 +132,54 @@ template <> otError LinkMetrics::Process(Arg aArgs[]) ExitNow(error = OT_ERROR_INVALID_ARGS); } - blocking = (aArgs[3] == "block"); - SuccessOrExit(error = otLinkMetricsQuery(GetInstancePtr(), &address, isSingle ? 0 : seriesId, isSingle ? &linkMetrics : nullptr, HandleLinkMetricsReport, this)); - if (blocking) + if (sync) { - mLinkMetricsQueryInProgress = true; - error = OT_ERROR_PENDING; + mQuerySync = true; + error = OT_ERROR_PENDING; } + exit: return error; } template <> otError LinkMetrics::Process(Arg aArgs[]) +{ + OT_UNUSED_VARIABLE(aArgs); + OutputLine("The command \"linkmetrics mgmt\" has been replaced by the command \"linkmetrics config\"."); + return OT_ERROR_INVALID_COMMAND; +} + +template <> otError LinkMetrics::Process(Arg aArgs[]) { otError error; otIp6Address address; otLinkMetricsSeriesFlags seriesFlags; + bool sync = true; bool clear = false; + if (aArgs[0] == "async") + { + sync = false; + aArgs++; + } + SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address)); ClearAllBytes(seriesFlags); /** - * @cli linkmetrics mgmt forward + * @cli linkmetrics config forward * @code - * linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 forward 1 dra pqmr - * Done - * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 + * linkmetrics config fe80:0:0:0:3092:f334:1455:1ad2 forward 1 dra pqmr + * Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 * Status: SUCCESS + * Done * @endcode - * @cparam linkmetrics mgmt @ca{peer-ipaddr} forward @ca{series-id} [@ca{ldraX}][@ca{pqmr}] + * @cparam linkmetrics config [@ca{async}] @ca{peer-ipaddr} forward @ca{series-id} [@ca{ldraX}][@ca{pqmr}] + * - `async`: Use the non-blocking mode. * - `peer-ipaddr`: Peer address. * - `series-id`: The Series ID. * - [`l`, `d`, `r`, and `a`] map to #otLinkMetricsSeriesFlags. `X` represents none of the @@ -212,9 +243,15 @@ template <> otError LinkMetrics::Process(Arg aArgs[]) VerifyOrExit(aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS); } - error = otLinkMetricsConfigForwardTrackingSeries(GetInstancePtr(), &address, seriesId, seriesFlags, - clear ? nullptr : &linkMetrics, - &LinkMetrics::HandleLinkMetricsMgmtResponse, this); + SuccessOrExit(error = otLinkMetricsConfigForwardTrackingSeries( + GetInstancePtr(), &address, seriesId, seriesFlags, clear ? nullptr : &linkMetrics, + &LinkMetrics::HandleLinkMetricsConfigForwardTrackingSeriesMgmtResponse, this)); + + if (sync) + { + mConfigForwardTrackingSeriesSync = true; + error = OT_ERROR_PENDING; + } } else if (aArgs[1] == "enhanced-ack") { @@ -223,15 +260,16 @@ template <> otError LinkMetrics::Process(Arg aArgs[]) otLinkMetrics *pLinkMetrics = &linkMetrics; /** - * @cli linkmetrics mgmt enhanced-ack clear + * @cli linkmetrics config enhanced-ack clear * @code - * linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack clear - * Done - * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 + * linkmetrics config fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack clear + * Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 * Status: Success + * Done * @endcode - * @cparam linkmetrics mgmt @ca{peer-ipaddr} enhanced-ack clear - * `peer-ipaddr` should be the Link Local address of the neighboring device. + * @cparam linkmetrics config [@ca{async}] @ca{peer-ipaddr} enhanced-ack clear + * - `async`: Use the non-blocking mode. + * - `peer-ipaddr` should be the Link Local address of the neighboring device. * @par * Sends a Link Metrics Management Request to clear an Enhanced-ACK Based Probing. * @sa otLinkMetricsConfigEnhAckProbing @@ -242,26 +280,27 @@ template <> otError LinkMetrics::Process(Arg aArgs[]) pLinkMetrics = nullptr; } /** - * @cli linkmetrics mgmt enhanced-ack register + * @cli linkmetrics config enhanced-ack register * @code - * linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm - * Done - * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 + * linkmetrics config fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm + * Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 * Status: Success + * Done * @endcode * @code - * > linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm r - * Done - * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 + * > linkmetrics config fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm r + * Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 * Status: Cannot support new series + * Done * @endcode - * @cparam linkmetrics mgmt @ca{peer-ipaddr} enhanced-ack register [@ca{qmr}][@ca{r}] - * [`q`, `m`, and `r`] map to #otLinkMetricsValues. Per spec 4.11.3.4.4.6, you can - * only use a maximum of two options at once, for example `q`, or `qm`. - * - `q`: Layer 2 LQI. - * - `m`: Link Margin. - * - `r`: RSSI. - * . + * @cparam linkmetrics config [@ca{async}] @ca{peer-ipaddr} enhanced-ack register [@ca{qmr}][@ca{r}] + * - `async`: Use the non-blocking mode. + * - [`q`, `m`, and `r`] map to #otLinkMetricsValues. Per spec 4.11.3.4.4.6, you can + * only use a maximum of two options at once, for example `q`, or `qm`. + * - `q`: Layer 2 LQI. + * - `m`: Link Margin. + * - `r`: RSSI. + * @par * The additional `r` is optional and only used for reference devices. When this option * is specified, Type/Average Enum of each Type Id Flags is set to reserved. This is * used to verify that the Probing Subject correctly handles invalid Type Id Flags, and @@ -286,9 +325,16 @@ template <> otError LinkMetrics::Process(Arg aArgs[]) ExitNow(error = OT_ERROR_INVALID_ARGS); } - error = otLinkMetricsConfigEnhAckProbing(GetInstancePtr(), &address, enhAckFlags, pLinkMetrics, - &LinkMetrics::HandleLinkMetricsMgmtResponse, this, - &LinkMetrics::HandleLinkMetricsEnhAckProbingIe, this); + SuccessOrExit( + error = otLinkMetricsConfigEnhAckProbing(GetInstancePtr(), &address, enhAckFlags, pLinkMetrics, + &LinkMetrics::HandleLinkMetricsConfigEnhAckProbingMgmtResponse, + this, &LinkMetrics::HandleLinkMetricsEnhAckProbingIe, this)); + + if (sync) + { + mConfigEnhAckProbingSync = true; + error = OT_ERROR_PENDING; + } } else { @@ -337,9 +383,7 @@ otError LinkMetrics::Process(Arg aArgs[]) } static constexpr Command kCommands[] = { - CmdEntry("mgmt"), - CmdEntry("probe"), - CmdEntry("query"), + CmdEntry("config"), CmdEntry("mgmt"), CmdEntry("probe"), CmdEntry("query"), CmdEntry("request"), }; static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); @@ -448,18 +492,49 @@ void LinkMetrics::HandleLinkMetricsReport(const otIp6Address *aAddress, OutputLine("Link Metrics Report, status: %s", LinkMetricsStatusToStr(aStatus)); } - if (mLinkMetricsQueryInProgress) + if (mQuerySync) { - mLinkMetricsQueryInProgress = false; + mQuerySync = false; OutputResult(OT_ERROR_NONE); } } -void LinkMetrics::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, - otLinkMetricsStatus aStatus, - void *aContext) +void LinkMetrics::HandleLinkMetricsConfigForwardTrackingSeriesMgmtResponse(const otIp6Address *aAddress, + otLinkMetricsStatus aStatus, + void *aContext) +{ + static_cast(aContext)->HandleLinkMetricsConfigForwardTrackingSeriesMgmtResponse(aAddress, aStatus); +} + +void LinkMetrics::HandleLinkMetricsConfigForwardTrackingSeriesMgmtResponse(const otIp6Address *aAddress, + otLinkMetricsStatus aStatus) { - static_cast(aContext)->HandleLinkMetricsMgmtResponse(aAddress, aStatus); + HandleLinkMetricsMgmtResponse(aAddress, aStatus); + + if (mConfigForwardTrackingSeriesSync) + { + mConfigForwardTrackingSeriesSync = false; + OutputResult(OT_ERROR_NONE); + } +} + +void LinkMetrics::HandleLinkMetricsConfigEnhAckProbingMgmtResponse(const otIp6Address *aAddress, + otLinkMetricsStatus aStatus, + void *aContext) +{ + static_cast(aContext)->HandleLinkMetricsConfigEnhAckProbingMgmtResponse(aAddress, aStatus); +} + +void LinkMetrics::HandleLinkMetricsConfigEnhAckProbingMgmtResponse(const otIp6Address *aAddress, + otLinkMetricsStatus aStatus) +{ + HandleLinkMetricsMgmtResponse(aAddress, aStatus); + + if (mConfigEnhAckProbingSync) + { + mConfigEnhAckProbingSync = false; + OutputResult(OT_ERROR_NONE); + } } void LinkMetrics::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus) diff --git a/src/cli/cli_link_metrics.hpp b/src/cli/cli_link_metrics.hpp index 40b67c604..13b65a559 100644 --- a/src/cli/cli_link_metrics.hpp +++ b/src/cli/cli_link_metrics.hpp @@ -96,10 +96,15 @@ class LinkMetrics : private Utils const otLinkMetricsValues *aMetricsValues, otLinkMetricsStatus aStatus); - static void HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, - otLinkMetricsStatus aStatus, - void *aContext); - + static void HandleLinkMetricsConfigForwardTrackingSeriesMgmtResponse(const otIp6Address *aAddress, + otLinkMetricsStatus aStatus, + void *aContext); + void HandleLinkMetricsConfigForwardTrackingSeriesMgmtResponse(const otIp6Address *aAddress, + otLinkMetricsStatus aStatus); + static void HandleLinkMetricsConfigEnhAckProbingMgmtResponse(const otIp6Address *aAddress, + otLinkMetricsStatus aStatus, + void *aContext); + void HandleLinkMetricsConfigEnhAckProbingMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus); void HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus); static void HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress, @@ -115,7 +120,9 @@ class LinkMetrics : private Utils void OutputResult(otError aError); - bool mLinkMetricsQueryInProgress; + bool mQuerySync : 1; + bool mConfigForwardTrackingSeriesSync : 1; + bool mConfigEnhAckProbingSync : 1; }; } // namespace Cli diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 118b692da..9f7bca092 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -3255,14 +3255,14 @@ def router_table(self): return router_table - def link_metrics_query_single_probe(self, dst_addr: str, linkmetrics_flags: str, block: str = ""): - cmd = 'linkmetrics query %s single %s %s' % (dst_addr, linkmetrics_flags, block) + def link_metrics_request_single_probe(self, dst_addr: str, linkmetrics_flags: str, mode: str = ''): + cmd = 'linkmetrics request %s %s single %s' % (mode, dst_addr, linkmetrics_flags) self.send_command(cmd) self.simulator.go(5) return self._parse_linkmetrics_query_result(self._expect_command_output()) - def link_metrics_query_forward_tracking_series(self, dst_addr: str, series_id: int, block: str = ""): - cmd = 'linkmetrics query %s forward %d %s' % (dst_addr, series_id, block) + def link_metrics_request_forward_tracking_series(self, dst_addr: str, series_id: int, mode: str = ''): + cmd = 'linkmetrics request %s %s forward %d' % (mode, dst_addr, series_id) self.send_command(cmd) self.simulator.go(5) return self._parse_linkmetrics_query_result(self._expect_command_output()) @@ -3288,12 +3288,13 @@ def _parse_linkmetrics_query_result(self, lines): result['Status'] = line[29:] return result - def link_metrics_mgmt_req_enhanced_ack_based_probing(self, - dst_addr: str, - enable: bool, - metrics_flags: str, - ext_flags=''): - cmd = "linkmetrics mgmt %s enhanced-ack" % (dst_addr) + def link_metrics_config_req_enhanced_ack_based_probing(self, + dst_addr: str, + enable: bool, + metrics_flags: str, + ext_flags='', + mode: str = ''): + cmd = "linkmetrics config %s %s enhanced-ack" % (mode, dst_addr) if enable: cmd = cmd + (" register %s %s" % (metrics_flags, ext_flags)) else: @@ -3301,9 +3302,13 @@ def link_metrics_mgmt_req_enhanced_ack_based_probing(self, self.send_command(cmd) self._expect_done() - def link_metrics_mgmt_req_forward_tracking_series(self, dst_addr: str, series_id: int, series_flags: str, - metrics_flags: str): - cmd = "linkmetrics mgmt %s forward %d %s %s" % (dst_addr, series_id, series_flags, metrics_flags) + def link_metrics_config_req_forward_tracking_series(self, + dst_addr: str, + series_id: int, + series_flags: str, + metrics_flags: str, + mode: str = ''): + cmd = "linkmetrics config %s %s forward %d %s %s" % (mode, dst_addr, series_id, series_flags, metrics_flags) self.send_command(cmd) self._expect_done() diff --git a/tests/scripts/thread-cert/v1_2_LowPower_7_1_01_SingleProbeLinkMetricsWithEnhancedAcks.py b/tests/scripts/thread-cert/v1_2_LowPower_7_1_01_SingleProbeLinkMetricsWithEnhancedAcks.py index dea419875..7b67796e7 100755 --- a/tests/scripts/thread-cert/v1_2_LowPower_7_1_01_SingleProbeLinkMetricsWithEnhancedAcks.py +++ b/tests/scripts/thread-cert/v1_2_LowPower_7_1_01_SingleProbeLinkMetricsWithEnhancedAcks.py @@ -99,7 +99,7 @@ def test(self): # ---- L = 0 # ---- Type/Average Enum = 1 (Exponential Moving Avg) # ---- Metrics Enum = 2 (Link Margin) - self.nodes[SED_1].link_metrics_mgmt_req_enhanced_ack_based_probing(leader_addr, True, 'm') + self.nodes[SED_1].link_metrics_config_req_enhanced_ack_based_probing(leader_addr, True, 'm', mode='async') self.simulator.go(5) # Step 6 - SSED_1 enables IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management @@ -115,7 +115,7 @@ def test(self): # ---- Type/Average Enum = 1 (Exponential Moving Avg) # ---- Metrics Enum = 2 (Link Margin) # ---- Metrics Enum = 3 (RSSI) - self.nodes[SSED_1].link_metrics_mgmt_req_enhanced_ack_based_probing(leader_addr, True, 'mr') + self.nodes[SSED_1].link_metrics_config_req_enhanced_ack_based_probing(leader_addr, True, 'mr', mode='async') self.simulator.go(5) # Step 8 - SSED_1 sends an MLE Data Message with setting Frame Version subfield within the MAC header of the @@ -131,7 +131,7 @@ def test(self): # Step 12 - SSED_1 clears its Enhanced ACK link metrics configuration by # sending a Link Metrics Management # Request to the DUT # Enh-ACK Flags = 0 (clear enhanced ACK link metric config) - self.nodes[SSED_1].link_metrics_mgmt_req_enhanced_ack_based_probing(leader_addr, False, '') + self.nodes[SSED_1].link_metrics_config_req_enhanced_ack_based_probing(leader_addr, False, '', mode='async') self.simulator.go(5) # Step 14 - SSED_1 Sends a MLE Data Message with setting Frame Version subfield within the MAC header of the @@ -154,7 +154,7 @@ def test(self): # ---- Metrics Enum = 1 (Layer 2 LQI) # ---- Metrics Enum = 2 (Link Margin) # ---- Metrics Enum = 3 (RSSI) - self.nodes[SSED_1].link_metrics_mgmt_req_enhanced_ack_based_probing(leader_addr, True, 'qmr') + self.nodes[SSED_1].link_metrics_config_req_enhanced_ack_based_probing(leader_addr, True, 'qmr', mode='async') self.simulator.go(5) # Step 18 - This step verifies that Enhanced ACKs cannot be enabled while requesting a reserved Type/Average @@ -168,7 +168,11 @@ def test(self): # ---- L = 0 # ---- Type/Average Enum = 2 (Reserved) # ---- Metrics Enum = 2 (Link Margin) - self.nodes[SSED_1].link_metrics_mgmt_req_enhanced_ack_based_probing(leader_addr, True, 'm', 'r') + self.nodes[SSED_1].link_metrics_config_req_enhanced_ack_based_probing(leader_addr, + True, + 'm', + 'r', + mode='async') self.simulator.go(5) def verify(self, pv): diff --git a/tests/scripts/thread-cert/v1_2_LowPower_7_1_02_SingleProbeLinkMetricsWithoutEnhancedAck.py b/tests/scripts/thread-cert/v1_2_LowPower_7_1_02_SingleProbeLinkMetricsWithoutEnhancedAck.py index 92ab70cd1..977547077 100755 --- a/tests/scripts/thread-cert/v1_2_LowPower_7_1_02_SingleProbeLinkMetricsWithoutEnhancedAck.py +++ b/tests/scripts/thread-cert/v1_2_LowPower_7_1_02_SingleProbeLinkMetricsWithoutEnhancedAck.py @@ -108,7 +108,7 @@ def test(self): # In this step, SED_1 should set its TxPower to 'High'. In simulation, this will be implemented by # setting Macfilter on the Rx side (Leader). self.nodes[LEADER].add_allowlist(sed_extaddr, -30) - res = self.nodes[SED_1].link_metrics_query_single_probe(leader_addr, 'r', 'block') + res = self.nodes[SED_1].link_metrics_request_single_probe(leader_addr, 'r') rss_1 = int(res['RSSI']) self.simulator.go(5) @@ -125,7 +125,7 @@ def test(self): # # In this step, SED_1 should set its TxPower to 'Low'. self.nodes[LEADER].add_allowlist(sed_extaddr, -95) - res = self.nodes[SED_1].link_metrics_query_single_probe(leader_addr, 'r', 'block') + res = self.nodes[SED_1].link_metrics_request_single_probe(leader_addr, 'r') rss_2 = int(res['RSSI']) self.simulator.go(5) @@ -142,7 +142,7 @@ def test(self): # --- Metric Type ID Flags # ---- Type / Average Enum = 1 (Exponential Moving Avg) # ---- Metric Enum = 1 (Layer 2 LQI) - self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'q', 'block') + self.nodes[SSED_1].link_metrics_request_single_probe(leader_addr, 'q') self.simulator.go(5) # Step 11 - SSED_1 sends a Single Probe Link Metric for Link Margin using MLE Data Request @@ -155,7 +155,7 @@ def test(self): # --- Metric Type ID Flags # ---- Type / Average Enum = 1 (Exponential Moving Avg) # ---- Metric Enum = 2 (Link Margin) - self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'm', 'block') + self.nodes[SSED_1].link_metrics_request_single_probe(leader_addr, 'm') self.simulator.go(5) # Step 13 - SSED_1 sends a Single Probe Link Metric using MLE Data Request @@ -170,7 +170,7 @@ def test(self): # ---- Metric Enum = 1 (Layer 2 LQI) # ---- Metric Enum = 2 (Link Margin) # ---- Metric Enum = 3 (RSSI) - self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'qmr', 'block') + self.nodes[SSED_1].link_metrics_request_single_probe(leader_addr, 'qmr') self.simulator.go(5) def verify(self, pv): diff --git a/tests/scripts/thread-cert/v1_2_LowPower_7_2_01_ForwardTrackingSeries.py b/tests/scripts/thread-cert/v1_2_LowPower_7_2_01_ForwardTrackingSeries.py index 7cb024156..6a2789999 100755 --- a/tests/scripts/thread-cert/v1_2_LowPower_7_2_01_ForwardTrackingSeries.py +++ b/tests/scripts/thread-cert/v1_2_LowPower_7_2_01_ForwardTrackingSeries.py @@ -103,7 +103,7 @@ def test(self): # -- L = 1 # -- Type/Average Enum = 0 (count) # -- Metrics Enum = 0 (count) - self.nodes[SED_1].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID, 'r', 'p') + self.nodes[SED_1].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID, 'r', 'p', 'async') self.simulator.go(5) @@ -111,7 +111,7 @@ def test(self): self.simulator.go(30) # Step 7 - SED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results - result = self.nodes[SED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID, 'block') + result = self.nodes[SED_1].link_metrics_request_forward_tracking_series(leader_addr, SERIES_ID) self.assertIn("PDU Counter", result) #self.simulator.go(5) @@ -120,7 +120,7 @@ def test(self): # Forward Series Flags = 0x00: # - Bits 0-7 = 0 # Concatenation of Link Metric Type ID Flags = NULL: - self.nodes[SED_1].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID, 'X', '') + self.nodes[SED_1].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID, 'X', '', 'async') self.simulator.go(5) @@ -134,7 +134,7 @@ def test(self): # -- L = 0 # -- Type/Average Enum = 1 (Exponential Moving Average) # -- Metrics Enum = 2 (Link Margin) - self.nodes[SSED_1].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'd', 'm') + self.nodes[SSED_1].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'd', 'm', 'async') self.simulator.go(5) @@ -145,19 +145,19 @@ def test(self): self.simulator.go(1) # Step 19 - SSED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results - result = self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2, 'block') + result = self.nodes[SSED_1].link_metrics_request_forward_tracking_series(leader_addr, SERIES_ID_2) self.assertIn("Margin", result) # Step 21 - SSED_1 clears the Forward Series Link Metrics # Forward Series Flags = 0x00: # - Bits 0-7 = 0 # Concatenation of Link Metric Type ID Flags = NULL - self.nodes[SSED_1].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'X', '') + self.nodes[SSED_1].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'X', '', 'async') self.simulator.go(5) # Step 23 - SSED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results - result = self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2, 'block') + result = self.nodes[SSED_1].link_metrics_request_forward_tracking_series(leader_addr, SERIES_ID_2) self.assertIn('Status', result) self.assertEqual(result['Status'], 'Series ID not recognized') diff --git a/tests/scripts/thread-cert/v1_2_LowPower_test_forward_tracking_series.py b/tests/scripts/thread-cert/v1_2_LowPower_test_forward_tracking_series.py index c1287fe5c..f8263de71 100755 --- a/tests/scripts/thread-cert/v1_2_LowPower_test_forward_tracking_series.py +++ b/tests/scripts/thread-cert/v1_2_LowPower_test_forward_tracking_series.py @@ -76,38 +76,39 @@ def test(self): # 1. Child configures a Forward Tracking Series successfully. # The Series tracks the count of MAC Data Request. # Child should get a response with status 0 (SUCCESS). - self.nodes[CHILD].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'r', 'p') + self.nodes[CHILD].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'r', 'p', 'async') self.simulator.go(1) # 2. Child configures the same Forward Tracking Series again. # Child should get a response with status 2 (SERIES_ID_ALREADY_REGISTERED). - self.nodes[CHILD].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'r', 'p') + self.nodes[CHILD].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'r', 'p', 'async') self.simulator.go(1) # 3. Child queries a Series that doesn't exist (using a wrong Series ID). # Child should get a report with status 3 (SERIES_ID_NOT_RECOGNIZED). - self.nodes[CHILD].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2) + self.nodes[CHILD].link_metrics_request_forward_tracking_series(leader_addr, SERIES_ID_2, 'async') self.simulator.go(1) # 4. Child queries a Series that don't have matched frames yet. # Child should get a report with status 4 (NO_MATCHING_FRAMES_RECEIVED). - self.nodes[CHILD].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_1) + self.nodes[CHILD].link_metrics_request_forward_tracking_series(leader_addr, SERIES_ID_1, 'async') self.simulator.go(1) # 5. Child clears a Forward Tracking Series that doesn't exist. # Child should get a response with status 3 (SERIES_ID_NOT_RECOGNIZED). - self.nodes[CHILD].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'X', '') + self.nodes[CHILD].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'X', '', 'async') self.simulator.go(1) # 6. Child clears a Forward Tracking Series successfully. # Child should get a response with status 0 (SUCCESS). - self.nodes[CHILD].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'X', '') + self.nodes[CHILD].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'X', '', 'async') self.simulator.go(1) # 7. Child configures a new Forward Tracking Series successfully. # The Series tracks the count of all MAC Data frames. # Child should get a response with status 0 (SUCCESS). - self.nodes[CHILD].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'd', 'pqmr') + self.nodes[CHILD].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'd', 'pqmr', + 'async') self.simulator.go(1) # 8. Child sends an MLE Link Probe message to the Subject for the newly configured Series. @@ -115,7 +116,7 @@ def test(self): # 9. Child queries the newly configured Series successfully. # Child should get a report with valid values. - self.nodes[CHILD].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2) + self.nodes[CHILD].link_metrics_request_forward_tracking_series(leader_addr, SERIES_ID_2, 'async') self.simulator.go(1) def verify(self, pv): diff --git a/tests/scripts/thread-cert/v1_2_test_single_probe.py b/tests/scripts/thread-cert/v1_2_test_single_probe.py index 5b29aaac8..1e228b5ce 100755 --- a/tests/scripts/thread-cert/v1_2_test_single_probe.py +++ b/tests/scripts/thread-cert/v1_2_test_single_probe.py @@ -70,7 +70,7 @@ def test(self): leader_messages = self.simulator.get_messages_sent_by(LEADER) # SSED_1 sends a Single Probe Link Metrics for L2 PDU count using MLE Data Request - result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'p', 'block') + result = self.nodes[SSED_1].link_metrics_request_single_probe(leader_addr, 'p') self.assertIn('PDU Counter', result) self.assertEqual(len(result), 1) @@ -79,7 +79,7 @@ def test(self): msg.assertMleMessageContainsTlv(mle.LinkMetricsReport) # SSED_1 sends a Single Probe Link Metrics for L2 LQI using MLE Data Request - result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'q', 'block') + result = self.nodes[SSED_1].link_metrics_request_single_probe(leader_addr, 'q') self.assertIn('LQI', result) self.assertEqual(len(result), 1) @@ -88,7 +88,7 @@ def test(self): msg.assertMleMessageContainsTlv(mle.LinkMetricsReport) # SSED_1 sends a Single Probe Link Metrics for Link Margin using MLE Data Request - result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'm', 'block') + result = self.nodes[SSED_1].link_metrics_request_single_probe(leader_addr, 'm') self.assertIn('Margin', result) self.assertEqual(len(result), 1) @@ -97,7 +97,7 @@ def test(self): msg.assertMleMessageContainsTlv(mle.LinkMetricsReport) # SSED_1 sends a Single Probe Link Metrics for Link Margin using MLE Data Request - result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'r', 'block') + result = self.nodes[SSED_1].link_metrics_request_single_probe(leader_addr, 'r') self.assertIn('RSSI', result) self.assertEqual(len(result), 1) @@ -106,7 +106,7 @@ def test(self): msg.assertMleMessageContainsTlv(mle.LinkMetricsReport) # SSED_1 sends a Single Probe Link Metrics for all metrics using MLE Data Request - result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'pqmr', 'block') + result = self.nodes[SSED_1].link_metrics_request_single_probe(leader_addr, 'pqmr') self.assertIn('PDU Counter', result) self.assertIn('LQI', result) self.assertIn('Margin', result) From 54d10720f62be8d28b912c732f8fa2010cb3c0ce Mon Sep 17 00:00:00 2001 From: Li Cao Date: Tue, 6 Aug 2024 06:09:52 +0800 Subject: [PATCH 081/160] [spinel] add spinel property to get/set dataset in raw TLVs (#10569) This commit adds two new spinel properties: `SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS` and `SPINEL_PROP_THREAD_PENDING_DATASET_TLVS`, as well as the get/set property handler on NCP side. Currently we can only transport dataset through spinel with fixed format. Using Tlvs allows us to append TLVs are not defined in the current Thread Specification. --- src/lib/spinel/spinel.h | 24 ++++++++++++++ src/ncp/ncp_base_dispatcher.cpp | 4 +++ src/ncp/ncp_base_mtd.cpp | 58 +++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index 9941dba75..d04743dcb 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -3388,6 +3388,30 @@ enum */ SPINEL_PROP_THREAD_BACKBONE_ROUTER_LOCAL_REGISTRATION_JITTER = SPINEL_PROP_THREAD_EXT__BEGIN + 59, + /// Thread Active Operational Dataset in raw TLVs format. + /** Format: `D` - Read-Write + * + * This property provides access to the current Thread Active Operational Dataset. A Thread device maintains the + * Operational Dataset that it has stored locally and the one currently in use by the partition to which it is + * attached. This property corresponds to the locally stored Dataset on the device. + * + * On write, any unknown/unsupported TLVs must be ignored. + * + */ + SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS = SPINEL_PROP_THREAD_EXT__BEGIN + 60, + + /// Thread Pending Operational Dataset in raw TLVs format. + /** Format: `D` - Read-Write + * + * This property provides access to the current locally stored Pending Operational Dataset. + * + * The formatting of this property follows the same rules as in SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS. + * + * On write, any unknown/unsupported TLVs must be ignored. + * + */ + SPINEL_PROP_THREAD_PENDING_DATASET_TLVS = SPINEL_PROP_THREAD_EXT__BEGIN + 61, + SPINEL_PROP_THREAD_EXT__END = 0x1600, SPINEL_PROP_IPV6__BEGIN = 0x60, diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index 167b7348a..5d2362b0c 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -334,6 +334,8 @@ NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_THREAD_BACKBONE_ROUTER_LOCAL_CONFIG), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_THREAD_BACKBONE_ROUTER_LOCAL_REGISTRATION_JITTER), #endif + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS), + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_THREAD_PENDING_DATASET_TLVS), #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_CHANNEL_MANAGER_NEW_CHANNEL), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_CHANNEL_MANAGER_DELAY), @@ -603,6 +605,8 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_THREAD_BACKBONE_ROUTER_LOCAL_REGISTER), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_THREAD_BACKBONE_ROUTER_LOCAL_REGISTRATION_JITTER), #endif + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS), + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_THREAD_PENDING_DATASET_TLVS), #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MESHCOP_COMMISSIONER_ANNOUNCE_BEGIN), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MESHCOP_COMMISSIONER_ENERGY_SCAN), diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 07047217f..fe15134c7 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -1370,6 +1370,30 @@ template <> otError NcpBase::HandlePropertyGet otError NcpBase::HandlePropertyGet(void) +{ + otError error = OT_ERROR_NONE; + otOperationalDatasetTlvs dataset; + + SuccessOrExit(error = otDatasetGetActiveTlvs(mInstance, &dataset)); + SuccessOrExit(error = mEncoder.WriteData(dataset.mTlvs, dataset.mLength)); + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertyGet(void) +{ + otError error = OT_ERROR_NONE; + otOperationalDatasetTlvs dataset; + + SuccessOrExit(error = otDatasetGetPendingTlvs(mInstance, &dataset)); + SuccessOrExit(error = mEncoder.WriteData(dataset.mTlvs, dataset.mLength)); + +exit: + return error; +} + otError NcpBase::DecodeOperationalDataset(otOperationalDataset &aDataset, const uint8_t **aTlvs, uint8_t *aTlvsLength, @@ -1651,6 +1675,40 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet(void) +{ + otError error = OT_ERROR_NONE; + const uint8_t *tlvs = nullptr; + uint16_t len = 0; + otOperationalDatasetTlvs dataset; + + SuccessOrExit(error = mDecoder.ReadData(tlvs, len)); + VerifyOrExit(len <= OT_OPERATIONAL_DATASET_MAX_LENGTH, error = OT_ERROR_PARSE); + memcpy(&dataset.mTlvs, tlvs, len); + dataset.mLength = static_cast(len); + SuccessOrExit(error = otDatasetSetActiveTlvs(mInstance, &dataset)); + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error = OT_ERROR_NONE; + const uint8_t *tlvs = nullptr; + uint16_t len = 0; + otOperationalDatasetTlvs dataset; + + SuccessOrExit(error = mDecoder.ReadData(tlvs, len)); + VerifyOrExit(len <= OT_OPERATIONAL_DATASET_MAX_LENGTH, error = OT_ERROR_PARSE); + memcpy(&dataset.mTlvs, tlvs, len); + dataset.mLength = static_cast(len); + SuccessOrExit(error = otDatasetSetPendingTlvs(mInstance, &dataset)); + +exit: + return error; +} + template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; From 91eef98f95a7bf62267f4f80fbdcf72e68afde9d Mon Sep 17 00:00:00 2001 From: Rongli Sun Date: Tue, 6 Aug 2024 06:10:36 +0800 Subject: [PATCH 082/160] [border-agent] log enhancements (#10549) This commit updates logs on secure session establishment and commissioner for accuracy. --- src/core/meshcop/border_agent.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index e084fa63c..f4eb18351 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -208,6 +208,10 @@ void BorderAgent::HandleCoapResponse(const ForwardContext &aForwardContext, LogInfo("Commissioner accepted - SessionId:%u ALOC:%s", sessionId, mCommissionerAloc.GetAddress().ToString().AsCString()); } + else + { + LogInfo("Commissioner rejected"); + } } SuccessOrExit(error = aForwardContext.ToHeader(*message, aResponse->GetCode())); @@ -640,13 +644,13 @@ void BorderAgent::HandleConnected(bool aConnected) { if (aConnected) { - LogInfo("Commissioner connected"); + LogInfo("SecureSession connected"); mState = kStateConnected; mTimer.Start(kKeepAliveTimeout); } else { - LogInfo("Commissioner disconnected"); + LogInfo("SecureSession disconnected"); IgnoreError(Get().RemoveReceiver(mUdpReceiver)); Get().RemoveUnicastAddress(mCommissionerAloc); From 18365adcf54982e83913a8514c34306c854a49f3 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Tue, 6 Aug 2024 06:11:54 +0800 Subject: [PATCH 083/160] [spinel] stop Spinel time sync when Thread satck is not running (#10573) The radio_spinel periodically send Spinel command SPINEL_PROP_RCP_TIMESTAMP to RCP to synchronize time between host and RCP. Even if Thread stack is not running, it will periodically wake up the Thread host. This commit stops the time sync when the Thread stack is not running to save host power. --- src/lib/spinel/radio_spinel.cpp | 10 ++++++---- src/lib/spinel/radio_spinel.hpp | 11 ++++++++++- src/posix/platform/openthread-core-posix-config.h | 7 +++++++ src/posix/platform/platform-posix.h | 9 +++++++++ src/posix/platform/radio.cpp | 8 ++++++++ src/posix/platform/system.cpp | 6 ++---- 6 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 5244bcefc..49d88cbb8 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -111,7 +111,8 @@ RadioSpinel::RadioSpinel(void) , mVendorRestorePropertiesCallback(nullptr) , mVendorRestorePropertiesContext(nullptr) #endif - , mEnableRcpTimeSync(false) + , mTimeSyncEnabled(false) + , mTimeSyncOn(false) , mSpinelDriver(nullptr) { memset(&mRadioSpinelMetrics, 0, sizeof(mRadioSpinelMetrics)); @@ -134,7 +135,7 @@ void RadioSpinel::Init(bool aSkipRcpCompatibilityCheck, mResetRadioOnStartup = aSoftwareReset; #endif - mEnableRcpTimeSync = aEnableRcpTimeSync; + mTimeSyncEnabled = aEnableRcpTimeSync; mSpinelDriver = aSpinelDriver; mSpinelDriver->SetFrameHandler(&HandleReceivedFrame, &HandleSavedFrame, this); @@ -798,7 +799,7 @@ void RadioSpinel::Process(const void *aContext) ProcessRadioStateMachine(); RecoverFromRcpFailure(); - if (mEnableRcpTimeSync) + if (mTimeSyncEnabled) { CalcRcpTimeOffset(); } @@ -1919,6 +1920,7 @@ void RadioSpinel::CalcRcpTimeOffset(void) * D = T1' - ((T0 + T2)/ 2) */ + EXPECT(mTimeSyncOn, NO_ACTION); EXPECT(!mIsTimeSynced || (otPlatTimeGet() >= GetNextRadioTimeRecalcStart()), NO_ACTION); LogDebg("Trying to get RCP time offset"); @@ -2231,7 +2233,7 @@ void RadioSpinel::RestoreProperties(void) } #endif - if (mEnableRcpTimeSync) + if (mTimeSyncEnabled) { CalcRcpTimeOffset(); } diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 0eb49108b..36acc7773 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -1106,6 +1106,14 @@ class RadioSpinel : private Logger void SetVendorRestorePropertiesCallback(otRadioSpinelVendorRestorePropertiesCallback aCallback, void *aContext); #endif // OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE + /** + * Enables or disables the time synchronization between the host and RCP. + * + * @param[in] aOn TRUE to turn on the time synchronization, FALSE otherwise. + * + */ + void SetTimeSyncState(bool aOn) { mTimeSyncOn = aOn; } + private: enum { @@ -1342,7 +1350,8 @@ class RadioSpinel : private Logger void *mVendorRestorePropertiesContext; #endif - bool mEnableRcpTimeSync; + bool mTimeSyncEnabled : 1; + bool mTimeSyncOn : 1; SpinelDriver *mSpinelDriver; }; diff --git a/src/posix/platform/openthread-core-posix-config.h b/src/posix/platform/openthread-core-posix-config.h index ebe784f45..a837ddc96 100644 --- a/src/posix/platform/openthread-core-posix-config.h +++ b/src/posix/platform/openthread-core-posix-config.h @@ -156,4 +156,11 @@ #define OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE 1 #endif +#ifndef OPENTHREAD_CONFIG_MAX_STATECHANGE_HANDLERS +/** + * The `system.cpp` has registered a state-changed callback handler. Another state-changed callback handler + * is reserved for application use. + */ +#define OPENTHREAD_CONFIG_MAX_STATECHANGE_HANDLERS 2 +#endif #endif // OPENTHREAD_CORE_POSIX_CONFIG_H_ diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h index ca897973b..822f3f48a 100644 --- a/src/posix/platform/platform-posix.h +++ b/src/posix/platform/platform-posix.h @@ -165,6 +165,15 @@ void platformRadioInit(const char *aUrl); */ void platformRadioDeinit(void); +/** + * Handles the state change events for the radio driver. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in] aFlags Flags that denote the state change events. + * + */ +void platformRadioHandleStateChange(otInstance *aInstance, otChangedFlags aFlags); + /** * Inputs a received radio frame. * diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index 46c3040b2..b76d0aa08 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -216,6 +216,14 @@ ot::Posix::RcpCapsDiag &GetRcpCapsDiag(void) { return sRadio.GetRcpCapsDiag(); } void platformRadioDeinit(void) { GetRadioSpinel().Deinit(); } +void platformRadioHandleStateChange(otInstance *aInstance, otChangedFlags aFlags) +{ + if (OT_CHANGED_THREAD_NETIF_STATE & aFlags) + { + GetRadioSpinel().SetTimeSyncState(otIp6IsEnabled(aInstance)); + } +} + void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) { OT_UNUSED_VARIABLE(aInstance); diff --git a/src/posix/platform/system.cpp b/src/posix/platform/system.cpp index 89da6af13..5f3640a56 100644 --- a/src/posix/platform/system.cpp +++ b/src/posix/platform/system.cpp @@ -65,7 +65,6 @@ bool gDryRun = false; CoprocessorType sCoprocessorType = OT_COPROCESSOR_UNKNOWN; -#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE || OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE static void processStateChange(otChangedFlags aFlags, void *aContext) { otInstance *instance = static_cast(aContext); @@ -80,8 +79,9 @@ static void processStateChange(otChangedFlags aFlags, void *aContext) #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE ot::Posix::InfraNetif::Get().HandleBackboneStateChange(instance, aFlags); #endif + + platformRadioHandleStateChange(instance, aFlags); } -#endif static const char *get802154RadioUrl(const otPlatformCoprocessorUrls &aUrls) { @@ -245,9 +245,7 @@ void platformSetUp(otPlatformConfig *aPlatformConfig) ot::Posix::Daemon::Get().SetUp(); #endif -#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE || OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE SuccessOrDie(otSetStateChangedCallback(gInstance, processStateChange, gInstance)); -#endif exit: return; From 05d6bd6eb3a2af47e500c9ab73a014db843f1b0a Mon Sep 17 00:00:00 2001 From: Handa Wang <7058128+superwhd@users.noreply.github.com> Date: Tue, 6 Aug 2024 06:17:11 +0800 Subject: [PATCH 084/160] [posix] allow custom implementation of `otPlatInfraIfDiscoverNat64Prefix` (#10566) This commit moves the macro guard `OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE` in the code so that it can allow custom implementation of `otPlatInfraIfDiscoverNat64Prefix` when `OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE` is disabled. The use case is on Android platform. We'll not let `ot-daemon` proactively discover the prefixes by DNS. Instead, System Server will do the discovery of NAT64 prefix on AIL and it will notify `ot-daemon` when the AIL NAT64 prefix is discovered/removed. --- src/core/border_router/infra_if.cpp | 4 ++++ src/posix/platform/infra_if.cpp | 20 +++++++++++-------- src/posix/platform/infra_if.hpp | 10 +++++++++- .../openthread-core-toranj-config-posix.h | 2 ++ 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/core/border_router/infra_if.cpp b/src/core/border_router/infra_if.cpp index 25efee5b8..d38c3b006 100644 --- a/src/core/border_router/infra_if.cpp +++ b/src/core/border_router/infra_if.cpp @@ -115,7 +115,11 @@ Error InfraIf::DiscoverNat64Prefix(void) const { OT_ASSERT(mInitialized); +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE return otPlatInfraIfDiscoverNat64Prefix(mIfIndex); +#else + return kErrorNotImplemented; +#endif } void InfraIf::DiscoverNat64PrefixDone(uint32_t aIfIndex, const Ip6::Prefix &aPrefix) diff --git a/src/posix/platform/infra_if.cpp b/src/posix/platform/infra_if.cpp index 0c9785228..e9aed0811 100644 --- a/src/posix/platform/infra_if.cpp +++ b/src/posix/platform/infra_if.cpp @@ -101,16 +101,12 @@ otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, } #endif +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE && OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex) { - OT_UNUSED_VARIABLE(aInfraIfIndex); - -#if OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE return ot::Posix::InfraNetif::Get().DiscoverNat64Prefix(aInfraIfIndex); -#else - return OT_ERROR_DROP; -#endif } +#endif bool otSysInfraIfIsRunning(void) { return ot::Posix::InfraNetif::Get().IsRunning(); } @@ -667,12 +663,13 @@ void InfraNetif::ReceiveIcmp6Message(void) } #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE -#if OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE && OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE const char InfraNetif::kWellKnownIpv4OnlyName[] = "ipv4only.arpa"; const otIp4Address InfraNetif::kWellKnownIpv4OnlyAddress1 = {{{192, 0, 0, 170}}}; const otIp4Address InfraNetif::kWellKnownIpv4OnlyAddress2 = {{{192, 0, 0, 171}}}; const uint8_t InfraNetif::kValidNat64PrefixLength[] = {96, 64, 56, 48, 40, 32}; +#ifdef __linux__ void InfraNetif::DiscoverNat64PrefixDone(union sigval sv) { struct gaicb *req = (struct gaicb *)sv.sival_ptr; @@ -750,9 +747,11 @@ void InfraNetif::DiscoverNat64PrefixDone(union sigval sv) freeaddrinfo((struct addrinfo *)req->ar_request); free(req); } +#endif // #ifdef __linux__ otError InfraNetif::DiscoverNat64Prefix(uint32_t aInfraIfIndex) { +#ifdef __linux__ otError error = OT_ERROR_NONE; struct addrinfo *hints = nullptr; struct gaicb *reqs[1] = {nullptr}; @@ -795,8 +794,13 @@ otError InfraNetif::DiscoverNat64Prefix(uint32_t aInfraIfIndex) free(reqs[0]); } return error; +#else + OT_UNUSED_VARIABLE(aInfraIfIndex); + + return OT_ERROR_NOT_IMPLEMENTED; +#endif // #ifdef __linux__ } -#endif // OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE +#endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE && OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE void InfraNetif::SetInfraNetifIcmp6SocketForBorderRouting(int aIcmp6Socket) diff --git a/src/posix/platform/infra_if.hpp b/src/posix/platform/infra_if.hpp index 3eee24821..f7139110a 100644 --- a/src/posix/platform/infra_if.hpp +++ b/src/posix/platform/infra_if.hpp @@ -174,6 +174,7 @@ class InfraNetif : public Mainloop::Source, public Logger, private N const uint8_t *aBuffer, uint16_t aBufferLength); +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE && OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE /** * Sends an asynchronous address lookup for the well-known host name "ipv4only.arpa" * to discover the NAT64 prefix. @@ -185,6 +186,7 @@ class InfraNetif : public Mainloop::Source, public Logger, private N * */ otError DiscoverNat64Prefix(uint32_t aInfraIfIndex); +#endif /** * Gets the infrastructure network interface name. @@ -240,12 +242,18 @@ class InfraNetif : public Mainloop::Source, public Logger, private N MulticastRoutingManager mMulticastRoutingManager; #endif + bool HasLinkLocalAddress(void) const; + #ifdef __linux__ void ReceiveNetLinkMessage(void); #endif - bool HasLinkLocalAddress(void) const; +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE && OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE +#ifdef __linux__ static void DiscoverNat64PrefixDone(union sigval sv); +#endif // #ifdef __linux__ +#endif + #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE void SetInfraNetifIcmp6SocketForBorderRouting(int aIcmp6Socket); void ReceiveIcmp6Message(void); diff --git a/tests/toranj/openthread-core-toranj-config-posix.h b/tests/toranj/openthread-core-toranj-config-posix.h index 2dc34b002..8a18e9c87 100644 --- a/tests/toranj/openthread-core-toranj-config-posix.h +++ b/tests/toranj/openthread-core-toranj-config-posix.h @@ -65,6 +65,8 @@ #define OPENTHREAD_POSIX_CONFIG_SPINEL_HDLC_INTERFACE_ENABLE 1 +#define OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE 1 + // Disabled explicitly on posix `toranj` to validate the build with this config #define OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE 0 From 069ba71f5e08613269edc4d2cce6d258270c64b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:18:36 -0600 Subject: [PATCH 085/160] github-actions: bump actions/setup-python from 5.1.0 to 5.1.1 (#10583) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/82c7e631bb3cdc910f68e0081d67478d79c6982d...39cd14951b08e74b54015e9e001cdefcf80e669f) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/otns.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml index 64208a3a0..a8293a54a 100644 --- a/.github/workflows/otns.yml +++ b/.github/workflows/otns.yml @@ -67,7 +67,7 @@ jobs: with: go-version: "1.20" - name: Set up Python - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: "3.9" - name: Bootstrap @@ -107,7 +107,7 @@ jobs: with: go-version: "1.20" - name: Set up Python - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: "3.9" - name: Bootstrap @@ -169,7 +169,7 @@ jobs: with: go-version: "1.20" - name: Set up Python - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: "3.9" - name: Bootstrap From 10366cb3bfb0c2c82873ce6876d2bcd7fccdd7e9 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 5 Aug 2024 15:28:32 -0700 Subject: [PATCH 086/160] [srp-client] update `MsgInfo` to include `mMessage` (#10577) This commit updates and renames the `Info` class to `MsgInfo`, which now includes `mMessage` as an `OwnedPtr` to the message to be prepared. This simplifies the code by encapsulating the `Message` and all its associated data needed to prepare an SRP update message into one data structure. This structure can be passed as a single input to different methods preparing various SRP update components, instead of as two separate inputs. The OwnedPtr ensures automatic freeing of the message upon any error. --- src/core/net/srp_client.cpp | 219 +++++++++++++++++++----------------- src/core/net/srp_client.hpp | 36 +++--- 2 files changed, 132 insertions(+), 123 deletions(-) diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index debc43749..504599f2f 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -971,25 +971,32 @@ void Client::SendUpdate(void) /* (7) kRemoved -> */ kRemoved, }; - Error error = kErrorNone; - Message *message = mSocket.NewMessage(); + Error error = kErrorNone; + MsgInfo info; uint32_t length; bool anyChanged; - VerifyOrExit(message != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = PrepareUpdateMessage(*message)); + info.mMessage.Reset(mSocket.NewMessage()); + VerifyOrExit(info.mMessage != nullptr, error = kErrorNoBufs); - length = message->GetLength() + sizeof(Ip6::Udp::Header) + sizeof(Ip6::Header); + SuccessOrExit(error = PrepareUpdateMessage(info)); + + length = info.mMessage->GetLength() + sizeof(Ip6::Udp::Header) + sizeof(Ip6::Header); if (length >= Ip6::kMaxDatagramLength) { LogInfo("Msg len %lu is larger than MTU, enabling single service mode", ToUlong(length)); mSingleServiceMode = true; - IgnoreError(message->SetLength(0)); - SuccessOrExit(error = PrepareUpdateMessage(*message)); + IgnoreError(info.mMessage->SetLength(0)); + SuccessOrExit(error = PrepareUpdateMessage(info)); } - SuccessOrExit(error = mSocket.SendTo(*message, Ip6::MessageInfo())); + SuccessOrExit(error = mSocket.SendTo(*info.mMessage, Ip6::MessageInfo())); + + // Ownership of the message is transferred to the socket upon a + // successful `SendTo()` call. + + info.mMessage.Release(); LogInfo("Send update, msg-id:0x%x", mNextMessageId); @@ -1048,7 +1055,6 @@ void Client::SendUpdate(void) LogInfo("Failed to send update: %s", ErrorToString(error)); mSingleServiceMode = false; - FreeMessage(message); SetState(kStateToRetry); @@ -1075,21 +1081,22 @@ void Client::SendUpdate(void) } } -Error Client::PrepareUpdateMessage(Message &aMessage) +Error Client::PrepareUpdateMessage(MsgInfo &aInfo) { constexpr uint16_t kHeaderOffset = 0; Error error = kErrorNone; Dns::UpdateHeader header; - Info info; - info.Clear(); + aInfo.mDomainNameOffset = MsgInfo::kUnknownOffset; + aInfo.mHostNameOffset = MsgInfo::kUnknownOffset; + aInfo.mRecordCount = 0; #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE - info.mKeyRef.SetKeyRef(kSrpEcdsaKeyRef); - SuccessOrExit(error = ReadOrGenerateKey(info.mKeyRef)); + aInfo.mKeyRef.SetKeyRef(kSrpEcdsaKeyRef); + SuccessOrExit(error = ReadOrGenerateKey(aInfo.mKeyRef)); #else - SuccessOrExit(error = ReadOrGenerateKey(info.mKeyPair)); + SuccessOrExit(error = ReadOrGenerateKey(aInfo.mKeyPair)); #endif header.SetMessageId(mNextMessageId); @@ -1106,29 +1113,29 @@ Error Client::PrepareUpdateMessage(Message &aMessage) header.SetZoneRecordCount(1); header.SetAdditionalRecordCount(1); - SuccessOrExit(error = aMessage.Append(header)); + SuccessOrExit(error = aInfo.mMessage->Append(header)); // Prepare Zone section - info.mDomainNameOffset = aMessage.GetLength(); - SuccessOrExit(error = Dns::Name::AppendName(mDomainName, aMessage)); - SuccessOrExit(error = aMessage.Append(Dns::Zone())); + aInfo.mDomainNameOffset = aInfo.mMessage->GetLength(); + SuccessOrExit(error = Dns::Name::AppendName(mDomainName, *aInfo.mMessage)); + SuccessOrExit(error = aInfo.mMessage->Append(Dns::Zone())); // Prepare Update section - SuccessOrExit(error = AppendServiceInstructions(aMessage, info)); - SuccessOrExit(error = AppendHostDescriptionInstruction(aMessage, info)); + SuccessOrExit(error = AppendServiceInstructions(aInfo)); + SuccessOrExit(error = AppendHostDescriptionInstruction(aInfo)); - header.SetUpdateRecordCount(info.mRecordCount); - aMessage.Write(kHeaderOffset, header); + header.SetUpdateRecordCount(aInfo.mRecordCount); + aInfo.mMessage->Write(kHeaderOffset, header); // Prepare Additional Data section - SuccessOrExit(error = AppendUpdateLeaseOptRecord(aMessage)); - SuccessOrExit(error = AppendSignature(aMessage, info)); + SuccessOrExit(error = AppendUpdateLeaseOptRecord(aInfo)); + SuccessOrExit(error = AppendSignature(aInfo)); header.SetAdditionalRecordCount(2); // Lease OPT and SIG RRs - aMessage.Write(kHeaderOffset, header); + aInfo.mMessage->Write(kHeaderOffset, header); exit: return error; @@ -1183,7 +1190,7 @@ Error Client::ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair) } #endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE -Error Client::AppendServiceInstructions(Message &aMessage, Info &aInfo) +Error Client::AppendServiceInstructions(MsgInfo &aInfo) { Error error = kErrorNone; @@ -1255,7 +1262,7 @@ Error Client::AppendServiceInstructions(Message &aMessage, Info &aInfo) if ((service.GetState() != kRegistered) && CanAppendService(service)) { - SuccessOrExit(error = AppendServiceInstruction(service, aMessage, aInfo)); + SuccessOrExit(error = AppendServiceInstruction(service, aInfo)); if (mSingleServiceMode) { @@ -1278,7 +1285,7 @@ Error Client::AppendServiceInstructions(Message &aMessage, Info &aInfo) // services on the same lease refresh schedule. service.SetState(kToRefresh); - SuccessOrExit(error = AppendServiceInstruction(service, aMessage, aInfo)); + SuccessOrExit(error = AppendServiceInstruction(service, aInfo)); } } } @@ -1342,7 +1349,7 @@ bool Client::CanAppendService(const Service &aService) return canAppend; } -Error Client::AppendServiceInstruction(Service &aService, Message &aMessage, Info &aInfo) +Error Client::AppendServiceInstruction(Service &aService, MsgInfo &aInfo) { Error error = kErrorNone; bool removing = ((aService.GetState() == kToRemove) || (aService.GetState() == kRemoving)); @@ -1360,24 +1367,24 @@ Error Client::AppendServiceInstruction(Service &aService, Message &aMessage, Inf // PTR record // "service name labels" + (pointer to) domain name. - serviceNameOffset = aMessage.GetLength(); - SuccessOrExit(error = Dns::Name::AppendMultipleLabels(aService.GetName(), aMessage)); - SuccessOrExit(error = Dns::Name::AppendPointerLabel(aInfo.mDomainNameOffset, aMessage)); + serviceNameOffset = aInfo.mMessage->GetLength(); + SuccessOrExit(error = Dns::Name::AppendMultipleLabels(aService.GetName(), *aInfo.mMessage)); + SuccessOrExit(error = Dns::Name::AppendPointerLabel(aInfo.mDomainNameOffset, *aInfo.mMessage)); // On remove, we use "Delete an RR from an RRSet" where class is set // to NONE and TTL to zero (RFC 2136 - section 2.5.4). rr.Init(Dns::ResourceRecord::kTypePtr, removing ? Dns::PtrRecord::kClassNone : Dns::PtrRecord::kClassInternet); rr.SetTtl(removing ? 0 : DetermineTtl()); - offset = aMessage.GetLength(); - SuccessOrExit(error = aMessage.Append(rr)); + offset = aInfo.mMessage->GetLength(); + SuccessOrExit(error = aInfo.mMessage->Append(rr)); // "Instance name" + (pointer to) service name. - instanceNameOffset = aMessage.GetLength(); - SuccessOrExit(error = Dns::Name::AppendLabel(aService.GetInstanceName(), aMessage)); - SuccessOrExit(error = Dns::Name::AppendPointerLabel(serviceNameOffset, aMessage)); + instanceNameOffset = aInfo.mMessage->GetLength(); + SuccessOrExit(error = Dns::Name::AppendLabel(aService.GetInstanceName(), *aInfo.mMessage)); + SuccessOrExit(error = Dns::Name::AppendPointerLabel(serviceNameOffset, *aInfo.mMessage)); - UpdateRecordLengthInMessage(rr, offset, aMessage); + UpdateRecordLengthInMessage(rr, offset, *aInfo.mMessage); aInfo.mRecordCount++; if (aService.HasSubType() && !removing) @@ -1389,25 +1396,25 @@ Error Client::AppendServiceInstruction(Service &aService, Message &aMessage, Inf { // subtype label + "_sub" label + (pointer to) service name. - SuccessOrExit(error = Dns::Name::AppendLabel(subTypeLabel, aMessage)); + SuccessOrExit(error = Dns::Name::AppendLabel(subTypeLabel, *aInfo.mMessage)); if (index == 0) { - subServiceNameOffset = aMessage.GetLength(); - SuccessOrExit(error = Dns::Name::AppendLabel("_sub", aMessage)); - SuccessOrExit(error = Dns::Name::AppendPointerLabel(serviceNameOffset, aMessage)); + subServiceNameOffset = aInfo.mMessage->GetLength(); + SuccessOrExit(error = Dns::Name::AppendLabel("_sub", *aInfo.mMessage)); + SuccessOrExit(error = Dns::Name::AppendPointerLabel(serviceNameOffset, *aInfo.mMessage)); } else { - SuccessOrExit(error = Dns::Name::AppendPointerLabel(subServiceNameOffset, aMessage)); + SuccessOrExit(error = Dns::Name::AppendPointerLabel(subServiceNameOffset, *aInfo.mMessage)); } // `rr` is already initialized as PTR. - offset = aMessage.GetLength(); - SuccessOrExit(error = aMessage.Append(rr)); + offset = aInfo.mMessage->GetLength(); + SuccessOrExit(error = aInfo.mMessage->Append(rr)); - SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage)); - UpdateRecordLengthInMessage(rr, offset, aMessage); + SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, *aInfo.mMessage)); + UpdateRecordLengthInMessage(rr, offset, *aInfo.mMessage); aInfo.mRecordCount++; } } @@ -1417,35 +1424,35 @@ Error Client::AppendServiceInstruction(Service &aService, Message &aMessage, Inf // "Delete all RRsets from a name" for Instance Name. - SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage)); - SuccessOrExit(error = AppendDeleteAllRrsets(aMessage)); + SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, *aInfo.mMessage)); + SuccessOrExit(error = AppendDeleteAllRrsets(aInfo)); aInfo.mRecordCount++; VerifyOrExit(!removing); // SRV RR - SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage)); + SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, *aInfo.mMessage)); srv.Init(); srv.SetTtl(DetermineTtl()); srv.SetPriority(aService.GetPriority()); srv.SetWeight(aService.GetWeight()); srv.SetPort(aService.GetPort()); - offset = aMessage.GetLength(); - SuccessOrExit(error = aMessage.Append(srv)); - SuccessOrExit(error = AppendHostName(aMessage, aInfo)); - UpdateRecordLengthInMessage(srv, offset, aMessage); + offset = aInfo.mMessage->GetLength(); + SuccessOrExit(error = aInfo.mMessage->Append(srv)); + SuccessOrExit(error = AppendHostName(aInfo)); + UpdateRecordLengthInMessage(srv, offset, *aInfo.mMessage); aInfo.mRecordCount++; // TXT RR - SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage)); + SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, *aInfo.mMessage)); rr.Init(Dns::ResourceRecord::kTypeTxt); - offset = aMessage.GetLength(); - SuccessOrExit(error = aMessage.Append(rr)); - SuccessOrExit(error = - Dns::TxtEntry::AppendEntries(aService.GetTxtEntries(), aService.GetNumTxtEntries(), aMessage)); - UpdateRecordLengthInMessage(rr, offset, aMessage); + offset = aInfo.mMessage->GetLength(); + SuccessOrExit(error = aInfo.mMessage->Append(rr)); + SuccessOrExit( + error = Dns::TxtEntry::AppendEntries(aService.GetTxtEntries(), aService.GetNumTxtEntries(), *aInfo.mMessage)); + UpdateRecordLengthInMessage(rr, offset, *aInfo.mMessage); aInfo.mRecordCount++; #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE @@ -1455,8 +1462,8 @@ Error Client::AppendServiceInstruction(Service &aService, Message &aMessage, Inf // is added here under `REFERENCE_DEVICE` config and is intended // for testing only. - SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage)); - SuccessOrExit(error = AppendKeyRecord(aMessage, aInfo)); + SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, *aInfo.mMessage)); + SuccessOrExit(error = AppendKeyRecord(aInfo)); } #endif @@ -1464,7 +1471,7 @@ Error Client::AppendServiceInstruction(Service &aService, Message &aMessage, Inf return error; } -Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) +Error Client::AppendHostDescriptionInstruction(MsgInfo &aInfo) { Error error = kErrorNone; @@ -1473,8 +1480,8 @@ Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) // "Delete all RRsets from a name" for Host Name. - SuccessOrExit(error = AppendHostName(aMessage, aInfo)); - SuccessOrExit(error = AppendDeleteAllRrsets(aMessage)); + SuccessOrExit(error = AppendHostName(aInfo)); + SuccessOrExit(error = AppendDeleteAllRrsets(aInfo)); aInfo.mRecordCount++; // AAAA RRs @@ -1491,7 +1498,7 @@ Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) { if (ShouldHostAutoAddressRegister(unicastAddress)) { - SuccessOrExit(error = AppendAaaaRecord(unicastAddress.GetAddress(), aMessage, aInfo)); + SuccessOrExit(error = AppendAaaaRecord(unicastAddress.GetAddress(), aInfo)); unicastAddress.mSrpRegistered = true; mAutoHostAddressCount++; } @@ -1505,7 +1512,7 @@ Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) { Ip6::Netif::UnicastAddress &mlEid = Get().GetMeshLocalEidUnicastAddress(); - SuccessOrExit(error = AppendAaaaRecord(mlEid.GetAddress(), aMessage, aInfo)); + SuccessOrExit(error = AppendAaaaRecord(mlEid.GetAddress(), aInfo)); mlEid.mSrpRegistered = true; mAutoHostAddressCount++; } @@ -1514,20 +1521,20 @@ Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) { for (uint8_t index = 0; index < mHostInfo.GetNumAddresses(); index++) { - SuccessOrExit(error = AppendAaaaRecord(mHostInfo.GetAddress(index), aMessage, aInfo)); + SuccessOrExit(error = AppendAaaaRecord(mHostInfo.GetAddress(index), aInfo)); } } // KEY RR - SuccessOrExit(error = AppendHostName(aMessage, aInfo)); - SuccessOrExit(error = AppendKeyRecord(aMessage, aInfo)); + SuccessOrExit(error = AppendHostName(aInfo)); + SuccessOrExit(error = AppendKeyRecord(aInfo)); exit: return error; } -Error Client::AppendAaaaRecord(const Ip6::Address &aAddress, Message &aMessage, Info &aInfo) const +Error Client::AppendAaaaRecord(const Ip6::Address &aAddress, MsgInfo &aInfo) const { Error error; Dns::ResourceRecord rr; @@ -1536,16 +1543,16 @@ Error Client::AppendAaaaRecord(const Ip6::Address &aAddress, Message &aMessage, rr.SetTtl(DetermineTtl()); rr.SetLength(sizeof(Ip6::Address)); - SuccessOrExit(error = AppendHostName(aMessage, aInfo)); - SuccessOrExit(error = aMessage.Append(rr)); - SuccessOrExit(error = aMessage.Append(aAddress)); + SuccessOrExit(error = AppendHostName(aInfo)); + SuccessOrExit(error = aInfo.mMessage->Append(rr)); + SuccessOrExit(error = aInfo.mMessage->Append(aAddress)); aInfo.mRecordCount++; exit: return error; } -Error Client::AppendKeyRecord(Message &aMessage, Info &aInfo) const +Error Client::AppendKeyRecord(MsgInfo &aInfo) const { Error error; Dns::KeyRecord key; @@ -1558,20 +1565,20 @@ Error Client::AppendKeyRecord(Message &aMessage, Info &aInfo) const key.SetProtocol(Dns::KeyRecord::kProtocolDnsSec); key.SetAlgorithm(Dns::KeyRecord::kAlgorithmEcdsaP256Sha256); key.SetLength(sizeof(Dns::KeyRecord) - sizeof(Dns::ResourceRecord) + sizeof(Crypto::Ecdsa::P256::PublicKey)); - SuccessOrExit(error = aMessage.Append(key)); + SuccessOrExit(error = aInfo.mMessage->Append(key)); #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE SuccessOrExit(error = aInfo.mKeyRef.GetPublicKey(publicKey)); #else SuccessOrExit(error = aInfo.mKeyPair.GetPublicKey(publicKey)); #endif - SuccessOrExit(error = aMessage.Append(publicKey)); + SuccessOrExit(error = aInfo.mMessage->Append(publicKey)); aInfo.mRecordCount++; exit: return error; } -Error Client::AppendDeleteAllRrsets(Message &aMessage) const +Error Client::AppendDeleteAllRrsets(MsgInfo &aInfo) const { // "Delete all RRsets from a name" (RFC 2136 - 2.5.3) // Name should be already appended in the message. @@ -1582,10 +1589,10 @@ Error Client::AppendDeleteAllRrsets(Message &aMessage) const rr.SetTtl(0); rr.SetLength(0); - return aMessage.Append(rr); + return aInfo.mMessage->Append(rr); } -Error Client::AppendHostName(Message &aMessage, Info &aInfo, bool aDoNotCompress) const +Error Client::AppendHostName(MsgInfo &aInfo, bool aDoNotCompress) const { Error error; @@ -1593,8 +1600,8 @@ Error Client::AppendHostName(Message &aMessage, Info &aInfo, bool aDoNotCompress { // Uncompressed (canonical form) of host name is used for SIG(0) // calculation. - SuccessOrExit(error = Dns::Name::AppendMultipleLabels(mHostInfo.GetName(), aMessage)); - error = Dns::Name::AppendName(mDomainName, aMessage); + SuccessOrExit(error = Dns::Name::AppendMultipleLabels(mHostInfo.GetName(), *aInfo.mMessage)); + error = Dns::Name::AppendName(mDomainName, *aInfo.mMessage); ExitNow(); } @@ -1602,20 +1609,20 @@ Error Client::AppendHostName(Message &aMessage, Info &aInfo, bool aDoNotCompress // compressed as pointer to the previous one. Otherwise, // append it and remember the offset. - if (aInfo.mHostNameOffset != Info::kUnknownOffset) + if (aInfo.mHostNameOffset != MsgInfo::kUnknownOffset) { - ExitNow(error = Dns::Name::AppendPointerLabel(aInfo.mHostNameOffset, aMessage)); + ExitNow(error = Dns::Name::AppendPointerLabel(aInfo.mHostNameOffset, *aInfo.mMessage)); } - aInfo.mHostNameOffset = aMessage.GetLength(); - SuccessOrExit(error = Dns::Name::AppendMultipleLabels(mHostInfo.GetName(), aMessage)); - error = Dns::Name::AppendPointerLabel(aInfo.mDomainNameOffset, aMessage); + aInfo.mHostNameOffset = aInfo.mMessage->GetLength(); + SuccessOrExit(error = Dns::Name::AppendMultipleLabels(mHostInfo.GetName(), *aInfo.mMessage)); + error = Dns::Name::AppendPointerLabel(aInfo.mDomainNameOffset, *aInfo.mMessage); exit: return error; } -Error Client::AppendUpdateLeaseOptRecord(Message &aMessage) +Error Client::AppendUpdateLeaseOptRecord(MsgInfo &aInfo) { Error error; Dns::OptRecord optRecord; @@ -1623,7 +1630,7 @@ Error Client::AppendUpdateLeaseOptRecord(Message &aMessage) uint16_t optionSize; // Append empty (root domain) as OPT RR name. - SuccessOrExit(error = Dns::Name::AppendTerminator(aMessage)); + SuccessOrExit(error = Dns::Name::AppendTerminator(*aInfo.mMessage)); // `Init()` sets the type and clears (set to zero) the extended // Response Code, version and all flags. @@ -1648,14 +1655,14 @@ Error Client::AppendUpdateLeaseOptRecord(Message &aMessage) optRecord.SetLength(optionSize); - SuccessOrExit(error = aMessage.Append(optRecord)); - error = aMessage.AppendBytes(&leaseOption, optionSize); + SuccessOrExit(error = aInfo.mMessage->Append(optRecord)); + error = aInfo.mMessage->AppendBytes(&leaseOption, optionSize); exit: return error; } -Error Client::AppendSignature(Message &aMessage, Info &aInfo) +Error Client::AppendSignature(MsgInfo &aInfo) { Error error; Dns::SigRecord sig; @@ -1679,9 +1686,9 @@ Error Client::AppendSignature(Message &aMessage, Info &aInfo) // as the signer's name. This is used for SIG(0) calculation only. // It will be overwritten with host name compressed. - offset = aMessage.GetLength(); - SuccessOrExit(error = aMessage.Append(sig)); - SuccessOrExit(error = AppendHostName(aMessage, aInfo, /* aDoNotCompress */ true)); + offset = aInfo.mMessage->GetLength(); + SuccessOrExit(error = aInfo.mMessage->Append(sig)); + SuccessOrExit(error = AppendHostName(aInfo, /* aDoNotCompress */ true)); // Calculate signature (RFC 2931): Calculated over "data" which is // concatenation of (1) the SIG RR RDATA wire format (including @@ -1693,11 +1700,11 @@ Error Client::AppendSignature(Message &aMessage, Info &aInfo) sha256.Start(); // (1) SIG RR RDATA wire format - len = aMessage.GetLength() - offset - sizeof(Dns::ResourceRecord); - sha256.Update(aMessage, offset + sizeof(Dns::ResourceRecord), len); + len = aInfo.mMessage->GetLength() - offset - sizeof(Dns::ResourceRecord); + sha256.Update(*aInfo.mMessage, offset + sizeof(Dns::ResourceRecord), len); // (2) Message from DNS header before SIG - sha256.Update(aMessage, 0, offset); + sha256.Update(*aInfo.mMessage, 0, offset); sha256.Finish(hash); #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE @@ -1709,16 +1716,16 @@ Error Client::AppendSignature(Message &aMessage, Info &aInfo) // Move back in message and append SIG RR now with compressed host // name (as signer's name) along with the calculated signature. - IgnoreError(aMessage.SetLength(offset)); + IgnoreError(aInfo.mMessage->SetLength(offset)); // SIG(0) uses owner name of root (single zero byte). - SuccessOrExit(error = Dns::Name::AppendTerminator(aMessage)); + SuccessOrExit(error = Dns::Name::AppendTerminator(*aInfo.mMessage)); - offset = aMessage.GetLength(); - SuccessOrExit(error = aMessage.Append(sig)); - SuccessOrExit(error = AppendHostName(aMessage, aInfo)); - SuccessOrExit(error = aMessage.Append(signature)); - UpdateRecordLengthInMessage(sig, offset, aMessage); + offset = aInfo.mMessage->GetLength(); + SuccessOrExit(error = aInfo.mMessage->Append(sig)); + SuccessOrExit(error = AppendHostName(aInfo)); + SuccessOrExit(error = aInfo.mMessage->Append(signature)); + UpdateRecordLengthInMessage(sig, offset, *aInfo.mMessage); exit: return error; diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index 284ff789c..ddb013be0 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -45,6 +45,7 @@ #include "common/non_copyable.hpp" #include "common/notifier.hpp" #include "common/numeric_limits.hpp" +#include "common/owned_ptr.hpp" #include "common/timer.hpp" #include "crypto/ecdsa.hpp" #include "net/dns_types.hpp" @@ -1043,17 +1044,18 @@ class Client : public InstanceLocator, private NonCopyable }; #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE - struct Info : public Clearable + struct MsgInfo { - static constexpr uint16_t kUnknownOffset = 0; // Unknown offset value (used when offset is not yet set). + static constexpr uint16_t kUnknownOffset = 0; - uint16_t mDomainNameOffset; // Offset of domain name serialization - uint16_t mHostNameOffset; // Offset of host name serialization. - uint16_t mRecordCount; // Number of resource records in Update section. + OwnedPtr mMessage; + uint16_t mDomainNameOffset; + uint16_t mHostNameOffset; + uint16_t mRecordCount; #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE - Crypto::Ecdsa::P256::KeyPairAsRef mKeyRef; // The ECDSA key ref for key-pair. + Crypto::Ecdsa::P256::KeyPairAsRef mKeyRef; #else - Crypto::Ecdsa::P256::KeyPair mKeyPair; // The ECDSA key pair. + Crypto::Ecdsa::P256::KeyPair mKeyPair; #endif }; @@ -1075,22 +1077,22 @@ class Client : public InstanceLocator, private NonCopyable void InvokeCallback(Error aError, const HostInfo &aHostInfo, const Service *aRemovedServices) const; void HandleHostInfoOrServiceChange(void); void SendUpdate(void); - Error PrepareUpdateMessage(Message &aMessage); + Error PrepareUpdateMessage(MsgInfo &aInfo); #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE Error ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPairAsRef &aKeyRef); #else Error ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair); #endif - Error AppendServiceInstructions(Message &aMessage, Info &aInfo); + Error AppendServiceInstructions(MsgInfo &aInfo); bool CanAppendService(const Service &aService); - Error AppendServiceInstruction(Service &aService, Message &aMessage, Info &aInfo); - Error AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo); - Error AppendKeyRecord(Message &aMessage, Info &aInfo) const; - Error AppendDeleteAllRrsets(Message &aMessage) const; - Error AppendHostName(Message &aMessage, Info &aInfo, bool aDoNotCompress = false) const; - Error AppendAaaaRecord(const Ip6::Address &aAddress, Message &aMessage, Info &aInfo) const; - Error AppendUpdateLeaseOptRecord(Message &aMessage); - Error AppendSignature(Message &aMessage, Info &aInfo); + Error AppendServiceInstruction(Service &aService, MsgInfo &aInfo); + Error AppendHostDescriptionInstruction(MsgInfo &aInfo); + Error AppendKeyRecord(MsgInfo &aInfo) const; + Error AppendDeleteAllRrsets(MsgInfo &aInfo) const; + Error AppendHostName(MsgInfo &aInfo, bool aDoNotCompress = false) const; + Error AppendAaaaRecord(const Ip6::Address &aAddress, MsgInfo &aInfo) const; + Error AppendUpdateLeaseOptRecord(MsgInfo &aInfo); + Error AppendSignature(MsgInfo &aInfo); void UpdateRecordLengthInMessage(Dns::ResourceRecord &aRecord, uint16_t aOffset, Message &aMessage) const; void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void ProcessResponse(Message &aMessage); From 1d35e30e0e3f60ad9242c52584287d5a6ff16120 Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Tue, 6 Aug 2024 22:49:27 +0800 Subject: [PATCH 087/160] [thread-cert] output docker stdout/stderr only when VERBOSE is non-zero (#10574) This commit supresses the docker output unless verbose mode is enabled, which helps preventing unnecessary output from cluttering the console. docker output is enabled when: * env VERBOSE is set to a non-zero value docker output is suppressed (redirecting to /dev/null) when: * VERBOSE is not explicitly set in env, or * env VERBOSE is set to 0 --- tests/scripts/thread-cert/node.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 9f7bca092..35a0aa5ae 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -139,7 +139,10 @@ def _launch_docker(self): f'trel://{config.BACKBONE_IFNAME}', ] + nat64_prefix logging.info(' '.join(cmd)) - self._docker_proc = subprocess.Popen(cmd, stdin=subprocess.DEVNULL, stdout=sys.stdout, stderr=sys.stderr) + self._docker_proc = subprocess.Popen(cmd, + stdin=subprocess.DEVNULL, + stdout=sys.stdout if self.verbose else subprocess.DEVNULL, + stderr=sys.stderr if self.verbose else subprocess.DEVNULL) launch_docker_deadline = time.time() + 300 launch_ok = False From 634745dd7244e0fd9963e472fa7b3ffe445982c3 Mon Sep 17 00:00:00 2001 From: Mia Yang <145632982+mia1yang@users.noreply.github.com> Date: Tue, 6 Aug 2024 14:54:02 +0000 Subject: [PATCH 088/160] [test] verify state bitmap when runtime enable/disable EphemeralKey mode (#10503) Meshcop ePSKc mode supported bitmap check. --- .../border_router/test_publish_meshcop_service.py | 13 +++++++++++++ tests/scripts/thread-cert/node.py | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py index 26b9b1408..77979dbb5 100755 --- a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py +++ b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py @@ -95,7 +95,19 @@ def test(self): br1.start() self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) + + # start to test ephemeral key mode (ePSKc) + br1.ephemeral_key_enabled = True + self.assertTrue(br1.ephemeral_key_enabled) + self.simulator.go(10) + self.check_meshcop_service(br1, host) + + # change ephemeral key mode (ePSKc) and check Meshcop + br1.ephemeral_key_enabled = False + self.assertFalse(br1.ephemeral_key_enabled) + self.simulator.go(10) self.check_meshcop_service(br1, host) + # end of ephemeral key mode (ePSKc) test br1.disable_backbone_router() self.simulator.go(10) @@ -201,6 +213,7 @@ def check_meshcop_service_by_data(self, br, service_data): br.get_backbone_router_state() != 'Disabled') # BBR is enabled or not self.assertEqual((state_bitmap >> 8 & 1), device_role not in ['disabled', 'detached'] and br.get_backbone_router_state() == 'Primary') # BBR is primary or not + self.assertEqual(bool(state_bitmap >> 11 & 1), br.ephemeral_key_enabled) # ePSKc is supported or not self.assertEqual(service_data['txt']['nn'], br.get_network_name()) self.assertEqual(service_data['txt']['rv'], '1') self.assertIn(service_data['txt']['tv'], ['1.1.0', '1.1.1', '1.2.0', '1.3.0']) diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 35a0aa5ae..8a5f370ab 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -491,6 +491,16 @@ def dns_upstream_query_state(self, value): raise ValueError("dns_upstream_query_state must be a bool") return self.set_dbus_property('DnsUpstreamQueryState', value) + @property + def ephemeral_key_enabled(self): + return bool(self.get_dbus_property('EphemeralKeyEnabled')) + + @ephemeral_key_enabled.setter + def ephemeral_key_enabled(self, value): + if type(value) is not bool: + raise ValueError("ephemeral_key_enabled must be a bool") + return self.set_dbus_property('EphemeralKeyEnabled', value) + def read_border_routing_counters_delta(self): old_counters = self._border_routing_counters new_counters = self.get_border_routing_counters() From 19dadd9c4524700a0747e1f40e6e1f36b80c9ce9 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 6 Aug 2024 07:56:26 -0700 Subject: [PATCH 089/160] [srp-client] add `KeyInfo` type alias for `KeyPair` or `KeyPairAsRef` (#10579) This commit introduces the `KeyInfo` type, which is a `typedef` to either `KeyPair` or `KeyPairAsRef` depending on whether the feature `KEY_REFERENCES_ENABLE` is enabled. This simplifies the code, removes repeated code, and eliminates extra `#if` checks when `KeyInfo` is used. --- src/core/net/srp_client.cpp | 39 ++++++++++--------------- src/core/net/srp_client.hpp | 58 ++++++++++++++++++------------------- 2 files changed, 43 insertions(+), 54 deletions(-) diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index 504599f2f..15c6c6efc 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -1093,12 +1093,11 @@ Error Client::PrepareUpdateMessage(MsgInfo &aInfo) aInfo.mRecordCount = 0; #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE - aInfo.mKeyRef.SetKeyRef(kSrpEcdsaKeyRef); - SuccessOrExit(error = ReadOrGenerateKey(aInfo.mKeyRef)); -#else - SuccessOrExit(error = ReadOrGenerateKey(aInfo.mKeyPair)); + aInfo.mKeyInfo.SetKeyRef(kSrpEcdsaKeyRef); #endif + SuccessOrExit(error = ReadOrGenerateKey(aInfo.mKeyInfo)); + header.SetMessageId(mNextMessageId); // SRP Update (DNS Update) message must have exactly one record in @@ -1142,48 +1141,48 @@ Error Client::PrepareUpdateMessage(MsgInfo &aInfo) } #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE -Error Client::ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPairAsRef &aKeyRef) +Error Client::ReadOrGenerateKey(KeyInfo &aKeyInfo) { Error error = kErrorNone; Crypto::Ecdsa::P256::KeyPair keyPair; - VerifyOrExit(!Crypto::Storage::HasKey(aKeyRef.GetKeyRef())); + VerifyOrExit(!Crypto::Storage::HasKey(aKeyInfo.GetKeyRef())); error = Get().Read(keyPair); if (error == kErrorNone) { - if (aKeyRef.ImportKeyPair(keyPair) != kErrorNone) + if (aKeyInfo.ImportKeyPair(keyPair) != kErrorNone) { - SuccessOrExit(error = aKeyRef.Generate()); + SuccessOrExit(error = aKeyInfo.Generate()); } IgnoreError(Get().Delete()); } else { - SuccessOrExit(error = aKeyRef.Generate()); + SuccessOrExit(error = aKeyInfo.Generate()); } exit: return error; } #else -Error Client::ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair) +Error Client::ReadOrGenerateKey(KeyInfo &aKeyInfo) { Error error; - error = Get().Read(aKeyPair); + error = Get().Read(aKeyInfo); if (error == kErrorNone) { Crypto::Ecdsa::P256::PublicKey publicKey; - if (aKeyPair.GetPublicKey(publicKey) == kErrorNone) + if (aKeyInfo.GetPublicKey(publicKey) == kErrorNone) { ExitNow(); } } - SuccessOrExit(error = aKeyPair.Generate()); - IgnoreError(Get().Save(aKeyPair)); + SuccessOrExit(error = aKeyInfo.Generate()); + IgnoreError(Get().Save(aKeyInfo)); exit: return error; @@ -1566,11 +1565,7 @@ Error Client::AppendKeyRecord(MsgInfo &aInfo) const key.SetAlgorithm(Dns::KeyRecord::kAlgorithmEcdsaP256Sha256); key.SetLength(sizeof(Dns::KeyRecord) - sizeof(Dns::ResourceRecord) + sizeof(Crypto::Ecdsa::P256::PublicKey)); SuccessOrExit(error = aInfo.mMessage->Append(key)); -#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE - SuccessOrExit(error = aInfo.mKeyRef.GetPublicKey(publicKey)); -#else - SuccessOrExit(error = aInfo.mKeyPair.GetPublicKey(publicKey)); -#endif + SuccessOrExit(error = aInfo.mKeyInfo.GetPublicKey(publicKey)); SuccessOrExit(error = aInfo.mMessage->Append(publicKey)); aInfo.mRecordCount++; @@ -1707,11 +1702,7 @@ Error Client::AppendSignature(MsgInfo &aInfo) sha256.Update(*aInfo.mMessage, 0, offset); sha256.Finish(hash); -#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE - SuccessOrExit(error = aInfo.mKeyRef.Sign(hash, signature)); -#else - SuccessOrExit(error = aInfo.mKeyPair.Sign(hash, signature)); -#endif + SuccessOrExit(error = aInfo.mKeyInfo.Sign(hash, signature)); // Move back in message and append SIG RR now with compressed host // name (as signer's name) along with the calculated signature. diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index ddb013be0..d3d28ebd3 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -966,6 +966,12 @@ class Client : public InstanceLocator, private NonCopyable kForServicesAppendedInMessage, }; +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + typedef Crypto::Ecdsa::P256::KeyPairAsRef KeyInfo; +#else + typedef Crypto::Ecdsa::P256::KeyPair KeyInfo; +#endif + class TxJitter : public Clearable { // Manages the random TX jitter to use when sending SRP update @@ -1052,37 +1058,29 @@ class Client : public InstanceLocator, private NonCopyable uint16_t mDomainNameOffset; uint16_t mHostNameOffset; uint16_t mRecordCount; -#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE - Crypto::Ecdsa::P256::KeyPairAsRef mKeyRef; -#else - Crypto::Ecdsa::P256::KeyPair mKeyPair; -#endif + KeyInfo mKeyInfo; }; - Error Start(const Ip6::SockAddr &aServerSockAddr, Requester aRequester); - void Stop(Requester aRequester, StopMode aMode); - void Resume(void); - void Pause(void); - void HandleNotifierEvents(Events aEvents); - void HandleRoleChanged(void); - void HandleUnicastAddressEvent(Ip6::Netif::AddressEvent aEvent, const Ip6::Netif::UnicastAddress &aAddress); - bool ShouldUpdateHostAutoAddresses(void) const; - bool ShouldHostAutoAddressRegister(const Ip6::Netif::UnicastAddress &aUnicastAddress) const; - Error UpdateHostInfoStateOnAddressChange(void); - void UpdateServiceStateToRemove(Service &aService); - State GetState(void) const { return mState; } - void SetState(State aState); - bool ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStateChangeMode aMode); - void InvokeCallback(Error aError) const; - void InvokeCallback(Error aError, const HostInfo &aHostInfo, const Service *aRemovedServices) const; - void HandleHostInfoOrServiceChange(void); - void SendUpdate(void); - Error PrepareUpdateMessage(MsgInfo &aInfo); -#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE - Error ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPairAsRef &aKeyRef); -#else - Error ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair); -#endif + Error Start(const Ip6::SockAddr &aServerSockAddr, Requester aRequester); + void Stop(Requester aRequester, StopMode aMode); + void Resume(void); + void Pause(void); + void HandleNotifierEvents(Events aEvents); + void HandleRoleChanged(void); + void HandleUnicastAddressEvent(Ip6::Netif::AddressEvent aEvent, const Ip6::Netif::UnicastAddress &aAddress); + bool ShouldUpdateHostAutoAddresses(void) const; + bool ShouldHostAutoAddressRegister(const Ip6::Netif::UnicastAddress &aUnicastAddress) const; + Error UpdateHostInfoStateOnAddressChange(void); + void UpdateServiceStateToRemove(Service &aService); + State GetState(void) const { return mState; } + void SetState(State aState); + bool ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStateChangeMode aMode); + void InvokeCallback(Error aError) const; + void InvokeCallback(Error aError, const HostInfo &aHostInfo, const Service *aRemovedServices) const; + void HandleHostInfoOrServiceChange(void); + void SendUpdate(void); + Error PrepareUpdateMessage(MsgInfo &aInfo); + Error ReadOrGenerateKey(KeyInfo &aKeyInfo); Error AppendServiceInstructions(MsgInfo &aInfo); bool CanAppendService(const Service &aService); Error AppendServiceInstruction(Service &aService, MsgInfo &aInfo); @@ -1123,7 +1121,7 @@ class Client : public InstanceLocator, private NonCopyable static const char *StateToString(State aState); void LogRetryWaitInterval(void) const; #else - void LogRetryWaitInterval(void) const {} + void LogRetryWaitInterval(void) const {} #endif static const char kDefaultDomainName[]; From cc16fc2d677cd0b5cb6a2ca6fcc16b741f6a54d9 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Thu, 8 Aug 2024 11:08:33 +0800 Subject: [PATCH 090/160] [otci] add link metrics commands to otci (#10580) --- tools/otci/otci/otci.py | 137 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 130 insertions(+), 7 deletions(-) diff --git a/tools/otci/otci/otci.py b/tools/otci/otci/otci.py index 46a21eb3f..90097956c 100644 --- a/tools/otci/otci/otci.py +++ b/tools/otci/otci/otci.py @@ -2148,13 +2148,136 @@ def set_domain_name(self, name: str): # # Link metrics management # - # TODO: linkmetrics mgmt forward [ldraX][pqmr] - # TODO: linkmetrics probe - # TODO: linkmetrics query single [pqmr] - # TODO: linkmetrics query forward - # TODO: linkquality - # TODO: linkquality - # + + def linkmetrics_config_enhanced_ack_clear(self, peer_addr: Union[str, Ip6Addr]) -> bool: + output = self.execute_command(f'linkmetrics config {peer_addr} enhanced-ack clear') + return self.__parse_linkmetrics_mgmt_response(peer_addr, output) + + def linkmetrics_config_enhanced_ack_register(self, + peer_addr: Union[str, Ip6Addr], + link_metrics_flags: str, + reference: bool = False) -> bool: + if self.__valid_flags(link_metrics_flags, 'qmr') is False: + raise ValueError(link_metrics_flags) + + output = self.execute_command( + f'linkmetrics config {peer_addr} enhanced-ack register {link_metrics_flags} {"r" if reference else ""}') + return self.__parse_linkmetrics_mgmt_response(peer_addr, output) + + def linkmetrics_config_forward(self, peer_addr: Union[str, Ip6Addr], seriesid: int, series_flags: str, + link_metrics_flags: str) -> bool: + if self.__valid_flags(series_flags, 'ldraX') is False: + raise ValueError(series_flags) + + if self.__valid_flags(link_metrics_flags, 'pqmr') is False: + raise ValueError(link_metrics_flags) + + output = self.execute_command( + f'linkmetrics config {peer_addr} forward {seriesid} {series_flags} {link_metrics_flags}') + return self.__parse_linkmetrics_mgmt_response(peer_addr, output) + + def linkmetrics_probe(self, peer_addr: Union[str, Ip6Addr], seriesid: int, length: int): + if length < 0 or length > 64: + raise ValueError(length) + + self.execute_command(f'linkmetrics probe {peer_addr} {seriesid} {length}') + + def linkmetrics_request_single(self, peer_addr: Union[str, Ip6Addr], link_metrics_flags: str) -> Dict[str, int]: + if self.__valid_flags(link_metrics_flags, 'pqmr') is False: + raise ValueError(link_metrics_flags) + + output = self.execute_command(f'linkmetrics request {peer_addr} single {link_metrics_flags}') + return self.__parse_linkmetrics_report(peer_addr, output) + + def linkmetrics_request_forward(self, peer_addr: Union[str, Ip6Addr], seriesid: int) -> Dict[str, int]: + output = self.execute_command(f'linkmetrics request {peer_addr} forward {seriesid}') + return self.__parse_linkmetrics_report(peer_addr, output) + + def __parse_linkmetrics_mgmt_response(self, peer_addr: Union[str, Ip6Addr], output: List[str]) -> bool: + # + # Example output: + # + # Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 + # Status: Success + # Done + # + + status = '' + report_received = False + ret = False + + for line in output: + if 'Received Link Metrics Management Response from' in line: + address = line.split(': ')[1].strip() + report_received = address == peer_addr + elif 'Status' in line: + status = line.split(':')[1].strip() + + return report_received and status == 'Success' + + def __parse_linkmetrics_report(self, peer_addr: Union[str, Ip6Addr], output: List[str]) -> Dict[str, int]: + # + # Example output: + # + # Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 + # + # - PDU Counter: 2 (Count/Summation) + # - LQI: 76 (Exponential Moving Average) + # - Margin: 82 (dB) (Exponential Moving Average) + # - RSSI: -18 (dBm) (Exponential Moving Average) + # Done + # + + results = {} + report_received = False + + for line in output: + if 'Received Link Metrics Report' in line: + address = line.split(': ')[1].strip() + report_received = address == peer_addr + elif 'Received Link Metrics data in Enh Ack from neighbor' in line: + # If the Enhanced-ACK Based Probing is enabled, the CLI will output the following + # link metrics info after executing the `linkmetrics request` command. This case is + # used to skip these Enhanced-ACK related link metrics info. + # + # Received Link Metrics data in Enh Ack from neighbor, short address:0x3400 , extended address:c6a24d6514cf9178 + # - LQI: 224 (Exponential Moving Average) + # - Margin: 0 (dB) (Exponential Moving Average) + # + # Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 + # + # - PDU Counter: 2 (Count/Summation) + # - LQI: 76 (Exponential Moving Average) + # - Margin: 82 (dB) (Exponential Moving Average) + # - RSSI: -18 (dBm) (Exponential Moving Average) + # Done + # + report_received = False + + if not report_received: + continue + + if '- LQI' in line: + results['lqi'] = self.__parse_numbers(line)[0] + elif '- Margin' in line: + results['margin'] = self.__parse_numbers(line)[0] + elif '- RSSI' in line: + results['rssi'] = self.__parse_numbers(line)[0] + elif '- PDU Counter' in line: + results['pdu_counter'] = self.__parse_numbers(line)[0] + + return results + + def __parse_numbers(self, line: str) -> List[int]: + values = re.findall("\-?\d+", line) + return list(map(int, values)) + + def __valid_flags(self, flags: str, flags_set: str): + # check for duplicate chars + if len(flags) != len(set(flags)): + return False + + return set(flags).issubset(set(flags_set)) # # Logging From d034b5c850ae33be917412f26399af76f3612c96 Mon Sep 17 00:00:00 2001 From: Li Cao Date: Thu, 8 Aug 2024 11:43:22 +0800 Subject: [PATCH 091/160] [spinel] add spinel property to send mgmt set dataset in TLVs format (#10587) This commit adds a new spinel property `SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS` to do network migration operation on NCP. The existing property `SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET` cannot be used for two reasons: 1. It uses a structured format instead of raw TLVs format. 2. The set handler of `SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET` doesn't call `otDatasetSendMgmtPendingSet` with a callback. On NCP, we want to know the result of the MGMT_SET operation. (accepted, rejected or timeout) This new property works similarly as `SPINEL_PROP_NET_LEAVE_GRACEFULLY` added in https://github.com/openthread/openthread/pull/10337 1. Host sets the property to NCP. 2. NCP will give an immediate response on the result of `otDatasetSendMgmtPendingSet`. - If succeeded, NCP will respond an empty property `SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS` - If failed, NCP will respond a LAST_STATUS of the error info. 3. When the callback of `otDatasetSendMgmtPendingSet` is called, NCP will send a notification of `SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS` to the host to notify the result of the operation. --- src/lib/spinel/spinel.c | 1 + src/lib/spinel/spinel.h | 19 +++++++++++++++++ src/ncp/changed_props_set.cpp | 1 + src/ncp/ncp_base.cpp | 1 + src/ncp/ncp_base.hpp | 6 ++++++ src/ncp/ncp_base_dispatcher.cpp | 2 ++ src/ncp/ncp_base_mtd.cpp | 36 +++++++++++++++++++++++++++++++++ 7 files changed, 66 insertions(+) diff --git a/src/lib/spinel/spinel.c b/src/lib/spinel/spinel.c index 03c107c5a..dc9e2ca25 100644 --- a/src/lib/spinel/spinel.c +++ b/src/lib/spinel/spinel.c @@ -1363,6 +1363,7 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) {SPINEL_PROP_THREAD_BACKBONE_ROUTER_LOCAL_REGISTER, "THREAD_BACKBONE_ROUTER_LOCAL_REGISTER"}, {SPINEL_PROP_THREAD_BACKBONE_ROUTER_LOCAL_REGISTRATION_JITTER, "THREAD_BACKBONE_ROUTER_LOCAL_REGISTRATION_JITTER"}, + {SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS, "THREAD_MGMT_SET_PENDING_DATASET_TLVS"}, {SPINEL_PROP_MESHCOP_JOINER_STATE, "MESHCOP_JOINER_STATE"}, {SPINEL_PROP_MESHCOP_JOINER_COMMISSIONING, "MESHCOP_JOINER_COMMISSIONING"}, {SPINEL_PROP_IPV6_LL_ADDR, "IPV6_LL_ADDR"}, diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index d04743dcb..f21a7a4e8 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -3412,6 +3412,25 @@ enum */ SPINEL_PROP_THREAD_PENDING_DATASET_TLVS = SPINEL_PROP_THREAD_EXT__BEGIN + 61, + /// Send MGMT_SET Thread Pending Operational Dataset (in TLV format). + /** Format: `D` - Write only + * + * This is write-only property. When written, it triggers a MGMT_PENDING_SET meshcop command to be sent to leader + * with the given Dataset. + * + * When setting this property, the spinel frame response will be: + * 1. A `LAST_STATUS` with the status of the transmission of MGMT_PENDING_SET command if it fails. + * 2. A `SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS` with no content. + * + * On response reception or timeout, another notification will be sent to the host: + * A `SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS` with a spinel_status_t indicating + * the result of MGMT_SET_PENDING. + * + * On write, any unknown/unsupported TLVs must be ignored. + * + */ + SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS = SPINEL_PROP_THREAD_EXT__BEGIN + 62, + SPINEL_PROP_THREAD_EXT__END = 0x1600, SPINEL_PROP_IPV6__BEGIN = 0x60, diff --git a/src/ncp/changed_props_set.cpp b/src/ncp/changed_props_set.cpp index f81bbf0d1..a1afabc1e 100644 --- a/src/ncp/changed_props_set.cpp +++ b/src/ncp/changed_props_set.cpp @@ -92,6 +92,7 @@ const ChangedPropsSet::Entry ChangedPropsSet::mSupportedProps[] = { {SPINEL_PROP_THREAD_NETWORK_TIME, SPINEL_STATUS_OK, false}, #endif {SPINEL_PROP_PARENT_RESPONSE_INFO, SPINEL_STATUS_OK, true}, + {SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS, SPINEL_STATUS_OK, false}, }; uint8_t ChangedPropsSet::GetNumEntries(void) const diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index a29da84e5..6896878fe 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -315,6 +315,7 @@ NcpBase::NcpBase(Instance *aInstance) , mRxSpinelOutOfOrderTidCounter(0) , mTxSpinelFrameCounter(0) , mDidInitialUpdates(false) + , mDatasetSendMgmtPendingSetResult(SPINEL_STATUS_OK) , mLogTimestampBase(0) #if OPENTHREAD_CONFIG_DIAG_ENABLE , mDiagOutput(nullptr) diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index 831705791..c326b8e36 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -624,6 +624,10 @@ class NcpBase void ThreadDetachGracefullyHandler(void); + static void DatasetSendMgmtPendingSetHandler(otError aResult, void *aContext); + + void DatasetSendMgmtPendingSetHandler(otError aResult); + protected: static NcpBase *sNcpInstance; static spinel_status_t ThreadErrorToSpinelStatus(otError aError); @@ -738,6 +742,8 @@ class NcpBase bool mDidInitialUpdates; + spinel_status_t mDatasetSendMgmtPendingSetResult; + uint64_t mLogTimestampBase; // Timestamp base used for logging #if OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index 5d2362b0c..c7b7c1fe8 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -336,6 +336,7 @@ NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey) #endif OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_THREAD_PENDING_DATASET_TLVS), + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS), #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_CHANNEL_MANAGER_NEW_CHANNEL), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_CHANNEL_MANAGER_DELAY), @@ -607,6 +608,7 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) #endif OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_THREAD_PENDING_DATASET_TLVS), + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS), #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MESHCOP_COMMISSIONER_ANNOUNCE_BEGIN), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MESHCOP_COMMISSIONER_ENERGY_SCAN), diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index fe15134c7..3c1faf7a6 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -1739,6 +1739,42 @@ template <> otError NcpBase::HandlePropertySet(aContext)->DatasetSendMgmtPendingSetHandler(aResult); +} + +void NcpBase::DatasetSendMgmtPendingSetHandler(otError aResult) +{ + mDatasetSendMgmtPendingSetResult = ThreadErrorToSpinelStatus(aResult); + mChangedPropsSet.AddProperty(SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS); + mUpdateChangedPropsTask.Post(); +} + +template <> otError NcpBase::HandlePropertyGet(void) +{ + return mEncoder.WriteUint32(mDatasetSendMgmtPendingSetResult); +} + +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error = OT_ERROR_NONE; + otOperationalDataset emptyDataset; + const uint8_t *data; + uint16_t len; + + memset(&emptyDataset, 0, sizeof(emptyDataset)); + + SuccessOrExit(error = mDecoder.ReadData(data, len)); + VerifyOrExit(len < OT_OPERATIONAL_DATASET_MAX_LENGTH, error = OT_ERROR_PARSE); + + error = otDatasetSendMgmtPendingSet(mInstance, &emptyDataset, data, static_cast(len), + DatasetSendMgmtPendingSetHandler, this); + +exit: + return error; +} + template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; From eb468b4e6d0cf1c50f0d759b36aa5d36fdea1816 Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Thu, 8 Aug 2024 22:37:19 +0800 Subject: [PATCH 092/160] [gitignore] ignore files in ot_testing folder (#10591) ot_testing/* are generated when running "script/test cert_suite" --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 779dedc59..7374eedf6 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ __pycache__ # Unit test files CTestTestfile.cmake ot-test-* +ot_testing/ Testing/ # Misc From 6635201aa190c3f8f929d0e2f5a7543134537ba2 Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Thu, 8 Aug 2024 22:37:57 +0800 Subject: [PATCH 093/160] [thread-cert] fix bug that docker network is not removed (#10590) In some cases that `_do_packet_verification` is False (e.g. verify() is not defined), the docker network is not correctly removed. docker network interface should always be removed at the end of the test if it was created at the begin of the test --- tests/scripts/thread-cert/thread_cert.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/scripts/thread-cert/thread_cert.py b/tests/scripts/thread-cert/thread_cert.py index 6da6dc2cc..5de900405 100644 --- a/tests/scripts/thread-cert/thread_cert.py +++ b/tests/scripts/thread-cert/thread_cert.py @@ -296,10 +296,12 @@ def tearDown(self): self.simulator.stop() + if self._has_backbone_traffic(): + self._remove_backbone_network() + if self._do_packet_verification: if self._has_backbone_traffic(): - self._remove_backbone_network() pcap_filename = self._merge_thread_backbone_pcaps() else: pcap_filename = self._get_thread_pcap_filename() From 67717618b79d609561b5485bd73a273fde2db547 Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Thu, 8 Aug 2024 22:44:26 +0800 Subject: [PATCH 094/160] [test] support multiple backbone interfaces in otbr docker test (#10550) In previous otbr docker tests, when creating docker containers, all the containers(otbr nodes) are connected to the same docker network bridge `backbone0` (when the env PORT_OFFSET is not set or set to 0), this means all the otbr nodes are connected to the same infrastructures. This commit adds support to enable user to config otbr instance infrastructures seperately in `TOPOLOGY` when defining test cases, this provides flexibility to run multi-ail related test cases. The format to define backbone interface per node is: ``` class NewTestCase(thread_cert.TestCase): ... BR = 1 ... TOPOLOGY = { BR: { ... 'is_otbr': True, 'backbone': , ... } ... } ... ``` `'backbone': ` is optional, when it's not given when defining a otbr node, the backbone is default as `BACKBONE_DOCKER_NETWORK_NAME`. The `` is suggested to be defined as `backbone[0-9]` to make it more easy to read and understand. This commit also adds test_multi_ail.py as an example test case to use this new method, this test case checks the two otbr nodes are connected to the different infra and are in the same Thread mesh network. --- .../border_router/test_multi_ail.py | 101 ++++++++++++++++++ tests/scripts/thread-cert/config.py | 6 +- tests/scripts/thread-cert/node.py | 7 +- tests/scripts/thread-cert/thread_cert.py | 46 +++++--- 4 files changed, 143 insertions(+), 17 deletions(-) create mode 100755 tests/scripts/thread-cert/border_router/test_multi_ail.py diff --git a/tests/scripts/thread-cert/border_router/test_multi_ail.py b/tests/scripts/thread-cert/border_router/test_multi_ail.py new file mode 100755 index 000000000..85423adb7 --- /dev/null +++ b/tests/scripts/thread-cert/border_router/test_multi_ail.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024, 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. +# + +import unittest +import ipaddress + +import config +import thread_cert + +IPV4_CIDR_ADDR_CMD = f'ip addr show {config.BACKBONE_IFNAME} | grep -w inet | grep -Eo "[0-9.]+/[0-9]+"' + + +class TwoBorderRoutersOnTwoInfrastructures(thread_cert.TestCase): + """ + Test that two border routers on different infrastructures can ping each other via Thread interface. + + Topology: + + -------(backbone0)-------- | ---------(backbone1)------- + | | + BR1 (Leader) .............. BR2 (Router) + + """ + USE_MESSAGE_FACTORY = False + + BR1 = 1 + BR2 = 2 + + TOPOLOGY = { + BR1: { + 'name': 'BR_1', + 'backbone_network': 'backbone0', + 'allowlist': [BR2], + 'is_otbr': True, + 'version': '1.3', + }, + BR2: { + 'name': 'BR_2', + 'backbone_network': 'backbone1', + 'allowlist': [BR1], + 'is_otbr': True, + 'version': '1.3', + } + } + + def test(self): + br1 = self.nodes[self.BR1] + br2 = self.nodes[self.BR2] + + # start nodes + br1.start() + self.simulator.go(2) + br2.start() + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) + + # check roles + self.assertEqual('leader', br1.get_state()) + self.assertEqual('router', br2.get_state()) + + # check two BRs AIL are in different subnets + br1_infra_ip_addr = br1.bash(IPV4_CIDR_ADDR_CMD) + br2_infra_ip_addr = br2.bash(IPV4_CIDR_ADDR_CMD) + + self.assertEqual(len(br1_infra_ip_addr), 1) + self.assertEqual(len(br2_infra_ip_addr), 1) + self.assertNotEqual(ipaddress.ip_network(br1_infra_ip_addr[0].strip(), strict=False), + ipaddress.ip_network(br2_infra_ip_addr[0].strip(), strict=False)) + + # ping each other + self.assertTrue(br1.ping(br2.get_ip6_address(config.ADDRESS_TYPE.ML_EID))) + self.assertTrue(br2.ping(br1.get_ip6_address(config.ADDRESS_TYPE.ML_EID))) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/tests/scripts/thread-cert/config.py b/tests/scripts/thread-cert/config.py index ebb90dd47..e7141d6df 100644 --- a/tests/scripts/thread-cert/config.py +++ b/tests/scripts/thread-cert/config.py @@ -64,8 +64,10 @@ DOMAIN_PREFIX_ALTER = 'fd00:7d04:7d04:7d04::/64' PORT_OFFSET = int(os.getenv('PORT_OFFSET', '0')) -BACKBONE_PREFIX = f'{0x9100 + PORT_OFFSET:04x}::/64' -BACKBONE_PREFIX_REGEX_PATTERN = f'^{0x9100 + PORT_OFFSET:04x}:' +BACKBONE_IPV6_ADDR_START_BASE = 0x9100 +BACKBONE_IPV6_ADDR_START = BACKBONE_IPV6_ADDR_START_BASE + PORT_OFFSET +BACKBONE_PREFIX = f'{BACKBONE_IPV6_ADDR_START:04x}::/64' +BACKBONE_PREFIX_REGEX_PATTERN = f'^{BACKBONE_IPV6_ADDR_START:04x}:' BACKBONE_DOCKER_NETWORK_NAME = f'backbone{PORT_OFFSET}' BACKBONE_IFNAME = 'eth0' THREAD_IFNAME = 'wpan0' diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 8a5f370ab..35e8f23f4 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -64,8 +64,9 @@ class OtbrDocker: _docker_proc = None _border_routing_counters = None - def __init__(self, nodeid: int, **kwargs): + def __init__(self, nodeid: int, backbone_network: str, **kwargs): self.verbose = int(float(os.getenv('VERBOSE', 0))) + self.backbone_network = backbone_network try: self._docker_name = config.OTBR_DOCKER_NAME_PREFIX + str(nodeid) self._prepare_ot_rcp_sim(nodeid) @@ -121,7 +122,7 @@ def _launch_docker(self): '--name', self._docker_name, '--network', - config.BACKBONE_DOCKER_NETWORK_NAME, + self.backbone_network, ] + dns + [ '-i', '--sysctl', @@ -151,7 +152,7 @@ def _launch_docker(self): try: subprocess.check_call(f'docker exec -i {self._docker_name} ot-ctl state', shell=True) launch_ok = True - logging.info("OTBR Docker %s Is Ready!", self._docker_name) + logging.info("OTBR Docker %s on %s Is Ready!", self._docker_name, self.backbone_network) break except subprocess.CalledProcessError: time.sleep(5) diff --git a/tests/scripts/thread-cert/thread_cert.py b/tests/scripts/thread-cert/thread_cert.py index 5de900405..58a9aebf5 100644 --- a/tests/scripts/thread-cert/thread_cert.py +++ b/tests/scripts/thread-cert/thread_cert.py @@ -162,14 +162,13 @@ def _setUp(self): else: nodeclass = Node - node = nodeclass( - i, - is_mtd=params['is_mtd'], - simulator=self.simulator, - name=params.get('name'), - version=params['version'], - is_bbr=params['is_bbr'], - ) + node = nodeclass(i, + is_mtd=params['is_mtd'], + simulator=self.simulator, + name=params.get('name'), + version=params['version'], + is_bbr=params['is_bbr'], + backbone_network=params['backbone_network']) if 'boot_delay' in params: self.simulator.go(params['boot_delay']) @@ -501,6 +500,9 @@ def _parse_params(self, params: Optional[dict]) -> dict: assert params.get('version', '') == '', params params['version'] = '' + # set default backbone network for bbr/otbr/host if not specified + params.setdefault('backbone_network', config.BACKBONE_DOCKER_NETWORK_NAME) + # use 1.3 node for 1.2 tests if params.get('version') == '1.2': params['version'] = '1.3' @@ -524,10 +526,30 @@ def _has_backbone_traffic(self): return False def _prepare_backbone_network(self): - network_name = config.BACKBONE_DOCKER_NETWORK_NAME - self.assure_run_ok( - f'docker network create --driver bridge --ipv6 --subnet {config.BACKBONE_PREFIX} -o "com.docker.network.bridge.name"="{network_name}" {network_name} || true', - shell=True) + """ + Prepares one or multiple backbone network(s). + + This method creates the backbone network based on the `backbone` values defined in the TOPOLOGY in each test. + If no backbone values are defined, it sets the default backbone network as BACKBONE_DOCKER_NETWORK_NAME + from the config module. + """ + # Use backbone_set to store all the backbone values in TOPOLOGY + backbone_set = set() + for node in self.TOPOLOGY: + backbone = self.TOPOLOGY[node].get('backbone_network') + if backbone: + backbone_set.add(backbone) + + # Set default backbone network if backbone_set is empty + if not backbone_set: + backbone_set.add(config.BACKBONE_DOCKER_NETWORK_NAME) + + # Iterate over the backbone_set and create backbone network(s) + for offset, backbone in enumerate(backbone_set, start=PORT_OFFSET): + backbone_prefix = f'{config.BACKBONE_IPV6_ADDR_START_BASE + offset:04x}::/64' + self.assure_run_ok( + f'docker network create --driver bridge --ipv6 --subnet {backbone_prefix} -o "com.docker.network.bridge.name"="{backbone}" {backbone} || true', + shell=True) def _remove_backbone_network(self): network_name = config.BACKBONE_DOCKER_NETWORK_NAME From 28b60114336bb053625e61d50dbcce30eba8a0fa Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Sat, 10 Aug 2024 05:55:36 +0800 Subject: [PATCH 095/160] [thread-cert] print elapsed time of each test when running cert suite (#10593) This is helpful to know the exact time that each test take when running bunch of tests in cert suite. --- tests/scripts/thread-cert/run_cert_suite.py | 26 ++++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/scripts/thread-cert/run_cert_suite.py b/tests/scripts/thread-cert/run_cert_suite.py index 09fc19d61..947948983 100755 --- a/tests/scripts/thread-cert/run_cert_suite.py +++ b/tests/scripts/thread-cert/run_cert_suite.py @@ -31,6 +31,7 @@ import os import queue import subprocess +import time import traceback from collections import Counter from typing import List @@ -158,29 +159,36 @@ def run_tests(scripts: List[str], multiply: int = 1, run_directory: str = None): script_ids = [(script, i) for script in scripts for i in range(multiply)] port_offset_pool = PortOffsetPool(MAX_JOBS) - def error_callback(port_offset, script, err): + def error_callback(port_offset, script, err, start_time): port_offset_pool.release(port_offset) + elapsed_time = round(time.time() - start_time) script_fail_count[script] += 1 if script_succ_count[script] + script_fail_count[script] == multiply: color = _COLOR_PASS if script_fail_count[script] == 0 else _COLOR_FAIL - print(f'{color}PASS {script_succ_count[script]} FAIL {script_fail_count[script]}{_COLOR_NONE} {script}') + print( + f'{color}PASS {script_succ_count[script]} FAIL {script_fail_count[script]}{_COLOR_NONE} {script} in {elapsed_time}s' + ) - def pass_callback(port_offset, script): + def pass_callback(port_offset, script, start_time): port_offset_pool.release(port_offset) + elapsed_time = round(time.time() - start_time) script_succ_count[script] += 1 if script_succ_count[script] + script_fail_count[script] == multiply: color = _COLOR_PASS if script_fail_count[script] == 0 else _COLOR_FAIL - print(f'{color}PASS {script_succ_count[script]} FAIL {script_fail_count[script]}{_COLOR_NONE} {script}') + print( + f'{color}PASS {script_succ_count[script]} FAIL {script_fail_count[script]}{_COLOR_NONE} {script} in {elapsed_time}s' + ) for script, i in script_ids: port_offset = port_offset_pool.allocate() - pool.apply_async( - run_cert, [i, port_offset, script, run_directory], - callback=lambda ret, port_offset=port_offset, script=script: pass_callback(port_offset, script), - error_callback=lambda err, port_offset=port_offset, script=script: error_callback( - port_offset, script, err)) + start_time = time.time() + pool.apply_async(run_cert, [i, port_offset, script, run_directory], + callback=lambda ret, port_offset=port_offset, script=script, start_time=start_time: + pass_callback(port_offset, script, start_time), + error_callback=lambda err, port_offset=port_offset, script=script, start_time=start_time: + error_callback(port_offset, script, err, start_time)) pool.close() pool.join() From df757ba0cf3405e5d36538f3c774500a08ff366e Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Sat, 10 Aug 2024 06:50:42 +0800 Subject: [PATCH 096/160] [test] test whether the RCP supports the Link Metrics (#10592) This commit adds a test case to `cp-tests` to test whether the RCP supports the Link Metrics. --- tools/cp-caps/README.md | 13 ++++++- tools/cp-caps/rcp_caps_test.py | 65 ++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/tools/cp-caps/README.md b/tools/cp-caps/README.md index 48ec8883b..b84387172 100644 --- a/tools/cp-caps/README.md +++ b/tools/cp-caps/README.md @@ -41,7 +41,7 @@ $ git clone git@github.com:openthread/ot-nrf528xx.git $ cd ot-nrf528xx/ $ git submodule update --init $ ./script/bootstrap -$ ./script/build nrf52840 UART_trans -DOT_DIAGNOSTIC=ON -DOT_CSL_RECEIVER=ON +$ ./script/build nrf52840 UART_trans -DOT_DIAGNOSTIC=ON -DOT_CSL_RECEIVER=ON -DOT_LINK_METRICS_INITIATOR=ON -DOT_LINK_METRICS_SUBJECT=ON $ arm-none-eabi-objcopy -O ihex build/bin/ot-cli-ftd ot-cli-ftd.hex $ nrfjprog -f nrf52 --chiperase --program ot-cli-ftd.hex --reset ``` @@ -62,6 +62,7 @@ options: -h, --help show this help message and exit -c, --csl test whether the RCP supports CSL transmitter -d, --diag-commands test whether the RCP supports all diag commands + -l, --link-metrics test whether the RCP supports link metrics -p, --data-poll test whether the RCP supports data poll -t, --throughput test the Thread network 1-hop throughput -v, --verbose output verbose information @@ -146,6 +147,16 @@ Data Poll Parent ----------------------------------------- OK Data Poll Child ------------------------------------------ OK ``` +### Test Link Metrics + +The parameter `-l` or `--link-metrics` starts to test whether the RCP supports link metrics. + +```bash +$ DUT_ADB_USB=1269UCKFZTAM95OR REF_CLI_SERIAL=/dev/ttyACM0 python3 ./tools/cp-caps/rcp_caps_test.py -l +Link Metrics Initiator ----------------------------------- OK +Link Metrics Subject ------------------------------------- OK +``` + ### Test Throughput The parameter `-t` or `--throughput` starts to test the Thread network 1-hop throughput of the DUT. diff --git a/tools/cp-caps/rcp_caps_test.py b/tools/cp-caps/rcp_caps_test.py index 432777859..96e46cecd 100644 --- a/tools/cp-caps/rcp_caps_test.py +++ b/tools/cp-caps/rcp_caps_test.py @@ -38,6 +38,7 @@ import otci from otci import OTCI +from otci.types import Ip6Addr logging.basicConfig(level=logging.WARNING) @@ -136,9 +137,62 @@ def test_throughput(self): self.__output_format_string('Throughput', self.__bitrate_to_string(results['receiver']['bitrate'])) + def test_link_metrics(self): + """Test whether the DUT supports Link Metrics Initiator and Subject.""" + self.__dataset = self.__get_default_dataset() + + self.__dut.factory_reset() + self.__ref.factory_reset() + + self.__dut.join(self.__dataset) + self.__dut.wait_for('state', 'leader') + + self.__ref.join(self.__dataset) + self.__ref.wait_for('state', ['child', 'router']) + + test_case = 'Link Metrics Initiator' + ref_linklocal_address = self.__ref.get_ipaddr_linklocal() + ret = self.__run_link_metrics_test_commands(initiator=self.__dut, subject_address=ref_linklocal_address) + self.__output_format_bool(test_case, ret) + + test_case = 'Link Metrics Subject' + dut_linklocal_address = self.__dut.get_ipaddr_linklocal() + ret = self.__run_link_metrics_test_commands(initiator=self.__ref, subject_address=dut_linklocal_address) + self.__output_format_bool(test_case, ret) + + self.__ref.leave() + self.__dut.leave() + # # Private methods # + def __run_link_metrics_test_commands(self, initiator: OTCI, subject_address: Ip6Addr) -> bool: + seriesid = 1 + series_flags = 'ldra' + link_metrics_flags = 'qr' + probe_length = 10 + + if not initiator.linkmetrics_config_enhanced_ack_register(subject_address, link_metrics_flags): + return False + + if not initiator.linkmetrics_config_forward(subject_address, seriesid, series_flags, link_metrics_flags): + return False + + initiator.linkmetrics_probe(subject_address, seriesid, probe_length) + + results = initiator.linkmetrics_request_single(subject_address, link_metrics_flags) + if not ('lqi' in results.keys() and 'rssi' in results.keys()): + return False + + results = initiator.linkmetrics_request_forward(subject_address, seriesid) + if not ('lqi' in results.keys() and 'rssi' in results.keys()): + return False + + if not initiator.linkmetrics_config_enhanced_ack_clear(subject_address): + return False + + return True + def __ref_iperf3_server_task(self, bind_address: str, timeout: int): self.__ref.iperf3_server(bind_address, timeout=timeout) @@ -535,6 +589,14 @@ def parse_arguments(): help='test whether the RCP supports CSL transmitter', ) + parser.add_argument( + '-l', + '--link-metrics', + action='store_true', + default=False, + help='test whether the RCP supports link metrics', + ) + parser.add_argument( '-d', '--diag-commands', @@ -588,6 +650,9 @@ def main(): if arguments.data_poll is True: rcp_caps.test_data_poll() + if arguments.link_metrics is True: + rcp_caps.test_link_metrics() + if arguments.throughput: rcp_caps.test_throughput() From ddbc99b8219bed13f8ba9c9dbd1be6e2b4dcdce5 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Tue, 13 Aug 2024 13:25:05 +0800 Subject: [PATCH 097/160] [radio] update the description of `otPlatradioSetChannelTargetPower()` (#10585) This commit updates the descrition of the function `otPlatRadioSetChannelTargetPower()` to make the function descrition more accurate, and updates the implementation of the function based on the latest description. --- include/openthread/instance.h | 2 +- include/openthread/platform/radio.h | 13 ++++++++----- src/core/utils/power_calibration.cpp | 12 +++++++++--- tests/unit/test_power_calibration.cpp | 5 +++-- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 677ddc5f6..613e456d6 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (431) +#define OPENTHREAD_API_VERSION (432) /** * @addtogroup api-instance diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index fc11416c6..45fbe4594 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -1380,16 +1380,19 @@ otError otPlatRadioClearCalibratedPowers(otInstance *aInstance); * @note This API is an optional radio platform API. It's up to the platform layer to implement it. * If this API is implemented, the function `otPlatRadioSetTransmitPower()` should be disabled. * - * The radio driver should set the actual output power to be less than or equal to the target power and as close - * as possible to the target power. + * The radio driver should set the actual output power to be less than or equal to the @p aTargetPower and as close + * as possible to the @p aTargetPower. If the @p aTargetPower is lower than the minimum output power supported + * by the platform, the output power should be set to the minimum output power supported by the platform. If the + * @p aTargetPower is higher than the maximum output power supported by the platform, the output power should be + * set to the maximum output power supported by the platform. If the @p aTargetPower is set to `INT16_MAX`, the + * corresponding channel is disabled. * * @param[in] aInstance The OpenThread instance structure. * @param[in] aChannel The radio channel. - * @param[in] aTargetPower The target power in 0.01dBm. Passing `INT16_MAX` will disable this channel to use the - * target power. + * @param[in] aTargetPower The target power in 0.01dBm. * * @retval OT_ERROR_NONE Successfully set the target power. - * @retval OT_ERROR_INVALID_ARGS The @p aChannel or @p aTargetPower is invalid. + * @retval OT_ERROR_INVALID_ARGS The @p aChannel is invalid. * @retval OT_ERROR_NOT_IMPLEMENTED The feature is not implemented. * */ diff --git a/src/core/utils/power_calibration.cpp b/src/core/utils/power_calibration.cpp index 0818d0440..4c0c4c724 100644 --- a/src/core/utils/power_calibration.cpp +++ b/src/core/utils/power_calibration.cpp @@ -144,6 +144,8 @@ Error PowerCalibration::GetPowerSettings(uint8_t aChannel, int16_t foundPower = kInvalidPower; int16_t targetPower; int16_t actualPower; + int16_t minPower = NumericLimits::kMax; + uint8_t minPowerIndex = kInvalidIndex; VerifyOrExit(IsChannelValid(aChannel), error = kErrorInvalidArgs); VerifyOrExit((mLastChannel != aChannel) || IsPowerUpdated()); @@ -151,6 +153,7 @@ Error PowerCalibration::GetPowerSettings(uint8_t aChannel, chIndex = aChannel - Radio::kChannelMin; targetPower = mTargetPowerTable[chIndex]; VerifyOrExit(targetPower != kInvalidPower, error = kErrorNotFound); + VerifyOrExit(mCalibratedPowerTables[chIndex].GetLength() > 0, error = kErrorNotFound); for (uint8_t i = 0; i < mCalibratedPowerTables[chIndex].GetLength(); i++) { @@ -161,11 +164,14 @@ Error PowerCalibration::GetPowerSettings(uint8_t aChannel, foundPower = actualPower; powerIndex = i; } + else if (actualPower < minPower) + { + minPower = actualPower; + minPowerIndex = i; + } } - VerifyOrExit(powerIndex != kInvalidIndex, error = kErrorNotFound); - - mCalibratedPowerIndex = powerIndex; + mCalibratedPowerIndex = (powerIndex != kInvalidIndex) ? powerIndex : minPowerIndex; mLastChannel = aChannel; exit: diff --git a/tests/unit/test_power_calibration.cpp b/tests/unit/test_power_calibration.cpp index 9c8067ba6..8e0faf175 100644 --- a/tests/unit/test_power_calibration.cpp +++ b/tests/unit/test_power_calibration.cpp @@ -67,8 +67,9 @@ void TestPowerCalibration(void) SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 4999)); rawPowerSettingLength = sizeof(rawPowerSetting); - VerifyOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength) == - OT_ERROR_NOT_FOUND); + SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength)); + VerifyOrQuit(rawPowerSettingLength == 1); + VerifyOrQuit(rawPowerSetting[0] == 0x00); SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 5000)); rawPowerSettingLength = sizeof(rawPowerSetting); From 509596fe2c96f8ccec08adc130516fa6cc13a1da Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Wed, 14 Aug 2024 00:21:43 +0800 Subject: [PATCH 098/160] [test] fix potential backbone name conflict when running cert suite (#10596) ### Background https://github.com/openthread/openthread/pull/10550 introduced a new way to support multiple backbone nework in otbr docker test. Though it works good while running a single test, a bug exists when running cert-suite, which runs a batch of tests in parallel. cert-suite allocates the name of the backbone interfaces dynamically by setting env PORT_OFFSET for each test, so there is potentially conflict exists if we hard code the `backbone_network` name in TOPOLOGY. This PR is targeting to fix this potential naming conflict. ### Fix We fix it by assigning a number for `backbone_network_id` in each BR definition in TOPOLOGY, instead of setting a fixed `backbone network` name. The final backbone network name is decided by both `PORT_OFFSET` env and the number of `backbone_network` (in `backbone{PORT_OFFSET}.{backbone_network}` format) For example, if `PORT_OFFSET` is 0 and `backbone_network_id` is 1, then backbone network name will be `backbone0.1`. For the tests that only use one backbone network and the `backbone_network_id` is not given, the backbone network name is by default `backbone{PORT_OFFSET}.0`. ### New test case format ``` class NewTestCase(thread_cert.TestCase): ... BR = 1 ... TOPOLOGY = { BR: { ... 'is_otbr': True, 'backbone_network_id': , ... } ... } ... ``` `` is any integer from 0, for each BR inside a single test, if `` is different, the BR use different backbone interfaces; the same `` inside a single test case means the same backbone network interface. `'backbone_network_id': ` is optional for single backbone test cases, when it's not given while defining a otbr node, the backbone is default as `backbone{PORT_OFFSET}.0`. For developers, if you are defining a new test which has multiple backbone interfaces, please ensure `backbone_network_id` is explicitly defined in each BR, otherwize an error is reported. --- .../border_router/test_multi_ail.py | 6 +- tests/scripts/thread-cert/config.py | 8 +-- tests/scripts/thread-cert/node.py | 2 + tests/scripts/thread-cert/run_cert_suite.py | 2 +- tests/scripts/thread-cert/thread_cert.py | 72 +++++++++++++------ 5 files changed, 60 insertions(+), 30 deletions(-) diff --git a/tests/scripts/thread-cert/border_router/test_multi_ail.py b/tests/scripts/thread-cert/border_router/test_multi_ail.py index 85423adb7..3cc601b54 100755 --- a/tests/scripts/thread-cert/border_router/test_multi_ail.py +++ b/tests/scripts/thread-cert/border_router/test_multi_ail.py @@ -42,7 +42,7 @@ class TwoBorderRoutersOnTwoInfrastructures(thread_cert.TestCase): Topology: - -------(backbone0)-------- | ---------(backbone1)------- + -------(backbone0.0)-------- | ---------(backbone0.1)------- | | BR1 (Leader) .............. BR2 (Router) @@ -55,14 +55,14 @@ class TwoBorderRoutersOnTwoInfrastructures(thread_cert.TestCase): TOPOLOGY = { BR1: { 'name': 'BR_1', - 'backbone_network': 'backbone0', + 'backbone_network_id': 0, 'allowlist': [BR2], 'is_otbr': True, 'version': '1.3', }, BR2: { 'name': 'BR_2', - 'backbone_network': 'backbone1', + 'backbone_network_id': 1, 'allowlist': [BR1], 'is_otbr': True, 'version': '1.3', diff --git a/tests/scripts/thread-cert/config.py b/tests/scripts/thread-cert/config.py index e7141d6df..b49380d4d 100644 --- a/tests/scripts/thread-cert/config.py +++ b/tests/scripts/thread-cert/config.py @@ -64,11 +64,11 @@ DOMAIN_PREFIX_ALTER = 'fd00:7d04:7d04:7d04::/64' PORT_OFFSET = int(os.getenv('PORT_OFFSET', '0')) -BACKBONE_IPV6_ADDR_START_BASE = 0x9100 -BACKBONE_IPV6_ADDR_START = BACKBONE_IPV6_ADDR_START_BASE + PORT_OFFSET -BACKBONE_PREFIX = f'{BACKBONE_IPV6_ADDR_START:04x}::/64' -BACKBONE_PREFIX_REGEX_PATTERN = f'^{BACKBONE_IPV6_ADDR_START:04x}:' +BACKBONE_IPV6_ADDR_START = f'{0x9100 + PORT_OFFSET:04x}' +BACKBONE_PREFIX = f'{BACKBONE_IPV6_ADDR_START}::/64' +BACKBONE_PREFIX_REGEX_PATTERN = f'^{BACKBONE_IPV6_ADDR_START}:' BACKBONE_DOCKER_NETWORK_NAME = f'backbone{PORT_OFFSET}' +BACKBONE_DOCKER_NETWORK_DEFAULT_ID = 0 BACKBONE_IFNAME = 'eth0' THREAD_IFNAME = 'wpan0' diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 35e8f23f4..24510527c 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -66,6 +66,8 @@ class OtbrDocker: def __init__(self, nodeid: int, backbone_network: str, **kwargs): self.verbose = int(float(os.getenv('VERBOSE', 0))) + + assert backbone_network is not None self.backbone_network = backbone_network try: self._docker_name = config.OTBR_DOCKER_NAME_PREFIX + str(nodeid) diff --git a/tests/scripts/thread-cert/run_cert_suite.py b/tests/scripts/thread-cert/run_cert_suite.py index 947948983..8618e9534 100755 --- a/tests/scripts/thread-cert/run_cert_suite.py +++ b/tests/scripts/thread-cert/run_cert_suite.py @@ -71,7 +71,7 @@ def run_cert(job_id: int, port_offset: int, script: str, run_directory: str): env['PYTHONPATH'] = os.path.dirname(os.path.abspath(__file__)) try: - print(f'Running {test_name}') + print(f'Running PORT_OFFSET={port_offset} {test_name}') with open(logfile, 'wt') as output: abs_script = os.path.abspath(script) subprocess.check_call(abs_script, diff --git a/tests/scripts/thread-cert/thread_cert.py b/tests/scripts/thread-cert/thread_cert.py index 58a9aebf5..e09b769fc 100644 --- a/tests/scripts/thread-cert/thread_cert.py +++ b/tests/scripts/thread-cert/thread_cert.py @@ -111,6 +111,10 @@ def __init__(self, *args, **kwargs): self._do_packet_verification = PACKET_VERIFICATION and hasattr(self, 'verify') \ and self.PACKET_VERIFICATION == PACKET_VERIFICATION + # store all the backbone network names that are used in the test case, + # it keeps empty when there's no backbone traffic in the test (no otbr or host nodes) + self._backbone_network_names = [] + def skipTest(self, reason: Any) -> None: self._testSkipped = True super(TestCase, self).skipTest(reason) @@ -153,7 +157,11 @@ def _setUp(self): params = self._parse_params(params) initial_topology[i] = params + backbone_network_name = self._construct_backbone_network_name(params.get('backbone_network_id')) \ + if self._has_backbone_traffic() else None + logging.info("Creating node %d: %r", i, params) + logging.info("Backbone network: %s", backbone_network_name) if params['is_otbr']: nodeclass = OtbrNode @@ -168,7 +176,7 @@ def _setUp(self): name=params.get('name'), version=params['version'], is_bbr=params['is_bbr'], - backbone_network=params['backbone_network']) + backbone_network=backbone_network_name) if 'boot_delay' in params: self.simulator.go(params['boot_delay']) @@ -465,6 +473,18 @@ def _collect_test_info_after_setup(self): ethaddr = node.get_ether_mac() test_info['ethaddrs'][i] = EthAddr(ethaddr).format_octets() + def _construct_backbone_network_name(self, backbone_network_id) -> str: + """ + Construct the name of the backbone network based on the given backbone network id from TOPOLOGY. If the + backbone_network_id is not defined in TOPOLOGY, use the default backbone network id. + """ + id = backbone_network_id if backbone_network_id is not None else config.BACKBONE_DOCKER_NETWORK_DEFAULT_ID + backbone_name = f'{config.BACKBONE_DOCKER_NETWORK_NAME}.{id}' + + assert backbone_name in self._backbone_network_names + + return backbone_name + def _output_test_info(self): """ Output test info to json file after tearDown @@ -500,9 +520,6 @@ def _parse_params(self, params: Optional[dict]) -> dict: assert params.get('version', '') == '', params params['version'] = '' - # set default backbone network for bbr/otbr/host if not specified - params.setdefault('backbone_network', config.BACKBONE_DOCKER_NETWORK_NAME) - # use 1.3 node for 1.2 tests if params.get('version') == '1.2': params['version'] = '1.3' @@ -527,35 +544,45 @@ def _has_backbone_traffic(self): def _prepare_backbone_network(self): """ - Prepares one or multiple backbone network(s). + Creates one or more backbone networks (Docker bridge networks) based on the TOPOLOGY definition. - This method creates the backbone network based on the `backbone` values defined in the TOPOLOGY in each test. - If no backbone values are defined, it sets the default backbone network as BACKBONE_DOCKER_NETWORK_NAME - from the config module. + * If `backbone_network_id` is defined in the TOPOLOGY: + * Network name: `backbone{PORT_OFFSET}.{backbone_network_id}` (e.g., "backbone0.0", "backbone0.1") + * Network prefix: `backbone{PORT_OFFSET}:{backbone_network_id}::/64` (e.g., "9100:0::/64", "9100:1::/64") + + * If `backbone_network_id` is undefined: + * Network name: `backbone{PORT_OFFSET}.0` (e.g., "backbone0.0") + * Network prefix: `backbone{PORT_OFFSET}::/64` (e.g., "9100::/64") """ - # Use backbone_set to store all the backbone values in TOPOLOGY - backbone_set = set() + # Create backbone_set to store all the backbone_ids by parsing TOPOLOGY. + backbone_id_set = set() for node in self.TOPOLOGY: - backbone = self.TOPOLOGY[node].get('backbone_network') - if backbone: - backbone_set.add(backbone) + id = self.TOPOLOGY[node].get('backbone_network_id') + if id is not None: + backbone_id_set.add(id) - # Set default backbone network if backbone_set is empty - if not backbone_set: - backbone_set.add(config.BACKBONE_DOCKER_NETWORK_NAME) + # Add default backbone network id if backbone_set is empty + if not backbone_id_set: + backbone_id_set.add(config.BACKBONE_DOCKER_NETWORK_DEFAULT_ID) # Iterate over the backbone_set and create backbone network(s) - for offset, backbone in enumerate(backbone_set, start=PORT_OFFSET): - backbone_prefix = f'{config.BACKBONE_IPV6_ADDR_START_BASE + offset:04x}::/64' + for id in backbone_id_set: + backbone = f'{config.BACKBONE_DOCKER_NETWORK_NAME}.{id}' + backbone_prefix = f'{config.BACKBONE_IPV6_ADDR_START}:{id}::/64' + self._backbone_network_names.append(backbone) self.assure_run_ok( f'docker network create --driver bridge --ipv6 --subnet {backbone_prefix} -o "com.docker.network.bridge.name"="{backbone}" {backbone} || true', shell=True) def _remove_backbone_network(self): - network_name = config.BACKBONE_DOCKER_NETWORK_NAME - self.assure_run_ok(f'docker network rm {network_name}', shell=True) + for network_name in self._backbone_network_names: + self.assure_run_ok(f'docker network rm {network_name}', shell=True) def _start_backbone_sniffer(self): + assert self._backbone_network_names, 'Internal Error: self._backbone_network_names is empty' + # TODO: support sniffer on multiple backbone networks + sniffer_interface = self._backbone_network_names[0] + # don't know why but I have to create the empty bbr.pcap first, otherwise tshark won't work # self.assure_run_ok("truncate --size 0 bbr.pcap && chmod 664 bbr.pcap", shell=True) pcap_file = self._get_backbone_pcap_filename() @@ -565,12 +592,13 @@ def _start_backbone_sniffer(self): pass dumpcap = pvutils.which_dumpcap() - self._dumpcap_proc = subprocess.Popen([dumpcap, '-i', config.BACKBONE_DOCKER_NETWORK_NAME, '-w', pcap_file], + self._dumpcap_proc = subprocess.Popen([dumpcap, '-i', sniffer_interface, '-w', pcap_file], stdout=sys.stdout, stderr=sys.stderr) time.sleep(0.2) assert self._dumpcap_proc.poll() is None, 'tshark terminated unexpectedly' - logging.info('Backbone sniffer launched successfully: pid=%s', self._dumpcap_proc.pid) + logging.info('Backbone sniffer launched successfully on interface %s, pid=%s', sniffer_interface, + self._dumpcap_proc.pid) os.chmod(pcap_file, stat.S_IWUSR | stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) def _get_backbone_pcap_filename(self): From 943d2302a3030793316d280a0dd076eca93608d5 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 13 Aug 2024 15:43:45 -0700 Subject: [PATCH 099/160] [thread] move `OT_THREAD_VERSION_*` constants to public header (#10599) This commit moves the definition of `OT_THREAD_VERSION_*` constants from the config header to the `openthread/thread.h` OT public header file (where `otThreadGetVersion()` is declared). This allows other projects integrating the OT stack to also see and use these constants. --- include/openthread/instance.h | 2 +- include/openthread/thread.h | 9 +++++++++ src/core/BUILD.gn | 5 ++++- src/core/openthread-core-config.h | 10 +--------- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 613e456d6..f5c12bbf1 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (432) +#define OPENTHREAD_API_VERSION (433) /** * @addtogroup api-instance diff --git a/include/openthread/thread.h b/include/openthread/thread.h index 4d814c119..f31d46b6c 100644 --- a/include/openthread/thread.h +++ b/include/openthread/thread.h @@ -53,6 +53,13 @@ extern "C" { * */ +#define OT_THREAD_VERSION_INVALID 0 ///< Invalid Thread version +#define OT_THREAD_VERSION_1_1 2 ///< Thread Version 1.1 +#define OT_THREAD_VERSION_1_2 3 ///< Thread Version 1.2 +#define OT_THREAD_VERSION_1_3 4 ///< Thread Version 1.3 +#define OT_THREAD_VERSION_1_3_1 5 ///< Thread Version 1.3.1 (alias for 1.4) +#define OT_THREAD_VERSION_1_4 5 ///< Thread Version 1.4 + /** * Maximum value length of Thread Base TLV. */ @@ -246,6 +253,8 @@ otError otThreadSetEnabled(otInstance *aInstance, bool aEnabled); /** * Gets the Thread protocol version. * + * The constants `OT_THREAD_VERSION_*` define the numerical version values. + * * @returns the Thread protocol version. * */ diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 90cc73ecb..2d5260800 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -846,7 +846,10 @@ source_set("libopenthread_core_config") { ":core_config", "../..:openthread_config", ] - public_deps = [ "../../include/openthread:openthread_config" ] + public_deps = [ + "../../include/openthread:openthread_config", + "../../include/openthread", + ] public_deps += openthread_core_config_deps } diff --git a/src/core/openthread-core-config.h b/src/core/openthread-core-config.h index 7465915e3..dfbaf72f5 100644 --- a/src/core/openthread-core-config.h +++ b/src/core/openthread-core-config.h @@ -35,15 +35,7 @@ #define OPENTHREAD_CORE_CONFIG_H_ #include - -#define OT_THREAD_VERSION_INVALID 0 - -#define OT_THREAD_VERSION_1_1 2 -#define OT_THREAD_VERSION_1_2 3 -#define OT_THREAD_VERSION_1_3 4 -// Support projects on legacy "1.3.1" version, which is now "1.4" -#define OT_THREAD_VERSION_1_3_1 5 -#define OT_THREAD_VERSION_1_4 5 +#include #define OPENTHREAD_CORE_CONFIG_H_IN From 3bcea2467bb33407df260133aadb562bd72968d8 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 13 Aug 2024 21:37:55 -0700 Subject: [PATCH 100/160] [ip6] style fixes and minor enhancements (#10560) This commit contains minor enhancements in the `Ip6` class: - Removes unnecessary `static_cast()` use - Combines repeated `case` statements - Use shorter variable/type names. - Style fixes. --- src/core/net/ip6.cpp | 47 +++++++++++++++++++++++--------------------- src/core/net/ip6.hpp | 14 ++++++++----- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 7cbdf059b..2c63841f9 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -228,7 +228,7 @@ Error Ip6::PrepareMulticastToLargerThanRealmLocal(Message &aMessage, const Heade // Use IP-in-IP encapsulation (RFC2473) and ALL_MPL_FORWARDERS address. tunnelHeader.InitVersionTrafficClassFlow(); - tunnelHeader.SetHopLimit(static_cast(kDefaultHopLimit)); + tunnelHeader.SetHopLimit(kDefaultHopLimit); tunnelHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(tunnelHeader)); tunnelHeader.GetDestination().SetToRealmLocalAllMplForwarders(); tunnelHeader.SetNextHeader(kProtoIp6); @@ -451,7 +451,7 @@ Error Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aI } else { - header.SetHopLimit(static_cast(kDefaultHopLimit)); + header.SetHopLimit(kDefaultHopLimit); } if (aMessageInfo.GetSockAddr().IsUnspecified() || aMessageInfo.GetSockAddr().IsMulticast()) @@ -828,8 +828,7 @@ Error Ip6::HandleExtensionHeaders(OwnedPtr &aMessagePtr, uint8_t &aNextHeader, bool &aReceive) { - Error error = kErrorNone; - + Error error = kErrorNone; ExtensionHeader extHeader; while (aReceive || aNextHeader == kProtoHopOpts) @@ -839,6 +838,7 @@ Error Ip6::HandleExtensionHeaders(OwnedPtr &aMessagePtr, switch (aNextHeader) { case kProtoHopOpts: + case kProtoDstOpts: SuccessOrExit(error = HandleOptions(*aMessagePtr, aHeader, aReceive)); break; @@ -847,10 +847,6 @@ Error Ip6::HandleExtensionHeaders(OwnedPtr &aMessagePtr, SuccessOrExit(error = HandleFragment(*aMessagePtr)); break; - case kProtoDstOpts: - SuccessOrExit(error = HandleOptions(*aMessagePtr, aHeader, aReceive)); - break; - case kProtoIp6: ExitNow(); @@ -862,7 +858,7 @@ Error Ip6::HandleExtensionHeaders(OwnedPtr &aMessagePtr, ExitNow(); } - aNextHeader = static_cast(extHeader.GetNextHeader()); + aNextHeader = extHeader.GetNextHeader(); } exit: @@ -1166,7 +1162,7 @@ Error Ip6::HandleDatagram(OwnedPtr aMessagePtr, bool aIsReassembled) aMessagePtr->SetOffset(sizeof(header)); // Process IPv6 Extension Headers - nextHeader = static_cast(header.GetNextHeader()); + nextHeader = header.GetNextHeader(); SuccessOrExit(error = HandleExtensionHeaders(aMessagePtr, header, nextHeader, receive)); if (receive && (nextHeader == kProtoIp6)) @@ -1414,49 +1410,55 @@ Error Ip6::RouteLookup(const Address &aSource, const Address &aDestination) cons } #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound) { - static constexpr uint8_t kPrefixLength = 48; - otPacketsAndBytes *counter = nullptr; - otPacketsAndBytes *internetCounter = nullptr; + static constexpr uint8_t kPrefixLength = 48; + + otPacketsAndBytes *counter = nullptr; + otPacketsAndBytes *internetCounter = nullptr; VerifyOrExit(!aHeader.GetSource().IsLinkLocalUnicast()); VerifyOrExit(!aHeader.GetDestination().IsLinkLocalUnicast()); - VerifyOrExit(aHeader.GetSource().GetPrefix() != Get().GetMeshLocalPrefix()); - VerifyOrExit(aHeader.GetDestination().GetPrefix() != Get().GetMeshLocalPrefix()); + VerifyOrExit(!Get().IsMeshLocalAddress(aHeader.GetSource())); + VerifyOrExit(!Get().IsMeshLocalAddress(aHeader.GetDestination())); if (aIsInbound) { VerifyOrExit(!Get().HasUnicastAddress(aHeader.GetSource())); + if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength)) { - internetCounter = &mBorderRoutingCounters.mInboundInternet; + internetCounter = &mBrCounters.mInboundInternet; } + if (aHeader.GetDestination().IsMulticast()) { VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal()); - counter = &mBorderRoutingCounters.mInboundMulticast; + counter = &mBrCounters.mInboundMulticast; } else { - counter = &mBorderRoutingCounters.mInboundUnicast; + counter = &mBrCounters.mInboundUnicast; } } else { VerifyOrExit(!Get().HasUnicastAddress(aHeader.GetDestination())); + if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength)) { - internetCounter = &mBorderRoutingCounters.mOutboundInternet; + internetCounter = &mBrCounters.mOutboundInternet; } + if (aHeader.GetDestination().IsMulticast()) { VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal()); - counter = &mBorderRoutingCounters.mOutboundMulticast; + counter = &mBrCounters.mOutboundMulticast; } else { - counter = &mBorderRoutingCounters.mOutboundUnicast; + counter = &mBrCounters.mOutboundUnicast; } } @@ -1473,7 +1475,8 @@ void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLe internetCounter->mBytes += aMessageLength; } } -#endif + +#endif // OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE // LCOV_EXCL_START diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp index 48616ed5e..39bcb00bd 100644 --- a/src/core/net/ip6.hpp +++ b/src/core/net/ip6.hpp @@ -327,13 +327,16 @@ class Ip6 : public InstanceLocator, private NonCopyable static const char *EcnToString(Ecn aEcn); #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + + typedef otBorderRoutingCounters BrCounters; ///< Border Routing counters. + /** * Returns a reference to the Border Routing counters. * * @returns A reference to the Border Routing counters. * */ - const otBorderRoutingCounters &GetBorderRoutingCounters(void) const { return mBorderRoutingCounters; } + const BrCounters &GetBorderRoutingCounters(void) const { return mBrCounters; } /** * Returns a reference to the Border Routing counters. @@ -341,14 +344,15 @@ class Ip6 : public InstanceLocator, private NonCopyable * @returns A reference to the Border Routing counters. * */ - otBorderRoutingCounters &GetBorderRoutingCounters(void) { return mBorderRoutingCounters; } + BrCounters &GetBorderRoutingCounters(void) { return mBrCounters; } /** * Resets the Border Routing counters. * */ - void ResetBorderRoutingCounters(void) { ClearAllBytes(mBorderRoutingCounters); } -#endif + void ResetBorderRoutingCounters(void) { ClearAllBytes(mBrCounters); } + +#endif // OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE @@ -446,7 +450,7 @@ class Ip6 : public InstanceLocator, private NonCopyable #endif #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE - otBorderRoutingCounters mBorderRoutingCounters; + BrCounters mBrCounters; #endif }; From fa26102fe580e11d4fef6f0fc01bd828c01cfdea Mon Sep 17 00:00:00 2001 From: Mia Yang <145632982+mia1yang@users.noreply.github.com> Date: Wed, 14 Aug 2024 04:40:40 +0000 Subject: [PATCH 101/160] [test] verify ephemeral key settings and `_meshcop-e` (#10537) --- .../test_publish_meshcop_service.py | 34 +++++++++++++++++-- tests/scripts/thread-cert/node.py | 12 +++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py index 77979dbb5..2b522d44c 100755 --- a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py +++ b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py @@ -102,11 +102,28 @@ def test(self): self.simulator.go(10) self.check_meshcop_service(br1, host) + # activate ePSKc mode + lifetime = 500_000 + ephemeral_key = br1.activate_ephemeral_key_mode(lifetime) + self.assertEqual(len(ephemeral_key), 9) + self.assertEqual(br1.get_ephemeral_key_state(), 'active') + # check Meshcop-e service + self.check_meshcop_e_service(host, True) + + # deactivate ePSKc mode + br1.deactivate_ephemeral_key_mode() + self.assertEqual(br1.get_ephemeral_key_state(), 'inactive') + self.simulator.go(10) + # check Meshcop-e service + self.check_meshcop_e_service(host, False) + # change ephemeral key mode (ePSKc) and check Meshcop br1.ephemeral_key_enabled = False self.assertFalse(br1.ephemeral_key_enabled) self.simulator.go(10) self.check_meshcop_service(br1, host) + # check Meshcop-e format + self.check_meshcop_e_service(host, False) # end of ephemeral key mode (ePSKc) test br1.disable_backbone_router() @@ -218,13 +235,24 @@ def check_meshcop_service_by_data(self, br, service_data): self.assertEqual(service_data['txt']['rv'], '1') self.assertIn(service_data['txt']['tv'], ['1.1.0', '1.1.1', '1.2.0', '1.3.0']) - def discover_all_meshcop_services(self, host): - instance_names = host.browse_mdns_services('_meshcop._udp') + def discover_services(self, host, type): + instance_names = host.browse_mdns_services(type) services = [] for instance_name in instance_names: - services.append(host.discover_mdns_service(instance_name, '_meshcop._udp', None)) + services.append(host.discover_mdns_service(instance_name, type, None)) return services + def discover_all_meshcop_services(self, host): + return self.discover_services(host, '_meshcop._udp') + + def check_meshcop_e_service(self, host, isactive): + services = self.discover_services(host, '_meshcop-e._udp') + # TODO: Meshcop-e port check etc. + if isactive: + self.assertTrue(len(services) > 0, msg='Meshcop-e service not found') + else: + self.assertEqual(len(services), 0, msg='Meshcop-e service still found after disabled') + if __name__ == '__main__': unittest.main() diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 24510527c..88c57dcd1 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -422,6 +422,12 @@ def _process_packet_counters(self, counter): def nat64_set_enabled(self, enable): return self.call_dbus_method('io.openthread.BorderRouter', 'SetNat64Enabled', enable) + def activate_ephemeral_key_mode(self, lifetime): + return self.call_dbus_method('io.openthread.BorderRouter', 'ActivateEphemeralKeyMode', lifetime) + + def deactivate_ephemeral_key_mode(self): + return self.call_dbus_method('io.openthread.BorderRouter', 'DeactivateEphemeralKeyMode') + @property def nat64_cidr(self): self.send_command('nat64 cidr') @@ -1896,6 +1902,12 @@ def set_state(self, state): self.send_command(cmd) self._expect_done() + def get_ephemeral_key_state(self): + cmd = 'ba ephemeralkey' + states = [r'inactive', r'active'] + self.send_command(cmd) + return self._expect_result(states) + def get_timeout(self): self.send_command('childtimeout') return self._expect_result(r'\d+') From 6e00d72c0e54a9bc2a7bfbfa1e85f30ee1d81bc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 22:28:19 -0700 Subject: [PATCH 102/160] github-actions: bump step-security/harden-runner from 2.8.1 to 2.9.1 (#10598) Bumps [step-security/harden-runner](https://github.com/step-security/harden-runner) from 2.8.1 to 2.9.1. - [Release notes](https://github.com/step-security/harden-runner/releases) - [Commits](https://github.com/step-security/harden-runner/compare/17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6...5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde) --- updated-dependencies: - dependency-name: step-security/harden-runner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 28 ++++++++++++++-------------- .github/workflows/codeql.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/fuzz.yml | 2 +- .github/workflows/makefile-check.yml | 2 +- .github/workflows/otci.yml | 2 +- .github/workflows/otns.yml | 6 +++--- .github/workflows/posix.yml | 12 ++++++------ .github/workflows/simulation-1.1.yml | 18 +++++++++--------- .github/workflows/simulation-1.2.yml | 14 +++++++------- .github/workflows/size.yml | 2 +- .github/workflows/toranj.yml | 10 +++++----- .github/workflows/unit.yml | 6 +++--- .github/workflows/version.yml | 2 +- 14 files changed, 54 insertions(+), 54 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 34ba41831..195fde2bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,7 +49,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -71,7 +71,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -85,7 +85,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -104,7 +104,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -144,7 +144,7 @@ jobs: CXX: ${{ matrix.compiler_cpp }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -163,7 +163,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -182,7 +182,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -238,7 +238,7 @@ jobs: gcc_extract_dir: arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -279,7 +279,7 @@ jobs: CXX: g++-${{ matrix.gcc_ver }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -312,7 +312,7 @@ jobs: CXX: clang++-${{ matrix.clang_ver }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -350,7 +350,7 @@ jobs: LDFLAGS: -m32 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -378,7 +378,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -414,7 +414,7 @@ jobs: CXX: ${{ matrix.CXX }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -438,7 +438,7 @@ jobs: image: openthread/environment steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 164b09f6f..ad324711f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -54,7 +54,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ea42cfcb2..5059f1065 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -55,7 +55,7 @@ jobs: - docker_name: environment steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 2a5dbc33c..b0d80661c 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/makefile-check.yml b/.github/workflows/makefile-check.yml index 2101e9517..a25b7412f 100644 --- a/.github/workflows/makefile-check.yml +++ b/.github/workflows/makefile-check.yml @@ -48,7 +48,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml index ad862253a..c6342231f 100644 --- a/.github/workflows/otci.yml +++ b/.github/workflows/otci.yml @@ -57,7 +57,7 @@ jobs: REAL_DEVICE: 0 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml index a8293a54a..74ddb0746 100644 --- a/.github/workflows/otns.yml +++ b/.github/workflows/otns.yml @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -160,7 +160,7 @@ jobs: STRESS_LEVEL: ${{ matrix.stress_level }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -208,7 +208,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index f07d532e4..3f251a8d6 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -52,7 +52,7 @@ jobs: CXXFLAGS: -DCLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER=1 -DOPENTHREAD_CONFIG_MLE_MAX_CHILDREN=15 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -138,7 +138,7 @@ jobs: VIRTUAL_TIME: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -182,7 +182,7 @@ jobs: OT_READLINE: 'readline' steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -232,7 +232,7 @@ jobs: OT_READLINE: 'off' steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -262,7 +262,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -296,7 +296,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml index 366f09303..ed491312c 100644 --- a/.github/workflows/simulation-1.1.yml +++ b/.github/workflows/simulation-1.1.yml @@ -55,7 +55,7 @@ jobs: MULTIPLY: 3 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -104,7 +104,7 @@ jobs: VIRTUAL_TIME: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -155,7 +155,7 @@ jobs: MESSAGE_USE_HEAP: ${{ matrix.message_use_heap }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -199,7 +199,7 @@ jobs: VIRTUAL_TIME: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -239,7 +239,7 @@ jobs: THREAD_VERSION: 1.1 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -280,7 +280,7 @@ jobs: THREAD_VERSION: 1.1 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -333,7 +333,7 @@ jobs: CXXFLAGS: "-DOPENTHREAD_CONFIG_LOG_PREPEND_UPTIME=0" steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -370,7 +370,7 @@ jobs: COVERAGE: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -404,7 +404,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml index d6870933f..1ac5c1c0d 100644 --- a/.github/workflows/simulation-1.2.yml +++ b/.github/workflows/simulation-1.2.yml @@ -66,7 +66,7 @@ jobs: arch: ["m32", "m64"] steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -128,7 +128,7 @@ jobs: INTER_OP_BBR: 0 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -199,7 +199,7 @@ jobs: MULTIPLY: 3 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -247,7 +247,7 @@ jobs: VIRTUAL_TIME: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -288,7 +288,7 @@ jobs: VIRTUAL_TIME: 0 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -338,7 +338,7 @@ jobs: INTER_OP: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -399,7 +399,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml index 2217c7273..cb00ccf2c 100644 --- a/.github/workflows/size.yml +++ b/.github/workflows/size.yml @@ -49,7 +49,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml index 120b8b820..ac2cb1597 100644 --- a/.github/workflows/toranj.yml +++ b/.github/workflows/toranj.yml @@ -59,7 +59,7 @@ jobs: TORANJ_EVENT_NAME: ${{ github.event_name }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -90,7 +90,7 @@ jobs: TORANJ_CLI: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -123,7 +123,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -164,7 +164,7 @@ jobs: runs-on: macos-14 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -187,7 +187,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 1c8bdcb22..3604c718e 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -49,7 +49,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -67,7 +67,7 @@ jobs: COVERAGE: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -104,7 +104,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index cadf03116..01337f6ba 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs From 5edc3673fdcd9e86f9473e61f62c93e837e25534 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 13 Aug 2024 23:48:25 -0700 Subject: [PATCH 103/160] [routing-manager] fix typo in `IsInitialPolicyEvaluationDone()` (#10607) --- src/core/border_router/routing_manager.cpp | 8 ++++---- src/core/border_router/routing_manager.hpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 02c930ae2..b50dd44b2 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -391,7 +391,7 @@ void RoutingManager::HandleSrpServerAutoEnableMode(void) { VerifyOrExit(Get().IsAutoEnableMode()); - if (IsInitalPolicyEvaluationDone()) + if (IsInitialPolicyEvaluationDone()) { Get().Enable(); } @@ -481,13 +481,13 @@ void RoutingManager::EvaluateRoutingPolicy(void) mNat64PrefixManager.Evaluate(); #endif - if (IsInitalPolicyEvaluationDone()) + if (IsInitialPolicyEvaluationDone()) { SendRouterAdvertisement(kAdvPrefixesFromNetData); } #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE - if (Get().IsAutoEnableMode() && IsInitalPolicyEvaluationDone()) + if (Get().IsAutoEnableMode() && IsInitialPolicyEvaluationDone()) { // If SRP server uses the auto-enable mode, we enable the SRP // server on the first RA transmission after we are done with @@ -501,7 +501,7 @@ void RoutingManager::EvaluateRoutingPolicy(void) ScheduleRoutingPolicyEvaluation(kForNextRa); } -bool RoutingManager::IsInitalPolicyEvaluationDone(void) const +bool RoutingManager::IsInitialPolicyEvaluationDone(void) const { // This method indicates whether or not we are done with the // initial policy evaluation and prefix and route setup, i.e., diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index c6e4c8dff..95dc4c0ec 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -1528,7 +1528,7 @@ class RoutingManager : public InstanceLocator Error LoadOrGenerateRandomBrUlaPrefix(void); void EvaluateRoutingPolicy(void); - bool IsInitalPolicyEvaluationDone(void) const; + bool IsInitialPolicyEvaluationDone(void) const; void ScheduleRoutingPolicyEvaluation(ScheduleMode aMode); void HandleRsSenderFinished(TimeMilli aStartTime); void SendRouterAdvertisement(RouterAdvTxMode aRaTxMode); From 8edc081fdc7dd6546181fb2470b0472014761a1d Mon Sep 17 00:00:00 2001 From: sarveshkumarv3 <86755931+sarveshkumarv3@users.noreply.github.com> Date: Wed, 14 Aug 2024 08:12:30 -0700 Subject: [PATCH 104/160] [spinel] make `SendCommand` public to allow calling without variable arguments (#10602) Changed access of SendCommand to public to allow calling without variable arguments. This is to address compilation error in ot-br-posix repo NcpSpinel::ThreadErasePersistentInfo (while building for OpenWrt v23.05.4 for a mips target)(error: 'args' may be used uninitialized [-Werror=maybe-uninitialized]) --- src/lib/spinel/spinel_driver.hpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/lib/spinel/spinel_driver.hpp b/src/lib/spinel/spinel_driver.hpp index 0c3657f6f..7372ad166 100644 --- a/src/lib/spinel/spinel_driver.hpp +++ b/src/lib/spinel/spinel_driver.hpp @@ -180,6 +180,20 @@ class SpinelDriver : public otSpinelDriver, public Logger const char *aFormat, va_list aArgs); + /* + * Sends a spinel command without arguments to the co-processor. + * + * @param[in] aCommand The spinel command. + * @param[in] aKey The spinel property key. + * @param[in] aTid The spinel transaction id. + * + * @retval OT_ERROR_NONE Successfully sent the command through spinel interface. + * @retval OT_ERROR_INVALID_STATE The spinel interface is in an invalid state. + * @retval OT_ERROR_NO_BUFS The spinel interface doesn't have enough buffer. + * + */ + otError SendCommand(uint32_t aCommand, spinel_prop_key_t aKey, spinel_tid_t aTid); + /* * Sets the handler to process the received spinel frame. * @@ -291,8 +305,6 @@ class SpinelDriver : public otSpinelDriver, public Logger void *aContext); void HandleInitialFrame(const uint8_t *aFrame, uint16_t aLength, uint8_t aHeader, bool &aSave); - otError SendCommand(uint32_t aCommand, spinel_prop_key_t aKey, spinel_tid_t aTid); - otError CheckSpinelVersion(void); otError GetCoprocessorVersion(void); otError GetCoprocessorCaps(void); From ec0b2991a857040deb7b157b7613f55291c1bb68 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Wed, 14 Aug 2024 10:44:46 -0700 Subject: [PATCH 105/160] [docker] add `linux/arm64` to platforms (#10605) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5059f1065..8f17792a7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -68,7 +68,7 @@ jobs: run: | DOCKER_IMAGE=openthread/${{ matrix.docker_name }} DOCKER_FILE=etc/docker/${{ matrix.docker_name }}/Dockerfile - DOCKER_PLATFORMS=linux/amd64 + DOCKER_PLATFORMS=linux/amd64,linux/arm64 VERSION=latest TAGS="--tag ${DOCKER_IMAGE}:${VERSION}" From 1645880b8e87dd32cebad880a912459edb5ea0e0 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 14 Aug 2024 12:40:48 -0700 Subject: [PATCH 106/160] [dsnsd-server] support handling of "A record" queries (#10364) This commit adds support for responding to "A record" queries in the DNS-SD server and discovery proxy. If the query matches a host registered with the SRP server, the host's IPv6 addresses are returned in the Additional Data section of the response. If the query is resolved by the proxy, the `otPlatDnssd` APIs are used to start/stop IPv4 address resolvers for the hostname on the infrastructure network. The `test_dnssd_discovery_proxy` unit test is updated to validate the new functionality. --- src/core/net/dnssd_server.cpp | 193 +++++++++++++++-- src/core/net/dnssd_server.hpp | 69 +++--- .../border_router/test_dnssd_server.py | 2 +- .../test_dnssd_server_multi_border_routers.py | 2 +- tests/unit/test_dnssd_discovery_proxy.cpp | 204 ++++++++++++++++-- 5 files changed, 406 insertions(+), 64 deletions(-) diff --git a/src/core/net/dnssd_server.cpp b/src/core/net/dnssd_server.cpp index 35c6cb793..5b75f4f06 100644 --- a/src/core/net/dnssd_server.cpp +++ b/src/core/net/dnssd_server.cpp @@ -335,6 +335,9 @@ Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode, bool &aS case ResourceRecord::kTypeAaaa: mType = kAaaaQuery; break; + case ResourceRecord::kTypeA: + mType = kAQuery; + break; default: ExitNow(rcode = Header::kResponseNotImplemented); } @@ -447,6 +450,7 @@ Error Server::Response::ParseQueryName(void) break; case kAaaaQuery: + case kAQuery: mOffsets.mHostName = sizeof(Header); break; } @@ -579,27 +583,42 @@ Error Server::Response::AppendHostAddresses(const Srp::Server::Host &aHost) addrs = aHost.GetAddresses(addrsLength); ttl = TimeMilli::MsecToSec(aHost.GetExpireTime() - TimerMilli::GetNow()); - return AppendHostAddresses(addrs, addrsLength, ttl); + return AppendHostAddresses(kIp6AddrType, addrs, addrsLength, ttl); } #endif -Error Server::Response::AppendHostAddresses(const HostInfo &aHostInfo) +Error Server::Response::AppendHostAddresses(AddrType aAddrType, const HostInfo &aHostInfo) { - return AppendHostAddresses(AsCoreTypePtr(aHostInfo.mAddresses), aHostInfo.mAddressNum, aHostInfo.mTtl); + return AppendHostAddresses(aAddrType, AsCoreTypePtr(aHostInfo.mAddresses), aHostInfo.mAddressNum, aHostInfo.mTtl); } Error Server::Response::AppendHostAddresses(const ServiceInstanceInfo &aInstanceInfo) { - return AppendHostAddresses(AsCoreTypePtr(aInstanceInfo.mAddresses), aInstanceInfo.mAddressNum, aInstanceInfo.mTtl); + return AppendHostAddresses(kIp6AddrType, AsCoreTypePtr(aInstanceInfo.mAddresses), aInstanceInfo.mAddressNum, + aInstanceInfo.mTtl); } -Error Server::Response::AppendHostAddresses(const Ip6::Address *aAddrs, uint16_t aAddrsLength, uint32_t aTtl) +Error Server::Response::AppendHostAddresses(AddrType aAddrType, + const Ip6::Address *aAddrs, + uint16_t aAddrsLength, + uint32_t aTtl) { Error error = kErrorNone; for (uint16_t index = 0; index < aAddrsLength; index++) { - SuccessOrExit(error = AppendAaaaRecord(aAddrs[index], aTtl)); + const Ip6::Address &address = aAddrs[index]; + + switch (aAddrType) + { + case kIp6AddrType: + SuccessOrExit(error = AppendAaaaRecord(address, aTtl)); + break; + + case kIp4AddrType: + SuccessOrExit(error = AppendARecord(address, aTtl)); + break; + } } exit: @@ -608,9 +627,11 @@ Error Server::Response::AppendHostAddresses(const Ip6::Address *aAddrs, uint16_t Error Server::Response::AppendAaaaRecord(const Ip6::Address &aAddress, uint32_t aTtl) { - Error error; + Error error = kErrorNone; AaaaRecord aaaaRecord; + VerifyOrExit(!aAddress.IsIp4Mapped()); + aaaaRecord.Init(); aaaaRecord.SetTtl(aTtl); aaaaRecord.SetAddress(aAddress); @@ -623,6 +644,26 @@ Error Server::Response::AppendAaaaRecord(const Ip6::Address &aAddress, uint32_t return error; } +Error Server::Response::AppendARecord(const Ip6::Address &aAddress, uint32_t aTtl) +{ + Error error = kErrorNone; + ARecord aRecord; + Ip4::Address ip4Address; + + SuccessOrExit(ip4Address.ExtractFromIp4MappedIp6Address(aAddress)); + + aRecord.Init(); + aRecord.SetTtl(aTtl); + aRecord.SetAddress(ip4Address); + + SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mHostName, *mMessage)); + SuccessOrExit(error = mMessage->Append(aRecord)); + IncResourceRecordCount(); + +exit: + return error; +} + #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE Error Server::Response::AppendTxtRecord(const Srp::Server::Service &aService) { @@ -704,6 +745,7 @@ const char *Server::Response::QueryTypeToString(QueryType aType) "TXT", // (2) kTxtQuery "SRV & TXT", // (3) kSrvTxtQuery "AAAA", // (4) kAaaaQuery + "A", // (5) kAQuery }; static_assert(0 == kPtrQuery, "kPtrQuery value is incorrect"); @@ -711,6 +753,7 @@ const char *Server::Response::QueryTypeToString(QueryType aType) static_assert(2 == kTxtQuery, "kTxtQuery value is incorrect"); static_assert(3 == kSrvTxtQuery, "kSrvTxtQuery value is incorrect"); static_assert(4 == kAaaaQuery, "kAaaaQuery value is incorrect"); + static_assert(5 == kAQuery, "kAQuery value is incorrect"); return kTypeNames[aType]; } @@ -737,11 +780,12 @@ Error Server::Response::ResolveBySrp(void) continue; } - if (mType == kAaaaQuery) + if ((mType == kAaaaQuery) || (mType == kAQuery)) { if (QueryNameMatches(host.GetFullName())) { - error = AppendHostAddresses(host); + mSection = (mType == kAaaaQuery) ? kAnswerSection : kAdditionalDataSection; + error = AppendHostAddresses(host); ExitNow(); } @@ -1191,9 +1235,14 @@ void Server::Response::Answer(const ServiceInstanceInfo &aInstanceInfo, const Ip void Server::Response::Answer(const HostInfo &aHostInfo, const Ip6::MessageInfo &aMessageInfo) { + // Caller already ensures that `mType` is either `kAaaaQuery` or + // `kAQuery`. + + AddrType addrType = (mType == kAaaaQuery) ? kIp6AddrType : kIp4AddrType; + mSection = kAnswerSection; - if (AppendHostAddresses(aHostInfo) != kErrorNone) + if (AppendHostAddresses(addrType, aHostInfo) != kErrorNone) { SetResponseCode(Header::kResponseServerFailure); } @@ -1238,6 +1287,7 @@ void Server::HandleDiscoveredServiceInstance(const char *aServiceFullName, const break; case kAaaaQuery: + case kAQuery: break; } @@ -1261,12 +1311,22 @@ void Server::HandleDiscoveredHost(const char *aHostFullName, const HostInfo &aHo info.ReadFrom(query); - if ((info.mType == kAaaaQuery) && QueryNameMatches(query, aHostFullName)) + switch (info.mType) { - Response response(GetInstance()); + case kAaaaQuery: + case kAQuery: + if (QueryNameMatches(query, aHostFullName)) + { + Response response(GetInstance()); - RemoveQueryAndPrepareResponse(query, info, response); - response.Answer(aHostInfo, info.mMessageInfo); + RemoveQueryAndPrepareResponse(query, info, response); + response.Answer(aHostInfo, info.mMessageInfo); + } + + break; + + default: + break; } } } @@ -1301,6 +1361,7 @@ Server::DnsQueryType Server::GetQueryTypeAndName(const otDnssdQuery *aQuery, Dns break; case kAaaaQuery: + case kAQuery: type = kDnsQueryResolveHost; break; } @@ -1483,6 +1544,9 @@ void Server::DiscoveryProxy::Resolve(ProxyQuery &aQuery, ProxyQueryInfo &aInfo) case kAaaaQuery: action = kResolvingIp6Address; break; + case kAQuery: + action = kResolvingIp4Address; + break; } Perform(action, aQuery, aInfo); @@ -1538,6 +1602,7 @@ void Server::DiscoveryProxy::ReadNameFor(ProxyAction aAction, ReadQueryInstanceName(aQuery, aInfo, aName); break; case kResolvingIp6Address: + case kResolvingIp4Address: ReadQueryHostName(aQuery, aInfo, aName); break; } @@ -1597,6 +1662,9 @@ void Server::DiscoveryProxy::UpdateProxy(Command aCommand, case kResolvingIp6Address: StartOrStopIp6Resolver(aCommand, aName); break; + case kResolvingIp4Address: + StartOrStopIp4Resolver(aCommand, aName); + break; } } @@ -1734,6 +1802,30 @@ void Server::DiscoveryProxy::StartOrStopIp6Resolver(Command aCommand, Name::Buff } } +void Server::DiscoveryProxy::StartOrStopIp4Resolver(Command aCommand, Name::Buffer &aHostName) +{ + // Start or stop an IPv4 address resolver for a given host name. + + Dnssd::AddressResolver resolver; + + IgnoreError(StripDomainName(aHostName)); + + resolver.mHostName = aHostName; + resolver.mInfraIfIndex = Get().GetIfIndex(); + resolver.mCallback = HandleIp4AddressResult; + + switch (aCommand) + { + case kStart: + Get().StartIp4AddressResolver(resolver); + break; + + case kStop: + Get().StopIp4AddressResolver(resolver); + break; + } +} + bool Server::DiscoveryProxy::QueryMatches(const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, ProxyAction aAction, @@ -1756,6 +1848,7 @@ bool Server::DiscoveryProxy::QueryMatches(const ProxyQuery &aQuery, VerifyOrExit(QueryInstanceNameMatches(aQuery, aInfo, aName)); break; case kResolvingIp6Address: + case kResolvingIp4Address: VerifyOrExit(QueryHostNameMatches(aQuery, aInfo, aName)); break; case kNoAction: @@ -1892,7 +1985,46 @@ void Server::DiscoveryProxy::HandleIp6AddressResult(const Dnssd::AddressResult & VerifyOrExit(hasValidAddress); ConstructFullName(aResult.mHostName, fullHostName); - HandleResult(kResolvingIp6Address, fullHostName, &Response::AppendHostAddresses, ProxyResult(aResult)); + HandleResult(kResolvingIp6Address, fullHostName, &Response::AppendHostIp6Addresses, ProxyResult(aResult)); + +exit: + return; +} + +void Server::DiscoveryProxy::HandleIp4AddressResult(otInstance *aInstance, const otPlatDnssdAddressResult *aResult) +{ + AsCoreType(aInstance).Get().mDiscoveryProxy.HandleIp4AddressResult(*aResult); +} + +void Server::DiscoveryProxy::HandleIp4AddressResult(const Dnssd::AddressResult &aResult) +{ + bool hasValidAddress = false; + Name::Buffer fullHostName; + + VerifyOrExit(mIsRunning); + VerifyOrExit(aResult.mInfraIfIndex == Get().GetIfIndex()); + + for (uint16_t index = 0; index < aResult.mAddressesLength; index++) + { + const Dnssd::AddressAndTtl &entry = aResult.mAddresses[index]; + const Ip6::Address &address = AsCoreType(&entry.mAddress); + + if (entry.mTtl == 0) + { + continue; + } + + if (address.IsIp4Mapped()) + { + hasValidAddress = true; + break; + } + } + + VerifyOrExit(hasValidAddress); + + ConstructFullName(aResult.mHostName, fullHostName); + HandleResult(kResolvingIp4Address, fullHostName, &Response::AppendHostIp4Addresses, ProxyResult(aResult)); exit: return; @@ -1944,6 +2076,7 @@ void Server::DiscoveryProxy::HandleResult(ProxyAction aAction, break; case kNoAction: case kResolvingIp6Address: + case kResolvingIp4Address: break; } @@ -2038,6 +2171,10 @@ bool Server::DiscoveryProxy::IsActionForAdditionalSection(ProxyAction aAction, Q VerifyOrExit(aQueryType == kAaaaQuery); break; + case kResolvingIp4Address: + VerifyOrExit(aQueryType == kAQuery); + break; + case kNoAction: case kBrowsing: ExitNow(); @@ -2079,7 +2216,7 @@ Error Server::Response::AppendTxtRecord(const ProxyResult &aResult) return AppendTxtRecord(txtResult->mTxtData, txtResult->mTxtDataLength, txtResult->mTtl); } -Error Server::Response::AppendHostAddresses(const ProxyResult &aResult) +Error Server::Response::AppendHostIp6Addresses(const ProxyResult &aResult) { Error error = kErrorNone; const Dnssd::AddressResult *addrResult = aResult.mAddressResult; @@ -2108,6 +2245,30 @@ Error Server::Response::AppendHostAddresses(const ProxyResult &aResult) return error; } +Error Server::Response::AppendHostIp4Addresses(const ProxyResult &aResult) +{ + Error error = kErrorNone; + const Dnssd::AddressResult *addrResult = aResult.mAddressResult; + + mSection = (mType == kAQuery) ? kAnswerSection : kAdditionalDataSection; + + for (uint16_t index = 0; index < addrResult->mAddressesLength; index++) + { + const Dnssd::AddressAndTtl &entry = addrResult->mAddresses[index]; + const Ip6::Address &address = AsCoreType(&entry.mAddress); + + if (entry.mTtl == 0) + { + continue; + } + + SuccessOrExit(error = AppendARecord(address, entry.mTtl)); + } + +exit: + return error; +} + bool Server::IsProxyAddressValid(const Ip6::Address &aAddress) { return !aAddress.IsLinkLocalUnicast() && !aAddress.IsMulticast() && !aAddress.IsUnspecified() && diff --git a/src/core/net/dnssd_server.hpp b/src/core/net/dnssd_server.hpp index f584e3b12..4174ac5ae 100644 --- a/src/core/net/dnssd_server.hpp +++ b/src/core/net/dnssd_server.hpp @@ -326,6 +326,7 @@ class Server : public InstanceLocator, private NonCopyable kTxtQuery, kSrvTxtQuery, kAaaaQuery, + kAQuery, }; enum Section : uint8_t @@ -334,6 +335,12 @@ class Server : public InstanceLocator, private NonCopyable kAdditionalDataSection, }; + enum AddrType : uint8_t + { + kIp6AddrType, + kIp4AddrType, + }; + #if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE enum ProxyAction : uint8_t { @@ -342,6 +349,7 @@ class Server : public InstanceLocator, private NonCopyable kResolvingSrv, kResolvingTxt, kResolvingIp6Address, + kResolvingIp4Address }; #endif @@ -384,33 +392,35 @@ class Server : public InstanceLocator, private NonCopyable { public: explicit Response(Instance &aInstance); - Error AllocateAndInitFrom(const Request &aRequest); - void InitFrom(ProxyQuery &aQuery, const ProxyQueryInfo &aInfo); - void SetResponseCode(ResponseCode aResponseCode) { mHeader.SetResponseCode(aResponseCode); } ResponseCode AddQuestionsFrom(const Request &aRequest); - Error ParseQueryName(void); - void ReadQueryName(Name::Buffer &aName) const; - bool QueryNameMatches(const char *aName) const; - Error AppendQueryName(void); - Error AppendPtrRecord(const char *aInstanceLabel, uint32_t aTtl); - Error AppendSrvRecord(const ServiceInstanceInfo &aInstanceInfo); - Error AppendSrvRecord(const char *aHostName, - uint32_t aTtl, - uint16_t aPriority, - uint16_t aWeight, - uint16_t aPort); - Error AppendTxtRecord(const ServiceInstanceInfo &aInstanceInfo); - Error AppendTxtRecord(const void *aTxtData, uint16_t aTxtLength, uint32_t aTtl); - Error AppendHostAddresses(const HostInfo &aHostInfo); - Error AppendHostAddresses(const ServiceInstanceInfo &aInstanceInfo); - Error AppendHostAddresses(const Ip6::Address *aAddrs, uint16_t aAddrsLength, uint32_t aTtl); - Error AppendAaaaRecord(const Ip6::Address &aAddress, uint32_t aTtl); - void UpdateRecordLength(ResourceRecord &aRecord, uint16_t aOffset); - void IncResourceRecordCount(void); - void Send(const Ip6::MessageInfo &aMessageInfo); - void Answer(const HostInfo &aHostInfo, const Ip6::MessageInfo &aMessageInfo); - void Answer(const ServiceInstanceInfo &aInstanceInfo, const Ip6::MessageInfo &aMessageInfo); - Error ExtractServiceInstanceLabel(const char *aInstanceName, Name::LabelBuffer &aLabel); + + Error AllocateAndInitFrom(const Request &aRequest); + void InitFrom(ProxyQuery &aQuery, const ProxyQueryInfo &aInfo); + void SetResponseCode(ResponseCode aResponseCode) { mHeader.SetResponseCode(aResponseCode); } + Error ParseQueryName(void); + void ReadQueryName(Name::Buffer &aName) const; + bool QueryNameMatches(const char *aName) const; + Error AppendQueryName(void); + Error AppendPtrRecord(const char *aInstanceLabel, uint32_t aTtl); + Error AppendSrvRecord(const ServiceInstanceInfo &aInstanceInfo); + Error AppendSrvRecord(const char *aHostName, + uint32_t aTtl, + uint16_t aPriority, + uint16_t aWeight, + uint16_t aPort); + Error AppendTxtRecord(const ServiceInstanceInfo &aInstanceInfo); + Error AppendTxtRecord(const void *aTxtData, uint16_t aTxtLength, uint32_t aTtl); + Error AppendHostAddresses(AddrType aAddrType, const HostInfo &aHostInfo); + Error AppendHostAddresses(const ServiceInstanceInfo &aInstanceInfo); + Error AppendHostAddresses(AddrType aAddrType, const Ip6::Address *aAddrs, uint16_t aAddrsLength, uint32_t aTtl); + Error AppendAaaaRecord(const Ip6::Address &aAddress, uint32_t aTtl); + Error AppendARecord(const Ip6::Address &aAddress, uint32_t aTtl); + void UpdateRecordLength(ResourceRecord &aRecord, uint16_t aOffset); + void IncResourceRecordCount(void); + void Send(const Ip6::MessageInfo &aMessageInfo); + void Answer(const HostInfo &aHostInfo, const Ip6::MessageInfo &aMessageInfo); + void Answer(const ServiceInstanceInfo &aInstanceInfo, const Ip6::MessageInfo &aMessageInfo); + Error ExtractServiceInstanceLabel(const char *aInstanceName, Name::LabelBuffer &aLabel); #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE Error ResolveBySrp(void); bool QueryNameMatchesService(const Srp::Server::Service &aService) const; @@ -422,8 +432,8 @@ class Server : public InstanceLocator, private NonCopyable Error AppendPtrRecord(const ProxyResult &aResult); Error AppendSrvRecord(const ProxyResult &aResult); Error AppendTxtRecord(const ProxyResult &aResult); - - Error AppendHostAddresses(const ProxyResult &aResult); + Error AppendHostIp6Addresses(const ProxyResult &aResult); + Error AppendHostIp4Addresses(const ProxyResult &aResult); #endif #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) @@ -493,16 +503,19 @@ class Server : public InstanceLocator, private NonCopyable void StartOrStopSrvResolver(Command aCommand, const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo); void StartOrStopTxtResolver(Command aCommand, const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo); void StartOrStopIp6Resolver(Command aCommand, Name::Buffer &aHostName); + void StartOrStopIp4Resolver(Command aCommand, Name::Buffer &aHostName); static void HandleBrowseResult(otInstance *aInstance, const otPlatDnssdBrowseResult *aResult); static void HandleSrvResult(otInstance *aInstance, const otPlatDnssdSrvResult *aResult); static void HandleTxtResult(otInstance *aInstance, const otPlatDnssdTxtResult *aResult); static void HandleIp6AddressResult(otInstance *aInstance, const otPlatDnssdAddressResult *aResult); + static void HandleIp4AddressResult(otInstance *aInstance, const otPlatDnssdAddressResult *aResult); void HandleBrowseResult(const Dnssd::BrowseResult &aResult); void HandleSrvResult(const Dnssd::SrvResult &aResult); void HandleTxtResult(const Dnssd::TxtResult &aResult); void HandleIp6AddressResult(const Dnssd::AddressResult &aResult); + void HandleIp4AddressResult(const Dnssd::AddressResult &aResult); void HandleResult(ProxyAction aAction, const Name::Buffer &aName, ResponseAppender aAppender, diff --git a/tests/scripts/thread-cert/border_router/test_dnssd_server.py b/tests/scripts/thread-cert/border_router/test_dnssd_server.py index 8bff2b2e6..6af3bd69a 100755 --- a/tests/scripts/thread-cert/border_router/test_dnssd_server.py +++ b/tests/scripts/thread-cert/border_router/test_dnssd_server.py @@ -196,7 +196,7 @@ def test(self): }) # check some invalid queries - for qtype in ['A', 'CNAME']: + for qtype in ['CNAME']: dig_result = digger.dns_dig(server_addr, host1_full_name, qtype) self._assert_dig_result_matches(dig_result, { 'status': 'NOTIMP', diff --git a/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py b/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py index 71166ef7d..a80d017b4 100755 --- a/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py +++ b/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py @@ -278,7 +278,7 @@ def is_int(x): self._verify_discovery_proxy_meshcop(br2_addr, br2.get_network_name(), host) # 4. Check some invalid queries - for qtype in ['A', 'CNAME']: + for qtype in ['CNAME']: dig_result = host.dns_dig(br2_addr, host1_full_name, qtype) self._assert_dig_result_matches(dig_result, { 'status': 'NOTIMP', diff --git a/tests/unit/test_dnssd_discovery_proxy.cpp b/tests/unit/test_dnssd_discovery_proxy.cpp index c2dfc0d04..09536821a 100644 --- a/tests/unit/test_dnssd_discovery_proxy.cpp +++ b/tests/unit/test_dnssd_discovery_proxy.cpp @@ -566,7 +566,7 @@ struct TxtResolverInfo : public Clearable otPlatDnssdTxtCallback mCallback; }; -struct Ip6AddrResolverInfo : public Clearable +struct IpAddrResolverInfo : public Clearable { bool HostNameMatches(const char *aName) const { return !strcmp(mHostName, aName); } @@ -592,16 +592,19 @@ struct InvokeOnStart : public Clearable const otPlatDnssdSrvResult *mSrvResult; const otPlatDnssdTxtResult *mTxtResult; const otPlatDnssdAddressResult *mIp6AddrResult; + const otPlatDnssdAddressResult *mIp4AddrResult; }; -static BrowserInfo sStartBrowserInfo; -static BrowserInfo sStopBrowserInfo; -static SrvResolverInfo sStartSrvResolverInfo; -static SrvResolverInfo sStopSrvResolverInfo; -static TxtResolverInfo sStartTxtResolverInfo; -static TxtResolverInfo sStopTxtResolverInfo; -static Ip6AddrResolverInfo sStartIp6AddrResolverInfo; -static Ip6AddrResolverInfo sStopIp6AddrResolverInfo; +static BrowserInfo sStartBrowserInfo; +static BrowserInfo sStopBrowserInfo; +static SrvResolverInfo sStartSrvResolverInfo; +static SrvResolverInfo sStopSrvResolverInfo; +static TxtResolverInfo sStartTxtResolverInfo; +static TxtResolverInfo sStopTxtResolverInfo; +static IpAddrResolverInfo sStartIp6AddrResolverInfo; +static IpAddrResolverInfo sStopIp6AddrResolverInfo; +static IpAddrResolverInfo sStartIp4AddrResolverInfo; +static IpAddrResolverInfo sStopIp4AddrResolverInfo; static InvokeOnStart sInvokeOnStart; @@ -615,6 +618,8 @@ void ResetPlatDnssdApiInfo(void) sStopTxtResolverInfo.Clear(); sStartIp6AddrResolverInfo.Clear(); sStopIp6AddrResolverInfo.Clear(); + sStartIp4AddrResolverInfo.Clear(); + sStopIp4AddrResolverInfo.Clear(); sInvokeOnStart.Clear(); } @@ -674,6 +679,21 @@ void InvokeIp6AddrResolverCallback(const otPlatDnssdAddressCallback aCallback, c aCallback(sInstance, &aResult); } +void InvokeIp4AddrResolverCallback(const otPlatDnssdAddressCallback aCallback, const otPlatDnssdAddressResult &aResult) +{ + Log("Invoking Ip4 resolver callback"); + Log(" hostName : %s", aResult.mHostName); + Log(" if-index : %u", aResult.mInfraIfIndex); + Log(" numAddresses : %u", aResult.mAddressesLength); + for (uint16_t index = 0; index < aResult.mAddressesLength; index++) + { + Log(" address[%u] : %s", index, AsCoreType(&aResult.mAddresses[index].mAddress).ToString().AsCString()); + Log(" ttl[%u] : %u", index, aResult.mAddresses[index].mTtl); + } + + aCallback(sInstance, &aResult); +} + otPlatDnssdState otPlatDnssdGetState(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); @@ -802,6 +822,40 @@ void otPlatDnssdStopIp6AddressResolver(otInstance *aInstance, const otPlatDnssdA } } +void otPlatDnssdStartIp4AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + VerifyOrQuit(aResolver != nullptr); + + Log("otPlatDnssdStartIp4AddressResolver(\"%s\")", aResolver->mHostName); + + VerifyOrQuit(aInstance == sInstance); + VerifyOrQuit(aResolver->mInfraIfIndex == kInfraIfIndex); + + sStartIp4AddrResolverInfo.UpdateFrom(aResolver); + + if (sInvokeOnStart.mIp4AddrResult != nullptr) + { + InvokeIp6AddrResolverCallback(aResolver->mCallback, *sInvokeOnStart.mIp4AddrResult); + } +} + +void otPlatDnssdStopIp4AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + VerifyOrQuit(aResolver != nullptr); + + Log("otPlatDnssdStopIp4AddressResolver(\"%s\")", aResolver->mHostName); + + VerifyOrQuit(aInstance == sInstance); + VerifyOrQuit(aResolver->mInfraIfIndex == kInfraIfIndex); + + sStopIp4AddrResolverInfo.UpdateFrom(aResolver); + + if (sInvokeOnStart.mIp6AddrResult != nullptr) + { + InvokeIp4AddrResolverCallback(aResolver->mCallback, *sInvokeOnStart.mIp4AddrResult); + } +} + //---------------------------------------------------------------------------------------------------------------------- void TestProxyBasic(void) @@ -810,15 +864,18 @@ void TestProxyBasic(void) const uint8_t kTxtData[] = {3, 'A', '=', '1', 0}; - Srp::Server *srpServer; - Srp::Client *srpClient; - Dns::Client *dnsClient; - Dns::ServiceDiscovery::Server *dnsServer; - Dnssd::BrowseResult browseResult; - Dnssd::SrvResult srvResult; - Dnssd::TxtResult txtResult; - Dnssd::AddressResult ip6AddrrResult; - Dnssd::AddressAndTtl addressAndTtl; + Srp::Server *srpServer; + Srp::Client *srpClient; + Dns::Client *dnsClient; + Dns::ServiceDiscovery::Server *dnsServer; + Dnssd::BrowseResult browseResult; + Dnssd::SrvResult srvResult; + Dnssd::TxtResult txtResult; + Dnssd::AddressResult ip6AddrrResult; + Dnssd::AddressResult ip4AddrrResult; + Dnssd::AddressAndTtl addressAndTtl; + NetworkData::ExternalRouteConfig routeConfig; + Ip6::Address address; Log("--------------------------------------------------------------------------------------------"); Log("TestProxyBasic"); @@ -853,6 +910,19 @@ void TestProxyBasic(void) AdvanceTime(2000); VerifyOrQuit(srpClient->IsRunning()); + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Add a route prefix (with NAT64 flag) to network data"); + + routeConfig.Clear(); + SuccessOrQuit(AsCoreType(&routeConfig.mPrefix.mPrefix).FromString("64:ff9b::")); + routeConfig.mPrefix.mLength = 96; + routeConfig.mPreference = NetworkData::kRoutePreferenceMedium; + routeConfig.mNat64 = true; + routeConfig.mStable = true; + + SuccessOrQuit(otBorderRouterAddRoute(sInstance, &routeConfig)); + SuccessOrQuit(otBorderRouterRegister(sInstance)); + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); sBrowseInfo.Reset(); @@ -870,6 +940,8 @@ void TestProxyBasic(void) VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStartBrowserInfo.ServiceTypeMatches("_avenger._udp")); @@ -896,6 +968,8 @@ void TestProxyBasic(void) VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStopBrowserInfo.ServiceTypeMatches("_avenger._udp")); VerifyOrQuit(sStopBrowserInfo.mCallback == sStartBrowserInfo.mCallback); @@ -959,6 +1033,8 @@ void TestProxyBasic(void) VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStopTxtResolverInfo.ServiceTypeMatches("_avenger._udp")); VerifyOrQuit(sStopTxtResolverInfo.ServiceInstanceMatches("hulk")); @@ -991,6 +1067,8 @@ void TestProxyBasic(void) VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("compound")); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallback == sStartIp6AddrResolverInfo.mCallback); @@ -1034,6 +1112,8 @@ void TestProxyBasic(void) VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStartSrvResolverInfo.ServiceTypeMatches("_avenger._udp")); VerifyOrQuit(sStartSrvResolverInfo.ServiceInstanceMatches("iron.man")); @@ -1062,6 +1142,8 @@ void TestProxyBasic(void) VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 0); @@ -1120,6 +1202,8 @@ void TestProxyBasic(void) VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 0); @@ -1147,6 +1231,8 @@ void TestProxyBasic(void) VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("starktower")); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallback == sStartIp6AddrResolverInfo.mCallback); @@ -1185,6 +1271,8 @@ void TestProxyBasic(void) VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("earth")); @@ -1214,6 +1302,8 @@ void TestProxyBasic(void) VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0); VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("earth")); VerifyOrQuit(sStopIp6AddrResolverInfo.mCallback == sStartIp6AddrResolverInfo.mCallback); @@ -1228,6 +1318,84 @@ void TestProxyBasic(void) VerifyOrQuit(sResolveAddressInfo.mHostAddresses[0] == AsCoreType(&addressAndTtl.mAddress)); VerifyOrQuit(sResolveAddressInfo.mTtl == kTtl); + Log("--------------------------------------------------------------------------------------------"); + + ResetPlatDnssdApiInfo(); + sResolveAddressInfo.Reset(); + + Log("ResolveIp4Address()"); + SuccessOrQuit(dnsClient->ResolveIp4Address("shield.default.service.arpa.", AddressCallback, sInstance)); + AdvanceTime(10); + + // Check that an IPv4 address resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartIp4AddrResolverInfo.HostNameMatches("shield")); + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke IPv4 Address Resolver callback"); + + SuccessOrQuit(AsCoreType(&addressAndTtl.mAddress).FromString("::ffff:1.2.3.4")); + addressAndTtl.mTtl = kTtl; + ip4AddrrResult.mHostName = "shield"; + ip4AddrrResult.mInfraIfIndex = kInfraIfIndex; + ip4AddrrResult.mAddresses = &addressAndTtl; + ip4AddrrResult.mAddressesLength = 1; + + InvokeIp4AddrResolverCallback(sStartIp4AddrResolverInfo.mCallback, ip4AddrrResult); + + AdvanceTime(10); + + // Check that the IPv4 address resolver is stopped + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp4AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStopIp4AddrResolverInfo.HostNameMatches("shield")); + VerifyOrQuit(sStopIp4AddrResolverInfo.mCallback == sStartIp4AddrResolverInfo.mCallback); + + // Check that response is sent to client and validate it + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveAddressInfo.mError); + + VerifyOrQuit(!strcmp(sResolveAddressInfo.mHostName, "shield.default.service.arpa.")); + VerifyOrQuit(sResolveAddressInfo.mNumHostAddresses == 1); + + // The 1.2.3.4 address with the NAT64 prefix + SuccessOrQuit(address.FromString("64:ff9b:0:0:0:0:102:304")); + VerifyOrQuit(sResolveAddressInfo.mHostAddresses[0] == address); + VerifyOrQuit(sResolveAddressInfo.mTtl == kTtl); + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveAddressInfo.mError); + + VerifyOrQuit(!strcmp(sResolveAddressInfo.mHostName, "shield.default.service.arpa.")); + VerifyOrQuit(sResolveAddressInfo.mNumHostAddresses == 1); + VerifyOrQuit(sResolveAddressInfo.mTtl == kTtl); + + VerifyOrQuit(sResolveAddressInfo.mHostAddresses[0] == address); + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); Log("Stop DNS-SD server"); From 9fd6596e7bd3b47d062a0f5d7d3b12fa059893c8 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 14 Aug 2024 12:41:18 -0700 Subject: [PATCH 107/160] [dns-client] track limited query (one question) servers (#10403) This commit updates `Dns::Client` to remember servers/resolvers that are known to have trouble with multiple-question queries. This information is learned from earlier interactions with the server. When `ResolveService()` is requested, if the user explicitly requests "optimize" service mode, the request is honored. Otherwise, if "optimize" service mode is chosen from the default configuration and the DNS server is known to have trouble with multiple-question queries, "separate" service mode is used instead. This commit also updates the `test_dns_client.cpp` unit test to validate the newly added behavior. --- src/core/net/dns_client.cpp | 65 +++++++++++++++++++++++++++++++ src/core/net/dns_client.hpp | 7 +++- src/core/net/dnssd_server.hpp | 21 ++++++++++ tests/unit/test_dns_client.cpp | 71 +++++++++++++++++++++++++++++----- 4 files changed, 154 insertions(+), 10 deletions(-) diff --git a/src/core/net/dns_client.cpp b/src/core/net/dns_client.cpp index 49a0d4d5a..1d41c87c6 100644 --- a/src/core/net/dns_client.cpp +++ b/src/core/net/dns_client.cpp @@ -791,6 +791,8 @@ void Client::Stop(void) IgnoreError(mEndpoint.Deinitialize()); } #endif + + mLimitedQueryServers.Clear(); } #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE @@ -938,6 +940,8 @@ Error Client::Resolve(const char *aInstanceLabel, info.mConfig.SetFrom(aConfig, mDefaultConfig); info.mShouldResolveHostAddr = aShouldResolveHostAddr; + CheckAndUpdateServiceMode(info.mConfig, aConfig); + switch (info.mConfig.GetServiceMode()) { case QueryConfig::kServiceModeSrvTxtSeparate: @@ -1402,6 +1406,11 @@ Error Client::ParseResponse(const Message &aResponseMessage, Query *&aQuery, Err aResponseError = Header::ResponseCodeToError(header.GetResponseCode()); + if ((aResponseError == kErrorNone) && (info.mQueryType == kServiceQuerySrvTxt)) + { + RecordServerAsCapableOfMultiQuestions(info.mConfig.GetServerSockAddr().GetAddress()); + } + exit: return error; } @@ -1591,6 +1600,60 @@ Error Client::ReplaceWithIp4Query(Query &aQuery) #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE +void Client::CheckAndUpdateServiceMode(QueryConfig &aConfig, const QueryConfig *aRequestConfig) const +{ + // If the user explicitly requested "optimize" mode, we honor that + // request. Otherwise, if "optimize" is chosen from the default + // config, we check if the DNS server is known to have trouble + // with multiple-question queries. If so, we switch to "separate" + // mode. + + if ((aRequestConfig != nullptr) && (aRequestConfig->GetServiceMode() == QueryConfig::kServiceModeSrvTxtOptimize)) + { + ExitNow(); + } + + VerifyOrExit(aConfig.GetServiceMode() == QueryConfig::kServiceModeSrvTxtOptimize); + + if (mLimitedQueryServers.Contains(aConfig.GetServerSockAddr().GetAddress())) + { + aConfig.SetServiceMode(QueryConfig::kServiceModeSrvTxtSeparate); + } + +exit: + return; +} + +void Client::RecordServerAsLimitedToSingleQuestion(const Ip6::Address &aServerAddress) +{ + VerifyOrExit(!aServerAddress.IsUnspecified()); + + VerifyOrExit(!mLimitedQueryServers.Contains(aServerAddress)); + + if (mLimitedQueryServers.IsFull()) + { + uint8_t randomIndex = Random::NonCrypto::GetUint8InRange(0, mLimitedQueryServers.GetMaxSize()); + + mLimitedQueryServers.Remove(mLimitedQueryServers[randomIndex]); + } + + IgnoreError(mLimitedQueryServers.PushBack(aServerAddress)); + +exit: + return; +} + +void Client::RecordServerAsCapableOfMultiQuestions(const Ip6::Address &aServerAddress) +{ + Ip6::Address *entry = mLimitedQueryServers.Find(aServerAddress); + + VerifyOrExit(entry != nullptr); + mLimitedQueryServers.Remove(*entry); + +exit: + return; +} + Error Client::ReplaceWithSeparateSrvTxtQueries(Query &aQuery) { Error error = kErrorFailed; @@ -1602,6 +1665,8 @@ Error Client::ReplaceWithSeparateSrvTxtQueries(Query &aQuery) VerifyOrExit(info.mQueryType == kServiceQuerySrvTxt); VerifyOrExit(info.mConfig.GetServiceMode() == QueryConfig::kServiceModeSrvTxtOptimize); + RecordServerAsLimitedToSingleQuestion(info.mConfig.GetServerSockAddr().GetAddress()); + secondQuery = aQuery.Clone(); VerifyOrExit(secondQuery != nullptr); diff --git a/src/core/net/dns_client.hpp b/src/core/net/dns_client.hpp index 9ac6a3868..5a86058d1 100644 --- a/src/core/net/dns_client.hpp +++ b/src/core/net/dns_client.hpp @@ -770,7 +770,8 @@ class Client : public InstanceLocator, private NonCopyable #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE private: - static constexpr uint16_t kMaxCnameAliasNameChanges = 40; + static constexpr uint16_t kMaxCnameAliasNameChanges = 40; + static constexpr uint8_t kLimitedQueryServersArraySize = 3; enum QueryType : uint8_t { @@ -858,6 +859,9 @@ class Client : public InstanceLocator, private NonCopyable void *aContext, const QueryConfig *aConfig, bool aShouldResolveHostAddr); + void CheckAndUpdateServiceMode(QueryConfig &aConfig, const QueryConfig *aRequestConfig) const; + void RecordServerAsLimitedToSingleQuestion(const Ip6::Address &aServerAddress); + void RecordServerAsCapableOfMultiQuestions(const Ip6::Address &aServerAddress); Error ReplaceWithSeparateSrvTxtQueries(Query &aQuery); void ResolveHostAddressIfNeeded(Query &aQuery, const Message &aResponseMessage); #endif @@ -925,6 +929,7 @@ class Client : public InstanceLocator, private NonCopyable #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE bool mUserDidSetDefaultAddress; #endif + Array mLimitedQueryServers; }; } // namespace Dns diff --git a/src/core/net/dnssd_server.hpp b/src/core/net/dnssd_server.hpp index 4174ac5ae..e20e71f4e 100644 --- a/src/core/net/dnssd_server.hpp +++ b/src/core/net/dnssd_server.hpp @@ -49,6 +49,7 @@ #include "border_router/infra_if.hpp" #include "common/as_core_type.hpp" #include "common/callback.hpp" +#include "common/equatable.hpp" #include "common/message.hpp" #include "common/non_copyable.hpp" #include "common/owned_ptr.hpp" @@ -96,6 +97,26 @@ class Server : public InstanceLocator, private NonCopyable */ class Counters : public otDnssdCounters, public Clearable { + public: + /** + * Returns the total number of processed queries (successful or failed responses). + * + * @return The total number of queries. + * + */ + uint32_t GetTotalQueries(void) const { return mSuccessResponse + GetTotalFailedQueries(); } + + /** + * Returns the total number of failed queries (any error response code). + * + * @return The total number of failed queries. + * + */ + uint32_t GetTotalFailedQueries(void) const + { + return mServerFailureResponse + mFormatErrorResponse + mNameErrorResponse + mNotImplementedResponse + + mOtherResponse; + } }; #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE diff --git a/tests/unit/test_dns_client.cpp b/tests/unit/test_dns_client.cpp index e91376689..b4947f585 100644 --- a/tests/unit/test_dns_client.cpp +++ b/tests/unit/test_dns_client.cpp @@ -497,15 +497,17 @@ void TestDnsClient(void) Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize, }; - Array addresses; - Srp::Server *srpServer; - Srp::Client *srpClient; - Srp::Client::Service service1; - Srp::Client::Service service2; - Dns::Client *dnsClient; - Dns::Client::QueryConfig queryConfig; - Dns::ServiceDiscovery::Server *dnsServer; - uint16_t heapAllocations; + Array addresses; + Srp::Server *srpServer; + Srp::Client *srpClient; + Srp::Client::Service service1; + Srp::Client::Service service2; + Dns::Client *dnsClient; + Dns::Client::QueryConfig queryConfig; + Dns::ServiceDiscovery::Server *dnsServer; + Dns::ServiceDiscovery::Server::Counters oldServerCounters; + Dns::ServiceDiscovery::Server::Counters newServerCounters; + uint16_t heapAllocations; Log("--------------------------------------------------------------------------------------------"); Log("TestDnsClient"); @@ -882,6 +884,8 @@ void TestDnsClient(void) queryConfig.Clear(); queryConfig.mServiceMode = static_cast(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize); + oldServerCounters = dnsServer->GetCounters(); + sResolveServiceInfo.Reset(); SuccessOrQuit(dnsClient->ResolveServiceAndHostAddress(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig)); @@ -907,6 +911,55 @@ void TestDnsClient(void) VerifyOrQuit(addresses.Contains(sResolveServiceInfo.mHostAddresses[index])); } + newServerCounters = dnsServer->GetCounters(); + + Log("Validate (using server counter) that client first tried to query SRV/TXT together and failed"); + Log("and then send separate queries (for SRV, TXT and AAAA)"); + Log(" Total : %2u -> %2u", oldServerCounters.GetTotalQueries(), newServerCounters.GetTotalQueries()); + Log(" Failed: %2u -> %2u", oldServerCounters.GetTotalFailedQueries(), newServerCounters.GetTotalFailedQueries()); + + VerifyOrQuit(newServerCounters.GetTotalFailedQueries() == 1 + oldServerCounters.GetTotalFailedQueries()); + VerifyOrQuit(newServerCounters.GetTotalQueries() == 4 + oldServerCounters.GetTotalQueries()); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Resolve service again now using `kServiceModeSrvTxtOptimize` as default config"); + Log("Client should already know that server is not capable of handling multi-question query"); + + queryConfig.Clear(); + queryConfig.mServiceMode = static_cast(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize); + + dnsClient->SetDefaultConfig(queryConfig); + + Log("ResolveService(%s,%s)", kInstance1Label, kService1FullName); + + oldServerCounters = dnsServer->GetCounters(); + + sResolveServiceInfo.Reset(); + SuccessOrQuit(dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, nullptr)); + + AdvanceTime(100); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveServiceInfo.mError); + + VerifyOrQuit(sResolveServiceInfo.mInfo.mTtl != 0); + VerifyOrQuit(sResolveServiceInfo.mInfo.mPort == service1.mPort); + VerifyOrQuit(sResolveServiceInfo.mInfo.mWeight == service1.mWeight); + VerifyOrQuit(strcmp(sResolveServiceInfo.mInfo.mHostNameBuffer, kHostFullName) == 0); + + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataTtl != 0); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataSize != 0); + + newServerCounters = dnsServer->GetCounters(); + + Log("Client should already know that server is not capable of handling multi-question query"); + Log("Check server counters to validate that client did send separate queries for TXT and SRV"); + Log(" Total : %2u -> %2u", oldServerCounters.GetTotalQueries(), newServerCounters.GetTotalQueries()); + Log(" Failed: %2u -> %2u", oldServerCounters.GetTotalFailedQueries(), newServerCounters.GetTotalFailedQueries()); + + VerifyOrQuit(newServerCounters.GetTotalFailedQueries() == oldServerCounters.GetTotalFailedQueries()); + VerifyOrQuit(newServerCounters.GetTotalQueries() == 2 + oldServerCounters.GetTotalQueries()); + dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeDisabled); Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); From fd7284b0e9cb6cfe273b9906ea3d63635ee3fac0 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 14 Aug 2024 12:58:04 -0700 Subject: [PATCH 108/160] [address-resolver] freshness timeout mechanism for cache entries (#10575) This commit introduces a freshness timeout mechanism for address cache entries which is used to decide when removing stale entries when the associated RLOC16 is unreachable. In `AddressResolver`, when resolving an EID from an existing cache entry, if the target RLOC16 is unreachable, the entry is typically considered stale and removed to allow a new address query to be sent. This commit adds a mechanism to skip this removal step if the entry has been recently updated, i.e., an `AddressNotify` has been received for it and its `FreshnessTimeout` has not yet expired. The `FreshnessTimeout` check prevents repeated address query transmissions when mesh routes are not yet discovered (e.g., after initial attach) or if there is a temporary link issue. --- src/core/thread/address_resolver.cpp | 39 +++++++++++++++++++++++----- src/core/thread/address_resolver.hpp | 12 +++++++-- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index 930bfbe05..b3f2b0336 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -493,18 +493,31 @@ Error AddressResolver::Resolve(const Ip6::Address &aEid, uint16_t &aRloc16, bool if ((entry != nullptr) && ((list == &mCachedList) || (list == &mSnoopedList))) { + bool isFresh; + list->PopAfter(prev); - if (Get().GetNextHop(entry->GetRloc16()) == Mle::kInvalidRloc16) + // If the `entry->GetRloc16()` is unreachable (there is no + // valid next hop towards it), it may be a stale entry. We + // clear the entry to allow new address query to be sent for + // it, unless the entry has been recently updated, i.e., we + // have recently received an `AddressNotify` for it and its + // `FreshnessTimeout` has not expired yet. + // + // The `FreshnessTimeout` check prevents repeated address + // query transmissions when mesh routes are not yet + // discovered (e.g., after initial attach) or if there is a + // temporary link issue. + + isFresh = (list == &mCachedList) && !entry->IsFreshnessTimeoutZero(); + + if (!isFresh && (Get().GetNextHop(entry->GetRloc16()) == Mle::kInvalidRloc16)) { - // If the `entry->GetRloc16()` is unreachable (there is no valid - // next hop towards it), we clear the entry so to start a new - // address query. - mCacheEntryPool.Free(*entry); entry = nullptr; } - else + + if (entry != nullptr) { // Push the entry at the head of cached list. @@ -701,6 +714,8 @@ void AddressResolver::HandleTmf(Coap::Message &aMessage, cons entry->SetRloc16(rloc16); entry->SetMeshLocalIid(meshLocalIid); entry->SetLastTransactionTime(lastTransactionTime); + entry->ResetFreshnessTimeout(); + Get().RegisterReceiver(TimeTicker::kAddressResolver); list->PopAfter(prev); mCachedList.Push(*entry); @@ -926,6 +941,15 @@ void AddressResolver::HandleTimeTick(void) { bool continueRxingTicks = false; + for (CacheEntry &entry : mCachedList) + { + if (!entry.IsFreshnessTimeoutZero()) + { + entry.DecrementFreshnessTimeout(); + continueRxingTicks = true; + } + } + for (CacheEntry &entry : mSnoopedList) { if (entry.IsTimeoutZero()) @@ -1128,7 +1152,8 @@ void AddressResolver::LogCacheEntryChange(EntryChange, Reason, const CacheEntry void AddressResolver::CacheEntry::Init(Instance &aInstance) { InstanceLocatorInit::Init(aInstance); - mNextIndex = kNoNextIndex; + mNextIndex = kNoNextIndex; + mFreshnessTimeout = 0; } AddressResolver::CacheEntry *AddressResolver::CacheEntry::GetNext(void) diff --git a/src/core/thread/address_resolver.hpp b/src/core/thread/address_resolver.hpp index 9e9799f19..84edeef5c 100644 --- a/src/core/thread/address_resolver.hpp +++ b/src/core/thread/address_resolver.hpp @@ -283,6 +283,10 @@ class AddressResolver : public InstanceLocator, private NonCopyable uint16_t GetTimeout(void) const { return mInfo.mOther.mTimeout; } void SetTimeout(uint16_t aTimeout) { mInfo.mOther.mTimeout = aTimeout; } + void DecrementFreshnessTimeout(void) { mFreshnessTimeout--; } + bool IsFreshnessTimeoutZero(void) const { return mFreshnessTimeout == 0; } + void ResetFreshnessTimeout(void) { mFreshnessTimeout = kFreshnessTimeout; } + uint16_t GetRetryDelay(void) const { return mInfo.mOther.mRetryDelay; } void SetRetryDelay(uint16_t aDelay) { mInfo.mOther.mRetryDelay = aDelay; } @@ -295,12 +299,16 @@ class AddressResolver : public InstanceLocator, private NonCopyable bool Matches(const Ip6::Address &aEid) const { return GetTarget() == aEid; } private: - static constexpr uint16_t kNoNextIndex = 0xffff; // `mNextIndex` value when at end of list. + static constexpr uint16_t kNoNextIndex = 0x3fff; // `mNextIndex` value when at end of list. static constexpr uint32_t kInvalidLastTransTime = 0xffffffff; // Value when `mLastTransactionTime` is invalid. + static constexpr uint8_t kFreshnessTimeout = 3; + + static_assert(kCacheEntries < kNoNextIndex, "kCacheEntries is too large and does not fit in 14 bit index"); Ip6::Address mTarget; uint16_t mRloc16; - uint16_t mNextIndex; + uint16_t mNextIndex : 14; + uint8_t mFreshnessTimeout : 2; union { From 54571af6a759c85d82d2fe40271c19b659fc61bb Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Wed, 14 Aug 2024 20:43:33 -0700 Subject: [PATCH 109/160] [github-actions] free up disk space for docker build (#10610) --- .github/workflows/docker.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8f17792a7..f062d6898 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -59,6 +59,9 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@main + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true From 93e838102c05af3cb34584033c1999c805b97120 Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Thu, 15 Aug 2024 22:35:18 +0800 Subject: [PATCH 110/160] [thread-cert] support to get link-local address of the infra interface (#10603) This commit targets to support getting infra link-local address of a OtbrNode in docker test, which is usefully for future test cases. The test_multi_ail.py is also updated to test the new method added. --- .../border_router/test_multi_ail.py | 22 ++++++++++++++----- tests/scripts/thread-cert/config.py | 3 ++- tests/scripts/thread-cert/node.py | 11 ++++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/tests/scripts/thread-cert/border_router/test_multi_ail.py b/tests/scripts/thread-cert/border_router/test_multi_ail.py index 3cc601b54..665e3d092 100755 --- a/tests/scripts/thread-cert/border_router/test_multi_ail.py +++ b/tests/scripts/thread-cert/border_router/test_multi_ail.py @@ -33,6 +33,8 @@ import config import thread_cert +from node import OtbrNode + IPV4_CIDR_ADDR_CMD = f'ip addr show {config.BACKBONE_IFNAME} | grep -w inet | grep -Eo "[0-9.]+/[0-9]+"' @@ -70,8 +72,8 @@ class TwoBorderRoutersOnTwoInfrastructures(thread_cert.TestCase): } def test(self): - br1 = self.nodes[self.BR1] - br2 = self.nodes[self.BR2] + br1: OtbrNode = self.nodes[self.BR1] + br2: OtbrNode = self.nodes[self.BR2] # start nodes br1.start() @@ -92,9 +94,19 @@ def test(self): self.assertNotEqual(ipaddress.ip_network(br1_infra_ip_addr[0].strip(), strict=False), ipaddress.ip_network(br2_infra_ip_addr[0].strip(), strict=False)) - # ping each other - self.assertTrue(br1.ping(br2.get_ip6_address(config.ADDRESS_TYPE.ML_EID))) - self.assertTrue(br2.ping(br1.get_ip6_address(config.ADDRESS_TYPE.ML_EID))) + # Ping test + br1_thread_link_local = br1.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL) + br2_thread_link_local = br2.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL) + br1_infra_link_local = br1.get_ip6_address(config.ADDRESS_TYPE.BACKBONE_LINK_LOCAL) + br2_infra_link_local = br2.get_ip6_address(config.ADDRESS_TYPE.BACKBONE_LINK_LOCAL) + + # ping each other using Thread link-local address + self.assertTrue(br1.ping(br2_thread_link_local)) + self.assertTrue(br2.ping(br1_thread_link_local)) + + # ping each other using Infra link-local address + self.assertFalse(br1.ping(br2_infra_link_local, interface=br1_infra_link_local)) + self.assertFalse(br2.ping(br1_infra_link_local, interface=br2_infra_link_local)) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/config.py b/tests/scripts/thread-cert/config.py index b49380d4d..3532690e6 100644 --- a/tests/scripts/thread-cert/config.py +++ b/tests/scripts/thread-cert/config.py @@ -106,13 +106,14 @@ class ADDRESS_TYPE(Enum): - LINK_LOCAL = 'LINK_LOCAL' + LINK_LOCAL = 'LINK_LOCAL' # For Thread interface link-local only GLOBAL = 'GLOBAL' RLOC = 'RLOC' ALOC = 'ALOC' ML_EID = 'ML_EID' DUA = 'DUA' BACKBONE_GUA = 'BACKBONE_GUA' + BACKBONE_LINK_LOCAL = 'BACKBONE_LINK_LOCAL' OMR = 'OMR' ONLINK_ULA = 'ONLINK_ULA' ONLINK_GUA = 'ONLINK_GUA' diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 88c57dcd1..1e8ed91a1 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -3804,6 +3804,8 @@ def get_ip6_address(self, address_type: config.ADDRESS_TYPE): """ if address_type == config.ADDRESS_TYPE.BACKBONE_GUA: return self._getBackboneGua() + elif address_type == config.ADDRESS_TYPE.BACKBONE_LINK_LOCAL: + return self._getInfraLinkLocalAddress() elif address_type == config.ADDRESS_TYPE.ONLINK_ULA: return self._getInfraUla() elif address_type == config.ADDRESS_TYPE.ONLINK_GUA: @@ -3835,6 +3837,15 @@ def _getInfraGua(self) -> Optional[str]: gua_prefix = config.ONLINK_GUA_PREFIX.split('::/')[0] return [addr for addr in self.get_ether_addrs() if addr.startswith(gua_prefix)] + def _getInfraLinkLocalAddress(self) -> Optional[str]: + """ Returns the link-local address autoconfigured on the infra link, which is started with "fe80". + """ + for addr in self.get_ether_addrs(): + if re.match(config.LINK_LOCAL_REGEX_PATTERN, addr, re.I): + return addr + + return None + def ping(self, *args, **kwargs): backbone = kwargs.pop('backbone', False) if backbone: From b5c57e6ad7ec2a1f34565be723bd1fe8dc3e1fa5 Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Fri, 16 Aug 2024 22:23:53 +0800 Subject: [PATCH 111/160] [thread-cert] wrap command `br peers` and `br routers` (#10613) This commit adds functions to read peer BRs and routers on infra link by wrapping the ot-ctl command `br peers` and `br routers`. `test_multi_ail.py` is also updated to test the new added functions. --- .../border_router/test_multi_ail.py | 8 +++++ tests/scripts/thread-cert/node.py | 32 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/tests/scripts/thread-cert/border_router/test_multi_ail.py b/tests/scripts/thread-cert/border_router/test_multi_ail.py index 665e3d092..66642fd7f 100755 --- a/tests/scripts/thread-cert/border_router/test_multi_ail.py +++ b/tests/scripts/thread-cert/border_router/test_multi_ail.py @@ -108,6 +108,14 @@ def test(self): self.assertFalse(br1.ping(br2_infra_link_local, interface=br1_infra_link_local)) self.assertFalse(br2.ping(br1_infra_link_local, interface=br2_infra_link_local)) + # br peers + self.assertEqual(br1.get_br_peers_rloc16s(), [br2.get_addr16()]) + self.assertEqual(br2.get_br_peers_rloc16s(), [br1.get_addr16()]) + + # br routers + self.assertEqual(br1.get_br_routers_ip_addresses(), []) + self.assertEqual(br2.get_br_routers_ip_addresses(), []) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 1e8ed91a1..9139541c8 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -2221,6 +2221,9 @@ def remove_prefix(self, prefix): self.send_command(cmd) self._expect_done() + # + # BR commands + # def enable_br(self): self.send_command('br enable') self._expect_done() @@ -2234,6 +2237,35 @@ def get_br_omr_prefix(self): self.send_command(cmd) return self._expect_command_output()[0] + def get_br_peers(self) -> List[str]: + # Example output of `br peers` command: + # rloc16:0xa800 age:00:00:50 + # rloc16:0x6800 age:00:00:51 + # Done + self.send_command('br peers') + return self._expect_command_output() + + def get_br_peers_rloc16s(self) -> List[int]: + """parse `br peers` output and return the list of RLOC16s""" + return [ + int(pair.split(':')[1], 16) + for line in self.get_br_peers() + for pair in line.split() + if pair.split(':')[0] == 'rloc16' + ] + + def get_br_routers(self) -> List[str]: + # Example output of `br routers` command: + # fe80:0:0:0:42:acff:fe14:3 (M:0 O:0 Stub:1) ms-since-rx:144160 reachable:yes age:00:17:36 (peer BR) + # fe80:0:0:0:42:acff:fe14:2 (M:0 O:0 Stub:1) ms-since-rx:45179 reachable:yes age:00:17:36 + # Done + self.send_command('br routers') + return self._expect_command_output() + + def get_br_routers_ip_addresses(self) -> List[IPv6Address]: + """parse `br routers` output and return the list of IPv6 addresses""" + return [IPv6Address(line.split()[0]) for line in self.get_br_routers()] + def get_netdata_omr_prefixes(self): omr_prefixes = [] for prefix in self.get_prefixes(): From 88bc4e95e063773784d0ef5c0d6122fa2eeeba93 Mon Sep 17 00:00:00 2001 From: Esko Dijk Date: Fri, 16 Aug 2024 19:23:17 +0200 Subject: [PATCH 112/160] [cli] clarify docs - parameter use for `dns config` to change service mode only (#10614) Also adds missing CLI outputs in examples. --- src/cli/README.md | 23 +++++++++++++++++++++-- src/cli/cli_dns.cpp | 11 +++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/cli/README.md b/src/cli/README.md index 856d57f1e..c633f229a 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -1284,7 +1284,7 @@ Done > ``` -### dns config \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] \[service mode] +### dns config \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] \[service mode] \[protocol] Set the default query config. @@ -1322,9 +1322,28 @@ Done > dns config Server: [fd00:0:0:0:0:0:0:2]:53 -ResponseTimeout: 3000 ms +ResponseTimeout: 6000 ms MaxTxAttempts: 3 RecursionDesired: yes +Nat64Mode: allow +TransportProtocol: udp +Done +``` + +This final example shows how only 'recursion desired' and the service mode are set, and all other parameters are set to their defaults: + +```bash +> dns config :: 0 0 0 1 srv_txt_sep +Done + +> dns config +Server: [2001:4860:4860:0:0:0:0:8888]:53 +ResponseTimeout: 6000 ms +MaxTxAttempts: 3 +RecursionDesired: yes +ServiceMode: srv_txt_sep +Nat64Mode: allow +TransportProtocol: udp Done ``` diff --git a/src/cli/cli_dns.cpp b/src/cli/cli_dns.cpp index c4d72d1c9..0ad238dd8 100644 --- a/src/cli/cli_dns.cpp +++ b/src/cli/cli_dns.cpp @@ -161,6 +161,9 @@ template <> otError Dns::Process(Arg aArgs[]) * ResponseTimeout: 5000 ms * MaxTxAttempts: 2 * RecursionDesired: no + * ServiceMode: srv_txt_opt + * Nat64Mode: allow + * TransportProtocol: udp * Done * @endcode * @code @@ -170,16 +173,20 @@ template <> otError Dns::Process(Arg aArgs[]) * @code * dns config * Server: [fd00:0:0:0:0:0:0:2]:53 - * ResponseTimeout: 3000 ms + * ResponseTimeout: 6000 ms * MaxTxAttempts: 3 * RecursionDesired: yes + * ServiceMode: srv_txt_opt + * Nat64Mode: allow + * TransportProtocol: udp * Done * @endcode * @par api_copy * #otDnsClientSetDefaultConfig * @cparam dns config [@ca{dns-server-IP}] [@ca{dns-server-port}] [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] [@ca{recursion-desired-boolean}] [@ca{service-mode}] + * --> [@ca{recursion-desired-boolean}] [@ca{service-mode}] [@ca{protocol}] * @par * We can leave some of the fields as unspecified (or use value zero). The * unspecified fields are replaced by the corresponding OT config option From 6209b0b85529be3e579730490d38a5bd432c2440 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 16 Aug 2024 10:24:16 -0700 Subject: [PATCH 113/160] [spinel] correct `SPINEL_PROP_IPV6_ADDRESS_TABLE` documentation (#10617) This commit aligns the documentation in `spinel.h` with the implementation for `SPINEL_PROP_IPV6_ADDRESS_TABLE` regarding the order of valid/preferred lifetime fields. --- src/lib/spinel/spinel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index f21a7a4e8..e1cb888ba 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -3467,8 +3467,8 @@ enum * * `6`: IPv6 Address * `C`: Network Prefix Length (in bits) - * `L`: Valid Lifetime * `L`: Preferred Lifetime + * `L`: Valid Lifetime * */ SPINEL_PROP_IPV6_ADDRESS_TABLE = SPINEL_PROP_IPV6__BEGIN + 3, From d0895415dad26665e030b063cd5fda194ccd05b7 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 16 Aug 2024 10:27:14 -0700 Subject: [PATCH 114/160] [netdata] update `GetNextDnsSrpUnicastInfo()` to use `Type` (#10616) This commit updates `Service::Manager::GetNextDnsSrpUnicastInfo()` to accept a `DnsSrpUnicast::Type`, indicating the desired entry type (either `kFromServiceData` or `kFromServerData`). This simplifies the code, which previously iterated over all types and performed type checks. Additionally, this change simplifies the `Publisher` methods used for counting existing DNS/SRP unicast entries of different types. --- src/core/net/srp_client.cpp | 26 ++----- src/core/net/srp_client.hpp | 2 +- src/core/thread/address_resolver.cpp | 8 +- src/core/thread/network_data_publisher.cpp | 87 +++++----------------- src/core/thread/network_data_publisher.hpp | 9 ++- src/core/thread/network_data_service.cpp | 61 ++++++++------- src/core/thread/network_data_service.hpp | 10 +-- tests/unit/test_network_data.cpp | 65 ++++++++++------ 8 files changed, 117 insertions(+), 151 deletions(-) diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index 15c6c6efc..a8a7473c8 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -2378,7 +2378,7 @@ void Client::ProcessAutoStart(void) return; } -Error Client::SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::Info &aInfo) const +Error Client::SelectUnicastEntry(DnsSrpUnicast::Type aType, DnsSrpUnicast::Info &aInfo) const { Error error = kErrorNotFound; DnsSrpUnicast::Info unicastInfo; @@ -2393,13 +2393,8 @@ Error Client::SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::I } #endif - while (Get().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone) + while (Get().GetNextDnsSrpUnicastInfo(iterator, aType, unicastInfo) == kErrorNone) { - if (unicastInfo.mOrigin != aOrigin) - { - continue; - } - if (mAutoStart.HasSelectedServer() && (GetServerAddress() == unicastInfo.mSockAddr)) { aInfo = unicastInfo; @@ -2441,9 +2436,9 @@ void Client::SelectNextServer(bool aDisallowSwitchOnRegisteredHost) // restarts the client with the new server (keeping the retry wait // interval as before). - Ip6::SockAddr serverSockAddr; - bool selectNext = false; - DnsSrpUnicast::Origin origin = DnsSrpUnicast::kFromServiceData; + Ip6::SockAddr serverSockAddr; + bool selectNext = false; + DnsSrpUnicast::Type type = DnsSrpUnicast::kFromServiceData; serverSockAddr.Clear(); @@ -2455,11 +2450,11 @@ void Client::SelectNextServer(bool aDisallowSwitchOnRegisteredHost) switch (mAutoStart.GetState()) { case AutoStart::kSelectedUnicastPreferred: - origin = DnsSrpUnicast::kFromServiceData; + type = DnsSrpUnicast::kFromServiceData; break; case AutoStart::kSelectedUnicast: - origin = DnsSrpUnicast::kFromServerData; + type = DnsSrpUnicast::kFromServerData; break; case AutoStart::kSelectedAnycast: @@ -2485,13 +2480,8 @@ void Client::SelectNextServer(bool aDisallowSwitchOnRegisteredHost) DnsSrpUnicast::Info unicastInfo; NetworkData::Service::Manager::Iterator iterator; - while (Get().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone) + while (Get().GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo) == kErrorNone) { - if (unicastInfo.mOrigin != origin) - { - continue; - } - if (selectNext) { serverSockAddr = unicastInfo.mSockAddr; diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index d3d28ebd3..33731668a 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -1110,7 +1110,7 @@ class Client : public InstanceLocator, private NonCopyable #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE void ApplyAutoStartGuardOnAttach(void); void ProcessAutoStart(void); - Error SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::Info &aInfo) const; + Error SelectUnicastEntry(DnsSrpUnicast::Type aType, DnsSrpUnicast::Info &aInfo) const; void HandleGuardTimer(void) {} #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE void SelectNextServer(bool aDisallowSwitchOnRegisteredHost); diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index b3f2b0336..78d5890d9 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -602,16 +602,12 @@ Error AddressResolver::ResolveUsingNetDataServices(const Ip6::Address &aEid, uin Error error = kErrorNotFound; NetworkData::Service::Manager::Iterator iterator; NetworkData::Service::DnsSrpUnicast::Info unicastInfo; + NetworkData::Service::DnsSrpUnicast::Type type = NetworkData::Service::DnsSrpUnicast::kFromServerData; VerifyOrExit(Get().GetDeviceMode().GetNetworkDataType() == NetworkData::kFullSet); - while (Get().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone) + while (Get().GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo) == kErrorNone) { - if (unicastInfo.mOrigin != NetworkData::Service::DnsSrpUnicast::kFromServerData) - { - continue; - } - if (aEid == unicastInfo.mSockAddr.GetAddress()) { aRloc16 = unicastInfo.mRloc16; diff --git a/src/core/thread/network_data_publisher.cpp b/src/core/thread/network_data_publisher.cpp index 5bf16d30e..2a12dc42e 100644 --- a/src/core/thread/network_data_publisher.cpp +++ b/src/core/thread/network_data_publisher.cpp @@ -44,7 +44,6 @@ #include "common/random.hpp" #include "instance/instance.hpp" #include "thread/network_data_local.hpp" -#include "thread/network_data_service.hpp" namespace ot { namespace NetworkData { @@ -661,12 +660,12 @@ void Publisher::DnsSrpServiceEntry::Process(void) case kTypeUnicastMeshLocalEid: { Service::DnsSrpAnycast::Info anycastInfo; - bool hasServiceDataEntry; - CountServerDataUnicastEntries(numEntries, numPreferredEntries, hasServiceDataEntry); + CountUnicastEntries(Service::DnsSrpUnicast::kFromServerData, numEntries, numPreferredEntries); desiredNumEntries = kDesiredNumUnicast; - if (hasServiceDataEntry || (Get().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone)) + if (HasAnyServiceDataUnicastEntry() || + (Get().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone)) { // If there is any service data unicast entry or anycast // entry, we set the desired number of server data @@ -681,7 +680,7 @@ void Publisher::DnsSrpServiceEntry::Process(void) case kTypeUnicast: desiredNumEntries = kDesiredNumUnicast; - CountServiceDataUnicastEntries(numEntries, numPreferredEntries); + CountUnicastEntries(Service::DnsSrpUnicast::kFromServiceData, numEntries, numPreferredEntries); break; } @@ -722,81 +721,33 @@ void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, ui } } -void Publisher::DnsSrpServiceEntry::CountServerDataUnicastEntries(uint8_t &aNumEntries, - uint8_t &aNumPreferredEntries, - bool &aHasServiceDataEntry) const +void Publisher::DnsSrpServiceEntry::CountUnicastEntries(Service::DnsSrpUnicast::Type aType, + uint8_t &aNumEntries, + uint8_t &aNumPreferredEntries) const { - // Count the number of server data DNS/SRP unicast entries in the - // Network Data. Also determine whether there is any service data - // DNS/SRP unicast entry (update `aHasServiceDataEntry`). + // Count the number of DNS/SRP unicast entries in the Network Data. - const ServiceTlv *serviceTlv = nullptr; - ServiceData data; + Service::Manager::Iterator iterator; + Service::DnsSrpUnicast::Info unicastInfo; - aHasServiceDataEntry = false; - - data.InitFrom(Service::DnsSrpUnicast::kServiceData); - - while ((serviceTlv = Get().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) != - nullptr) + while (Get().GetNextDnsSrpUnicastInfo(iterator, aType, unicastInfo) == kErrorNone) { - TlvIterator subTlvIterator(*serviceTlv); - const ServerTlv *serverSubTlv; + aNumEntries++; - if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData)) + if (IsPreferred(unicastInfo.mRloc16)) { - aHasServiceDataEntry = true; - } - - while (((serverSubTlv = subTlvIterator.Iterate())) != nullptr) - { - if (serverSubTlv->GetServerDataLength() < sizeof(Service::DnsSrpUnicast::ServerData)) - { - continue; - } - - aNumEntries++; - - if (IsPreferred(serverSubTlv->GetServer16())) - { - aNumPreferredEntries++; - } + aNumPreferredEntries++; } } } -void Publisher::DnsSrpServiceEntry::CountServiceDataUnicastEntries(uint8_t &aNumEntries, - uint8_t &aNumPreferredEntries) const +bool Publisher::DnsSrpServiceEntry::HasAnyServiceDataUnicastEntry(void) const { - // Count the number of service data DNS/SRP unicast entries in - // the Network Data. - - const ServiceTlv *serviceTlv = nullptr; - ServiceData data; + Service::Manager::Iterator iterator; + Service::DnsSrpUnicast::Info unicastInfo; + Service::DnsSrpUnicast::Type type = Service::DnsSrpUnicast::kFromServiceData; - data.InitFrom(Service::DnsSrpUnicast::kServiceData); - - while ((serviceTlv = Get().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) != - nullptr) - { - TlvIterator subTlvIterator(*serviceTlv); - const ServerTlv *serverSubTlv; - - if (serviceTlv->GetServiceDataLength() < sizeof(Service::DnsSrpUnicast::ServiceData)) - { - continue; - } - - while (((serverSubTlv = subTlvIterator.Iterate())) != nullptr) - { - aNumEntries++; - - if (IsPreferred(serverSubTlv->GetServer16())) - { - aNumPreferredEntries++; - } - } - } + return (Get().GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo) == kErrorNone); } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/core/thread/network_data_publisher.hpp b/src/core/thread/network_data_publisher.hpp index 4503efb1e..0ef546b93 100644 --- a/src/core/thread/network_data_publisher.hpp +++ b/src/core/thread/network_data_publisher.hpp @@ -56,6 +56,7 @@ #include "common/string.hpp" #include "common/timer.hpp" #include "net/ip6_address.hpp" +#include "thread/network_data_service.hpp" #include "thread/network_data_types.hpp" namespace ot { @@ -437,10 +438,10 @@ class Publisher : public InstanceLocator, private NonCopyable void Notify(Event aEvent) const; void Process(void); void CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const; - void CountServiceDataUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const; - void CountServerDataUnicastEntries(uint8_t &aNumEntries, - uint8_t &aNumPreferredEntries, - bool &aHasServiceDataEntry) const; + void CountUnicastEntries(Service::DnsSrpUnicast::Type aType, + uint8_t &aNumEntries, + uint8_t &aNumPreferredEntries) const; + bool HasAnyServiceDataUnicastEntry(void) const; Info mInfo; Callback mCallback; diff --git a/src/core/thread/network_data_service.cpp b/src/core/thread/network_data_service.cpp index 2891e4c57..c11c03ef4 100644 --- a/src/core/thread/network_data_service.cpp +++ b/src/core/thread/network_data_service.cpp @@ -260,21 +260,44 @@ Error Manager::FindPreferredDnsSrpAnycastInfo(DnsSrpAnycast::Info &aInfo) const return error; } -Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Info &aInfo) const +Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, + DnsSrpUnicast::Type aType, + DnsSrpUnicast::Info &aInfo) const { - Error error = kErrorNone; - ServiceData serviceData; - - serviceData.InitFrom(DnsSrpUnicast::kServiceData); + Error error = kErrorNone; - while (true) + do { + ServiceData serviceData; + // Process Server sub-TLVs in the current Service TLV. while (IterateToNextServer(aIterator) == kErrorNone) { ServerData data; + if (aType == DnsSrpUnicast::kFromServiceData) + { + const DnsSrpUnicast::ServiceData *dnsServiceData; + + if (aIterator.mServiceTlv->GetServiceDataLength() < sizeof(DnsSrpUnicast::ServiceData)) + { + // Break from `while(IterateToNextServer())` loop + // to skip over the Service TLV and all its + // sub-TLVs and go to the next one. + break; + } + + aIterator.mServiceTlv->GetServiceData(serviceData); + dnsServiceData = reinterpret_cast(serviceData.GetBytes()); + aInfo.mSockAddr.SetAddress(dnsServiceData->GetAddress()); + aInfo.mSockAddr.SetPort(dnsServiceData->GetPort()); + aInfo.mRloc16 = aIterator.mServerSubTlv->GetServer16(); + ExitNow(); + } + + // `aType` is `kFromServerData`. + // Server sub-TLV can contain address and port info // (then we parse and return the info), or it can be // empty (then we skip over it). @@ -288,7 +311,6 @@ Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Info aInfo.mSockAddr.SetAddress(serverData->GetAddress()); aInfo.mSockAddr.SetPort(serverData->GetPort()); - aInfo.mOrigin = DnsSrpUnicast::kFromServerData; aInfo.mRloc16 = aIterator.mServerSubTlv->GetServer16(); ExitNow(); } @@ -301,7 +323,6 @@ Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Info aInfo.mSockAddr.GetAddress().SetToRoutingLocator(Get().GetMeshLocalPrefix(), aIterator.mServerSubTlv->GetServer16()); aInfo.mSockAddr.SetPort(BigEndian::ReadUint16(data.GetBytes())); - aInfo.mOrigin = DnsSrpUnicast::kFromServerData; aInfo.mRloc16 = aIterator.mServerSubTlv->GetServer16(); ExitNow(); } @@ -309,29 +330,17 @@ Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Info // Find the next matching Service TLV. + serviceData.InitFrom(DnsSrpUnicast::kServiceData); aIterator.mServiceTlv = Get().FindNextThreadService(aIterator.mServiceTlv, serviceData, NetworkData::kServicePrefixMatch); + aIterator.mServerSubTlv = nullptr; - VerifyOrExit(aIterator.mServiceTlv != nullptr, error = kErrorNotFound); - - if (aIterator.mServiceTlv->GetServiceDataLength() >= sizeof(DnsSrpUnicast::ServiceData)) - { - // The Service TLV data contains the address and port info. + // If we have a valid Service TLV, restart the loop + // to process its Server sub-TLVs. - const DnsSrpUnicast::ServiceData *dnsServiceData; + } while (aIterator.mServiceTlv != nullptr); - aIterator.mServiceTlv->GetServiceData(serviceData); - dnsServiceData = reinterpret_cast(serviceData.GetBytes()); - aInfo.mSockAddr.SetAddress(dnsServiceData->GetAddress()); - aInfo.mSockAddr.SetPort(dnsServiceData->GetPort()); - aInfo.mOrigin = DnsSrpUnicast::kFromServiceData; - aInfo.mRloc16 = Mle::kInvalidRloc16; - ExitNow(); - } - - // Go back to the start of `while (true)` loop to - // process the Server sub-TLVs in the new Service TLV. - } + error = kErrorNotFound; exit: return error; diff --git a/src/core/thread/network_data_service.hpp b/src/core/thread/network_data_service.hpp index 6fca444aa..356ad072d 100644 --- a/src/core/thread/network_data_service.hpp +++ b/src/core/thread/network_data_service.hpp @@ -238,10 +238,10 @@ class DnsSrpUnicast static const uint8_t kServiceData = kServiceNumber; /** - * Represents the origin a `DnsSrpUnicast` entry. + * Represents the `DnsSrpUnicast` entry type. * */ - enum Origin : uint8_t + enum Type : uint8_t { kFromServiceData, ///< Socket address is from service data. kFromServerData, ///< Socket address is from server data. @@ -254,8 +254,7 @@ class DnsSrpUnicast struct Info { Ip6::SockAddr mSockAddr; ///< The socket address (IPv6 address and port) of the DNS/SRP server. - Origin mOrigin; ///< The origin of the socket address (whether from service or server data). - uint16_t mRloc16; ///< The BR RLOC16 adding the entry (only used when `mOrigin == kFromServerData`). + uint16_t mRloc16; ///< The BR RLOC16 adding the entry. }; /** @@ -581,13 +580,14 @@ class Manager : public InstanceLocator, private NonCopyable * method). * * @param[in,out] aIterator A reference to an iterator. + * @param[in] aType The entry type, `kFromServiceData` (preferred) or `kFromServerData` (non-preferred). * @param[out] aInfo A reference to `DnsSrpUnicast::Info` to return the info. * * @retval kErrorNone Successfully got the next info. @p aInfo and @p aIterator are updated. * @retval kErrorNotFound No more matching entries in the Network Data. * */ - Error GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Info &aInfo) const; + Error GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Type aType, DnsSrpUnicast::Info &aInfo) const; private: #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE diff --git a/tests/unit/test_network_data.cpp b/tests/unit/test_network_data.cpp index 4740a2fff..8a1b4a721 100644 --- a/tests/unit/test_network_data.cpp +++ b/tests/unit/test_network_data.cpp @@ -612,11 +612,6 @@ void TestNetworkDataFindNextService(void) void TestNetworkDataDsnSrpServices(void) { - static const char *kOriginStrings[] = { - "service-data", // (0) Service::DnsSrpUnicast::kFromServiceData - "server-data", // (1) Service::DnsSrpUnicast::kFromServerData - }; - class TestLeader : public Leader { public: @@ -652,19 +647,18 @@ void TestNetworkDataDsnSrpServices(void) struct UnicastEntry { - const char *mAddress; - uint16_t mPort; - Service::DnsSrpUnicast::Origin mOrigin; - uint16_t mRloc16; + const char *mAddress; + uint16_t mPort; + uint16_t mRloc16; - bool Matches(Service::DnsSrpUnicast::Info aInfo) const + bool Matches(const Service::DnsSrpUnicast::Info &aInfo) const { Ip6::SockAddr sockAddr; SuccessOrQuit(sockAddr.GetAddress().FromString(mAddress)); sockAddr.SetPort(mPort); - return (aInfo.mSockAddr == sockAddr) && (aInfo.mOrigin == mOrigin) && (aInfo.mRloc16 == mRloc16); + return (aInfo.mSockAddr == sockAddr) && (aInfo.mRloc16 == mRloc16); } }; @@ -684,12 +678,17 @@ void TestNetworkDataDsnSrpServices(void) {0xfc12, 0x03}, }; - const UnicastEntry kUnicastEntries[] = { - {"fdde:ad00:beef:0:2d0e:c627:5556:18d9", 0x1234, Service::DnsSrpUnicast::kFromServiceData, 0xfffe}, - {"fd00:aabb:ccdd:eeff:11:2233:4455:6677", 0xabcd, Service::DnsSrpUnicast::kFromServerData, 0x6c00}, - {"fdde:ad00:beef:0:0:ff:fe00:2800", 0x5678, Service::DnsSrpUnicast::kFromServerData, 0x2800}, - {"fd00:1234:5678:9abc:def0:123:4567:89ab", 0x0e, Service::DnsSrpUnicast::kFromServerData, 0x4c00}, - {"fdde:ad00:beef:0:0:ff:fe00:6c00", 0xcd12, Service::DnsSrpUnicast::kFromServerData, 0x6c00}, + const UnicastEntry kUnicastEntriesFromServerData[] = { + {"fd00:aabb:ccdd:eeff:11:2233:4455:6677", 0xabcd, 0x6c00}, + {"fdde:ad00:beef:0:0:ff:fe00:2800", 0x5678, 0x2800}, + {"fd00:1234:5678:9abc:def0:123:4567:89ab", 0x0e, 0x4c00}, + {"fdde:ad00:beef:0:0:ff:fe00:6c00", 0xcd12, 0x6c00}, + }; + + const UnicastEntry kUnicastEntriesFromServiceData[] = { + {"fdde:ad00:beef:0:2d0e:c627:5556:18d9", 0x1234, 0x0000}, + {"fdde:ad00:beef:0:2d0e:c627:5556:18d9", 0x1234, 0x6c00}, + {"fdde:ad00:beef:0:2d0e:c627:5556:18d9", 0x1234, 0x2800}, }; const uint16_t kExpectedRlocs[] = {0x6c00, 0x2800, 0x4c00, 0x0000}; @@ -700,6 +699,7 @@ void TestNetworkDataDsnSrpServices(void) Service::Manager::Iterator iterator; Service::DnsSrpAnycast::Info anycastInfo; Service::DnsSrpUnicast::Info unicastInfo; + Service::DnsSrpUnicast::Type type; Rlocs rlocs; reinterpret_cast(instance->Get()).Populate(kNetworkData, sizeof(kNetworkData)); @@ -749,20 +749,39 @@ void TestNetworkDataDsnSrpServices(void) "FindPreferredDnsSrpAnycastInfo() returned invalid info"); printf("\n\n- - - - - - - - - - - - - - - - - - - -"); - printf("\nDNS/SRP Unicast Service entries\n"); + printf("\nDNS/SRP Unicast Service entries (server data)\n"); + + iterator.Clear(); + type = Service::DnsSrpUnicast::kFromServerData; + + for (const UnicastEntry &entry : kUnicastEntriesFromServerData) + { + SuccessOrQuit(manager.GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo)); + printf("\nunicastInfo { %s, rloc16:%04x }", unicastInfo.mSockAddr.ToString().AsCString(), + unicastInfo.mRloc16); + + VerifyOrQuit(entry.Matches(unicastInfo), "GetNextDnsSrpUnicastInfo() returned incorrect info"); + } + + VerifyOrQuit(manager.GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo) == kErrorNotFound, + "GetNextDnsSrpUnicastInfo() returned unexpected extra entry"); + + printf("\n\n- - - - - - - - - - - - - - - - - - - -"); + printf("\nDNS/SRP Unicast Service entries (service data)\n"); iterator.Clear(); + type = Service::DnsSrpUnicast::kFromServiceData; - for (const UnicastEntry &entry : kUnicastEntries) + for (const UnicastEntry &entry : kUnicastEntriesFromServiceData) { - SuccessOrQuit(manager.GetNextDnsSrpUnicastInfo(iterator, unicastInfo)); - printf("\nunicastInfo { %s, origin:%s, rloc16:%04x }", unicastInfo.mSockAddr.ToString().AsCString(), - kOriginStrings[unicastInfo.mOrigin], unicastInfo.mRloc16); + SuccessOrQuit(manager.GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo)); + printf("\nunicastInfo { %s, rloc16:%04x }", unicastInfo.mSockAddr.ToString().AsCString(), + unicastInfo.mRloc16); VerifyOrQuit(entry.Matches(unicastInfo), "GetNextDnsSrpUnicastInfo() returned incorrect info"); } - VerifyOrQuit(manager.GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNotFound, + VerifyOrQuit(manager.GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo) == kErrorNotFound, "GetNextDnsSrpUnicastInfo() returned unexpected extra entry"); printf("\n"); From e4558664a8ef1bded10a55b2d8689140efc2a8c8 Mon Sep 17 00:00:00 2001 From: Esko Dijk Date: Fri, 16 Aug 2024 21:19:23 +0200 Subject: [PATCH 115/160] [coap] fixes in parameter name and in comments. (#10615) --- src/core/coap/coap.cpp | 4 ++-- src/core/coap/coap.hpp | 2 +- src/core/coap/coap_message.hpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp index 38ba54e9b..33369a327 100644 --- a/src/core/coap/coap.cpp +++ b/src/core/coap/coap.cpp @@ -160,13 +160,13 @@ Message *CoapBase::InitMessage(Message *aMessage, Type aType, Uri aUri) return aMessage; } -Message *CoapBase::InitResponse(Message *aMessage, const Message &aResponse) +Message *CoapBase::InitResponse(Message *aMessage, const Message &aRequest) { Error error = kErrorNone; VerifyOrExit(aMessage != nullptr); - SuccessOrExit(error = aMessage->SetDefaultResponseHeader(aResponse)); + SuccessOrExit(error = aMessage->SetDefaultResponseHeader(aRequest)); SuccessOrExit(error = aMessage->SetPayloadMarker()); exit: diff --git a/src/core/coap/coap.hpp b/src/core/coap/coap.hpp index aeae3bb74..968c845ca 100644 --- a/src/core/coap/coap.hpp +++ b/src/core/coap/coap.hpp @@ -887,7 +887,7 @@ class CoapBase : public InstanceLocator, private NonCopyable }; Message *InitMessage(Message *aMessage, Type aType, Uri aUri); - Message *InitResponse(Message *aMessage, const Message &aResponse); + Message *InitResponse(Message *aMessage, const Message &aRequest); static void HandleRetransmissionTimer(Timer &aTimer); void HandleRetransmissionTimer(void); diff --git a/src/core/coap/coap_message.hpp b/src/core/coap/coap_message.hpp index 6156df8ce..f985ff731 100644 --- a/src/core/coap/coap_message.hpp +++ b/src/core/coap/coap_message.hpp @@ -777,7 +777,7 @@ class Message : public ot::Message bool IsReset(void) const { return (GetType() == kTypeReset); } /** - * Indicates whether or not the header is a confirmable Put request (i.e, `kTypeConfirmable` with + * Indicates whether or not the header is a confirmable Post request (i.e, `kTypeConfirmable` with * `kCodePost`). * * @retval TRUE Message is a confirmable Post request. @@ -787,7 +787,7 @@ class Message : public ot::Message bool IsConfirmablePostRequest(void) const; /** - * Indicates whether or not the header is a non-confirmable Put request (i.e, `kTypeNonConfirmable` with + * Indicates whether or not the header is a non-confirmable Post request (i.e, `kTypeNonConfirmable` with * `kCodePost`). * * @retval TRUE Message is a non-confirmable Post request. From 4e053fe5176960b865f8f5e6a6c4cf5891bd4def Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 08:51:55 -0700 Subject: [PATCH 116/160] github-actions: bump github/codeql-action from 3.25.11 to 3.26.2 (#10623) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.11 to 3.26.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/b611370bb5703a7efb587f9d136a52ea24c5c38c...429e1977040da7a23b6822b13c129cd1ba93dbb2) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/scorecards.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ad324711f..ec5eefac5 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -66,7 +66,7 @@ jobs: sudo apt-get --no-install-recommends install -y ninja-build libreadline-dev libncurses-dev - name: Initialize CodeQL - uses: github/codeql-action/init@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/init@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -80,6 +80,6 @@ jobs: ./script/test build - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/analyze@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index b9dc91dc2..f177b6dd6 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -95,6 +95,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v2.1.27 + uses: github/codeql-action/upload-sarif@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v2.1.27 with: sarif_file: results.sarif From fb7b457ab225f9a5b32c359b47eaf51a9f73a46a Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Tue, 20 Aug 2024 02:24:59 +0800 Subject: [PATCH 117/160] [posix] update the max size of file path to PATH_MAX (#10622) The length of the configuration file path may exceed the default defined max length 255. This commit changes the name from `kFileNameMaxSize` to `kFilePathMaxSize` and sets its value to the PATH_MAX. --- src/posix/platform/config_file.cpp | 4 ++-- src/posix/platform/config_file.hpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/posix/platform/config_file.cpp b/src/posix/platform/config_file.cpp index c8a7dddb4..bd48f56a2 100644 --- a/src/posix/platform/config_file.cpp +++ b/src/posix/platform/config_file.cpp @@ -47,7 +47,7 @@ ConfigFile::ConfigFile(const char *aFilePath) : mFilePath(aFilePath) { assert(mFilePath != nullptr); - VerifyOrDie(strlen(mFilePath) + strlen(kSwapSuffix) < kFileNameMaxSize, OT_EXIT_FAILURE); + VerifyOrDie(strlen(mFilePath) + strlen(kSwapSuffix) < kFilePathMaxSize, OT_EXIT_FAILURE); } bool ConfigFile::HasKey(const char *aKey) const @@ -163,7 +163,7 @@ otError ConfigFile::Add(const char *aKey, const char *aValue) otError ConfigFile::Clear(const char *aKey) { otError error = OT_ERROR_NONE; - char swapFile[kFileNameMaxSize]; + char swapFile[kFilePathMaxSize]; char line[kLineMaxSize]; FILE *fp = nullptr; FILE *fpSwap = nullptr; diff --git a/src/posix/platform/config_file.hpp b/src/posix/platform/config_file.hpp index 1590ed284..71038f411 100644 --- a/src/posix/platform/config_file.hpp +++ b/src/posix/platform/config_file.hpp @@ -30,6 +30,7 @@ #define OT_POSIX_PLATFORM_CONFIG_FILE_HPP_ #include +#include #include #include @@ -115,7 +116,7 @@ class ConfigFile const char *kCommentDelimiter = "#"; const char *kSwapSuffix = ".swap"; static constexpr uint16_t kLineMaxSize = 512; - static constexpr uint16_t kFileNameMaxSize = 255; + static constexpr uint16_t kFilePathMaxSize = PATH_MAX; void Strip(char *aString) const; From f127469f7dd3e95833572b4628a20f35cb086504 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 20 Aug 2024 08:29:33 -0700 Subject: [PATCH 118/160] [routing-manager] check reachability & send ICMPv6 unreach err (#10548) This commit introduces a mechanism to check for reachability of messages forwarded by the BR and send an ICMPv6 Destination Unreachable error to the sender if needed. Specifically, if the Border Router (BR) decides to forward an IPv6 message outside the AIL and the message's source address matches a BR-generated ULA OMR prefix (with low preference), and the destination is unreachable using this source address, then an ICMPv6 Destination Unreachable message is sent back to the sender. For example, this situation can occur when a local, non-infrastructure-derived ULA OMR prefix is published alongside a `::/0` route (due to discovered PIO/RIO prefixes by the BR). A Thread mesh device may try to reach addresses beyond the local AIL (e.g., the global internet) using the ULA OMR prefix, which would be unreachable. This feature is controlled by an OT config flag, enabled by default. Alternatively, this functionality may be implemented within the platform layer, in which case the configuration should be disabled. This commit also adds a test case `test-504-br-icmp-unreach-err.py` validating the newly added behavior. --- include/openthread/border_routing.h | 12 ++ include/openthread/icmp6.h | 5 +- include/openthread/instance.h | 2 +- src/cli/cli_br.cpp | 15 +- src/core/api/border_routing_api.cpp | 10 ++ src/core/border_router/routing_manager.cpp | 98 ++++++++++ src/core/border_router/routing_manager.hpp | 34 ++++ src/core/config/border_routing.h | 37 ++++ src/core/net/icmp6.hpp | 5 +- src/core/net/ip6.cpp | 7 + src/core/thread/network_data_leader.cpp | 18 ++ src/core/thread/network_data_leader.hpp | 11 ++ tests/toranj/cli/cli.py | 3 + .../cli/test-504-br-icmp-unreach-err.py | 167 ++++++++++++++++++ tests/toranj/openthread-core-toranj-config.h | 2 + tests/toranj/start.sh | 2 + 16 files changed, 422 insertions(+), 6 deletions(-) create mode 100755 tests/toranj/cli/test-504-br-icmp-unreach-err.py diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h index d45903249..272005160 100644 --- a/include/openthread/border_routing.h +++ b/include/openthread/border_routing.h @@ -598,6 +598,18 @@ void otBorderRoutingDhcp6PdSetRequestCallback(otInstance otBorderRoutingRequestDhcp6PdCallback aCallback, void *aContext); +/** + * Sets the local on-link prefix. + * + * Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE`. + * + * This is intended for testing only and using it will make the BR non-compliant with the Thread Specification. + * + * @param[in] aPrefix The on-link prefix to use. + * + */ +void otBorderRoutingSetOnLinkPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix); + /** * @} * diff --git a/include/openthread/icmp6.h b/include/openthread/icmp6.h index 925871cda..e7359ac29 100644 --- a/include/openthread/icmp6.h +++ b/include/openthread/icmp6.h @@ -76,8 +76,9 @@ typedef enum otIcmp6Type */ typedef enum otIcmp6Code { - OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE = 0, ///< Destination Unreachable No Route - OT_ICMP6_CODE_FRAGM_REAS_TIME_EX = 1, ///< Fragment Reassembly Time Exceeded + OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE = 0, ///< Destination Unreachable (Type 1) - No Route + OT_ICMP6_CODE_DST_UNREACH_PROHIBITED = 1, ///< Destination Unreachable (Type 1) - Administratively Prohibited + OT_ICMP6_CODE_FRAGM_REAS_TIME_EX = 1, ///< Time Exceeded (Type 3) - Fragment Reassembly } otIcmp6Code; #define OT_ICMP6_HEADER_DATA_SIZE 4 ///< Size of ICMPv6 Header. diff --git a/include/openthread/instance.h b/include/openthread/instance.h index f5c12bbf1..891398807 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (433) +#define OPENTHREAD_API_VERSION (434) /** * @addtogroup api-instance diff --git a/src/cli/cli_br.cpp b/src/cli/cli_br.cpp index b49c774ce..98a5be48c 100644 --- a/src/cli/cli_br.cpp +++ b/src/cli/cli_br.cpp @@ -258,7 +258,20 @@ template <> otError Br::Process(Arg aArgs[]) otError error = OT_ERROR_NONE; PrefixType outputPrefixTypes; - SuccessOrExit(error = ParsePrefixTypeArgs(aArgs, outputPrefixTypes)); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE + if (aArgs[0] == "test") + { + otIp6Prefix prefix; + + SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix)); + otBorderRoutingSetOnLinkPrefix(GetInstancePtr(), &prefix); + ExitNow(); + } +#endif + + error = ParsePrefixTypeArgs(aArgs, outputPrefixTypes); + + SuccessOrExit(error); /** * @cli br onlinkprefix local diff --git a/src/core/api/border_routing_api.cpp b/src/core/api/border_routing_api.cpp index a7e477f8c..bb110cac8 100644 --- a/src/core/api/border_routing_api.cpp +++ b/src/core/api/border_routing_api.cpp @@ -217,6 +217,7 @@ uint16_t otBorderRoutingCountPeerBrs(otInstance *aInstance, uint32_t *aMinAge) #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE + void otBorderRoutingDhcp6PdSetEnabled(otInstance *aInstance, bool aEnabled) { AsCoreType(aInstance).Get().SetDhcp6PdEnabled(aEnabled); @@ -237,4 +238,13 @@ void otBorderRoutingDhcp6PdSetRequestCallback(otInstance #endif +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE + +void otBorderRoutingSetOnLinkPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix) +{ + AsCoreType(aInstance).Get().SetOnLinkPrefix(AsCoreType(aPrefix)); +} + +#endif + #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index b50dd44b2..cd7c4c19f 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -817,6 +817,62 @@ bool RoutingManager::NetworkDataContainsUlaRoute(void) const return contains; } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE + +void RoutingManager::CheckReachabilityToSendIcmpError(const Message &aMessage, const Ip6::Header &aIp6Header) +{ + bool matchesUlaOmrLowPrf = false; + NetworkData::Iterator iterator = NetworkData::kIteratorInit; + NetworkData::OnMeshPrefixConfig prefixConfig; + Ip6::MessageInfo messageInfo; + + VerifyOrExit(IsRunning() && IsInitialPolicyEvaluationDone()); + + VerifyOrExit(!aIp6Header.GetDestination().IsMulticast()); + + // Validate that source matches a ULA OMR prefix with low preference + // (indicating it is not infrastructure-derived). + + while (Get().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone) + { + if (IsValidOmrPrefix(prefixConfig) && prefixConfig.GetPrefix().IsUniqueLocal() && + aIp6Header.GetSource().MatchesPrefix(prefixConfig.GetPrefix())) + { + if (prefixConfig.GetPreference() >= NetworkData::kRoutePreferenceMedium) + { + matchesUlaOmrLowPrf = false; + break; + } + + matchesUlaOmrLowPrf = true; + + // Keep checking other prefixes, as the same prefix might + // be added with a higher preference by another BR. + } + } + + VerifyOrExit(matchesUlaOmrLowPrf); + + VerifyOrExit(!mRxRaTracker.IsAddressOnLink(aIp6Header.GetDestination())); + VerifyOrExit(!mRxRaTracker.IsAddressReachableThroughExplicitRoute(aIp6Header.GetDestination())); + VerifyOrExit(!Get().IsNat64(aIp6Header.GetDestination())); + + LogInfo("Send ICMP unreachable for fwd msg with local ULA src and non-local dst"); + LogInfo(" src: %s", aIp6Header.GetSource().ToString().AsCString()); + LogInfo(" dst: %s", aIp6Header.GetDestination().ToString().AsCString()); + + messageInfo.Clear(); + messageInfo.SetPeerAddr(aIp6Header.GetSource()); + + IgnoreError(Get().SendError(Ip6::Icmp::Header::kTypeDstUnreach, + Ip6::Icmp::Header::kCodeDstUnreachProhibited, messageInfo, aMessage)); + +exit: + return; +} + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE + #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) void RoutingManager::LogPrefixInfoOption(const Ip6::Prefix &aPrefix, @@ -1804,6 +1860,48 @@ void RoutingManager::RxRaTracker::SetHeaderFlagsOn(RouterAdvert::Header &aHeader } } +bool RoutingManager::RxRaTracker::IsAddressOnLink(const Ip6::Address &aAddress) const +{ + bool isOnLink = false; + + for (const Router &router : mRouters) + { + for (const OnLinkPrefix &onLinkPrefix : router.mOnLinkPrefixes) + { + isOnLink = aAddress.MatchesPrefix(onLinkPrefix.GetPrefix()); + VerifyOrExit(!isOnLink); + } + } + +exit: + return isOnLink; +} + +bool RoutingManager::RxRaTracker::IsAddressReachableThroughExplicitRoute(const Ip6::Address &aAddress) const +{ + // Checks whether the `aAddress` matches any discovered route + // prefix excluding `::/0`. + + bool isReachable = false; + + for (const Router &router : mRouters) + { + for (const RoutePrefix &routePrefix : router.mRoutePrefixes) + { + if (routePrefix.GetPrefix().GetLength() == 0) + { + continue; + } + + isReachable = aAddress.MatchesPrefix(routePrefix.GetPrefix()); + VerifyOrExit(!isReachable); + } + } + +exit: + return isReachable; +} + void RoutingManager::RxRaTracker::InitIterator(PrefixTableIterator &aIterator) const { static_cast(aIterator).Init(mRouters.GetHead(), Uptime::MsecToSec(Get().GetUptime())); diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 95dc4c0ec..6aa622abb 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -602,6 +602,34 @@ class RoutingManager : public InstanceLocator #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE +#if OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE + /** + * Determines whether to send an ICMPv6 Destination Unreachable error to the sender based on reachability and + * source address. + * + * Specifically, if the Border Router (BR) decides to forward a unicast IPv6 message outside the AIL and the + * message's source address matches a BR-generated ULA OMR prefix (with low preference), and the destination is + * unreachable using this source address, then an ICMPv6 Destination Unreachable message is sent back to the sender. + * + * @param[in] aMessage The message. + * @param[in] aIp6Header The IPv6 header of @p aMessage. + * + */ + void CheckReachabilityToSendIcmpError(const Message &aMessage, const Ip6::Header &aIp6Header); +#endif + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE + /** + * Sets the local on-link prefix. + * + * This is intended for testing only and using it will make a device non-compliant with the Thread Specification. + * + * @param[in] aPrefix The on-link prefix to use. + * + */ + void SetOnLinkPrefix(const Ip6::Prefix &aPrefix) { mOnLinkPrefixManager.SetLocalPrefix(aPrefix); } +#endif + private: //------------------------------------------------------------------------------------------------------------------ // Constants @@ -859,6 +887,9 @@ class RoutingManager : public InstanceLocator const RouterAdvert::Header &GetLocalRaHeaderToMirror(void) const { return mLocalRaHeader; } + bool IsAddressOnLink(const Ip6::Address &aAddress) const; + bool IsAddressReachableThroughExplicitRoute(const Ip6::Address &aAddress) const; + // Iterating over discovered items void InitIterator(PrefixTableIterator &aIterator) const; Error GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const; @@ -1178,6 +1209,9 @@ class RoutingManager : public InstanceLocator void HandleNetDataChange(void); void HandleExtPanIdChange(void); void HandleTimer(void); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE + void SetLocalPrefix(const Ip6::Prefix &aPrefix) { mLocalPrefix = aPrefix; } +#endif private: enum State : uint8_t // State of `mLocalPrefix` diff --git a/src/core/config/border_routing.h b/src/core/config/border_routing.h index 7a91d6a39..652387800 100644 --- a/src/core/config/border_routing.h +++ b/src/core/config/border_routing.h @@ -88,6 +88,31 @@ #define OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE #endif +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE + * + * Define to 1 to allow Routing Manager to check for reachability of messages being forwarded by the BR and determine + * whether to send an ICMPv6 Destination Unreachable error back to the sender. + * + * Specifically, if the Border Router (BR) decides to forward a unicast IPv6 message outside the AIL and the message's + * source address matches a BR-generated ULA OMR prefix (with low preference), and the destination is unreachable + * using this source address, then an ICMPv6 Destination Unreachable message is sent back to the sender. + * + * For example, this situation can occur when a local, non-infrastructure-derived ULA OMR prefix is published alongside + * a `::/0` route (due to discovered PIO/RIO prefixes by the BR). A Thread mesh device may try to reach addresses + * beyond the local AIL (e.g., the global internet) using its ULA OMR address as source, which would be unreachable. + * + * Alternatively, this functionality may be implemented within the platform layer, in which case this configuration + * should be disabled. Note that the platform layer is always responsible for implementing generation of "ICMPv6 + * Destination Unreachable - No Route" messages. This reachability function will only generate "ICMPv6 Destination + * Unreachable - Communication Administratively Prohibited" messages for specific cases where there may be a + * default route to the destination but the source address type prohibits usable communication with this destination. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE +#define OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE 1 +#endif + /** * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS * @@ -177,6 +202,18 @@ #define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE + * + * Define to 1 to enable testing related APIs to be provided by the `RoutingManager`. + * + * This is intended for testing only. Production devices SHOULD set this to zero. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE +#define OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE 0 +#endif + /** * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MOCK_PLAT_APIS_ENABLE * diff --git a/src/core/net/icmp6.hpp b/src/core/net/icmp6.hpp index 48dc6ccf9..8ea9df0db 100644 --- a/src/core/net/icmp6.hpp +++ b/src/core/net/icmp6.hpp @@ -100,8 +100,9 @@ class Icmp : public InstanceLocator, private NonCopyable */ enum Code : uint8_t { - kCodeDstUnreachNoRoute = OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE, ///< Destination Unreachable No Route - kCodeFragmReasTimeEx = OT_ICMP6_CODE_FRAGM_REAS_TIME_EX, ///< Fragment Reassembly Time Exceeded + kCodeDstUnreachNoRoute = OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE, ///< Dest Unreachable - No Route + kCodeDstUnreachProhibited = OT_ICMP6_CODE_DST_UNREACH_PROHIBITED, ///< Dest Unreachable - Admin Prohibited + kCodeFragmReasTimeEx = OT_ICMP6_CODE_FRAGM_REAS_TIME_EX, ///< Time Exceeded - Frag Reassembly }; static constexpr uint8_t kTypeFieldOffset = 0; ///< The byte offset of Type field in ICMP6 header. diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 2c63841f9..6ca08a024 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -958,6 +958,13 @@ Error Ip6::PassToHost(OwnedPtr &aMessagePtr, // Do not pass IPv6 packets that exceed kMinimalMtu. VerifyOrExit(aMessagePtr->GetLength() <= kMinimalMtu, error = kErrorDrop); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE + if (!aReceive) + { + Get().CheckReachabilityToSendIcmpError(*aMessagePtr, aHeader); + } +#endif + // If the sender used mesh-local address as source, do not pass to // host unless this message is intended for this device itself. if (Get().IsMeshLocalAddress(aHeader.GetSource())) diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp index d6742a923..ead2a95a7 100644 --- a/src/core/thread/network_data_leader.cpp +++ b/src/core/thread/network_data_leader.cpp @@ -132,6 +132,24 @@ Error Leader::GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const return error; } +bool Leader::IsNat64(const Ip6::Address &aAddress) const +{ + bool isNat64 = false; + Iterator iterator = kIteratorInit; + ExternalRouteConfig config; + + while (GetNextExternalRoute(iterator, config) == kErrorNone) + { + if (config.mNat64 && config.GetPrefix().IsValidNat64() && aAddress.MatchesPrefix(config.GetPrefix())) + { + isNat64 = true; + break; + } + } + + return isNat64; +} + const PrefixTlv *Leader::FindNextMatchingPrefixTlv(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const { // This method iterates over Prefix TLVs which match a given IPv6 diff --git a/src/core/thread/network_data_leader.hpp b/src/core/thread/network_data_leader.hpp index 22566d531..4cb109eef 100644 --- a/src/core/thread/network_data_leader.hpp +++ b/src/core/thread/network_data_leader.hpp @@ -327,6 +327,17 @@ class Leader : public MutableNetworkData, private NonCopyable */ Error GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const; + /** + * Indicates whether or not the given IPv6 address matches any NAT64 prefixes. + * + * @param[in] aAddress An IPv6 address to check. + * + * @retval TRUE If @p aAddress matches a NAT64 prefix. + * @retval FALSE If @p aAddress does not match a NAT64 prefix. + * + */ + bool IsNat64(const Ip6::Address &aAddress) const; + #if OPENTHREAD_FTD /** * Defines the match mode constants to compare two RLOC16 values. diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index 06f7bd3c9..fef3be27e 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -802,6 +802,9 @@ def br_get_favored_onlinkprefix(self): def br_get_local_onlinkprefix(self): return self._cli_single_output('br onlinkprefix local') + def br_set_test_local_onlinkprefix(self, prefix): + self._cli_no_output('br onlinkprefix test', prefix) + def br_get_routeprf(self): return self._cli_single_output('br routeprf') diff --git a/tests/toranj/cli/test-504-br-icmp-unreach-err.py b/tests/toranj/cli/test-504-br-icmp-unreach-err.py new file mode 100755 index 000000000..9cb846399 --- /dev/null +++ b/tests/toranj/cli/test-504-br-icmp-unreach-err.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024, 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: +# +# BR sending ICMPv6 Unreachable error. +# +# - - - AIL- - - +# | | +# BR1 BR2 +# | +# router2 +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 20 +cli.Node.set_time_speedup_factor(speedup) + +br1 = cli.Node() +br2 = cli.Node() +router2 = cli.Node() + +IF_INDEX = 1 + +# ----------------------------------------------------------------------------------------------------------------------- +# Test implementation + +# Start `br1` on its own Thread network with a custom OMR +# prefix and a custom on-link prefix. Both prefixes are +# non-ULA. + +br1.form('net1') +verify(br1.get_state() == 'leader') + +br1.add_prefix('1000::/64', 'paors') +br1.register_netdata() + +br1.br_init(IF_INDEX, 1) +br1.br_enable() + +br1.br_set_test_local_onlinkprefix('2000::/64') + +time.sleep(2) +verify(br1.br_get_state() == 'running') + +br1_local_onlink = br1.br_get_local_onlinkprefix() +br1_favored_onlink = br1.br_get_favored_onlinkprefix().split()[0] +verify(br1_local_onlink == br1_favored_onlink) +verify(br1_local_onlink == '2000:0:0:0::/64') + +br1_local_omr = br1.br_get_local_omrprefix() +br1_favored_omr = br1.br_get_favored_omrprefix().split()[0] +verify(br1_favored_omr == '1000:0:0:0::/64') + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Start `br2` on its own Thread network. + +br2.form('net2') +router2.join(br2) +verify(br2.get_state() == 'leader') +verify(router2.get_state() == 'router') + +br2.br_init(IF_INDEX, 1) +br2.br_enable() + +time.sleep(2) +verify(br2.br_get_state() == 'running') + +br2_local_omr = br2.br_get_local_omrprefix() +br2_favored_omr = br2.br_get_favored_omrprefix().split()[0] +verify(br2_local_omr == br2_favored_omr) + +br2_local_onlink = br2.br_get_local_onlinkprefix() +br2_favored_onlink = br2.br_get_favored_onlinkprefix().split()[0] +verify(br2_local_onlink != br2_favored_onlink) +verify(br2_favored_onlink == '2000:0:0:0::/64') + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Validate that the `br2` published '::/0' route due to +# discovery of a non-ULA on-link prefix. + +for node in [br2, router2]: + routes = node.get_netdata_routes() + verify(len(routes) == 1) + verify(routes[0].startswith('::/0 s')) + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Send a ping from `router2` to an AIL address (derived from the +# advertised on-link prefix). Verify that, BR2 receives and +# forwards the message, and BR2 does not send an ICMPv6 error, as +# the destination is within the AIL. + +router2.ping('2000::1', verify_success=False) + +verify(br2.get_br_counter_unicast_outbound_packets() == 1) + +rx_history = router2.cli('history rx list') +verify(len(rx_history) == 0) + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Send a ping from `router2` to an address on BR1 mesh network +# (derived from its OMR prefix). Verify that again it is +# received and forwarded by BR2 and again no ICMPv6 error is +# sent. + +router2.ping('1000::1', verify_success=False) + +verify(br2.get_br_counter_unicast_outbound_packets() == 2) + +rx_history = router2.cli('history rx list') +verify(len(rx_history) == 0) + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Send a ping to an outside AIL address, make sure BR2 +# does receive and forward it and now also sends an ICMPv6 +# error + +router2.ping('3000::', verify_success=False) + +verify(br2.get_br_counter_unicast_outbound_packets() == 3) + +rx_history = router2.cli('history rx list') +verify(rx_history[1].strip().startswith('type:ICMP6(Unreach)')) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h index 9f6b66a8d..8e6bbebe9 100644 --- a/tests/toranj/openthread-core-toranj-config.h +++ b/tests/toranj/openthread-core-toranj-config.h @@ -57,6 +57,8 @@ #define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 1 +#define OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE 1 + #define OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 1 #define OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE 1 diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh index d242752b9..482774c85 100755 --- a/tests/toranj/start.sh +++ b/tests/toranj/start.sh @@ -202,6 +202,8 @@ if [ "$TORANJ_CLI" = 1 ]; then run cli/test-500-two-brs-two-networks.py run cli/test-501-multi-br-failure-recovery.py run cli/test-502-multi-br-leader-failure-recovery.py + run cli/test-503-peer-tbr-discovery.py + run cli/test-504-br-icmp-unreach-err.py run cli/test-601-channel-manager-channel-change.py # Skip the "channel-select" test on a TREL only radio link, since it # requires energy scan which is not supported in this case. From afac808a81522732c5ba7bfcf5a971b8f4ae5da5 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 20 Aug 2024 08:31:02 -0700 Subject: [PATCH 119/160] [netdata] enhance `GetNextDnsSrpAnycastInfo()` to include RLOC16 (#10620) This commit updates `Service::Manager::GetNextDnsSrpAnycastInfo()` to iterate over Server sub-TLVs and include the RLOC16 of each entry in the returned `Info` structure. The unit test `test_network_data` is updated accordingly. This change simplifies the `Publisher` method used for counting existing anycast entries in the Network Data. --- src/core/thread/network_data_publisher.cpp | 32 ++++++++--------- src/core/thread/network_data_publisher.hpp | 1 + src/core/thread/network_data_service.cpp | 42 +++++++++++++++------- src/core/thread/network_data_service.hpp | 1 + tests/unit/test_network_data.cpp | 41 +++++++++++---------- 5 files changed, 68 insertions(+), 49 deletions(-) diff --git a/src/core/thread/network_data_publisher.cpp b/src/core/thread/network_data_publisher.cpp index 2a12dc42e..ef1d8a5b3 100644 --- a/src/core/thread/network_data_publisher.cpp +++ b/src/core/thread/network_data_publisher.cpp @@ -658,14 +658,10 @@ void Publisher::DnsSrpServiceEntry::Process(void) break; case kTypeUnicastMeshLocalEid: - { - Service::DnsSrpAnycast::Info anycastInfo; - CountUnicastEntries(Service::DnsSrpUnicast::kFromServerData, numEntries, numPreferredEntries); desiredNumEntries = kDesiredNumUnicast; - if (HasAnyServiceDataUnicastEntry() || - (Get().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone)) + if (HasAnyServiceDataUnicastEntry() || HasAnyAnycastEntry()) { // If there is any service data unicast entry or anycast // entry, we set the desired number of server data @@ -676,7 +672,6 @@ void Publisher::DnsSrpServiceEntry::Process(void) } break; - } case kTypeUnicast: desiredNumEntries = kDesiredNumUnicast; @@ -697,23 +692,16 @@ void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, ui // "sequence number" value). We prefer the entries associated with // smaller RLCO16. - Service::DnsSrpAnycast::ServiceData serviceData(mInfo.GetSequenceNumber()); - const ServiceTlv *serviceTlv = nullptr; - ServiceData data; - - data.Init(&serviceData, serviceData.GetLength()); + Service::Manager::Iterator iterator; + Service::DnsSrpAnycast::Info anycastInfo; - while ((serviceTlv = Get().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) != - nullptr) + while (Get().GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNone) { - TlvIterator subTlvIterator(*serviceTlv); - const ServerTlv *serverSubTlv; - - while ((serverSubTlv = subTlvIterator.Iterate()) != nullptr) + if (anycastInfo.mSequenceNumber == mInfo.GetSequenceNumber()) { aNumEntries++; - if (IsPreferred(serverSubTlv->GetServer16())) + if (IsPreferred(anycastInfo.mRloc16)) { aNumPreferredEntries++; } @@ -721,6 +709,14 @@ void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, ui } } +bool Publisher::DnsSrpServiceEntry::HasAnyAnycastEntry(void) const +{ + Service::Manager::Iterator iterator; + Service::DnsSrpAnycast::Info anycastInfo; + + return (Get().GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNone); +} + void Publisher::DnsSrpServiceEntry::CountUnicastEntries(Service::DnsSrpUnicast::Type aType, uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const diff --git a/src/core/thread/network_data_publisher.hpp b/src/core/thread/network_data_publisher.hpp index 0ef546b93..d1a3becc0 100644 --- a/src/core/thread/network_data_publisher.hpp +++ b/src/core/thread/network_data_publisher.hpp @@ -438,6 +438,7 @@ class Publisher : public InstanceLocator, private NonCopyable void Notify(Event aEvent) const; void Process(void); void CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const; + bool HasAnyAnycastEntry(void) const; void CountUnicastEntries(Service::DnsSrpUnicast::Type aType, uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const; diff --git a/src/core/thread/network_data_service.cpp b/src/core/thread/network_data_service.cpp index c11c03ef4..45bc7b327 100644 --- a/src/core/thread/network_data_service.cpp +++ b/src/core/thread/network_data_service.cpp @@ -168,27 +168,43 @@ bool Manager::IsBackboneRouterPreferredTo(const ServerTlv &aSer Error Manager::GetNextDnsSrpAnycastInfo(Iterator &aIterator, DnsSrpAnycast::Info &aInfo) const { - Error error = kErrorNone; - ServiceData serviceData; - const ServiceTlv *tlv = aIterator.mServiceTlv; - - serviceData.InitFrom(DnsSrpAnycast::kServiceData); + Error error = kErrorNone; do { - tlv = Get().FindNextThreadService(tlv, serviceData, NetworkData::kServicePrefixMatch); - VerifyOrExit(tlv != nullptr, error = kErrorNotFound); + ServiceData serviceData; + + // Process the next Server sub-TLV in the current Service TLV. + + if (IterateToNextServer(aIterator) == kErrorNone) + { + aIterator.mServiceTlv->GetServiceData(serviceData); + + if (serviceData.GetLength() >= sizeof(DnsSrpAnycast::ServiceData)) + { + const DnsSrpAnycast::ServiceData *dnsServiceData = + reinterpret_cast(serviceData.GetBytes()); + + Get().GetServiceAloc(aIterator.mServiceTlv->GetServiceId(), aInfo.mAnycastAddress); + aInfo.mSequenceNumber = dnsServiceData->GetSequenceNumber(); + aInfo.mRloc16 = aIterator.mServerSubTlv->GetServer16(); + ExitNow(); + } + } - } while (tlv->GetServiceDataLength() < sizeof(DnsSrpAnycast::ServiceData)); + // Find the next matching Service TLV. - tlv->GetServiceData(serviceData); + serviceData.InitFrom(DnsSrpAnycast::kServiceData); + aIterator.mServiceTlv = + Get().FindNextThreadService(aIterator.mServiceTlv, serviceData, NetworkData::kServicePrefixMatch); + aIterator.mServerSubTlv = nullptr; - Get().GetServiceAloc(tlv->GetServiceId(), aInfo.mAnycastAddress); + // If we have a valid Service TLV, restart the loop + // to process its Server sub-TLVs. - aInfo.mSequenceNumber = - reinterpret_cast(serviceData.GetBytes())->GetSequenceNumber(); + } while (aIterator.mServiceTlv != nullptr); - aIterator.mServiceTlv = tlv; + error = kErrorNotFound; exit: return error; diff --git a/src/core/thread/network_data_service.hpp b/src/core/thread/network_data_service.hpp index 356ad072d..612eb3885 100644 --- a/src/core/thread/network_data_service.hpp +++ b/src/core/thread/network_data_service.hpp @@ -173,6 +173,7 @@ class DnsSrpAnycast { Ip6::Address mAnycastAddress; ///< The anycast address associated with the DNS/SRP servers. uint8_t mSequenceNumber; ///< Sequence number used to notify SRP client if they need to re-register. + uint16_t mRloc16; ///< The RLOC16 of the entry. }; /** diff --git a/tests/unit/test_network_data.cpp b/tests/unit/test_network_data.cpp index 8a1b4a721..ea38087de 100644 --- a/tests/unit/test_network_data.cpp +++ b/tests/unit/test_network_data.cpp @@ -635,13 +635,14 @@ void TestNetworkDataDsnSrpServices(void) { uint16_t mAloc16; uint8_t mSequenceNumber; + uint16_t mRloc16; bool Matches(Service::DnsSrpAnycast::Info aInfo) const { VerifyOrQuit(aInfo.mAnycastAddress.GetIid().IsAnycastServiceLocator()); return (aInfo.mAnycastAddress.GetIid().GetLocator() == mAloc16) && - (aInfo.mSequenceNumber == mSequenceNumber); + (aInfo.mSequenceNumber == mSequenceNumber) && (aInfo.mRloc16 == mRloc16); } }; @@ -663,19 +664,20 @@ void TestNetworkDataDsnSrpServices(void) }; const uint8_t kNetworkData[] = { - 0x0b, 0x08, 0x80, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x28, 0x00, 0x0b, 0x08, 0x81, 0x02, 0x5c, 0xff, 0x0d, 0x02, - 0x6c, 0x00, 0x0b, 0x09, 0x82, 0x02, 0x5c, 0x03, 0x0d, 0x03, 0x4c, 0x00, 0xaa, 0x0b, 0x35, 0x83, 0x13, 0x5d, - 0xfd, 0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0x00, 0x2d, 0x0e, 0xc6, 0x27, 0x55, 0x56, 0x18, 0xd9, 0x12, 0x34, - 0x0d, 0x02, 0x00, 0x00, 0x0d, 0x14, 0x6c, 0x00, 0xfd, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, - 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xab, 0xcd, 0x0d, 0x04, 0x28, 0x00, 0x56, 0x78, 0x0b, 0x23, 0x84, 0x01, - 0x5d, 0x0d, 0x02, 0x00, 0x00, 0x0d, 0x14, 0x4c, 0x00, 0xfd, 0x00, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, - 0xf0, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0x00, 0x0e, 0x0d, 0x04, 0x6c, 0x00, 0xcd, 0x12, + 0x0b, 0x08, 0x80, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x28, 0x00, 0x0b, 0x08, 0x81, 0x02, 0x5c, 0xff, 0x0d, + 0x02, 0x6c, 0x00, 0x0b, 0x09, 0x82, 0x02, 0x5c, 0x03, 0x0d, 0x03, 0x4c, 0x00, 0xaa, 0x0b, 0x35, 0x83, + 0x13, 0x5d, 0xfd, 0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0x00, 0x2d, 0x0e, 0xc6, 0x27, 0x55, 0x56, 0x18, + 0xd9, 0x12, 0x34, 0x0d, 0x02, 0x00, 0x00, 0x0d, 0x14, 0x6c, 0x00, 0xfd, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, + 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xab, 0xcd, 0x0d, 0x04, 0x28, 0x00, 0x56, + 0x78, 0x0b, 0x23, 0x84, 0x01, 0x5d, 0x0d, 0x02, 0x00, 0x00, 0x0d, 0x14, 0x4c, 0x00, 0xfd, 0x00, 0x12, + 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0x00, 0x0e, 0x0d, 0x04, + 0x6c, 0x00, 0xcd, 0x12, 0x0b, 0x08, 0x84, 0x01, 0x5c, 0x0d, 0x02, 0x14, 0x01, 0x0d, 0x0b, 0x10, 0x83, + 0x02, 0x5c, 0xfe, 0x0d, 0x02, 0x12, 0x00, 0x0d, 0x02, 0x12, 0x01, 0x0d, 0x02, 0x16, 0x00, }; const AnycastEntry kAnycastEntries[] = { - {0xfc10, 0x02}, - {0xfc11, 0xff}, - {0xfc12, 0x03}, + {0xfc10, 0x02, 0x2800}, {0xfc11, 0xff, 0x6c00}, {0xfc12, 0x03, 0x4c00}, + {0xfc13, 0xfe, 0x1200}, {0xfc13, 0xfe, 0x1201}, {0xfc13, 0xfe, 0x1600}, }; const UnicastEntry kUnicastEntriesFromServerData[] = { @@ -691,7 +693,9 @@ void TestNetworkDataDsnSrpServices(void) {"fdde:ad00:beef:0:2d0e:c627:5556:18d9", 0x1234, 0x2800}, }; - const uint16_t kExpectedRlocs[] = {0x6c00, 0x2800, 0x4c00, 0x0000}; + const uint16_t kExpectedRlocs[] = {0x6c00, 0x2800, 0x4c00, 0x0000, 0x1200, 0x1201, 0x1600, 0x1401}; + const uint16_t kExpectedRouterRlocs[] = {0x6c00, 0x2800, 0x4c00, 0x0000, 0x1200, 0x1600}; + const uint16_t kExpectedChildRlocs[] = {0x1201, 0x1401}; const uint8_t kPreferredAnycastEntryIndex = 2; @@ -712,10 +716,10 @@ void TestNetworkDataDsnSrpServices(void) VerifyRlocsArray(rlocs, kExpectedRlocs); instance->Get().FindRlocs(kAnyBrOrServer, kRouterRoleOnly, rlocs); - VerifyRlocsArray(rlocs, kExpectedRlocs); + VerifyRlocsArray(rlocs, kExpectedRouterRlocs); instance->Get().FindRlocs(kAnyBrOrServer, kChildRoleOnly, rlocs); - VerifyOrQuit(rlocs.GetLength() == 0); + VerifyRlocsArray(rlocs, kExpectedChildRlocs); instance->Get().FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs); VerifyOrQuit(rlocs.GetLength() == 0); @@ -729,8 +733,8 @@ void TestNetworkDataDsnSrpServices(void) { SuccessOrQuit(manager.GetNextDnsSrpAnycastInfo(iterator, anycastInfo)); - printf("\nanycastInfo { %s, seq:%d }", anycastInfo.mAnycastAddress.ToString().AsCString(), - anycastInfo.mSequenceNumber); + printf("\nanycastInfo { %s, seq:%d, rlco16:%04x }", anycastInfo.mAnycastAddress.ToString().AsCString(), + anycastInfo.mSequenceNumber, anycastInfo.mRloc16); VerifyOrQuit(entry.Matches(anycastInfo), "GetNextDnsSrpAnycastInfo() returned incorrect info"); } @@ -955,10 +959,11 @@ void TestNetworkDataDsnSrpAnycastSeqNumSelection(void) { SuccessOrQuit(manager.GetNextDnsSrpAnycastInfo(iterator, anycastInfo)); - printf("\n { %s, seq:%d }", anycastInfo.mAnycastAddress.ToString().AsCString(), - anycastInfo.mSequenceNumber); + printf("\n { %s, seq:%d, rlco16:%04x }", anycastInfo.mAnycastAddress.ToString().AsCString(), + anycastInfo.mSequenceNumber, anycastInfo.mRloc16); VerifyOrQuit(anycastInfo.mSequenceNumber == test.mSeqNumbers[index]); + VerifyOrQuit(anycastInfo.mRloc16 == 0x5000 + index); } VerifyOrQuit(manager.GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNotFound); From 8a0ea2b69203dba703a5e38ad949b25b413c518a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Bida?= Date: Thu, 22 Aug 2024 04:21:10 +0200 Subject: [PATCH 120/160] [tcat] implement new tcat General commands (#10526) New General TLV's implemented: - Get network name - Get device id - Get ext pan ID - get provisioning URL --- include/openthread/instance.h | 2 +- include/openthread/tcat.h | 55 ++++--- src/cli/README_TCAT.md | 87 +++++++++++ src/cli/cli_tcat.cpp | 93 +++++++++--- src/core/common/tlvs.cpp | 24 ++- src/core/common/tlvs.hpp | 5 +- src/core/meshcop/tcat_agent.cpp | 130 +++++++++++++--- src/core/meshcop/tcat_agent.hpp | 19 ++- .../scripts/expect/cli-tcat-advertisement.exp | 42 ++++-- tests/scripts/expect/cli-tcat.exp | 25 ++++ tools/tcat_ble_client/cli/base_commands.py | 141 ++++++++++++------ tools/tcat_ble_client/cli/cli.py | 7 +- tools/tcat_ble_client/cli/command.py | 3 +- tools/tcat_ble_client/tlv/tcat_tlv.py | 4 + 14 files changed, 502 insertions(+), 135 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 891398807..48b1203a3 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (434) +#define OPENTHREAD_API_VERSION (435) /** * @addtogroup api-instance diff --git a/include/openthread/tcat.h b/include/openthread/tcat.h index d1b9c8c45..1733d9fb5 100644 --- a/include/openthread/tcat.h +++ b/include/openthread/tcat.h @@ -67,9 +67,10 @@ extern "C" { #define OT_TCAT_MAX_SERVICE_NAME_LENGTH \ 15 ///< Maximum string length of a UDP or TCP service name (does not include null char). -#define OT_TCAT_ADVERTISEMENT_MAX_LEN 29 ///< Maximum length of TCAT advertisement. -#define OT_TCAT_OPCODE 0x2 ///< TCAT Advertisement Operation Code. -#define OT_TCAT_MAX_VENDORID_SIZE 5 ///< TCAT max size of vendor ID including null as termination. +#define OT_TCAT_ADVERTISEMENT_MAX_LEN 29 ///< Maximum length of TCAT advertisement. +#define OT_TCAT_OPCODE 0x2 ///< TCAT Advertisement Operation Code. +#define OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE 5 ///< TCAT max size of any type of advertised Device ID. +#define OT_TCAT_MAX_DEVICEID_SIZE 64 ///< TCAT max size of device ID. /** * Represents TCAT status code. @@ -116,10 +117,10 @@ typedef enum otTcatCommandClass } otTcatCommandClass; /** - * Represents Device ID type. + * Represents Advertised Device ID type. (used during TCAT advertisement) * */ -typedef enum otTcatDeviceIdType +typedef enum otTcatAdvertisedDeviceIdType { OT_TCAT_DEVICE_ID_EMPTY = 0, ///< Vendor device ID type not set OT_TCAT_DEVICE_ID_OUI24 = 1, ///< Vendor device ID type IEEE OUI-24 @@ -127,14 +128,24 @@ typedef enum otTcatDeviceIdType OT_TCAT_DEVICE_ID_DISCRIMINATOR = 3, ///< Vendor device ID type Device Discriminator OT_TCAT_DEVICE_ID_IANAPEN = 4, ///< Vendor device ID type IANA PEN OT_TCAT_DEVICE_ID_MAX = 5, ///< Vendor device ID type size -} otTcatDeviceIdType; +} otTcatAdvertisedDeviceIdType; -typedef struct otTcatDeviceId +typedef struct otTcatAdvertisedDeviceId { - otTcatDeviceIdType mDeviceIdType; - uint16_t mDeviceIdLen; - uint8_t mDeviceId[OT_TCAT_MAX_VENDORID_SIZE]; -} otTcatDeviceId; + otTcatAdvertisedDeviceIdType mDeviceIdType; + uint16_t mDeviceIdLen; + uint8_t mDeviceId[OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE]; +} otTcatAdvertisedDeviceId; + +/** + * Represents General Device ID type. + * + */ +typedef struct otTcatGeneralDeviceId +{ + uint16_t mDeviceIdLen; + uint8_t mDeviceId[OT_TCAT_MAX_DEVICEID_SIZE]; +} otTcatGeneralDeviceId; /** * This structure represents a TCAT vendor information. @@ -144,16 +155,18 @@ typedef struct otTcatDeviceId */ typedef struct otTcatVendorInfo { - const char *mProvisioningUrl; ///< Provisioning URL path string - const char *mVendorName; ///< Vendor name string - const char *mVendorModel; ///< Vendor model string - const char *mVendorSwVersion; ///< Vendor software version string - const char *mVendorData; ///< Vendor specific data string - const char *mPskdString; ///< Vendor managed pre-shared key for device - const char *mInstallCode; ///< Vendor managed install code string - const otTcatDeviceId *mDeviceIds; /** Vendor managed device ID array. - (if NULL: device ID is set to EUI-64 in binary format) - Array is terminated like C string with OT_TCAT_DEVICE_ID_EMPTY */ + const char *mProvisioningUrl; ///< Provisioning URL path string + const char *mVendorName; ///< Vendor name string + const char *mVendorModel; ///< Vendor model string + const char *mVendorSwVersion; ///< Vendor software version string + const char *mVendorData; ///< Vendor specific data string + const char *mPskdString; ///< Vendor managed pre-shared key for device + const char *mInstallCode; ///< Vendor managed install code string + const otTcatAdvertisedDeviceId *mAdvertisedDeviceIds; /** Vendor managed advertised device ID array. + Array is terminated like C string with OT_TCAT_DEVICE_ID_EMPTY */ + const otTcatGeneralDeviceId *mGeneralDeviceId; /** Vendor managed general device ID array. + (if NULL: device ID is set to EUI-64 in binary format)*/ + } otTcatVendorInfo; /** diff --git a/src/cli/README_TCAT.md b/src/cli/README_TCAT.md index 066a57109..b96ebd481 100644 --- a/src/cli/README_TCAT.md +++ b/src/cli/README_TCAT.md @@ -2,16 +2,103 @@ ## Command List +- advid [#advid] +- devid [#devid] - help [#help] - start [#start] - stop [#stop] +### advid + +Displays currently set TCAT advertised ids. + +```bash +tcat advid +type oui24, value: f378aa +Done +``` + +### advid ianapen \ + +Sets TCAT advertised ianapen id. + +```bash +tcat advid ianapen f378aabb +Done +``` + +### advid oui24 \ + +Sets TCAT advertised oui24 id. + +```bash +tcat advid oui24 f378aa +Done +``` + +### advid oui36 \ + +Sets TCAT advertised oui36 id. + +```bash +tcat advid oui36 f378aabbcc +Done +``` + +### advid discriminator \ + +Sets TCAT advertised discriminator id. + +```bash +tcat advid discriminator f378aabbdd +Done +``` + +### advid clear + +Clears TCAT advertised id. + +```bash +tcat advid clear +Done +``` + +### devid + +Displays currently set TCAT device id. + +```bash +tcat devid +abcd +Done +``` + +### devid \ + +Sets TCAT device id. + +```bash +tcat devid abcd +Done +``` + +### devid clear + +Clears TCAT device id. + +```bash +tcat devid clear +Done +``` + ### help print help ```bash tcat help +advid +devid help start stop diff --git a/src/cli/cli_tcat.cpp b/src/cli/cli_tcat.cpp index ab29bd0fd..4dda80d0c 100644 --- a/src/cli/cli_tcat.cpp +++ b/src/cli/cli_tcat.cpp @@ -32,6 +32,7 @@ #include "cli/cli_tcat.hpp" #include "common/code_utils.hpp" +#include "common/error.hpp" #include @@ -88,11 +89,26 @@ namespace ot { namespace Cli { -otTcatDeviceId sVendorDeviceIds[OT_TCAT_DEVICE_ID_MAX]; +otTcatAdvertisedDeviceId sAdvertisedDeviceIds[OT_TCAT_DEVICE_ID_MAX]; +otTcatGeneralDeviceId sGeneralDeviceId; const char kPskdVendor[] = "JJJJJJ"; const char kUrl[] = "dummy_url"; +static bool IsDeviceIdSet(void) +{ + bool ret = false; + for (const otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds) + { + if (vendorDeviceId.mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY) + { + ExitNow(ret = true); + } + } +exit: + return ret; +} + static void HandleBleSecureReceive(otInstance *aInstance, const otMessage *aMessage, int32_t aOffset, @@ -121,30 +137,26 @@ static void HandleBleSecureReceive(otInstance *aInstance, IgnoreReturnValue(otBleSecureFlush(aInstance)); } -template <> otError Tcat::Process(Arg aArgs[]) +template <> otError Tcat::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; - otTcatDeviceId devId; - static const char *const kVendorIdTypes[] = {"empty", "oui24", "oui36", "discriminator", "ianapen"}; + otTcatAdvertisedDeviceId devId; + static const char *const kVendorIdTypes[] = {"clear", "oui24", "oui36", "discriminator", "ianapen"}; - mVendorInfo.mDeviceIds = sVendorDeviceIds; + mVendorInfo.mAdvertisedDeviceIds = sAdvertisedDeviceIds; if (aArgs[0].IsEmpty()) { - if (mVendorInfo.mDeviceIds[0].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY) + if (mVendorInfo.mAdvertisedDeviceIds[0].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY) { - OutputLine("Set vendorIds:"); - for (size_t i = 0; mVendorInfo.mDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++) + OutputLine("Set advertisedIds:"); + for (size_t i = 0; mVendorInfo.mAdvertisedDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++) { - OutputFormat("type %s, value: ", kVendorIdTypes[mVendorInfo.mDeviceIds[i].mDeviceIdType]); - OutputBytesLine(const_cast(mVendorInfo.mDeviceIds[i].mDeviceId), - mVendorInfo.mDeviceIds[i].mDeviceIdLen); + OutputFormat("type %s, value: ", kVendorIdTypes[mVendorInfo.mAdvertisedDeviceIds[i].mDeviceIdType]); + OutputBytesLine(const_cast(mVendorInfo.mAdvertisedDeviceIds[i].mDeviceId), + mVendorInfo.mAdvertisedDeviceIds[i].mDeviceIdLen); } } - else - { - OutputLine("%s", kVendorIdTypes[OT_TCAT_DEVICE_ID_EMPTY]); - } ExitNow(); } @@ -166,7 +178,7 @@ template <> otError Tcat::Process(Arg aArgs[]) } else if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_EMPTY]) { - for (otTcatDeviceId &vendorDeviceId : sVendorDeviceIds) + for (otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds) { vendorDeviceId.mDeviceIdType = OT_TCAT_DEVICE_ID_EMPTY; vendorDeviceId.mDeviceIdLen = 0; @@ -178,11 +190,11 @@ template <> otError Tcat::Process(Arg aArgs[]) ExitNow(error = OT_ERROR_INVALID_ARGS); } - if (!aArgs[1].IsEmpty() && aArgs[1].GetLength() < (OT_TCAT_MAX_VENDORID_SIZE * 2 + 1)) + if (!aArgs[1].IsEmpty() && aArgs[1].GetLength() < (OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE * 2 + 1)) { - devId.mDeviceIdLen = OT_TCAT_MAX_VENDORID_SIZE; + devId.mDeviceIdLen = OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE; SuccessOrExit(error = aArgs[1].ParseAsHexString(devId.mDeviceIdLen, devId.mDeviceId)); - for (otTcatDeviceId &vendorDeviceId : sVendorDeviceIds) + for (otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds) { if (vendorDeviceId.mDeviceIdType == devId.mDeviceIdType || vendorDeviceId.mDeviceIdType == OT_TCAT_DEVICE_ID_EMPTY) @@ -200,6 +212,35 @@ template <> otError Tcat::Process(Arg aArgs[]) return error; } +template <> otError Tcat::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + if (aArgs[0].IsEmpty()) + { + if (sGeneralDeviceId.mDeviceIdLen != 0) + { + OutputLine("TCAT DeviceId:"); + OutputBytesLine(sGeneralDeviceId.mDeviceId, sGeneralDeviceId.mDeviceIdLen); + } + ExitNow(); + } + + if (aArgs[0] == "clear") + { + ClearAllBytes(sGeneralDeviceId); + } + else + { + VerifyOrExit(aArgs[0].GetLength() < (OT_TCAT_MAX_DEVICEID_SIZE * 2 + 1), error = OT_ERROR_INVALID_ARGS); + sGeneralDeviceId.mDeviceIdLen = OT_TCAT_MAX_DEVICEID_SIZE; + SuccessOrExit(error = aArgs[0].ParseAsHexString(sGeneralDeviceId.mDeviceIdLen, sGeneralDeviceId.mDeviceId)); + } + +exit: + return error; +} + template <> otError Tcat::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -210,6 +251,16 @@ template <> otError Tcat::Process(Arg aArgs[]) mVendorInfo.mPskdString = kPskdVendor; mVendorInfo.mProvisioningUrl = kUrl; + if (IsDeviceIdSet()) + { + mVendorInfo.mAdvertisedDeviceIds = sAdvertisedDeviceIds; + } + + if (sGeneralDeviceId.mDeviceIdLen != 0) + { + mVendorInfo.mGeneralDeviceId = &sGeneralDeviceId; + } + otBleSecureSetCertificate(GetInstancePtr(), reinterpret_cast(OT_CLI_TCAT_X509_CERT), sizeof(OT_CLI_TCAT_X509_CERT), reinterpret_cast(OT_CLI_TCAT_PRIV_KEY), sizeof(OT_CLI_TCAT_PRIV_KEY)); @@ -244,10 +295,12 @@ otError Tcat::Process(Arg aArgs[]) aCommandString, &Tcat::Process \ } - static constexpr Command kCommands[] = {CmdEntry("start"), CmdEntry("stop"), CmdEntry("vendorid")}; + static constexpr Command kCommands[] = {CmdEntry("advid"), CmdEntry("devid"), CmdEntry("start"), CmdEntry("stop")}; static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); +#undef CmdEntry + otError error = OT_ERROR_NONE; const Command *command; diff --git a/src/core/common/tlvs.cpp b/src/core/common/tlvs.cpp index 6f02bc405..a91db29fe 100644 --- a/src/core/common/tlvs.cpp +++ b/src/core/common/tlvs.cpp @@ -269,16 +269,24 @@ template Error Tlv::AppendUintTlv(Message &aMessage, uint8_t aType, uin template Error Tlv::AppendUintTlv(Message &aMessage, uint8_t aType, uint16_t aValue); template Error Tlv::AppendUintTlv(Message &aMessage, uint8_t aType, uint32_t aValue); -Error Tlv::AppendTlv(Message &aMessage, uint8_t aType, const void *aValue, uint8_t aLength) +Error Tlv::AppendTlv(Message &aMessage, uint8_t aType, const void *aValue, uint16_t aLength) { - Error error = kErrorNone; - Tlv tlv; - - OT_ASSERT(aLength <= Tlv::kBaseTlvMaxLength); + Error error = kErrorNone; + ExtendedTlv extTlv; + Tlv tlv; - tlv.SetType(aType); - tlv.SetLength(aLength); - SuccessOrExit(error = aMessage.Append(tlv)); + if (aLength > kBaseTlvMaxLength) + { + extTlv.SetType(aType); + extTlv.SetLength(aLength); + SuccessOrExit(error = aMessage.Append(extTlv)); + } + else + { + tlv.SetType(aType); + tlv.SetLength(static_cast(aLength)); + SuccessOrExit(error = aMessage.Append(tlv)); + } VerifyOrExit(aLength > 0); error = aMessage.AppendBytes(aValue, aLength); diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index f89d0d643..54e555720 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -574,6 +574,9 @@ class Tlv /** * Appends a TLV with a given type and value to a message. * + * If the TLV length is longer than maximum base TLV size defined by `kBaseTlvMaxLength` then + * appends extended TLV. + * * On success this method grows the message by the size of the TLV. * * @param[in] aMessage The message to append to. @@ -585,7 +588,7 @@ class Tlv * @retval kErrorNoBufs Insufficient available buffers to grow the message. * */ - static Error AppendTlv(Message &aMessage, uint8_t aType, const void *aValue, uint8_t aLength); + static Error AppendTlv(Message &aMessage, uint8_t aType, const void *aValue, uint16_t aLength); /** * Appends a TLV with a given type and value to a message. diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index 49470828d..8f2c7c2e7 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -33,6 +33,7 @@ #include "tcat_agent.hpp" #include +#include "meshcop/network_name.hpp" #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE @@ -55,7 +56,11 @@ RegisterLogModule("TcatAgent"); bool TcatAgent::VendorInfo::IsValid(void) const { - return mProvisioningUrl == nullptr || IsValidUtf8String(mProvisioningUrl) || mPskdString != nullptr; + return (mProvisioningUrl == nullptr || + (IsValidUtf8String(mProvisioningUrl) && + (static_cast(StringLength(mProvisioningUrl, kProvisioningUrlMaxLength)) < + kProvisioningUrlMaxLength))) && + mPskdString != nullptr; } TcatAgent::TcatAgent(Instance &aInstance) @@ -414,17 +419,24 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg error = HandleDecomission(); break; case kTlvPing: - error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length); - if (error == kErrorNone) - { - response = true; - } + error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length, response); + break; + case kTlvGetNetworkName: + error = HandleGetNetworkName(aOutgoingMessage, response); + break; + case kTlvGetDeviceId: + error = HandleGetDeviceId(aOutgoingMessage, response); + break; + case kTlvGetExtendedPanID: + error = HandleGetExtPanId(aOutgoingMessage, response); + break; + case kTlvGetProvisioningURL: + error = HandleGetProvisioningUrl(aOutgoingMessage, response); break; default: error = kErrorInvalidCommand; } } - if (!response) { StatusCode statusCode; @@ -514,7 +526,8 @@ Error TcatAgent::HandleDecomission(void) Error TcatAgent::HandlePing(const Message &aIncomingMessage, Message &aOutgoingMessage, uint16_t aOffset, - uint16_t aLength) + uint16_t aLength, + bool &response) { Error error = kErrorNone; ot::ExtendedTlv extTlv; @@ -535,6 +548,85 @@ Error TcatAgent::HandlePing(const Message &aIncomingMessage, } SuccessOrExit(error = aOutgoingMessage.AppendBytesFromMessage(aIncomingMessage, aOffset, aLength)); + response = true; + +exit: + return error; +} + +Error TcatAgent::HandleGetNetworkName(Message &aOutgoingMessage, bool &response) +{ + Error error = kErrorNone; + MeshCoP::NameData nameData = Get().GetNetworkName().GetAsData(); + + VerifyOrExit(Get().IsCommissioned(), error = kErrorInvalidState); +#if !OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME + VerifyOrExit(nameData.GetLength() > 0, error = kErrorInvalidState); +#endif + + SuccessOrExit( + error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, nameData.GetBuffer(), nameData.GetLength())); + response = true; + +exit: + return error; +} + +Error TcatAgent::HandleGetDeviceId(Message &aOutgoingMessage, bool &response) +{ + const uint8_t *deviceId; + uint16_t length = 0; + Mac::ExtAddress eui64; + Error error = kErrorNone; + + if (mVendorInfo->mGeneralDeviceId != nullptr) + { + length = mVendorInfo->mGeneralDeviceId->mDeviceIdLen; + deviceId = mVendorInfo->mGeneralDeviceId->mDeviceId; + } + + if (length == 0) + { + Get().GetIeeeEui64(eui64); + + length = sizeof(Mac::ExtAddress); + deviceId = eui64.m8; + } + + SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, deviceId, length)); + + response = true; + +exit: + return error; +} + +Error TcatAgent::HandleGetExtPanId(Message &aOutgoingMessage, bool &response) +{ + Error error; + + VerifyOrExit(Get().IsCommissioned(), error = kErrorInvalidState); + + SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, + &Get().GetExtPanId(), sizeof(ExtendedPanId))); + response = true; + +exit: + return error; +} + +Error TcatAgent::HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &response) +{ + Error error = kErrorNone; + uint16_t length; + + VerifyOrExit(mVendorInfo->mProvisioningUrl != nullptr, error = kErrorInvalidState); + + length = StringLength(mVendorInfo->mProvisioningUrl, kProvisioningUrlMaxLength); + VerifyOrExit(length > 0 && length <= Tlv::kBaseTlvMaxLength, error = kErrorInvalidState); + + error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, mVendorInfo->mProvisioningUrl, length); + response = true; exit: return error; @@ -585,31 +677,31 @@ Error TcatAgent::GetAdvertisementData(uint16_t &aLen, uint8_t *aAdvertisementDat aAdvertisementData[2] = OPENTHREAD_CONFIG_THREAD_VERSION << 4 | OT_TCAT_OPCODE; aLen++; - if (mVendorInfo->mDeviceIds != nullptr) + if (mVendorInfo->mAdvertisedDeviceIds != nullptr) { - for (uint8_t i = 0; mVendorInfo->mDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++) + for (uint8_t i = 0; mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++) { - switch (MapEnum(mVendorInfo->mDeviceIds[i].mDeviceIdType)) + switch (MapEnum(mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType)) { case kTcatDeviceIdOui24: SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui24, - mVendorInfo->mDeviceIds[i].mDeviceIdLen, - mVendorInfo->mDeviceIds[i].mDeviceId); + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen, + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId); break; case kTcatDeviceIdOui36: SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui36, - mVendorInfo->mDeviceIds[i].mDeviceIdLen, - mVendorInfo->mDeviceIds[i].mDeviceId); + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen, + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId); break; case kTcatDeviceIdDiscriminator: SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvDeviceDiscriminator, - mVendorInfo->mDeviceIds[i].mDeviceIdLen, - mVendorInfo->mDeviceIds[i].mDeviceId); + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen, + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId); break; case kTcatDeviceIdIanaPen: SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorIanaPen, - mVendorInfo->mDeviceIds[i].mDeviceIdLen, - mVendorInfo->mDeviceIds[i].mDeviceId); + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen, + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId); break; default: break; diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp index 5d6d0dd97..a76008513 100644 --- a/src/core/meshcop/tcat_agent.hpp +++ b/src/core/meshcop/tcat_agent.hpp @@ -168,6 +168,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable kTlvPing = 10, ///< TCAT ping request TLV kTlvGetDeviceId = 11, ///< TCAT device ID query TLV kTlvGetExtendedPanID = 12, ///< TCAT extended PAN ID query TLV + kTlvGetProvisioningURL = 13, ///< TCAT provisioning URL query TLV kTlvPresentPskdHash = 16, ///< TCAT commissioner rights elevation request TLV using PSKd hash kTlvPresentPskcHash = 17, ///< TCAT commissioner rights elevation request TLV using PSKc hash kTlvPresentInstallCodeHash = 18, ///< TCAT commissioner rights elevation request TLV using install code @@ -353,7 +354,15 @@ class TcatAgent : public InstanceLocator, private NonCopyable Error HandleSingleTlv(const Message &aIncomingMessage, Message &aOutgoingMessage); Error HandleSetActiveOperationalDataset(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength); Error HandleDecomission(void); - Error HandlePing(const Message &aIncomingMessage, Message &aOutgoingMessage, uint16_t aOffset, uint16_t aLength); + Error HandlePing(const Message &aIncomingMessage, + Message &aOutgoingMessage, + uint16_t aOffset, + uint16_t aLength, + bool &response); + Error HandleGetNetworkName(Message &aOutgoingMessage, bool &response); + Error HandleGetDeviceId(Message &aOutgoingMessage, bool &response); + Error HandleGetExtPanId(Message &aOutgoingMessage, bool &response); + Error HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &response); Error HandleStartThreadInterface(void); bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags, @@ -362,8 +371,10 @@ class TcatAgent : public InstanceLocator, private NonCopyable bool CanProcessTlv(uint8_t aTlvType) const; CommandClass GetCommandClass(uint8_t aTlvType) const; - static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; - static constexpr uint16_t kPingPayloadMaxLength = 512; + static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; + static constexpr uint16_t kPingPayloadMaxLength = 512; + static constexpr uint16_t kProvisioningUrlMaxLength = 64; + static constexpr uint16_t kTcatMaxDeviceIdSize = OT_TCAT_MAX_DEVICEID_SIZE; JoinerPskd mJoinerPskd; const VendorInfo *mVendorInfo; @@ -389,7 +400,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable DefineCoreType(otTcatVendorInfo, MeshCoP::TcatAgent::VendorInfo); DefineMapEnum(otTcatApplicationProtocol, MeshCoP::TcatAgent::TcatApplicationProtocol); -DefineMapEnum(otTcatDeviceIdType, MeshCoP::TcatAgent::TcatDeviceIdType); +DefineMapEnum(otTcatAdvertisedDeviceIdType, MeshCoP::TcatAgent::TcatDeviceIdType); // Command class TLVs typedef UintTlvInfo ResponseWithStatusTlv; diff --git a/tests/scripts/expect/cli-tcat-advertisement.exp b/tests/scripts/expect/cli-tcat-advertisement.exp index 37628459d..cf5e05134 100755 --- a/tests/scripts/expect/cli-tcat-advertisement.exp +++ b/tests/scripts/expect/cli-tcat-advertisement.exp @@ -32,44 +32,60 @@ source "tests/scripts/expect/_common.exp" spawn_node 1 "cli" switch_node 1 -send "tcat vendorid ianapen f378\n" +send "tcat advid ianapen f378\n" expect_line "Done" -send "tcat vendorid\n" +send "tcat advid\n" expect_line "type ianapen, value: f378" expect_line "Done" -send "tcat vendorid ianapen f378aabb\n" +send "tcat advid ianapen f378aabb\n" expect_line "Done" -send "tcat vendorid\n" +send "tcat advid\n" expect_line "type ianapen, value: f378aabb" expect_line "Done" -send "tcat vendorid oui24 f378aa\n" +send "tcat advid oui24 f378aa\n" expect_line "Done" -send "tcat vendorid\n" +send "tcat advid\n" expect_line "type oui24, value: f378aa" expect_line "Done" -send "tcat vendorid oui36 f378aabbcc\n" +send "tcat advid oui36 f378aabbcc\n" expect_line "Done" -send "tcat vendorid\n" +send "tcat advid\n" expect_line "type oui36, value: f378aabbcc" expect_line "Done" -send "tcat vendorid discriminator f378aabbdd\n" +send "tcat advid discriminator f378aabbdd\n" expect_line "Done" -send "tcat vendorid\n" +send "tcat advid\n" expect_line "type discriminator, value: f378aabbdd" expect_line "Done" -send "tcat vendorid empty\n" +send "tcat advid clear\n" expect_line "Done" -send "tcat vendorid\n" -expect_line "empty" +send "tcat advid\n" +expect_line "Done" + +send "tcat devid\n" +expect_line "Done" + +send "tcat devid aaaa\n" +expect_line "aaaa" +expect_line "Done" + +send "tcat devid\n" +expect_line "aaaa" +expect_line "Done" + +send "tcat devid clear\n" +expect_line "Done" + +send "tcat devid\n" expect_line "Done" diff --git a/tests/scripts/expect/cli-tcat.exp b/tests/scripts/expect/cli-tcat.exp index 9617cb39a..53d344b89 100755 --- a/tests/scripts/expect/cli-tcat.exp +++ b/tests/scripts/expect/cli-tcat.exp @@ -38,6 +38,11 @@ expect_line "Done" spawn python "tools/tcat_ble_client/bbtc.py" --simulation 1 --cert_path "tools/tcat_ble_client/auth" set py_client "$spawn_id" expect_line "Done" + +send "network_name\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x06" + send "commission\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" expect_line "\tVALUE:\t0x00" @@ -58,6 +63,26 @@ send "ping 512\n" expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" expect_line "\tLEN:\t512" +send "network_name\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t15" +expect_line "\tVALUE:\t0x4f70656e5468726561642d63363465" + +send "device_id\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t8" +expect_line "\tVALUE:\t0x18b4300000000001" + +send "ext_panid\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t8" +expect_line "\tVALUE:\t0xef1398c2fd504b67" + +send "provisioning_url\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t9" +expect_line "\tVALUE:\t0x64756d6d795f75726c" + send "exit\n" expect eof diff --git a/tools/tcat_ble_client/cli/base_commands.py b/tools/tcat_ble_client/cli/base_commands.py index bb0258ee2..411bc821e 100644 --- a/tools/tcat_ble_client/cli/base_commands.py +++ b/tools/tcat_ble_client/cli/base_commands.py @@ -26,6 +26,7 @@ POSSIBILITY OF SUCH DAMAGE. """ +from abc import abstractmethod from ble.ble_connection_constants import BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, \ BBTC_RX_CHAR_UUID from ble.ble_stream import BleStream @@ -54,15 +55,21 @@ async def execute_default(self, args, context): return CommandResultNone() -class HelloCommand(Command): +class BleCommand(Command): - def get_help_string(self) -> str: - return 'Send round trip "Hello world!" message.' + @abstractmethod + def get_log_string(self) -> str: + pass + + @abstractmethod + def prepare_data(self, context): + pass async def execute_default(self, args, context): bless: BleStreamSecure = context['ble_sstream'] - print('Sending hello world...') - data = TLV(TcatTLVType.APPLICATION.value, bytes('Hello world!', 'ascii')).to_bytes() + + print(self.get_log_string()) + data = self.prepare_data(context) response = await bless.send_with_resp(data) if not response: return @@ -70,39 +77,90 @@ async def execute_default(self, args, context): return CommandResultTLV(tlv_response) -class CommissionCommand(Command): +class HelloCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Sending hello world...' + + def get_help_string(self) -> str: + return 'Send round trip "Hello world!" message.' + + def prepare_data(self, context): + return TLV(TcatTLVType.APPLICATION.value, bytes('Hello world!', 'ascii')).to_bytes() + + +class CommissionCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Commissioning...' def get_help_string(self) -> str: return 'Update the connected device with current dataset.' - async def execute_default(self, args, context): - bless: BleStreamSecure = context['ble_sstream'] + def prepare_data(self, context): dataset: ThreadDataset = context['dataset'] - - print('Commissioning...') dataset_bytes = dataset.to_bytes() - data = TLV(TcatTLVType.ACTIVE_DATASET.value, dataset_bytes).to_bytes() - response = await bless.send_with_resp(data) - if not response: - return - tlv_response = TLV.from_bytes(response) - return CommandResultTLV(tlv_response) + return TLV(TcatTLVType.ACTIVE_DATASET.value, dataset_bytes).to_bytes() -class DecommissionCommand(Command): +class DecommissionCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Disabling Thread and decommissioning device...' def get_help_string(self) -> str: return 'Stop Thread interface and decommission device from current network.' - async def execute_default(self, args, context): - bless: BleStreamSecure = context['ble_sstream'] - print('Disabling Thread and decommissioning device...') - data = (TLV(TcatTLVType.DECOMMISSION.value, bytes()).to_bytes()) - response = await bless.send_with_resp(data) - if not response: - return - tlv_response = TLV.from_bytes(response) - return CommandResultTLV(tlv_response) + def prepare_data(self, context): + return TLV(TcatTLVType.DECOMMISSION.value, bytes()).to_bytes() + + +class GetDeviceIdCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Retrieving device id.' + + def get_help_string(self) -> str: + return 'Get unique identifier for the TCAT device.' + + def prepare_data(self, context): + return TLV(TcatTLVType.GET_DEVICE_ID.value, bytes()).to_bytes() + + +class GetExtPanIDCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Retrieving extended PAN ID.' + + def get_help_string(self) -> str: + return 'Get extended PAN ID that is commissioned in the active dataset.' + + def prepare_data(self, context): + return TLV(TcatTLVType.GET_EXT_PAN_ID.value, bytes()).to_bytes() + + +class GetProvisioningUrlCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Retrieving provisioning url.' + + def get_help_string(self) -> str: + return 'Get a URL for an application suited to commission the TCAT device.' + + def prepare_data(self, context): + return TLV(TcatTLVType.GET_PROVISIONING_URL.value, bytes()).to_bytes() + + +class GetNetworkNameCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Retrieving network name.' + + def get_help_string(self) -> str: + return 'Get the Thread network name that is commissioned in the active dataset.' + + def prepare_data(self, context): + return TLV(TcatTLVType.GET_NETWORK_NAME.value, bytes()).to_bytes() class PingCommand(Command): @@ -136,37 +194,28 @@ async def execute_default(self, args, context): return CommandResultTLV(tlv_response) -class ThreadStartCommand(Command): +class ThreadStartCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Enabling Thread...' def get_help_string(self) -> str: return 'Enable thread interface.' - async def execute_default(self, args, context): - bless: BleStreamSecure = context['ble_sstream'] + def prepare_data(self, context): + return TLV(TcatTLVType.THREAD_START.value, bytes()).to_bytes() - print('Enabling Thread...') - data = TLV(TcatTLVType.THREAD_START.value, bytes()).to_bytes() - response = await bless.send_with_resp(data) - if not response: - return - tlv_response = TLV.from_bytes(response) - return CommandResultTLV(tlv_response) +class ThreadStopCommand(BleCommand): -class ThreadStopCommand(Command): + def get_log_string(self) -> str: + return 'Disabling Thread...' def get_help_string(self) -> str: return 'Disable thread interface.' - async def execute_default(self, args, context): - bless: BleStreamSecure = context['ble_sstream'] - print('Disabling Thread...') - data = TLV(TcatTLVType.THREAD_STOP.value, bytes()).to_bytes() - response = await bless.send_with_resp(data) - if not response: - return - tlv_response = TLV.from_bytes(response) - return CommandResultTLV(tlv_response) + def prepare_data(self, context): + return TLV(TcatTLVType.THREAD_STOP.value, bytes()).to_bytes() class ThreadStateCommand(Command): diff --git a/tools/tcat_ble_client/cli/cli.py b/tools/tcat_ble_client/cli/cli.py index 28c7fb9a1..01e33bac3 100644 --- a/tools/tcat_ble_client/cli/cli.py +++ b/tools/tcat_ble_client/cli/cli.py @@ -29,7 +29,8 @@ import readline import shlex from ble.ble_stream_secure import BleStreamSecure -from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, PingCommand, +from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, GetDeviceIdCommand, + GetExtPanIDCommand, GetNetworkNameCommand, GetProvisioningUrlCommand, PingCommand, ThreadStateCommand, ScanCommand) from cli.dataset_commands import (DatasetCommand) from dataset.dataset import ThreadDataset @@ -44,6 +45,10 @@ def __init__(self, dataset: ThreadDataset, ble_sstream: Optional[BleStreamSecure 'hello': HelloCommand(), 'commission': CommissionCommand(), 'decommission': DecommissionCommand(), + 'device_id': GetDeviceIdCommand(), + 'ext_panid': GetExtPanIDCommand(), + 'provisioning_url': GetProvisioningUrlCommand(), + 'network_name': GetNetworkNameCommand(), 'ping': PingCommand(), 'dataset': DatasetCommand(), 'thread': ThreadStateCommand(), diff --git a/tools/tcat_ble_client/cli/command.py b/tools/tcat_ble_client/cli/command.py index f494a3e7d..e6b819b10 100644 --- a/tools/tcat_ble_client/cli/command.py +++ b/tools/tcat_ble_client/cli/command.py @@ -28,6 +28,7 @@ from tlv.tlv import TLV from tlv.tcat_tlv import TcatTLVType +from ble.ble_stream_secure import BleStreamSecure from abc import ABC, abstractmethod @@ -57,7 +58,7 @@ async def execute_subcommand(self, args, context) -> CommandResult: return await self._subcommands[args[0]].execute(args[1:], context) @abstractmethod - async def execute_default(self, args, context) -> CommandResult: + async def execute_default(self, args, context): pass @abstractmethod diff --git a/tools/tcat_ble_client/tlv/tcat_tlv.py b/tools/tcat_ble_client/tlv/tcat_tlv.py index a276cafb2..7d26864ae 100644 --- a/tools/tcat_ble_client/tlv/tcat_tlv.py +++ b/tools/tcat_ble_client/tlv/tcat_tlv.py @@ -31,8 +31,12 @@ class TcatTLVType(Enum): RESPONSE_W_STATUS = 0x01 RESPONSE_W_PAYLOAD = 0x02 + GET_NETWORK_NAME = 0x08 DISCONNECT = 0x09 PING = 0x0A + GET_DEVICE_ID = 0x0B + GET_EXT_PAN_ID = 0x0C + GET_PROVISIONING_URL = 0x0D ACTIVE_DATASET = 0x20 DECOMMISSION = 0x60 APPLICATION = 0x82 From 697adfd112aef13ec3052136c59dab971fd4c9cb Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 21 Aug 2024 19:25:10 -0700 Subject: [PATCH 121/160] [netdata] centralize service/server TLV logic in `Service::Manager` (#10624) This commit updates the `NetworkData::Service::Manager` class to provide helper methods for adding and removing different service types (DNS/SRP anycast or unicast services, backbone router service). With this change, the definitions of `ServiceData` and `ServerData` formats for different service types are now `private` to the `Service::Manager` class. This centralizes the logic for constructing and parsing the service/server TLVs, making it easier to update and add new fields to these formats in the future. --- src/core/backbone_router/bbr_leader.cpp | 3 +- src/core/backbone_router/bbr_local.cpp | 12 +- src/core/net/srp_client.cpp | 30 +- src/core/net/srp_client.hpp | 7 +- src/core/thread/address_resolver.cpp | 8 +- src/core/thread/network_data_publisher.cpp | 48 +- src/core/thread/network_data_publisher.hpp | 6 +- src/core/thread/network_data_service.cpp | 84 ++- src/core/thread/network_data_service.hpp | 587 ++++++++------------- tests/unit/test_network_data.cpp | 24 +- 10 files changed, 311 insertions(+), 498 deletions(-) diff --git a/src/core/backbone_router/bbr_leader.cpp b/src/core/backbone_router/bbr_leader.cpp index 85cb08be1..00545b5e7 100644 --- a/src/core/backbone_router/bbr_leader.cpp +++ b/src/core/backbone_router/bbr_leader.cpp @@ -75,8 +75,7 @@ Error Leader::GetServiceId(uint8_t &aServiceId) const Error error = kErrorNone; VerifyOrExit(HasPrimary(), error = kErrorNotFound); - error = Get().GetServiceId( - /* aServerStable */ true, aServiceId); + error = Get().GetBackboneRouterServiceId(aServiceId); exit: return error; diff --git a/src/core/backbone_router/bbr_local.cpp b/src/core/backbone_router/bbr_local.cpp index 2b4ab5f02..e787607d0 100644 --- a/src/core/backbone_router/bbr_local.cpp +++ b/src/core/backbone_router/bbr_local.cpp @@ -174,8 +174,7 @@ Error Local::SetConfig(const Config &aConfig) Error Local::AddService(RegisterMode aMode) { - Error error = kErrorInvalidState; - NetworkData::Service::BackboneRouter::ServerData serverData; + Error error = kErrorInvalidState; VerifyOrExit(mState != kStateDisabled && Get().IsAttached()); @@ -189,11 +188,8 @@ Error Local::AddService(RegisterMode aMode) break; } - serverData.SetSequenceNumber(mSequenceNumber); - serverData.SetReregistrationDelay(mReregistrationDelay); - serverData.SetMlrTimeout(mMlrTimeout); - - SuccessOrExit(error = Get().Add(serverData)); + SuccessOrExit(error = Get().AddBackboneRouterService( + mSequenceNumber, mReregistrationDelay, mMlrTimeout)); Get().HandleServerDataUpdated(); mIsServiceAdded = true; @@ -207,7 +203,7 @@ void Local::RemoveService(void) { Error error; - SuccessOrExit(error = Get().Remove()); + SuccessOrExit(error = Get().RemoveBackboneRouterService()); Get().HandleServerDataUpdated(); mIsServiceAdded = false; diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index a8a7473c8..0b4c66e32 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -2244,11 +2244,11 @@ void Client::ApplyAutoStartGuardOnAttach(void) void Client::ProcessAutoStart(void) { - Ip6::SockAddr serverSockAddr; - DnsSrpAnycast::Info anycastInfo; - DnsSrpUnicast::Info unicastInfo; - AutoStart::State oldAutoStartState = mAutoStart.GetState(); - bool shouldRestart = false; + Ip6::SockAddr serverSockAddr; + DnsSrpAnycastInfo anycastInfo; + DnsSrpUnicastInfo unicastInfo; + AutoStart::State oldAutoStartState = mAutoStart.GetState(); + bool shouldRestart = false; // If auto start mode is enabled, we check the Network Data entries // to discover and select the preferred SRP server to register with. @@ -2274,7 +2274,7 @@ void Client::ProcessAutoStart(void) serverSockAddr.Clear(); - if (SelectUnicastEntry(DnsSrpUnicast::kFromServiceData, unicastInfo) == kErrorNone) + if (SelectUnicastEntry(NetworkData::Service::kAddrInServiceData, unicastInfo) == kErrorNone) { mAutoStart.SetState(AutoStart::kSelectedUnicastPreferred); serverSockAddr = unicastInfo.mSockAddr; @@ -2299,7 +2299,7 @@ void Client::ProcessAutoStart(void) mAutoStart.SetState(AutoStart::kSelectedAnycast); } - else if (SelectUnicastEntry(DnsSrpUnicast::kFromServerData, unicastInfo) == kErrorNone) + else if (SelectUnicastEntry(NetworkData::Service::kAddrInServerData, unicastInfo) == kErrorNone) { mAutoStart.SetState(AutoStart::kSelectedUnicast); serverSockAddr = unicastInfo.mSockAddr; @@ -2378,10 +2378,10 @@ void Client::ProcessAutoStart(void) return; } -Error Client::SelectUnicastEntry(DnsSrpUnicast::Type aType, DnsSrpUnicast::Info &aInfo) const +Error Client::SelectUnicastEntry(DnsSrpUnicastType aType, DnsSrpUnicastInfo &aInfo) const { Error error = kErrorNotFound; - DnsSrpUnicast::Info unicastInfo; + DnsSrpUnicastInfo unicastInfo; NetworkData::Service::Manager::Iterator iterator; #if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE Settings::SrpClientInfo savedInfo; @@ -2436,9 +2436,9 @@ void Client::SelectNextServer(bool aDisallowSwitchOnRegisteredHost) // restarts the client with the new server (keeping the retry wait // interval as before). - Ip6::SockAddr serverSockAddr; - bool selectNext = false; - DnsSrpUnicast::Type type = DnsSrpUnicast::kFromServiceData; + Ip6::SockAddr serverSockAddr; + bool selectNext = false; + DnsSrpUnicastType type = NetworkData::Service::kAddrInServiceData; serverSockAddr.Clear(); @@ -2450,11 +2450,11 @@ void Client::SelectNextServer(bool aDisallowSwitchOnRegisteredHost) switch (mAutoStart.GetState()) { case AutoStart::kSelectedUnicastPreferred: - type = DnsSrpUnicast::kFromServiceData; + type = NetworkData::Service::kAddrInServiceData; break; case AutoStart::kSelectedUnicast: - type = DnsSrpUnicast::kFromServerData; + type = NetworkData::Service::kAddrInServerData; break; case AutoStart::kSelectedAnycast: @@ -2477,7 +2477,7 @@ void Client::SelectNextServer(bool aDisallowSwitchOnRegisteredHost) do { - DnsSrpUnicast::Info unicastInfo; + DnsSrpUnicastInfo unicastInfo; NetworkData::Service::Manager::Iterator iterator; while (Get().GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo) == kErrorNone) diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index 33731668a..3892b6c6c 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -75,8 +75,9 @@ class Client : public InstanceLocator, private NonCopyable friend class ot::Notifier; friend class ot::Ip6::Netif; - using DnsSrpUnicast = NetworkData::Service::DnsSrpUnicast; - using DnsSrpAnycast = NetworkData::Service::DnsSrpAnycast; + using DnsSrpUnicastInfo = NetworkData::Service::DnsSrpUnicastInfo; + using DnsSrpUnicastType = NetworkData::Service::DnsSrpUnicastType; + using DnsSrpAnycastInfo = NetworkData::Service::DnsSrpAnycastInfo; public: /** @@ -1110,7 +1111,7 @@ class Client : public InstanceLocator, private NonCopyable #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE void ApplyAutoStartGuardOnAttach(void); void ProcessAutoStart(void); - Error SelectUnicastEntry(DnsSrpUnicast::Type aType, DnsSrpUnicast::Info &aInfo) const; + Error SelectUnicastEntry(DnsSrpUnicastType aType, DnsSrpUnicastInfo &aInfo) const; void HandleGuardTimer(void) {} #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE void SelectNextServer(bool aDisallowSwitchOnRegisteredHost); diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index 78d5890d9..2831ba5b5 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -599,10 +599,10 @@ Error AddressResolver::ResolveUsingNetDataServices(const Ip6::Address &aEid, uin // service entries. Returns `kErrorNone` and updates `aRloc16` // if successful, otherwise returns `kErrorNotFound`. - Error error = kErrorNotFound; - NetworkData::Service::Manager::Iterator iterator; - NetworkData::Service::DnsSrpUnicast::Info unicastInfo; - NetworkData::Service::DnsSrpUnicast::Type type = NetworkData::Service::DnsSrpUnicast::kFromServerData; + Error error = kErrorNotFound; + NetworkData::Service::Manager::Iterator iterator; + NetworkData::Service::DnsSrpUnicastInfo unicastInfo; + NetworkData::Service::DnsSrpUnicastType type = NetworkData::Service::kAddrInServerData; VerifyOrExit(Get().GetDeviceMode().GetNetworkDataType() == NetworkData::kFullSet); diff --git a/src/core/thread/network_data_publisher.cpp b/src/core/thread/network_data_publisher.cpp index ef1d8a5b3..aa419ed61 100644 --- a/src/core/thread/network_data_publisher.cpp +++ b/src/core/thread/network_data_publisher.cpp @@ -572,18 +572,17 @@ void Publisher::DnsSrpServiceEntry::Add(void) switch (GetType()) { case kTypeAnycast: - SuccessOrExit(Get().Add( - Service::DnsSrpAnycast::ServiceData(mInfo.GetSequenceNumber()))); + SuccessOrExit(Get().AddDnsSrpAnycastService(mInfo.GetSequenceNumber())); break; case kTypeUnicast: - SuccessOrExit(Get().Add( - Service::DnsSrpUnicast::ServiceData(mInfo.GetAddress(), mInfo.GetPort()))); + SuccessOrExit( + Get().AddDnsSrpUnicastServiceWithAddrInServiceData(mInfo.GetAddress(), mInfo.GetPort())); break; case kTypeUnicastMeshLocalEid: - SuccessOrExit(Get().Add( - Service::DnsSrpUnicast::ServerData(mInfo.GetAddress(), mInfo.GetPort()))); + SuccessOrExit( + Get().AddDnsSrpUnicastServiceWithAddrInServerData(mInfo.GetAddress(), mInfo.GetPort())); break; } @@ -604,17 +603,16 @@ void Publisher::DnsSrpServiceEntry::Remove(State aNextState) switch (GetType()) { case kTypeAnycast: - SuccessOrExit(Get().Remove( - Service::DnsSrpAnycast::ServiceData(mInfo.GetSequenceNumber()))); + SuccessOrExit(Get().RemoveDnsSrpAnycastService(mInfo.GetSequenceNumber())); break; case kTypeUnicast: - SuccessOrExit(Get().Remove( - Service::DnsSrpUnicast::ServiceData(mInfo.GetAddress(), mInfo.GetPort()))); + SuccessOrExit(Get().RemoveDnsSrpUnicastServiceWithAddrInServiceData(mInfo.GetAddress(), + mInfo.GetPort())); break; case kTypeUnicastMeshLocalEid: - SuccessOrExit(Get().Remove()); + SuccessOrExit(Get().RemoveDnsSrpUnicastServiceWithAddrInServerData()); break; } @@ -658,7 +656,7 @@ void Publisher::DnsSrpServiceEntry::Process(void) break; case kTypeUnicastMeshLocalEid: - CountUnicastEntries(Service::DnsSrpUnicast::kFromServerData, numEntries, numPreferredEntries); + CountUnicastEntries(Service::kAddrInServerData, numEntries, numPreferredEntries); desiredNumEntries = kDesiredNumUnicast; if (HasAnyServiceDataUnicastEntry() || HasAnyAnycastEntry()) @@ -675,7 +673,7 @@ void Publisher::DnsSrpServiceEntry::Process(void) case kTypeUnicast: desiredNumEntries = kDesiredNumUnicast; - CountUnicastEntries(Service::DnsSrpUnicast::kFromServiceData, numEntries, numPreferredEntries); + CountUnicastEntries(Service::kAddrInServiceData, numEntries, numPreferredEntries); break; } @@ -692,8 +690,8 @@ void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, ui // "sequence number" value). We prefer the entries associated with // smaller RLCO16. - Service::Manager::Iterator iterator; - Service::DnsSrpAnycast::Info anycastInfo; + Service::Manager::Iterator iterator; + Service::DnsSrpAnycastInfo anycastInfo; while (Get().GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNone) { @@ -711,20 +709,20 @@ void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, ui bool Publisher::DnsSrpServiceEntry::HasAnyAnycastEntry(void) const { - Service::Manager::Iterator iterator; - Service::DnsSrpAnycast::Info anycastInfo; + Service::Manager::Iterator iterator; + Service::DnsSrpAnycastInfo anycastInfo; return (Get().GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNone); } -void Publisher::DnsSrpServiceEntry::CountUnicastEntries(Service::DnsSrpUnicast::Type aType, - uint8_t &aNumEntries, - uint8_t &aNumPreferredEntries) const +void Publisher::DnsSrpServiceEntry::CountUnicastEntries(Service::DnsSrpUnicastType aType, + uint8_t &aNumEntries, + uint8_t &aNumPreferredEntries) const { // Count the number of DNS/SRP unicast entries in the Network Data. - Service::Manager::Iterator iterator; - Service::DnsSrpUnicast::Info unicastInfo; + Service::Manager::Iterator iterator; + Service::DnsSrpUnicastInfo unicastInfo; while (Get().GetNextDnsSrpUnicastInfo(iterator, aType, unicastInfo) == kErrorNone) { @@ -739,9 +737,9 @@ void Publisher::DnsSrpServiceEntry::CountUnicastEntries(Service::DnsSrpUnicast:: bool Publisher::DnsSrpServiceEntry::HasAnyServiceDataUnicastEntry(void) const { - Service::Manager::Iterator iterator; - Service::DnsSrpUnicast::Info unicastInfo; - Service::DnsSrpUnicast::Type type = Service::DnsSrpUnicast::kFromServiceData; + Service::Manager::Iterator iterator; + Service::DnsSrpUnicastInfo unicastInfo; + Service::DnsSrpUnicastType type = Service::kAddrInServiceData; return (Get().GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo) == kErrorNone); } diff --git a/src/core/thread/network_data_publisher.hpp b/src/core/thread/network_data_publisher.hpp index d1a3becc0..ca97cd405 100644 --- a/src/core/thread/network_data_publisher.hpp +++ b/src/core/thread/network_data_publisher.hpp @@ -439,9 +439,9 @@ class Publisher : public InstanceLocator, private NonCopyable void Process(void); void CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const; bool HasAnyAnycastEntry(void) const; - void CountUnicastEntries(Service::DnsSrpUnicast::Type aType, - uint8_t &aNumEntries, - uint8_t &aNumPreferredEntries) const; + void CountUnicastEntries(Service::DnsSrpUnicastType aType, + uint8_t &aNumEntries, + uint8_t &aNumPreferredEntries) const; bool HasAnyServiceDataUnicastEntry(void) const; Info mInfo; diff --git a/src/core/thread/network_data_service.cpp b/src/core/thread/network_data_service.cpp index 45bc7b327..6382b9f35 100644 --- a/src/core/thread/network_data_service.cpp +++ b/src/core/thread/network_data_service.cpp @@ -43,20 +43,10 @@ namespace ot { namespace NetworkData { namespace Service { -// Definitions of static const class member variables to allow ODR-use -// (One Definition Rule), e.g., to get address of `kServiceData`. - -#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) -const uint8_t BackboneRouter::kServiceData; -#endif -const uint8_t DnsSrpAnycast::kServiceData; -const uint8_t DnsSrpUnicast::kServiceData; - #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE Error Manager::AddService(const void *aServiceData, uint8_t aServiceDataLength, - bool aServerStable, const void *aServerData, uint8_t aServerDataLength) { @@ -66,7 +56,7 @@ Error Manager::AddService(const void *aServiceData, serviceData.Init(aServiceData, aServiceDataLength); serverData.Init(aServerData, aServerDataLength); - return Get().AddService(kThreadEnterpriseNumber, serviceData, aServerStable, serverData); + return Get().AddService(kThreadEnterpriseNumber, serviceData, /* aServerStable */ true, serverData); } Error Manager::RemoveService(const void *aServiceData, uint8_t aServiceDataLength) @@ -80,28 +70,26 @@ Error Manager::RemoveService(const void *aServiceData, uint8_t aServiceDataLengt #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE -Error Manager::GetServiceId(const void *aServiceData, - uint8_t aServiceDataLength, - bool aServerStable, - uint8_t &aServiceId) const +Error Manager::GetServiceId(uint8_t aServiceNumber, uint8_t &aServiceId) const { ServiceData serviceData; - serviceData.Init(aServiceData, aServiceDataLength); + serviceData.InitFrom(aServiceNumber); - return Get().GetServiceId(kThreadEnterpriseNumber, serviceData, aServerStable, aServiceId); + return Get().GetServiceId(kThreadEnterpriseNumber, serviceData, /* aServerStable */ true, aServiceId); } #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) void Manager::GetBackboneRouterPrimary(ot::BackboneRouter::Config &aConfig) const { - const ServerTlv *rvalServerTlv = nullptr; - const BackboneRouter::ServerData *rvalServerData = nullptr; - const ServiceTlv *serviceTlv = nullptr; - ServiceData serviceData; + const ServerTlv *rvalServerTlv = nullptr; + const BbrServerData *rvalServerData = nullptr; + const ServiceTlv *serviceTlv = nullptr; + uint8_t bbrServiceNumber = kBackboneRouterServiceNumber; + ServiceData serviceData; - serviceData.Init(&BackboneRouter::kServiceData, BackboneRouter::kServiceDataMinSize); + serviceData.InitFrom(bbrServiceNumber); aConfig.mServer16 = Mle::kInvalidRloc16; @@ -114,17 +102,17 @@ void Manager::GetBackboneRouterPrimary(ot::BackboneRouter::Config &aConfig) cons while (IterateToNextServer(iterator) == kErrorNone) { - ServerData data; - const BackboneRouter::ServerData *serverData; + ServerData data; + const BbrServerData *serverData; iterator.mServerSubTlv->GetServerData(data); - if (data.GetLength() < sizeof(BackboneRouter::ServerData)) + if (data.GetLength() < sizeof(BbrServerData)) { continue; } - serverData = reinterpret_cast(data.GetBytes()); + serverData = reinterpret_cast(data.GetBytes()); if (rvalServerTlv == nullptr || IsBackboneRouterPreferredTo(*iterator.mServerSubTlv, *serverData, *rvalServerTlv, *rvalServerData)) @@ -146,10 +134,10 @@ void Manager::GetBackboneRouterPrimary(ot::BackboneRouter::Config &aConfig) cons return; } -bool Manager::IsBackboneRouterPreferredTo(const ServerTlv &aServerTlv, - const BackboneRouter::ServerData &aServerData, - const ServerTlv &aOtherServerTlv, - const BackboneRouter::ServerData &aOtherServerData) const +bool Manager::IsBackboneRouterPreferredTo(const ServerTlv &aServerTlv, + const BbrServerData &aServerData, + const ServerTlv &aOtherServerTlv, + const BbrServerData &aOtherServerData) const { bool isPreferred; uint16_t leaderRloc16 = Get().GetLeaderRloc16(); @@ -166,9 +154,10 @@ bool Manager::IsBackboneRouterPreferredTo(const ServerTlv &aSer #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) -Error Manager::GetNextDnsSrpAnycastInfo(Iterator &aIterator, DnsSrpAnycast::Info &aInfo) const +Error Manager::GetNextDnsSrpAnycastInfo(Iterator &aIterator, DnsSrpAnycastInfo &aInfo) const { - Error error = kErrorNone; + Error error = kErrorNone; + uint8_t serviceNumber = kDnsSrpAnycastServiceNumber; do { @@ -180,10 +169,10 @@ Error Manager::GetNextDnsSrpAnycastInfo(Iterator &aIterator, DnsSrpAnycast::Info { aIterator.mServiceTlv->GetServiceData(serviceData); - if (serviceData.GetLength() >= sizeof(DnsSrpAnycast::ServiceData)) + if (serviceData.GetLength() >= sizeof(DnsSrpAnycastServiceData)) { - const DnsSrpAnycast::ServiceData *dnsServiceData = - reinterpret_cast(serviceData.GetBytes()); + const DnsSrpAnycastServiceData *dnsServiceData = + reinterpret_cast(serviceData.GetBytes()); Get().GetServiceAloc(aIterator.mServiceTlv->GetServiceId(), aInfo.mAnycastAddress); aInfo.mSequenceNumber = dnsServiceData->GetSequenceNumber(); @@ -194,7 +183,7 @@ Error Manager::GetNextDnsSrpAnycastInfo(Iterator &aIterator, DnsSrpAnycast::Info // Find the next matching Service TLV. - serviceData.InitFrom(DnsSrpAnycast::kServiceData); + serviceData.InitFrom(serviceNumber); aIterator.mServiceTlv = Get().FindNextThreadService(aIterator.mServiceTlv, serviceData, NetworkData::kServicePrefixMatch); aIterator.mServerSubTlv = nullptr; @@ -210,12 +199,12 @@ Error Manager::GetNextDnsSrpAnycastInfo(Iterator &aIterator, DnsSrpAnycast::Info return error; } -Error Manager::FindPreferredDnsSrpAnycastInfo(DnsSrpAnycast::Info &aInfo) const +Error Manager::FindPreferredDnsSrpAnycastInfo(DnsSrpAnycastInfo &aInfo) const { - Error error = kErrorNotFound; - Iterator iterator; - DnsSrpAnycast::Info info; - DnsSrpAnycast::Info maxNumericalSeqNumInfo; + Error error = kErrorNotFound; + Iterator iterator; + DnsSrpAnycastInfo info; + DnsSrpAnycastInfo maxNumericalSeqNumInfo; // Determine the entry with largest seq number in two ways: // `aInfo` will track the largest using serial number arithmetic @@ -276,11 +265,10 @@ Error Manager::FindPreferredDnsSrpAnycastInfo(DnsSrpAnycast::Info &aInfo) const return error; } -Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, - DnsSrpUnicast::Type aType, - DnsSrpUnicast::Info &aInfo) const +Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicastType aType, DnsSrpUnicastInfo &aInfo) const { - Error error = kErrorNone; + Error error = kErrorNone; + uint8_t serviceNumber = kDnsSrpUnicastServiceNumber; do { @@ -292,7 +280,7 @@ Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, { ServerData data; - if (aType == DnsSrpUnicast::kFromServiceData) + if (aType == kAddrInServiceData) { const DnsSrpUnicast::ServiceData *dnsServiceData; @@ -312,7 +300,7 @@ Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, ExitNow(); } - // `aType` is `kFromServerData`. + // `aType` is `kAddrInServerData`. // Server sub-TLV can contain address and port info // (then we parse and return the info), or it can be @@ -346,7 +334,7 @@ Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, // Find the next matching Service TLV. - serviceData.InitFrom(DnsSrpUnicast::kServiceData); + serviceData.InitFrom(serviceNumber); aIterator.mServiceTlv = Get().FindNextThreadService(aIterator.mServiceTlv, serviceData, NetworkData::kServicePrefixMatch); aIterator.mServerSubTlv = nullptr; diff --git a/src/core/thread/network_data_service.hpp b/src/core/thread/network_data_service.hpp index 612eb3885..9d6c3b80a 100644 --- a/src/core/thread/network_data_service.hpp +++ b/src/core/thread/network_data_service.hpp @@ -52,316 +52,35 @@ namespace Service { const uint32_t kThreadEnterpriseNumber = ServiceTlv::kThreadEnterpriseNumber; ///< Thread enterprise number. -#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) - /** - * Implements Thread Network Data "Backbone Router Service" server data generation and parsing. + * Represents information about an DNS/SRP server parsed from related Network Data service entries. * */ -class BackboneRouter +struct DnsSrpAnycastInfo { -public: - /** - * This constant variable represents the Backbone Router service data. - * - * The service data contains only the service number (THREAD_SERVICE_DATA_BBR) as a single byte. - * - */ - static const uint8_t kServiceData = 0x01; - static constexpr uint8_t kServiceDataMinSize = 1; - - /** - * Implements the generation and parsing of "Backbone Router Service" server data. - * - */ - OT_TOOL_PACKED_BEGIN - class ServerData - { - public: - /** - * Returns the length (in bytes) of server data. - * - * @returns The server data length in bytes. - * - */ - uint8_t GetLength(void) const { return sizeof(ServerData); } - - /** - * Returns the sequence number of Backbone Router. - * - * @returns The sequence number of the Backbone Router. - * - */ - uint8_t GetSequenceNumber(void) const { return mSequenceNumber; } - - /** - * Sets the sequence number of Backbone Router. - * - * @param[in] aSequenceNumber The sequence number of Backbone Router. - * - */ - void SetSequenceNumber(uint8_t aSequenceNumber) { mSequenceNumber = aSequenceNumber; } - - /** - * Returns the Registration Delay (in seconds) of Backbone Router. - * - * @returns The BBR Registration Delay (in seconds) of Backbone Router. - * - */ - uint16_t GetReregistrationDelay(void) const { return BigEndian::HostSwap16(mReregistrationDelay); } - - /** - * Sets the Registration Delay (in seconds) of Backbone Router. - * - * @param[in] aReregistrationDelay The Registration Delay (in seconds) of Backbone Router. - * - */ - void SetReregistrationDelay(uint16_t aReregistrationDelay) - { - mReregistrationDelay = BigEndian::HostSwap16(aReregistrationDelay); - } - - /** - * Returns the multicast listener report timeout (in seconds) of Backbone Router. - * - * @returns The multicast listener report timeout (in seconds) of Backbone Router. - * - */ - uint32_t GetMlrTimeout(void) const { return BigEndian::HostSwap32(mMlrTimeout); } - - /** - * Sets multicast listener report timeout (in seconds) of Backbone Router. - * - * @param[in] aMlrTimeout The multicast listener report timeout (in seconds) of Backbone Router. - * - */ - void SetMlrTimeout(uint32_t aMlrTimeout) { mMlrTimeout = BigEndian::HostSwap32(aMlrTimeout); } - - private: - uint8_t mSequenceNumber; - uint16_t mReregistrationDelay; - uint32_t mMlrTimeout; - } OT_TOOL_PACKED_END; - - BackboneRouter(void) = delete; + Ip6::Address mAnycastAddress; ///< The anycast address associated with the DNS/SRP servers. + uint8_t mSequenceNumber; ///< Sequence number used to notify SRP client if they need to re-register. + uint16_t mRloc16; ///< The RLOC16 of the entry. }; -#endif // #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) - /** - * Implements Thread Network Data "DNS/SRP Service Anycast Address" generation and parsing. + * Represents the `DnsSrpUnicast` entry type. * */ -class DnsSrpAnycast +enum DnsSrpUnicastType : uint8_t { -public: - static constexpr uint8_t kServiceNumber = 0x5c; ///< The service number of a `DnsSrpAnycast` entry. - - /** - * This constant variable represents the short version of service data. - * - * The short version of service data contains only service number as a single byte. - * - */ - static const uint8_t kServiceData = kServiceNumber; - - /** - * Represents information about an DNS/SRP server parsed from related Network Data service entries. - * - */ - struct Info - { - Ip6::Address mAnycastAddress; ///< The anycast address associated with the DNS/SRP servers. - uint8_t mSequenceNumber; ///< Sequence number used to notify SRP client if they need to re-register. - uint16_t mRloc16; ///< The RLOC16 of the entry. - }; - - /** - * Represents the "DNS/SRP Service (Anycast)" service data. - * - */ - OT_TOOL_PACKED_BEGIN - class ServiceData - { - public: - /** - * Initializes the `ServiceData` object. - * - * @param[in] aSequenceNumber The sequence number of "DNS/SRP server" service. - * - */ - explicit ServiceData(uint8_t aSequenceNumber) - : mServiceNumber(kServiceNumber) - , mSequenceNumber(aSequenceNumber) - { - OT_UNUSED_VARIABLE(mServiceNumber); - } - - /** - * Returns the length (in bytes) of service data. - * - * @returns The data length in bytes. - * - */ - uint8_t GetLength(void) const { return sizeof(ServiceData); } - - /** - * Returns the sequence number. - * - * @returns The sequence number. - * - */ - uint8_t GetSequenceNumber(void) const { return mSequenceNumber; } - - private: - uint8_t mServiceNumber; - uint8_t mSequenceNumber; - } OT_TOOL_PACKED_END; - - DnsSrpAnycast(void) = delete; + kAddrInServiceData, ///< Socket address is from service data. + kAddrInServerData, ///< Socket address is from server data. }; /** - * Implements Thread Network Data DNS/SRP Service (Unicast Address) generation and parsing. + * Represents information about an DNS/SRP server parsed from related Network Data service entries. * */ -class DnsSrpUnicast +struct DnsSrpUnicastInfo { -public: - static constexpr uint8_t kServiceNumber = 0x5d; ///< The service number of `DnsSrpUnicast` entry. - - /** - * This constant variable represents the short version of service data. - * - * The short version of service data contains only service number as a single byte. - * - */ - static const uint8_t kServiceData = kServiceNumber; - - /** - * Represents the `DnsSrpUnicast` entry type. - * - */ - enum Type : uint8_t - { - kFromServiceData, ///< Socket address is from service data. - kFromServerData, ///< Socket address is from server data. - }; - - /** - * Represents information about an DNS/SRP server parsed from related Network Data service entries. - * - */ - struct Info - { - Ip6::SockAddr mSockAddr; ///< The socket address (IPv6 address and port) of the DNS/SRP server. - uint16_t mRloc16; ///< The BR RLOC16 adding the entry. - }; - - /** - * Represents long version of "DNS/SRP Service (Unicast)" service data. - * - */ - OT_TOOL_PACKED_BEGIN - class ServiceData - { - public: - /** - * Initializes the `ServiceData` object. - * - * @param[in] aAddress The IPv6 address of DNS/SRP server. - * @param[in] aPort The port number of DNS/SRP server. - * - */ - explicit ServiceData(const Ip6::Address &aAddress, uint16_t aPort) - : mServiceNumber(kServiceNumber) - , mAddress(aAddress) - , mPort(BigEndian::HostSwap16(aPort)) - { - OT_UNUSED_VARIABLE(mServiceNumber); - } - - /** - * Returns the length (in bytes) of service data. - * - * @returns The data length in bytes. - * - */ - uint8_t GetLength(void) const { return sizeof(ServiceData); } - - /** - * Returns the IPv6 address. - * - * @returns The IPv6 address - * - */ - const Ip6::Address &GetAddress(void) const { return mAddress; } - - /** - * Returns the port number. - * - * @returns The port number. - * - */ - uint16_t GetPort(void) const { return BigEndian::HostSwap16(mPort); } - - private: - uint8_t mServiceNumber; - Ip6::Address mAddress; - uint16_t mPort; - } OT_TOOL_PACKED_END; - - /** - * Represents long version of "DNS/SRP Service (Unicast)" server data. - * - */ - OT_TOOL_PACKED_BEGIN - class ServerData - { - public: - /** - * Initializes the `ServerData` object. - * - * @param[in] aAddress The IPv6 address of DNS/SRP server. - * @param[in] aPort The port number of DNS/SRP server. - * - */ - ServerData(const Ip6::Address &aAddress, uint16_t aPort) - : mAddress(aAddress) - , mPort(BigEndian::HostSwap16(aPort)) - { - } - - /** - * Returns the length (in bytes) of server data. - * - * @returns The data length in bytes. - * - */ - uint8_t GetLength(void) const { return sizeof(ServerData); } - - /** - * Returns the IPv6 address. - * - * @returns The IPv6 address - * - */ - const Ip6::Address &GetAddress(void) const { return mAddress; } - - /** - * Returns the port number. - * - * @returns The port number. - * - */ - uint16_t GetPort(void) const { return BigEndian::HostSwap16(mPort); } - - private: - Ip6::Address mAddress; - uint16_t mPort; - } OT_TOOL_PACKED_END; - - DnsSrpUnicast(void) = delete; + Ip6::SockAddr mSockAddr; ///< The socket address (IPv6 address and port) of the DNS/SRP server. + uint16_t mRloc16; ///< The BR RLOC16 adding the entry. }; /** @@ -418,122 +137,117 @@ class Manager : public InstanceLocator, private NonCopyable #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE /** - * Adds a Thread Service entry to the local Thread Network Data. - * - * This version of `Add()` is intended for use with a `ServiceType` that has a constant service data - * format with a non-empty and potentially non-const server data format (provided as input parameter). - * - * The template type `ServiceType` has the following requirements: - * - It MUST have a constant variable `ServiceType::kServiceData` specifying the service data. - * - It MUST define nested type `ServiceType::ServerData` representing the server data (and its format). - * - The `ServiceType::ServerData` MUST provide `GetLength()` method returning the length of server data. - * - * @tparam ServiceType The service type to be added. + * Adds a DNS/SRP Anycast Service entry to the local Thread Network Data. * - * @param[in] aServerData The server data. - * @param[in] aServerStable The Stable flag value for Server TLV. + * @param[in] aSequenceNumber The anycast sequence number. * * @retval kErrorNone Successfully added the Service entry. * @retval kErrorNoBufs Insufficient space to add the Service entry. * */ - template - Error Add(const typename ServiceType::ServerData &aServerData, bool aServerStable = true) + Error AddDnsSrpAnycastService(uint8_t aSequenceNumber) { - return AddService(&ServiceType::kServiceData, sizeof(ServiceType::kServiceData), aServerStable, &aServerData, - aServerData.GetLength()); + return AddService(DnsSrpAnycastServiceData(aSequenceNumber)); } /** - * Adds a Thread Service entry to the local Thread Network Data. + * Removes a DNS/SRP Anycast Service entry from local Thread Network Data. * - * This version of `Add()` is intended for use with a `ServiceType` that has a non-const service data - * format (provided as input parameter) with an empty server data. + * @param[in] aSequenceNumber The anycast sequence number. * - * The template type `ServiceType` has the following requirements: - * - It MUST define nested type `ServiceType::ServiceData` representing the service data (and its format). - * - The `ServiceType::ServiceData` MUST provide `GetLength()` method returning the length of service data. + * @retval kErrorNone Successfully removed the Service entry. + * @retval kErrorNotFound Could not find the Service entry. * - * @tparam ServiceType The service type to be added. + */ + Error RemoveDnsSrpAnycastService(uint8_t aSequenceNumber) + { + return RemoveService(DnsSrpAnycastServiceData(aSequenceNumber)); + } + + /** + * Adds a DNS/SRP Unicast Service entry with address in Service Data to the local Thread Network Data. * - * @param[in] aServiceData The service data. - * @param[in] aServerStable The Stable flag value for Server TLV. + * @param[in] aAddress The unicast address. + * @param[in] aPort The port number. * * @retval kErrorNone Successfully added the Service entry. * @retval kErrorNoBufs Insufficient space to add the Service entry. * */ - template - Error Add(const typename ServiceType::ServiceData &aServiceData, bool aServerStable = true) + Error AddDnsSrpUnicastServiceWithAddrInServiceData(const Ip6::Address &aAddress, uint16_t aPort) { - return AddService(&aServiceData, aServiceData.GetLength(), aServerStable, nullptr, 0); + return AddService(DnsSrpUnicast::ServiceData(aAddress, aPort)); } /** - * Removes a Thread Service entry from the local Thread Network Data. - * - * This version of `Remove()` is intended for use with a `ServiceType` that has a constant service data - * format. - * - * The template type `ServiceType` has the following requirements: - * - It MUST have a constant variable `ServiceType::kServiceData` specifying the service data. + * Removes a DNS/SRP Unicast Service entry with address in Service Data from the local Thread Network Data. * - * @tparam ServiceType The service type to be removed. + * @param[in] aAddress The unicast address. + * @param[in] aPort The port number. * * @retval kErrorNone Successfully removed the Service entry. * @retval kErrorNotFound Could not find the Service entry. * */ - template Error Remove(void) + Error RemoveDnsSrpUnicastServiceWithAddrInServiceData(const Ip6::Address &aAddress, uint16_t aPort) { - return RemoveService(&ServiceType::kServiceData, sizeof(ServiceType::kServiceData)); + return RemoveService(DnsSrpUnicast::ServiceData(aAddress, aPort)); } /** - * Removes a Thread Service entry from the local Thread Network Data. + * Adds a DNS/SRP Unicast Service entry with address in Server Data to the local Thread Network Data. * - * This version of `Remove()` is intended for use with a `ServiceType` that has a non-const service - * data format (provided as input parameter). + * @param[in] aAddress The unicast address. + * @param[in] aPort The port number. * - * The template type `ServiceType` has the following requirements: - * - It MUST define nested type `ServiceType::ServiceData` representing the service data (and its format). - * - The `ServiceType::ServiceData` MUST provide `GetLength()` method returning the length of service data. - * - * @tparam ServiceType The service type to be removed. - * - * @param[in] aServiceData The service data. - * - * @retval kErrorNone Successfully removed the Service entry. - * @retval kErrorNotFound Could not find the Service entry. + * @retval kErrorNone Successfully added the Service entry. + * @retval kErrorNoBufs Insufficient space to add the Service entry. * */ - template Error Remove(const typename ServiceType::ServiceData &aServiceData) + Error AddDnsSrpUnicastServiceWithAddrInServerData(const Ip6::Address &aAddress, uint16_t aPort) { - return RemoveService(&aServiceData, aServiceData.GetLength()); + return AddService(kDnsSrpUnicastServiceNumber, DnsSrpUnicast::ServerData(aAddress, aPort)); } -#endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE - /** - * Gets the Service ID for the specified service from Thread Network Data. + * Removes a DNS/SRP Unicast Service entry with address in Server Data from the local Thread Network Data. * - * The template type `ServiceType` has the following requirements: - * - It MUST have a constant `ServiceType::kServiceNumber` specifying the service number. + * @retval kErrorNone Successfully removed the Service entry. + * @retval kErrorNotFound Could not find the Service entry. * - * @tparam ServiceType The service type to be added. + */ + Error RemoveDnsSrpUnicastServiceWithAddrInServerData(void) { return RemoveService(kDnsSrpUnicastServiceNumber); } + +#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) + /** + * Adds a Backbone Router Service entry to the local Thread Network Data. * - * @param[in] aServerStable The Stable flag value for Server TLV - * @param[out] aServiceId A reference where to put the Service ID. + * @param[in] aSequenceNumber The sequence number of Backbone Router. + * @param[in] aReregistrationDelay The Registration Delay (in seconds) of Backbone Router. + * @param[in] aMlrTimeout The multicast listener report timeout (in seconds) of Backbone Router. * - * @retval kErrorNone Successfully got the Service ID. - * @retval kErrorNotFound The specified service was not found. + * @retval kErrorNone Successfully added the Service entry. + * @retval kErrorNoBufs Insufficient space to add the Service entry. * */ - template Error GetServiceId(bool aServerStable, uint8_t &aServiceId) const + Error AddBackboneRouterService(uint8_t aSequenceNumber, uint16_t aReregistrationDelay, uint32_t aMlrTimeout) { - return GetServiceId(&ServiceType::kServiceData, sizeof(ServiceType::kServiceData), aServerStable, aServiceId); + return AddService(kBackboneRouterServiceNumber, + BbrServerData(aSequenceNumber, aReregistrationDelay, aMlrTimeout)); } + /** + * Removes the Backbone Router Service entry from the local Thread Network Data. + * + * @retval kErrorNone Successfully removed the Service entry. + * @retval kErrorNotFound Could not find the Service entry. + * + */ + Error RemoveBackboneRouterService(void) { return RemoveService(kBackboneRouterServiceNumber); } +#endif + +#endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE + #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) /** * Gets the Primary Backbone Router (PBBR) in the Thread Network Data. @@ -542,6 +256,20 @@ class Manager : public InstanceLocator, private NonCopyable * */ void GetBackboneRouterPrimary(ot::BackboneRouter::Config &aConfig) const; + + /** + * Gets the Service ID of Backbone Router service from Thread Network Data. + * + * @param[out] aServiceId A reference where to put the Service ID. + * + * @retval kErrorNone Successfully got the Service ID. + * @retval kErrorNotFound The specified service was not found. + * + */ + Error GetBackboneRouterServiceId(uint8_t &aServiceId) const + { + return GetServiceId(kBackboneRouterServiceNumber, aServiceId); + } #endif /** @@ -551,13 +279,13 @@ class Manager : public InstanceLocator, private NonCopyable * method). * * @param[in,out] aIterator A reference to an iterator. - * @param[out] aInfo A reference to `DnsSrpAnycast::Info` to return the info. + * @param[out] aInfo A reference to `DnsSrpAnycastInfo` to return the info. * * @retval kErrorNone Successfully got the next info. @p aInfo and @p aIterator are updated. * @retval kErrorNotFound No more matching entries in the Network Data. * */ - Error GetNextDnsSrpAnycastInfo(Iterator &aIterator, DnsSrpAnycast::Info &aInfo) const; + Error GetNextDnsSrpAnycastInfo(Iterator &aIterator, DnsSrpAnycastInfo &aInfo) const; /** * Finds the preferred DNS/SRP info among all the Thread Network Data "DNS/SRP Service Anycast Address" @@ -566,13 +294,13 @@ class Manager : public InstanceLocator, private NonCopyable * The preferred entry is determined based on the sequence number value where a larger value (in the sense * specified by Serial Number Arithmetic logic in RFC-1982) is considered more recent and therefore preferred. * - * @param[out] aInfo A reference to `DnsSrpAnycast::Info` to return the info. + * @param[out] aInfo A reference to `DnsSrpAnycastInfo` to return the info. * * @retval kErrorNone Successfully found the preferred info. @p aInfo is updated. * @retval kErrorNotFound No "DNS/SRP Service Anycast" entry in Network Data. * */ - Error FindPreferredDnsSrpAnycastInfo(DnsSrpAnycast::Info &aInfo) const; + Error FindPreferredDnsSrpAnycastInfo(DnsSrpAnycastInfo &aInfo) const; /** * Gets the next DNS/SRP info from the Thread Network Data "DNS/SRP Service Unicast Address" entries. @@ -581,36 +309,139 @@ class Manager : public InstanceLocator, private NonCopyable * method). * * @param[in,out] aIterator A reference to an iterator. - * @param[in] aType The entry type, `kFromServiceData` (preferred) or `kFromServerData` (non-preferred). - * @param[out] aInfo A reference to `DnsSrpUnicast::Info` to return the info. + * @param[in] aType The entry type, `kAddrInServiceData` or `kAddrInServerData` + * @param[out] aInfo A reference to `DnsSrpUnicastInfo` to return the info. * * @retval kErrorNone Successfully got the next info. @p aInfo and @p aIterator are updated. * @retval kErrorNotFound No more matching entries in the Network Data. * */ - Error GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Type aType, DnsSrpUnicast::Info &aInfo) const; + Error GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicastType aType, DnsSrpUnicastInfo &aInfo) const; private: + static constexpr uint8_t kBackboneRouterServiceNumber = 0x01; + static constexpr uint8_t kDnsSrpAnycastServiceNumber = 0x5c; + static constexpr uint8_t kDnsSrpUnicastServiceNumber = 0x5d; + + OT_TOOL_PACKED_BEGIN + class DnsSrpAnycastServiceData + { + public: + explicit DnsSrpAnycastServiceData(uint8_t aSequenceNumber) + : mServiceNumber(kDnsSrpAnycastServiceNumber) + , mSequenceNumber(aSequenceNumber) + { + OT_UNUSED_VARIABLE(mServiceNumber); + } + + uint8_t GetSequenceNumber(void) const { return mSequenceNumber; } + + private: + uint8_t mServiceNumber; + uint8_t mSequenceNumber; + } OT_TOOL_PACKED_END; + + class DnsSrpUnicast + { + public: + OT_TOOL_PACKED_BEGIN + struct ServiceData + { + public: + explicit ServiceData(const Ip6::Address &aAddress, uint16_t aPort) + : mServiceNumber(kDnsSrpUnicastServiceNumber) + , mAddress(aAddress) + , mPort(BigEndian::HostSwap16(aPort)) + { + OT_UNUSED_VARIABLE(mServiceNumber); + } + + const Ip6::Address &GetAddress(void) const { return mAddress; } + uint16_t GetPort(void) const { return BigEndian::HostSwap16(mPort); } + + private: + uint8_t mServiceNumber; + Ip6::Address mAddress; + uint16_t mPort; + } OT_TOOL_PACKED_END; + + OT_TOOL_PACKED_BEGIN + class ServerData + { + public: + ServerData(const Ip6::Address &aAddress, uint16_t aPort) + : mAddress(aAddress) + , mPort(BigEndian::HostSwap16(aPort)) + { + } + + const Ip6::Address &GetAddress(void) const { return mAddress; } + uint16_t GetPort(void) const { return BigEndian::HostSwap16(mPort); } + + private: + Ip6::Address mAddress; + uint16_t mPort; + } OT_TOOL_PACKED_END; + + DnsSrpUnicast(void) = delete; + }; + +#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) + OT_TOOL_PACKED_BEGIN + class BbrServerData + { + public: + BbrServerData(uint8_t aSequenceNumber, uint16_t aReregDelay, uint32_t aMlrTimeout) + : mSequenceNumber(aSequenceNumber) + , mReregDelay(BigEndian::HostSwap16(aReregDelay)) + , mMlrTimeout(BigEndian::HostSwap32(aMlrTimeout)) + { + } + + uint8_t GetSequenceNumber(void) const { return mSequenceNumber; } + uint16_t GetReregistrationDelay(void) const { return BigEndian::HostSwap16(mReregDelay); } + uint32_t GetMlrTimeout(void) const { return BigEndian::HostSwap32(mMlrTimeout); } + + private: + uint8_t mSequenceNumber; + uint16_t mReregDelay; + uint32_t mMlrTimeout; + } OT_TOOL_PACKED_END; +#endif + #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE + template Error AddService(const ServiceDataType &aServiceData) + { + return AddService(&aServiceData, sizeof(ServiceDataType), nullptr, 0); + } + + template Error AddService(uint8_t aServiceNumber, const ServerDataType &aServerData) + { + return AddService(&aServiceNumber, sizeof(uint8_t), &aServerData, sizeof(ServerDataType)); + } + Error AddService(const void *aServiceData, uint8_t aServiceDataLength, - bool aServerStable, - const void *aServerData, - uint8_t aServerDataLength); + const void *aServerData = nullptr, + uint8_t aServerDataLength = 0); + + template Error RemoveService(const ServiceDataType &aServiceData) + { + return RemoveService(&aServiceData, sizeof(ServiceDataType)); + } + + Error RemoveService(uint8_t aServiceNumber) { return RemoveService(&aServiceNumber, sizeof(uint8_t)); } Error RemoveService(const void *aServiceData, uint8_t aServiceDataLength); #endif - Error GetServiceId(const void *aServiceData, - uint8_t aServiceDataLength, - bool aServerStable, - uint8_t &aServiceId) const; + Error GetServiceId(uint8_t aServiceNumber, uint8_t &aServiceId) const; Error IterateToNextServer(Iterator &aIterator) const; #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) - bool IsBackboneRouterPreferredTo(const ServerTlv &aServerTlv, - const BackboneRouter::ServerData &aServerData, - const ServerTlv &aOtherServerTlv, - const BackboneRouter::ServerData &aOtherServerData) const; + bool IsBackboneRouterPreferredTo(const ServerTlv &aServerTlv, + const BbrServerData &aServerData, + const ServerTlv &aOtherServerTlv, + const BbrServerData &aOtherServerData) const; #endif }; diff --git a/tests/unit/test_network_data.cpp b/tests/unit/test_network_data.cpp index ea38087de..ae7b0e785 100644 --- a/tests/unit/test_network_data.cpp +++ b/tests/unit/test_network_data.cpp @@ -637,7 +637,7 @@ void TestNetworkDataDsnSrpServices(void) uint8_t mSequenceNumber; uint16_t mRloc16; - bool Matches(Service::DnsSrpAnycast::Info aInfo) const + bool Matches(Service::DnsSrpAnycastInfo aInfo) const { VerifyOrQuit(aInfo.mAnycastAddress.GetIid().IsAnycastServiceLocator()); @@ -652,7 +652,7 @@ void TestNetworkDataDsnSrpServices(void) uint16_t mPort; uint16_t mRloc16; - bool Matches(const Service::DnsSrpUnicast::Info &aInfo) const + bool Matches(const Service::DnsSrpUnicastInfo &aInfo) const { Ip6::SockAddr sockAddr; @@ -699,12 +699,12 @@ void TestNetworkDataDsnSrpServices(void) const uint8_t kPreferredAnycastEntryIndex = 2; - Service::Manager &manager = instance->Get(); - Service::Manager::Iterator iterator; - Service::DnsSrpAnycast::Info anycastInfo; - Service::DnsSrpUnicast::Info unicastInfo; - Service::DnsSrpUnicast::Type type; - Rlocs rlocs; + Service::Manager &manager = instance->Get(); + Service::Manager::Iterator iterator; + Service::DnsSrpAnycastInfo anycastInfo; + Service::DnsSrpUnicastInfo unicastInfo; + Service::DnsSrpUnicastType type; + Rlocs rlocs; reinterpret_cast(instance->Get()).Populate(kNetworkData, sizeof(kNetworkData)); @@ -756,7 +756,7 @@ void TestNetworkDataDsnSrpServices(void) printf("\nDNS/SRP Unicast Service entries (server data)\n"); iterator.Clear(); - type = Service::DnsSrpUnicast::kFromServerData; + type = Service::kAddrInServerData; for (const UnicastEntry &entry : kUnicastEntriesFromServerData) { @@ -774,7 +774,7 @@ void TestNetworkDataDsnSrpServices(void) printf("\nDNS/SRP Unicast Service entries (service data)\n"); iterator.Clear(); - type = Service::DnsSrpUnicast::kFromServiceData; + type = Service::kAddrInServiceData; for (const UnicastEntry &entry : kUnicastEntriesFromServiceData) { @@ -947,8 +947,8 @@ void TestNetworkDataDsnSrpAnycastSeqNumSelection(void) for (const TestInfo &test : kTests) { - Service::Manager::Iterator iterator; - Service::DnsSrpAnycast::Info anycastInfo; + Service::Manager::Iterator iterator; + Service::DnsSrpAnycastInfo anycastInfo; reinterpret_cast(instance->Get()).Populate(test.mNetworkData, test.mNetworkDataLength); From e90792da0b9fafdc05ac663226162957c1510813 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 21 Aug 2024 19:30:56 -0700 Subject: [PATCH 122/160] [routing-manager] advertise deprecating RIO prefixes with low preference (#10625) This commit updates `RoutingManager::RioAdvertiser` to advertise deprecating prefixes with `NetworkData::kkRoutePreferenceLow`. `test_routing_manager` is updated to validate this new behavior. --- src/core/border_router/routing_manager.cpp | 23 +++++++++++++-------- src/core/border_router/routing_manager.hpp | 5 ++++- tests/unit/test_routing_manager.cpp | 24 +++++++++++++++------- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index cd7c4c19f..f110ed69b 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -3086,7 +3086,9 @@ Error RoutingManager::RioAdvertiser::InvalidatPrevRios(RouterAdvert::TxMessage & for (const RioPrefix &prefix : mPrefixes) { - SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage)); + RoutePreference preference = prefix.mIsDeprecating ? NetworkData::kRoutePreferenceLow : mPreference; + + SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, preference, aRaMessage)); } #if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE @@ -3188,7 +3190,8 @@ Error RoutingManager::RioAdvertiser::AppendRios(RouterAdvert::TxMessage &aRaMess { if (nextTime.GetNow() >= prefix.mExpirationTime) { - SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage)); + SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, + NetworkData::kRoutePreferenceLow, aRaMessage)); continue; } } @@ -3201,7 +3204,8 @@ Error RoutingManager::RioAdvertiser::AppendRios(RouterAdvert::TxMessage &aRaMess if (mPrefixes.PushBack(prefix) != kErrorNone) { LogWarn("Too many deprecating on-mesh prefixes, removing %s", prefix.mPrefix.ToString().AsCString()); - SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage)); + SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, NetworkData::kRoutePreferenceLow, + aRaMessage)); } nextTime.UpdateIfEarlier(prefix.mExpirationTime); @@ -3211,14 +3215,16 @@ Error RoutingManager::RioAdvertiser::AppendRios(RouterAdvert::TxMessage &aRaMess for (const RioPrefix &prefix : mPrefixes) { - uint32_t lifetime = kDefaultOmrPrefixLifetime; + uint32_t lifetime = kDefaultOmrPrefixLifetime; + RoutePreference preference = mPreference; if (prefix.mIsDeprecating) { - lifetime = TimeMilli::MsecToSec(prefix.mExpirationTime - nextTime.GetNow()); + lifetime = TimeMilli::MsecToSec(prefix.mExpirationTime - nextTime.GetNow()); + preference = NetworkData::kRoutePreferenceLow; } - SuccessOrExit(error = AppendRio(prefix.mPrefix, lifetime, aRaMessage)); + SuccessOrExit(error = AppendRio(prefix.mPrefix, lifetime, preference, aRaMessage)); } mTimer.FireAtIfEarlier(nextTime); @@ -3229,12 +3235,13 @@ Error RoutingManager::RioAdvertiser::AppendRios(RouterAdvert::TxMessage &aRaMess Error RoutingManager::RioAdvertiser::AppendRio(const Ip6::Prefix &aPrefix, uint32_t aRouteLifetime, + RoutePreference aPreference, RouterAdvert::TxMessage &aRaMessage) { Error error; - SuccessOrExit(error = aRaMessage.AppendRouteInfoOption(aPrefix, aRouteLifetime, mPreference)); - LogRouteInfoOption(aPrefix, aRouteLifetime, mPreference); + SuccessOrExit(error = aRaMessage.AppendRouteInfoOption(aPrefix, aRouteLifetime, aPreference)); + LogRouteInfoOption(aPrefix, aRouteLifetime, aPreference); exit: return error; diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 6aa622abb..8dfef663b 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -1302,7 +1302,10 @@ class RoutingManager : public InstanceLocator void SetPreferenceBasedOnRole(void); void UpdatePreference(RoutePreference aPreference); - Error AppendRio(const Ip6::Prefix &aPrefix, uint32_t aRouteLifetime, RouterAdvert::TxMessage &aRaMessage); + Error AppendRio(const Ip6::Prefix &aPrefix, + uint32_t aRouteLifetime, + RoutePreference aPreference, + RouterAdvert::TxMessage &aRaMessage); using RioTimer = TimerMilliIn; diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp index dd983cd0c..1cf7a47c3 100644 --- a/tests/unit/test_routing_manager.cpp +++ b/tests/unit/test_routing_manager.cpp @@ -183,6 +183,8 @@ Array sDeprecatingPrefixes; static constexpr uint16_t kMaxRioPrefixes = 10; +using NetworkData::RoutePreference; + struct RioPrefix { RioPrefix(void) = default; @@ -191,12 +193,14 @@ struct RioPrefix : mSawInRa(false) , mPrefix(aPrefix) , mLifetime(0) + , mPreference(NetworkData::kRoutePreferenceMedium) { } - bool mSawInRa; // Indicate whether or not this prefix was seen in the emitted RA (as RIO). - Ip6::Prefix mPrefix; // The RIO prefix. - uint32_t mLifetime; // The RIO prefix lifetime - only valid when `mSawInRa` + bool mSawInRa; // Indicate whether or not this prefix was seen in the emitted RA (as RIO). + Ip6::Prefix mPrefix; // The RIO prefix. + uint32_t mLifetime; // The RIO prefix lifetime - only valid when `mSawInRa` + RoutePreference mPreference; // The RIO preference - only valid when `mSawInRa` }; class ExpectedRios : public Array @@ -531,8 +535,9 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket) { if (prefix == rioPrefix.mPrefix) { - rioPrefix.mSawInRa = true; - rioPrefix.mLifetime = rio.GetRouteLifetime(); + rioPrefix.mSawInRa = true; + rioPrefix.mLifetime = rio.GetRouteLifetime(); + rioPrefix.mPreference = rio.GetPreference(); } } @@ -741,8 +746,6 @@ void VerifyNoOmrPrefixInNetData(void) VerifyOrQuit(otNetDataGetNextOnMeshPrefix(sInstance, &iterator, &prefixConfig) != kErrorNone); } -using NetworkData::RoutePreference; - enum ExternalRouteMode : uint8_t { kNoRoute, @@ -1419,6 +1422,7 @@ void TestOmrSelection(void) VerifyOrQuit(sRaValidated); VerifyOrQuit(sExpectedRios.SawAll()); VerifyOrQuit(sExpectedRios[0].mLifetime == kRioValidLifetime); + VerifyOrQuit(sExpectedRios[0].mPreference == NetworkData::kRoutePreferenceMedium); Log("Received RA was validated"); @@ -1460,7 +1464,9 @@ void TestOmrSelection(void) VerifyOrQuit(sRaValidated); VerifyOrQuit(sExpectedRios.SawAll()); VerifyOrQuit(sExpectedRios[0].mLifetime == kRioValidLifetime); + VerifyOrQuit(sExpectedRios[0].mPreference == NetworkData::kRoutePreferenceMedium); VerifyOrQuit(sExpectedRios[1].mLifetime <= kRioDeprecatingLifetime); + VerifyOrQuit(sExpectedRios[1].mPreference == NetworkData::kRoutePreferenceLow); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check Network Data. We should now see that the local OMR prefix @@ -1491,7 +1497,9 @@ void TestOmrSelection(void) VerifyOrQuit(sRaValidated); VerifyOrQuit(sExpectedRios.SawAll()); VerifyOrQuit(sExpectedRios[0].mLifetime <= kRioDeprecatingLifetime); + VerifyOrQuit(sExpectedRios[0].mPreference == NetworkData::kRoutePreferenceLow); VerifyOrQuit(sExpectedRios[1].mLifetime == kRioValidLifetime); + VerifyOrQuit(sExpectedRios[1].mPreference == NetworkData::kRoutePreferenceMedium); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check Network Data. We should see that the local OMR prefix is @@ -1514,6 +1522,7 @@ void TestOmrSelection(void) VerifyOrQuit(sExpectedRios.SawAll()); VerifyOrQuit(sExpectedRios[0].mLifetime == 0); VerifyOrQuit(sExpectedRios[1].mLifetime == kRioValidLifetime); + VerifyOrQuit(sExpectedRios[0].mPreference == NetworkData::kRoutePreferenceLow); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2202,6 +2211,7 @@ void TestDomainPrefixAsOmr(void) VerifyOrQuit(sExpectedRios[1].mPrefix == localOmr); VerifyOrQuit(sExpectedRios[1].mSawInRa); VerifyOrQuit(sExpectedRios[1].mLifetime <= kRioDeprecatingLifetime); + VerifyOrQuit(sExpectedRios[1].mPreference == NetworkData::kRoutePreferenceLow); // Wait long enough for deprecating RIO prefix to expire AdvanceTime(3200000); From 24e930654eb2d8491f970060f28edb613de06527 Mon Sep 17 00:00:00 2001 From: Yang Sun Date: Thu, 22 Aug 2024 14:02:44 +0800 Subject: [PATCH 123/160] [epskc] add API for ePSKc telemetry (#10608) Adds API to get border agent counters which include counters for ePSKc, PSKc and coap messages. --- include/openthread/border_agent.h | 32 +++++ include/openthread/coap_secure.h | 29 +++-- include/openthread/instance.h | 2 +- src/cli/README.md | 27 ++++ src/cli/cli.cpp | 52 ++++++++ src/cli/cli.hpp | 6 +- src/cli/cli_coap_secure.cpp | 14 +- src/cli/cli_coap_secure.hpp | 4 +- src/core/api/border_agent_api.cpp | 5 + src/core/api/coap_secure_api.cpp | 8 +- src/core/coap/coap_secure.cpp | 17 ++- src/core/coap/coap_secure.hpp | 27 ++-- src/core/meshcop/border_agent.cpp | 71 +++++++++- src/core/meshcop/border_agent.hpp | 13 +- src/core/meshcop/commissioner.cpp | 13 +- src/core/meshcop/commissioner.hpp | 4 +- src/core/meshcop/joiner.cpp | 8 +- src/core/meshcop/joiner.hpp | 4 +- src/core/meshcop/secure_transport.cpp | 11 +- src/core/meshcop/secure_transport.hpp | 27 ++-- src/core/radio/ble_secure.cpp | 12 +- src/core/radio/ble_secure.hpp | 4 +- .../test_ephemeral_key_counters.py | 123 ++++++++++++++++++ tests/scripts/thread-cert/node.py | 25 ++++ 24 files changed, 452 insertions(+), 86 deletions(-) create mode 100755 tests/scripts/thread-cert/border_router/test_ephemeral_key_counters.py diff --git a/include/openthread/border_agent.h b/include/openthread/border_agent.h index 33578e77a..38bc1dc9c 100644 --- a/include/openthread/border_agent.h +++ b/include/openthread/border_agent.h @@ -110,6 +110,38 @@ typedef enum otBorderAgentState OT_BORDER_AGENT_STATE_ACTIVE = 2, ///< Border agent is connected with external commissioner. } otBorderAgentState; +typedef struct otBorderAgentCounters +{ + uint32_t mEpskcActivations; ///< The number of ePSKc activations + uint32_t mEpskcDeactivationClears; ///< The number of ePSKc deactivations via API + uint32_t mEpskcDeactivationTimeouts; ///< The number of ePSKc deactivations due to timeout + uint32_t mEpskcDeactivationMaxAttempts; ///< The number of ePSKc deactivations due to reached max attempts + uint32_t mEpskcDeactivationDisconnects; ///< The number of ePSKc deactivations due to commissioner disconnected + uint32_t mEpskcInvalidBaStateErrors; ///< The number of invalid border agent state errors at ePSKc activation + uint32_t mEpskcInvalidArgsErrors; ///< The number of invalid args errors at ePSKc activation + uint32_t mEpskcStartSecureSessionErrors; ///< The number of start secure session errors at ePSKc activation + uint32_t mEpskcSecureSessionSuccesses; ///< The number of established secure sessions with ePSKc + uint32_t mEpskcSecureSessionFailures; ///< The number of failed secure sessions with ePSKc + uint32_t mEpskcCommissionerPetitions; ///< The number of successful commissioner petitions with ePSKc + + uint32_t mPskcSecureSessionSuccesses; ///< The number of established secure sessions with PSKc + uint32_t mPskcSecureSessionFailures; ///< The number of failed secure sessions with PSKc + uint32_t mPskcCommissionerPetitions; ///< The number of successful commissioner petitions with PSKc + + uint32_t mMgmtActiveGets; ///< The number of MGMT_ACTIVE_GET.req sent over secure sessions + uint32_t mMgmtPendingGets; ///< The number of MGMT_PENDING_GET.req sent over secure sessions +} otBorderAgentCounters; + +/** + * Gets the counters of the Thread Border Agent. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns A pointer to the Border Agent counters. + * + */ +const otBorderAgentCounters *otBorderAgentGetCounters(otInstance *aInstance); + /** * Gets the #otBorderAgentState of the Thread Border Agent role. * diff --git a/include/openthread/coap_secure.h b/include/openthread/coap_secure.h index 25b56bf40..012750fc0 100644 --- a/include/openthread/coap_secure.h +++ b/include/openthread/coap_secure.h @@ -67,14 +67,27 @@ extern "C" { #define OT_DEFAULT_COAP_SECURE_PORT 5684 ///< Default CoAP Secure port, as specified in RFC 7252 +/** + * CoAP secure connection event types. + * + */ +typedef enum otCoapSecureConnectEvent +{ + OT_COAP_SECURE_CONNECTED = 0, ///< Connection established + OT_COAP_SECURE_DISCONNECTED_PEER_CLOSED, ///< Disconnected by peer + OT_COAP_SECURE_DISCONNECTED_LOCAL_CLOSED, ///< Disconnected locally + OT_COAP_SECURE_DISCONNECTED_MAX_ATTEMPTS, ///< Disconnected due to reaching the max connection attempts + OT_COAP_SECURE_DISCONNECTED_ERROR, ///< Disconnected due to an error +} otCoapSecureConnectEvent; + /** * Pointer is called when the DTLS connection state changes. * - * @param[in] aConnected true, if a connection was established, false otherwise. + * @param[in] aEvent The connection event. * @param[in] aContext A pointer to arbitrary context information. * */ -typedef void (*otHandleCoapSecureClientConnect)(bool aConnected, void *aContext); +typedef void (*otHandleCoapSecureClientConnect)(otCoapSecureConnectEvent aEvent, void *aContext); /** * Callback function pointer to notify when the CoAP secure agent is automatically stopped due to reaching the maximum @@ -368,17 +381,17 @@ void otCoapSecureRemoveBlockWiseResource(otInstance *aInstance, otCoapBlockwiseR void otCoapSecureSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandler, void *aContext); /** - * Sets the connected callback to indicate, when - * a Client connect to the CoAP Secure server. + * Sets the connect event callback to indicate when + * a Client connection to the CoAP Secure server has changed. * * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aHandler A pointer to a function that will be called once DTLS connection is established. + * @param[in] aHandler A pointer to a function that will be called once DTLS connection has changed. * @param[in] aContext A pointer to arbitrary context information. May be NULL if not used. * */ -void otCoapSecureSetClientConnectedCallback(otInstance *aInstance, - otHandleCoapSecureClientConnect aHandler, - void *aContext); +void otCoapSecureSetClientConnectEventCallback(otInstance *aInstance, + otHandleCoapSecureClientConnect aHandler, + void *aContext); /** * Sends a CoAP response block-wise from the CoAP Secure server. diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 48b1203a3..bbc4d4d72 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (435) +#define OPENTHREAD_API_VERSION (436) /** * @addtogroup api-instance diff --git a/src/cli/README.md b/src/cli/README.md index c633f229a..23f9f1664 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -453,6 +453,33 @@ Disables callback from Border Agent for ephemeral key state changes. Done ``` +### ba counters + +Get the border agent counter values. + +Note that it requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE` to output the ePSKc counters. + +```bash +> ba counters +epskcActivation: 2 +epskcApiDeactivation: 1 +epskcTimeoutDeactivation: 1 +epskcMaxAttemptDeactivation: 0 +epskcDisconnectDeactivation: 0 +epskcInvalidBaStateError: 1 +epskcInvalidArgsError: 1 +epskcStartSecureSessionError: 0 +epskcSecureSessionSuccess: 0 +epskcSecureSessionFailure: 0 +epskcCommissionerPetition: 0 +pskcSecureSessionSuccess: 0 +pskcSecureSessionFailure: 0 +pskcCommissionerPetition: 0 +mgmtActiveGet: 0 +mgmtPendingGet: 0 +Done +``` + ### bufferinfo Show the current message buffer information. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 5d32f8f31..be6e144b8 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -541,6 +541,36 @@ template <> otError Interpreter::Process(Arg aArgs[]) } } #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + /** + * @cli ba counters + * @code + * ba counters + * epskcActivation: 0 + * epskcApiDeactivation: 0 + * epskcTimeoutDeactivation: 0 + * epskcMaxAttemptDeactivation: 0 + * epskcDisconnectDeactivation: 0 + * epskcInvalidBaStateError: 0 + * epskcInvalidArgsError: 0 + * epskcStartSecureSessionError: 0 + * epskcSecureSessionSuccess: 0 + * epskcSecureSessionFailure: 0 + * epskcCommissionerPetition: 0 + * pskcSecureSessionSuccess: 0 + * pskcSecureSessionFailure: 0 + * pskcCommissionerPetition: 0 + * mgmtActiveGet: 0 + * mgmtPendingGet: 0 + * Done + * @endcode + * @par + * Gets the border agent counters. + * @sa otBorderAgentGetCounters + */ + else if (aArgs[0] == "counters") + { + OutputBorderAgentCounters(*otBorderAgentGetCounters(GetInstancePtr())); + } else { ExitNow(error = OT_ERROR_INVALID_COMMAND); @@ -550,6 +580,28 @@ template <> otError Interpreter::Process(Arg aArgs[]) return error; } +void Interpreter::OutputBorderAgentCounters(const otBorderAgentCounters &aCounters) +{ +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + OutputLine("epskcActivation: %lu ", ToUlong(aCounters.mEpskcActivations)); + OutputLine("epskcApiDeactivation: %lu ", ToUlong(aCounters.mEpskcDeactivationClears)); + OutputLine("epskcTimeoutDeactivation: %lu ", ToUlong(aCounters.mEpskcDeactivationTimeouts)); + OutputLine("epskcMaxAttemptDeactivation: %lu ", ToUlong(aCounters.mEpskcDeactivationMaxAttempts)); + OutputLine("epskcDisconnectDeactivation: %lu ", ToUlong(aCounters.mEpskcDeactivationDisconnects)); + OutputLine("epskcInvalidBaStateError: %lu ", ToUlong(aCounters.mEpskcInvalidBaStateErrors)); + OutputLine("epskcInvalidArgsError: %lu ", ToUlong(aCounters.mEpskcInvalidArgsErrors)); + OutputLine("epskcStartSecureSessionError: %lu ", ToUlong(aCounters.mEpskcStartSecureSessionErrors)); + OutputLine("epskcSecureSessionSuccess: %lu ", ToUlong(aCounters.mEpskcSecureSessionSuccesses)); + OutputLine("epskcSecureSessionFailure: %lu ", ToUlong(aCounters.mEpskcSecureSessionFailures)); + OutputLine("epskcCommissionerPetition: %lu ", ToUlong(aCounters.mEpskcCommissionerPetitions)); +#endif + OutputLine("pskcSecureSessionSuccess: %lu ", ToUlong(aCounters.mPskcSecureSessionSuccesses)); + OutputLine("pskcSecureSessionFailure: %lu ", ToUlong(aCounters.mPskcSecureSessionFailures)); + OutputLine("pskcCommissionerPetition: %lu ", ToUlong(aCounters.mPskcCommissionerPetitions)); + OutputLine("mgmtActiveGet: %lu ", ToUlong(aCounters.mMgmtActiveGets)); + OutputLine("mgmtPendingGet: %lu", ToUlong(aCounters.mMgmtPendingGets)); +} + #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE void Interpreter::HandleBorderAgentEphemeralKeyStateChange(void *aContext) { diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index c8e3e4d87..a8d89a4dd 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -40,6 +40,7 @@ #include +#include #include #include #include @@ -314,9 +315,12 @@ class Interpreter : public OutputImplementer, public Utils void HandleSntpResponse(uint64_t aTime, otError aResult); #endif -#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE + void OutputBorderAgentCounters(const otBorderAgentCounters &aCounters); +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE static void HandleBorderAgentEphemeralKeyStateChange(void *aContext); void HandleBorderAgentEphemeralKeyStateChange(void); +#endif #endif static void HandleDetachGracefullyResult(void *aContext); diff --git a/src/cli/cli_coap_secure.cpp b/src/cli/cli_coap_secure.cpp index 2ff4f6819..e0e5115b8 100644 --- a/src/cli/cli_coap_secure.cpp +++ b/src/cli/cli_coap_secure.cpp @@ -210,7 +210,7 @@ template <> otError CoapSecure::Process(Arg aArgs[]) * Starts the CoAP Secure service. @moreinfo{@coaps}. * @sa otCoapSecureStart * @sa otCoapSecureSetSslAuthMode - * @sa otCoapSecureSetClientConnectedCallback + * @sa otCoapSecureSetClientConnectEventCallback */ template <> otError CoapSecure::Process(Arg aArgs[]) { @@ -235,7 +235,7 @@ template <> otError CoapSecure::Process(Arg aArgs[]) } otCoapSecureSetSslAuthMode(GetInstancePtr(), verifyPeerCert); - otCoapSecureSetClientConnectedCallback(GetInstancePtr(), &CoapSecure::HandleConnected, this); + otCoapSecureSetClientConnectEventCallback(GetInstancePtr(), &CoapSecure::HandleConnectEvent, this); #if CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER otCoapSecureSetDefaultHandler(GetInstancePtr(), &CoapSecure::DefaultHandler, this); @@ -629,7 +629,7 @@ template <> otError CoapSecure::Process(Arg aArgs[]) SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort)); } - SuccessOrExit(error = otCoapSecureConnect(GetInstancePtr(), &sockaddr, &CoapSecure::HandleConnected, this)); + SuccessOrExit(error = otCoapSecureConnect(GetInstancePtr(), &sockaddr, &CoapSecure::HandleConnectEvent, this)); exit: return error; @@ -783,14 +783,14 @@ void CoapSecure::Stop(void) otCoapSecureStop(GetInstancePtr()); } -void CoapSecure::HandleConnected(bool aConnected, void *aContext) +void CoapSecure::HandleConnectEvent(otCoapSecureConnectEvent aEvent, void *aContext) { - static_cast(aContext)->HandleConnected(aConnected); + static_cast(aContext)->HandleConnectEvent(aEvent); } -void CoapSecure::HandleConnected(bool aConnected) +void CoapSecure::HandleConnectEvent(otCoapSecureConnectEvent aEvent) { - if (aConnected) + if (aEvent == OT_COAP_SECURE_CONNECTED) { OutputLine("coaps connected"); } diff --git a/src/cli/cli_coap_secure.hpp b/src/cli/cli_coap_secure.hpp index 92a7a95e5..fb1e8e399 100644 --- a/src/cli/cli_coap_secure.hpp +++ b/src/cli/cli_coap_secure.hpp @@ -137,8 +137,8 @@ class CoapSecure : private Utils void DefaultHandler(otMessage *aMessage, const otMessageInfo *aMessageInfo); #endif // CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER - static void HandleConnected(bool aConnected, void *aContext); - void HandleConnected(bool aConnected); + static void HandleConnectEvent(otCoapSecureConnectEvent aEvent, void *aContext); + void HandleConnectEvent(otCoapSecureConnectEvent aEvent); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE otCoapBlockwiseResource mResource; diff --git a/src/core/api/border_agent_api.cpp b/src/core/api/border_agent_api.cpp index c6d321ee9..98a7bb37b 100644 --- a/src/core/api/border_agent_api.cpp +++ b/src/core/api/border_agent_api.cpp @@ -110,4 +110,9 @@ void otBorderAgentSetEphemeralKeyCallback(otInstance *aIns #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE +const otBorderAgentCounters *otBorderAgentGetCounters(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetCounters(); +} + #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE diff --git a/src/core/api/coap_secure_api.cpp b/src/core/api/coap_secure_api.cpp index e5ac4c014..d83afc3cf 100644 --- a/src/core/api/coap_secure_api.cpp +++ b/src/core/api/coap_secure_api.cpp @@ -178,11 +178,11 @@ void otCoapSecureRemoveResource(otInstance *aInstance, otCoapResource *aResource AsCoreType(aInstance).GetApplicationCoapSecure().RemoveResource(AsCoreType(aResource)); } -void otCoapSecureSetClientConnectedCallback(otInstance *aInstance, - otHandleCoapSecureClientConnect aHandler, - void *aContext) +void otCoapSecureSetClientConnectEventCallback(otInstance *aInstance, + otHandleCoapSecureClientConnect aHandler, + void *aContext) { - AsCoreType(aInstance).GetApplicationCoapSecure().SetConnectedCallback(aHandler, aContext); + AsCoreType(aInstance).GetApplicationCoapSecure().SetConnectEventCallback(aHandler, aContext); } void otCoapSecureSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandler, void *aContext) diff --git a/src/core/coap/coap_secure.cpp b/src/core/coap/coap_secure.cpp index 875d6c0f6..7b54058ca 100644 --- a/src/core/coap/coap_secure.cpp +++ b/src/core/coap/coap_secure.cpp @@ -85,8 +85,8 @@ Error CoapSecure::Open(uint16_t aMaxAttempts, AutoStopCallback aCallback, void * SuccessOrExit(mDtls.SetMaxConnectionAttempts(aMaxAttempts, HandleDtlsAutoClose, this)); mAutoStopCallback.Set(aCallback, aContext); - mConnectedCallback.Clear(); - SuccessOrExit(mDtls.Open(HandleDtlsReceive, HandleDtlsConnected, this)); + mConnectEventCallback.Clear(); + SuccessOrExit(mDtls.Open(HandleDtlsReceive, HandleDtlsConnectEvent, this)); error = kErrorNone; @@ -102,9 +102,9 @@ void CoapSecure::Stop(void) ClearRequestsAndResponses(); } -Error CoapSecure::Connect(const Ip6::SockAddr &aSockAddr, ConnectedCallback aCallback, void *aContext) +Error CoapSecure::Connect(const Ip6::SockAddr &aSockAddr, ConnectEventCallback aCallback, void *aContext) { - mConnectedCallback.Set(aCallback, aContext); + mConnectEventCallback.Set(aCallback, aContext); return mDtls.Connect(aSockAddr); } @@ -178,12 +178,15 @@ Error CoapSecure::Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageIn return kErrorNone; } -void CoapSecure::HandleDtlsConnected(void *aContext, bool aConnected) +void CoapSecure::HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent, void *aContext) { - return static_cast(aContext)->HandleDtlsConnected(aConnected); + return static_cast(aContext)->HandleDtlsConnectEvent(aEvent); } -void CoapSecure::HandleDtlsConnected(bool aConnected) { mConnectedCallback.InvokeIfSet(aConnected); } +void CoapSecure::HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent) +{ + mConnectEventCallback.InvokeIfSet(aEvent); +} void CoapSecure::HandleDtlsAutoClose(void *aContext) { diff --git a/src/core/coap/coap_secure.hpp b/src/core/coap/coap_secure.hpp index 0565ef562..a2517adc3 100644 --- a/src/core/coap/coap_secure.hpp +++ b/src/core/coap/coap_secure.hpp @@ -53,13 +53,10 @@ class CoapSecure : public CoapBase { public: /** - * Pointer is called once DTLS connection is established. - * - * @param[in] aConnected TRUE if a connection was established, FALSE otherwise. - * @param[in] aContext A pointer to arbitrary context information. + * Function pointer which is called reporting a connection event (when connection established or disconnected) * */ - typedef void (*ConnectedCallback)(bool aConnected, void *aContext); + typedef otHandleCoapSecureClientConnect ConnectEventCallback; /** * Callback to notify when the agent is automatically stopped due to reaching the maximum number of connection @@ -122,9 +119,9 @@ class CoapSecure : public CoapBase * @param[in] aContext A pointer to arbitrary context information. * */ - void SetConnectedCallback(ConnectedCallback aCallback, void *aContext) + void SetConnectEventCallback(ConnectEventCallback aCallback, void *aContext) { - mConnectedCallback.Set(aCallback, aContext); + mConnectEventCallback.Set(aCallback, aContext); } /** @@ -143,7 +140,7 @@ class CoapSecure : public CoapBase * @retval kErrorNone Successfully started DTLS connection. * */ - Error Connect(const Ip6::SockAddr &aSockAddr, ConnectedCallback aCallback, void *aContext); + Error Connect(const Ip6::SockAddr &aSockAddr, ConnectEventCallback aCallback, void *aContext); /** * Indicates whether or not the DTLS session is active. @@ -421,8 +418,8 @@ class CoapSecure : public CoapBase } Error Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleDtlsConnected(void *aContext, bool aConnected); - void HandleDtlsConnected(bool aConnected); + static void HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent, void *aContext); + void HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent); static void HandleDtlsAutoClose(void *aContext); void HandleDtlsAutoClose(void); @@ -433,11 +430,11 @@ class CoapSecure : public CoapBase static void HandleTransmit(Tasklet &aTasklet); void HandleTransmit(void); - MeshCoP::SecureTransport mDtls; - Callback mConnectedCallback; - Callback mAutoStopCallback; - ot::MessageQueue mTransmitQueue; - TaskletContext mTransmitTask; + MeshCoP::SecureTransport mDtls; + Callback mConnectEventCallback; + Callback mAutoStopCallback; + ot::MessageQueue mTransmitQueue; + TaskletContext mTransmitTask; }; } // namespace Coap diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index f4eb18351..6ae830f50 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -205,6 +205,17 @@ void BorderAgent::HandleCoapResponse(const ForwardContext &aForwardContext, IgnoreError(Get().AddReceiver(mUdpReceiver)); mState = kStateAccepted; +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + if (mUsingEphemeralKey) + { + mCounters.mEpskcCommissionerPetitions++; + } + else +#endif + { + mCounters.mPskcCommissionerPetitions++; + } + LogInfo("Commissioner accepted - SessionId:%u ALOC:%s", sessionId, mCommissionerAloc.GetAddress().ToString().AsCString()); } @@ -252,6 +263,7 @@ BorderAgent::BorderAgent(Instance &aInstance) #endif { mCommissionerAloc.InitAsThreadOriginMeshLocal(); + ClearAllBytes(mCounters); } #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE @@ -468,6 +480,7 @@ void BorderAgent::HandleTmf(Coap::Message &aMessage, const template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { HandleTmfDatasetGet(aMessage, aMessageInfo, Dataset::kActive); + mCounters.mMgmtActiveGets++; } template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) @@ -478,6 +491,7 @@ template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { HandleTmfDatasetGet(aMessage, aMessageInfo, Dataset::kPending); + mCounters.mMgmtPendingGets++; } template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) @@ -635,18 +649,28 @@ void BorderAgent::HandleTmfDatasetGet(Coap::Message &aMessage, FreeMessageOnError(response, error); } -void BorderAgent::HandleConnected(bool aConnected, void *aContext) +void BorderAgent::HandleConnected(SecureTransport::ConnectEvent aEvent, void *aContext) { - static_cast(aContext)->HandleConnected(aConnected); + static_cast(aContext)->HandleConnected(aEvent); } -void BorderAgent::HandleConnected(bool aConnected) +void BorderAgent::HandleConnected(SecureTransport::ConnectEvent aEvent) { - if (aConnected) + if (aEvent == SecureTransport::kConnected) { LogInfo("SecureSession connected"); mState = kStateConnected; mTimer.Start(kKeepAliveTimeout); +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + if (mUsingEphemeralKey) + { + mCounters.mEpskcSecureSessionSuccesses++; + } + else +#endif + { + mCounters.mPskcSecureSessionSuccesses++; + } } else { @@ -658,12 +682,24 @@ void BorderAgent::HandleConnected(bool aConnected) if (mUsingEphemeralKey) { RestartAfterRemovingEphemeralKey(); + if (aEvent == SecureTransport::kDisconnectedError) + { + mCounters.mEpskcSecureSessionFailures++; + } + if (aEvent == SecureTransport::kDisconnectedPeerClosed) + { + mCounters.mEpskcDeactivationDisconnects++; + } } else #endif { mState = kStateStarted; mUdpProxyPort = 0; + if (aEvent == SecureTransport::kDisconnectedError) + { + mCounters.mPskcSecureSessionFailures++; + } } } } @@ -702,7 +738,7 @@ Error BorderAgent::Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLen SuccessOrExit(error = Get().SetPsk(aPsk, aPskLength)); - Get().SetConnectedCallback(HandleConnected, this); + Get().SetConnectEventCallback(HandleConnected, this); mState = kStateStarted; mUdpProxyPort = 0; @@ -776,6 +812,7 @@ Error BorderAgent::SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, ui { mUsingEphemeralKey = false; IgnoreError(Start(mOldUdpPort)); + mCounters.mEpskcStartSecureSessionErrors++; ExitNow(); } @@ -789,10 +826,23 @@ Error BorderAgent::SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, ui aTimeout = Min(aTimeout, kMaxEphemeralKeyTimeout); mEphemeralKeyTimer.Start(aTimeout); + mCounters.mEpskcActivations++; LogInfo("Allow ephemeral key for %lu msec on port %u", ToUlong(aTimeout), GetUdpPort()); exit: + switch (error) + { + case kErrorInvalidState: + mCounters.mEpskcInvalidBaStateErrors++; + break; + case kErrorInvalidArgs: + mCounters.mEpskcInvalidArgsErrors++; + break; + default: + break; + } + return error; } @@ -801,6 +851,16 @@ void BorderAgent::ClearEphemeralKey(void) VerifyOrExit(mUsingEphemeralKey); LogInfo("Clearing ephemeral key"); + + if (mEphemeralKeyTimer.IsRunning()) + { + mCounters.mEpskcDeactivationClears++; + } + else + { + mCounters.mEpskcDeactivationTimeouts++; + } + mEphemeralKeyTimer.Stop(); switch (mState) @@ -847,6 +907,7 @@ void BorderAgent::HandleSecureAgentStopped(void) { LogInfo("Reached max allowed connection attempts with ephemeral key"); RestartAfterRemovingEphemeralKey(); + mCounters.mEpskcDeactivationMaxAttempts++; } #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE diff --git a/src/core/meshcop/border_agent.hpp b/src/core/meshcop/border_agent.hpp index 4e52ad179..1cc57e55f 100644 --- a/src/core/meshcop/border_agent.hpp +++ b/src/core/meshcop/border_agent.hpp @@ -238,6 +238,14 @@ class BorderAgent : public InstanceLocator, private NonCopyable #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + /** + * Gets the set of border agent counters. + * + * @returns The border agent counters. + * + */ + const otBorderAgentCounters *GetCounters(void) { return &mCounters; } + /** * Returns the UDP Proxy port to which the commissioner is currently * bound. @@ -285,8 +293,8 @@ class BorderAgent : public InstanceLocator, private NonCopyable void SendErrorMessage(const ForwardContext &aForwardContext, Error aError); void SendErrorMessage(const Coap::Message &aRequest, bool aSeparate, Error aError); - static void HandleConnected(bool aConnected, void *aContext); - void HandleConnected(bool aConnected); + static void HandleConnected(SecureTransport::ConnectEvent aEvent, void *aContext); + void HandleConnected(SecureTransport::ConnectEvent aEvent); template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); @@ -333,6 +341,7 @@ class BorderAgent : public InstanceLocator, private NonCopyable EphemeralKeyTask mEphemeralKeyTask; Callback mEphemeralKeyCallback; #endif + otBorderAgentCounters mCounters; }; DeclareTmfHandler(BorderAgent, kUriRelayRx); diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp index ce379dc99..8d71ca311 100644 --- a/src/core/meshcop/commissioner.cpp +++ b/src/core/meshcop/commissioner.cpp @@ -127,19 +127,20 @@ void Commissioner::SignalJoinerEvent(JoinerEvent aEvent, const Joiner *aJoiner) return; } -void Commissioner::HandleSecureAgentConnected(bool aConnected, void *aContext) +void Commissioner::HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent, void *aContext) { - static_cast(aContext)->HandleSecureAgentConnected(aConnected); + static_cast(aContext)->HandleSecureAgentConnectEvent(aEvent); } -void Commissioner::HandleSecureAgentConnected(bool aConnected) +void Commissioner::HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent) { - if (!aConnected) + bool isConnected = (aEvent == SecureTransport::kConnected); + if (!isConnected) { mJoinerSessionTimer.Stop(); } - SignalJoinerEvent(aConnected ? kJoinerEventConnected : kJoinerEventEnd, mActiveJoiner); + SignalJoinerEvent(isConnected ? kJoinerEventConnected : kJoinerEventEnd, mActiveJoiner); } Commissioner::Joiner *Commissioner::GetUnusedJoinerEntry(void) @@ -287,7 +288,7 @@ Error Commissioner::Start(StateCallback aStateCallback, JoinerCallback aJoinerCa #endif SuccessOrExit(error = Get().Start(SendRelayTransmit, this)); - Get().SetConnectedCallback(&Commissioner::HandleSecureAgentConnected, this); + Get().SetConnectEventCallback(&Commissioner::HandleSecureAgentConnectEvent, this); mStateCallback.Set(aStateCallback, aCallbackContext); mJoinerCallback.Set(aJoinerCallback, aCallbackContext); diff --git a/src/core/meshcop/commissioner.hpp b/src/core/meshcop/commissioner.hpp index fda668c44..adfff53a8 100644 --- a/src/core/meshcop/commissioner.hpp +++ b/src/core/meshcop/commissioner.hpp @@ -440,8 +440,8 @@ class Commissioner : public InstanceLocator, private NonCopyable Error aResult); void HandleLeaderKeepAliveResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); - static void HandleSecureAgentConnected(bool aConnected, void *aContext); - void HandleSecureAgentConnected(bool aConnected); + static void HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent, void *aContext); + void HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent); template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp index a1ff9bf1c..dcd55aa3a 100644 --- a/src/core/meshcop/joiner.cpp +++ b/src/core/meshcop/joiner.cpp @@ -382,16 +382,16 @@ Error Joiner::Connect(JoinerRouter &aRouter) return error; } -void Joiner::HandleSecureCoapClientConnect(bool aConnected, void *aContext) +void Joiner::HandleSecureCoapClientConnect(SecureTransport::ConnectEvent aEvent, void *aContext) { - static_cast(aContext)->HandleSecureCoapClientConnect(aConnected); + static_cast(aContext)->HandleSecureCoapClientConnect(aEvent); } -void Joiner::HandleSecureCoapClientConnect(bool aConnected) +void Joiner::HandleSecureCoapClientConnect(SecureTransport::ConnectEvent aEvent) { VerifyOrExit(mState == kStateConnect); - if (aConnected) + if (aEvent == SecureTransport::kConnected) { SetState(kStateConnected); SendJoinerFinalize(); diff --git a/src/core/meshcop/joiner.hpp b/src/core/meshcop/joiner.hpp index ce430a737..c260258bd 100644 --- a/src/core/meshcop/joiner.hpp +++ b/src/core/meshcop/joiner.hpp @@ -199,8 +199,8 @@ class Joiner : public InstanceLocator, private NonCopyable static void HandleDiscoverResult(Mle::DiscoverScanner::ScanResult *aResult, void *aContext); void HandleDiscoverResult(Mle::DiscoverScanner::ScanResult *aResult); - static void HandleSecureCoapClientConnect(bool aConnected, void *aContext); - void HandleSecureCoapClientConnect(bool aConnected); + static void HandleSecureCoapClientConnect(SecureTransport::ConnectEvent aEvent, void *aContext); + void HandleSecureCoapClientConnect(SecureTransport::ConnectEvent aEvent); static void HandleJoinerFinalizeResponse(void *aContext, otMessage *aMessage, diff --git a/src/core/meshcop/secure_transport.cpp b/src/core/meshcop/secure_transport.cpp index bfb5db6d9..3cb0333f7 100644 --- a/src/core/meshcop/secure_transport.cpp +++ b/src/core/meshcop/secure_transport.cpp @@ -1043,15 +1043,15 @@ void SecureTransport::HandleTimer(void) if ((mMaxConnectionAttempts > 0) && (mRemainingConnectionAttempts == 0)) { Close(); - mConnectedCallback.InvokeIfSet(false); + mConnectEvent = kDisconnectedMaxAttempts; mAutoCloseCallback.InvokeIfSet(); } else { SetState(kStateOpen); mTimer.Stop(); - mConnectedCallback.InvokeIfSet(false); } + mConnectedCallback.InvokeIfSet(mConnectEvent); } } @@ -1070,7 +1070,8 @@ void SecureTransport::Process(void) if (mSsl.MBEDTLS_PRIVATE(state) == MBEDTLS_SSL_HANDSHAKE_OVER) { SetState(kStateConnected); - mConnectedCallback.InvokeIfSet(true); + mConnectEvent = kConnected; + mConnectedCallback.InvokeIfSet(mConnectEvent); } } else @@ -1092,6 +1093,7 @@ void SecureTransport::Process(void) { case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: mbedtls_ssl_close_notify(&mSsl); + mConnectEvent = kDisconnectedPeerClosed; ExitNow(shouldDisconnect = true); OT_UNREACHABLE_CODE(break); @@ -1100,6 +1102,7 @@ void SecureTransport::Process(void) case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE: mbedtls_ssl_close_notify(&mSsl); + mConnectEvent = kDisconnectedError; ExitNow(shouldDisconnect = true); OT_UNREACHABLE_CODE(break); @@ -1108,6 +1111,7 @@ void SecureTransport::Process(void) { mbedtls_ssl_send_alert_message(&mSsl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC); + mConnectEvent = kDisconnectedError; ExitNow(shouldDisconnect = true); } @@ -1118,6 +1122,7 @@ void SecureTransport::Process(void) { mbedtls_ssl_send_alert_message(&mSsl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + mConnectEvent = kDisconnectedError; ExitNow(shouldDisconnect = true); } diff --git a/src/core/meshcop/secure_transport.hpp b/src/core/meshcop/secure_transport.hpp index b5749a951..a4f577775 100644 --- a/src/core/meshcop/secure_transport.hpp +++ b/src/core/meshcop/secure_transport.hpp @@ -69,6 +69,8 @@ #endif #endif +#include + #include "common/callback.hpp" #include "common/locator.hpp" #include "common/log.hpp" @@ -87,8 +89,22 @@ namespace MeshCoP { class SecureTransport : public InstanceLocator { public: + typedef otCoapSecureConnectEvent ConnectEvent; ///< A connect event. + + static constexpr ConnectEvent kConnected = OT_COAP_SECURE_CONNECTED; + static constexpr ConnectEvent kDisconnectedPeerClosed = OT_COAP_SECURE_DISCONNECTED_PEER_CLOSED; + static constexpr ConnectEvent kDisconnectedLocalClosed = OT_COAP_SECURE_DISCONNECTED_LOCAL_CLOSED; + static constexpr ConnectEvent kDisconnectedMaxAttempts = OT_COAP_SECURE_DISCONNECTED_MAX_ATTEMPTS; + static constexpr ConnectEvent kDisconnectedError = OT_COAP_SECURE_DISCONNECTED_ERROR; + static constexpr uint8_t kPskMaxLength = 32; ///< Maximum PSK length. + /** + * Function pointer which is called reporting a connection event (when connection established or disconnected) + * + */ + typedef otHandleCoapSecureClientConnect ConnectedHandler; + /** * Initializes the SecureTransport object. * @@ -99,15 +115,6 @@ class SecureTransport : public InstanceLocator */ explicit SecureTransport(Instance &aInstance, bool aLayerTwoSecurity, bool aDatagramTransport = true); - /** - * Pointer is called when a connection is established or torn down. - * - * @param[in] aContext A pointer to application-specific context. - * @param[in] aConnected TRUE if a connection was established, FALSE otherwise. - * - */ - typedef void (*ConnectedHandler)(void *aContext, bool aConnected); - /** * Pointer is called when data is received from the session. * @@ -670,6 +677,8 @@ class SecureTransport : public InstanceLocator Message::SubType mMessageSubType; Message::SubType mMessageDefaultSubType; + + ConnectEvent mConnectEvent; }; } // namespace MeshCoP diff --git a/src/core/radio/ble_secure.cpp b/src/core/radio/ble_secure.cpp index dab82e125..e18c3fd07 100644 --- a/src/core/radio/ble_secure.cpp +++ b/src/core/radio/ble_secure.cpp @@ -83,7 +83,7 @@ Error BleSecure::Start(ConnectCallback aConnectHandler, ReceiveCallback aReceive SuccessOrExit(error = otPlatBleGapAdvSetData(&GetInstance(), advertisementData, advertisementLen)); SuccessOrExit(error = otPlatBleGapAdvStart(&GetInstance(), OT_BLE_ADV_INTERVAL_DEFAULT)); - SuccessOrExit(error = mTls.Open(&BleSecure::HandleTlsReceive, &BleSecure::HandleTlsConnected, this)); + SuccessOrExit(error = mTls.Open(&BleSecure::HandleTlsReceive, &BleSecure::HandleTlsConnectEvent, this)); SuccessOrExit(error = mTls.Bind(HandleTransport, this)); exit: @@ -321,14 +321,14 @@ Error BleSecure::HandleBleMtuUpdate(uint16_t aMtu) return error; } -void BleSecure::HandleTlsConnected(void *aContext, bool aConnected) +void BleSecure::HandleTlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent, void *aContext) { - return static_cast(aContext)->HandleTlsConnected(aConnected); + return static_cast(aContext)->HandleTlsConnectEvent(aEvent); } -void BleSecure::HandleTlsConnected(bool aConnected) +void BleSecure::HandleTlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent) { - if (aConnected) + if (aEvent == MeshCoP::SecureTransport::kConnected) { if (mReceivedMessage == nullptr) { @@ -358,7 +358,7 @@ void BleSecure::HandleTlsConnected(bool aConnected) } } - mConnectCallback.InvokeIfSet(&GetInstance(), aConnected, true); + mConnectCallback.InvokeIfSet(&GetInstance(), aEvent == MeshCoP::SecureTransport::kConnected, true); exit: return; diff --git a/src/core/radio/ble_secure.hpp b/src/core/radio/ble_secure.hpp index 84c9aa9ee..4ddf351bb 100644 --- a/src/core/radio/ble_secure.hpp +++ b/src/core/radio/ble_secure.hpp @@ -464,8 +464,8 @@ class BleSecure : public InstanceLocator, private NonCopyable static constexpr uint8_t kPacketBufferSize = OT_BLE_ATT_MTU_MAX - kGattOverhead; static constexpr uint16_t kTxBleHandle = 0; // Characteristics Handle for TX (not used) - static void HandleTlsConnected(void *aContext, bool aConnected); - void HandleTlsConnected(bool aConnected); + static void HandleTlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent, void *aContext); + void HandleTlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent); static void HandleTlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength); void HandleTlsReceive(uint8_t *aBuf, uint16_t aLength); diff --git a/tests/scripts/thread-cert/border_router/test_ephemeral_key_counters.py b/tests/scripts/thread-cert/border_router/test_ephemeral_key_counters.py new file mode 100755 index 000000000..8bca67951 --- /dev/null +++ b/tests/scripts/thread-cert/border_router/test_ephemeral_key_counters.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024, 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. +# +import logging +import unittest + +import config +import thread_cert + +# Test description: +# This test verifies the counters of ephemeral key activations/deactivations. +# +# Topology: +# -------------- +# | +# BR1 +# + +BR1 = 1 + + +class EphemeralKeyCountersTest(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + + TOPOLOGY = { + BR1: { + 'name': 'BR_1', + 'is_otbr': True, + 'version': '1.4', + 'network_name': 'ot-br1', + }, + } + + def test(self): + br1 = self.nodes[BR1] + + counters = br1.get_border_agent_counters() + self.assertEqual(counters['epskcActivation'], 0) + self.assertEqual(counters['epskcApiDeactivation'], 0) + self.assertEqual(counters['epskcTimeoutDeactivation'], 0) + self.assertEqual(counters['epskcMaxAttemptDeactivation'], 0) + self.assertEqual(counters['epskcDisconnectDeactivation'], 0) + self.assertEqual(counters['epskcInvalidBaStateError'], 0) + self.assertEqual(counters['epskcInvalidArgsError'], 0) + self.assertEqual(counters['epskcStartSecureSessionError'], 0) + self.assertEqual(counters['epskcSecureSessionSuccess'], 0) + self.assertEqual(counters['epskcSecureSessionFailure'], 0) + self.assertEqual(counters['epskcCommissionerPetition'], 0) + self.assertEqual(counters['pskcSecureSessionSuccess'], 0) + self.assertEqual(counters['pskcSecureSessionFailure'], 0) + self.assertEqual(counters['pskcCommissionerPetition'], 0) + self.assertEqual(counters['mgmtActiveGet'], 0) + self.assertEqual(counters['mgmtPendingGet'], 0) + + # activate epskc before border agent is up returns an error + br1.set_epskc('123456789') + + counters = br1.get_border_agent_counters() + self.assertEqual(counters['epskcActivation'], 0) + self.assertEqual(counters['epskcInvalidBaStateError'], 1) + + br1.set_active_dataset(updateExisting=True, network_name='ot-br1') + br1.start() + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) + self.assertEqual('leader', br1.get_state()) + + # activate epskc and let it timeout + br1.set_epskc('123456789', 10) + self.simulator.go(1) + + counters = br1.get_border_agent_counters() + self.assertEqual(counters['epskcActivation'], 1) + self.assertEqual(counters['epskcApiDeactivation'], 0) + self.assertEqual(counters['epskcTimeoutDeactivation'], 1) + + # activate epskc and clear it + br1.set_epskc('123456789', 10000) + self.simulator.go(1) + br1.clear_epskc() + + counters = br1.get_border_agent_counters() + self.assertEqual(counters['epskcActivation'], 2) + self.assertEqual(counters['epskcApiDeactivation'], 1) + self.assertEqual(counters['epskcTimeoutDeactivation'], 1) + + # set epskc with invalid passcode + br1.set_epskc('123') + self.simulator.go(1) + + counters = br1.get_border_agent_counters() + self.assertEqual(counters['epskcActivation'], 2) + self.assertEqual(counters['epskcApiDeactivation'], 1) + self.assertEqual(counters['epskcTimeoutDeactivation'], 1) + self.assertEqual(counters['epskcInvalidArgsError'], 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 9139541c8..da1d96b1f 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1451,6 +1451,31 @@ def reset_trel_counters(self): self.send_command(cmd) self._expect_done() + def set_epskc(self, keystring: str, timeout=120000, port=0): + cmd = 'ba ephemeralkey set ' + keystring + ' ' + str(timeout) + ' ' + str(port) + self.send_command(cmd) + self._expect(r"(Done|Error .*)") + + def clear_epskc(self): + cmd = 'ba ephemeralkey clear' + self.send_command(cmd) + self._expect_done() + + def get_border_agent_counters(self): + cmd = 'ba counters' + self.send_command(cmd) + result = self._expect_command_output() + + counters = {} + for line in result: + m = re.match(r'(\w+)\: (\d+)', line) + if m: + counter_name = m.group(1) + counter_value = m.group(2) + + counters[counter_name] = int(counter_value) + return counters + def _encode_txt_entry(self, entry): """Encodes the TXT entry to the DNS-SD TXT record format as a HEX string. From 1a2d5f0458fba6b505793930da29cd091e52a293 Mon Sep 17 00:00:00 2001 From: gabekassel Date: Thu, 22 Aug 2024 16:18:37 -0700 Subject: [PATCH 124/160] [core] valgrind reported memory access bugs (#9833) * posix: check for nlmsg error tlv attributes if we couldn't set NETLINK_EXT_ACK, there's no extra nlmsg attributes in the error. avoid UB and walking uninitialized memory by checking the flag for those attributes. for us, this avoids segfaults and in one instance, an infinite loop while walking the non-existant attributes. Signed-off-by: Nick Owens * posix: zero initialize sigaction struct before use this removes a valgrind warning about use of uninitialized memory in a syscall when backtrace is enabled. Signed-off-by: Nick Owens * key_manager: zero initialize otSecurityPolicy valgrind reports that otSecurityPolicy is used uninitialized, so just make it zero. clear all bytes when setting to default Signed-off-by: Nick Owens --- src/core/thread/key_manager.cpp | 1 + src/core/thread/key_manager.hpp | 2 +- src/posix/platform/backtrace.cpp | 2 ++ src/posix/platform/netif.cpp | 26 +++++++++++++++----------- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/core/thread/key_manager.cpp b/src/core/thread/key_manager.cpp index 9f40c9ec2..dd12eaa2d 100644 --- a/src/core/thread/key_manager.cpp +++ b/src/core/thread/key_manager.cpp @@ -65,6 +65,7 @@ const uint8_t KeyManager::kTrelInfoString[] = {'T', 'h', 'r', 'e', 'a', 'd', 'O' void SecurityPolicy::SetToDefault(void) { + Clear(); mRotationTime = kDefaultKeyRotationTime; SetToDefaultFlags(); } diff --git a/src/core/thread/key_manager.hpp b/src/core/thread/key_manager.hpp index 56a64acb3..6091c4a05 100644 --- a/src/core/thread/key_manager.hpp +++ b/src/core/thread/key_manager.hpp @@ -68,7 +68,7 @@ namespace ot { * Represents Security Policy Rotation and Flags. * */ -class SecurityPolicy : public otSecurityPolicy, public Equatable +class SecurityPolicy : public otSecurityPolicy, public Equatable, public Clearable { public: /** diff --git a/src/posix/platform/backtrace.cpp b/src/posix/platform/backtrace.cpp index 1f697ca57..988718d7a 100644 --- a/src/posix/platform/backtrace.cpp +++ b/src/posix/platform/backtrace.cpp @@ -164,6 +164,8 @@ void platformBacktraceInit(void) { struct sigaction sigact; + memset(&sigact, 0, sizeof(struct sigaction)); + sigact.sa_sigaction = &signalCritical; sigact.sa_flags = SA_RESTART | SA_SIGINFO | SA_NOCLDWAIT; diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp index 12bee4256..37eb4cb05 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -1751,19 +1751,23 @@ static void HandleNetlinkResponse(struct nlmsghdr *msg) requestPayloadLength = NLMSG_PAYLOAD(&err->msg, 0); } - rtaLength = NLMSG_PAYLOAD(msg, sizeof(struct nlmsgerr)) - requestPayloadLength; - - for (struct rtattr *rta = ERR_RTA(err, requestPayloadLength); RTA_OK(rta, rtaLength); - rta = RTA_NEXT(rta, rtaLength)) + // Only extract inner TLV error if flag is set + if (msg->nlmsg_flags & NLM_F_ACK_TLVS) { - if (rta->rta_type == NLMSGERR_ATTR_MSG) - { - errorMsg = reinterpret_cast(RTA_DATA(rta)); - break; - } - else + rtaLength = NLMSG_PAYLOAD(msg, sizeof(struct nlmsgerr)) - requestPayloadLength; + + for (struct rtattr *rta = ERR_RTA(err, requestPayloadLength); RTA_OK(rta, rtaLength); + rta = RTA_NEXT(rta, rtaLength)) { - LogDebg("Ignoring netlink response attribute %d (request#%u)", rta->rta_type, requestSeq); + if (rta->rta_type == NLMSGERR_ATTR_MSG) + { + errorMsg = reinterpret_cast(RTA_DATA(rta)); + break; + } + else + { + LogDebg("Ignoring netlink response attribute %d (request#%u)", rta->rta_type, requestSeq); + } } } From 1c5ad3403d624c0e57b7a6462aa82d67bf46c690 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 23 Aug 2024 10:26:35 -0700 Subject: [PATCH 125/160] [core] replace `strcmp()` with `StringMatch()` (#10629) This commit replaces the use of `strcmp()` with `StringMatch()`, which is an OT-specific internal function for comparing strings. This ensures consistent use of internal helper functions. --- src/core/coap/coap.cpp | 5 ++-- src/core/common/heap_string.cpp | 2 +- src/core/common/string.cpp | 5 ++++ src/core/common/string.hpp | 14 ++++++++++- src/core/diags/factory_diags.cpp | 39 ++++++++++++++++--------------- src/core/net/srp_client.cpp | 2 +- src/core/radio/trel_interface.cpp | 4 ++-- src/core/utils/parse_cmdline.cpp | 2 +- 8 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp index 33369a327..177d2bd6e 100644 --- a/src/core/coap/coap.cpp +++ b/src/core/coap/coap.cpp @@ -35,6 +35,7 @@ #include "common/locator_getters.hpp" #include "common/log.hpp" #include "common/random.hpp" +#include "common/string.hpp" #include "instance/instance.hpp" #include "net/ip6.hpp" #include "net/udp6.hpp" @@ -1360,7 +1361,7 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo for (const ResourceBlockWise &resource : mBlockWiseResources) { - if (strcmp(resource.GetUriPath(), uriPath) != 0) + if (!StringMatch(resource.GetUriPath(), uriPath)) { continue; } @@ -1427,7 +1428,7 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo for (const Resource &resource : mResources) { - if (strcmp(resource.mUriPath, uriPath) == 0) + if (StringMatch(resource.mUriPath, uriPath)) { resource.HandleRequest(aMessage, aMessageInfo); error = kErrorNone; diff --git a/src/core/common/heap_string.cpp b/src/core/common/heap_string.cpp index 060bfa2d2..b286b3268 100644 --- a/src/core/common/heap_string.cpp +++ b/src/core/common/heap_string.cpp @@ -89,7 +89,7 @@ bool String::operator==(const char *aCString) const bool isEqual; VerifyOrExit((aCString != nullptr) && (mStringBuffer != nullptr), isEqual = (mStringBuffer == aCString)); - isEqual = (strcmp(mStringBuffer, aCString) == 0); + isEqual = StringMatch(mStringBuffer, aCString); exit: return isEqual; diff --git a/src/core/common/string.cpp b/src/core/common/string.cpp index 6117f67eb..b7f11de02 100644 --- a/src/core/common/string.cpp +++ b/src/core/common/string.cpp @@ -158,6 +158,11 @@ bool StringEndsWith(const char *aString, const char *aSubString, StringMatchMode return (subLen > 0) && (len >= subLen) && (Match(&aString[len - subLen], aSubString, aMode) != kNoMatch); } +bool StringMatch(const char *aFirstString, const char *aSecondString) +{ + return Match(aFirstString, aSecondString, kStringExactMatch) == kFullMatch; +} + bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatchMode aMode) { return Match(aFirstString, aSecondString, aMode) == kFullMatch; diff --git a/src/core/common/string.hpp b/src/core/common/string.hpp index 00f903c3d..360be44b5 100644 --- a/src/core/common/string.hpp +++ b/src/core/common/string.hpp @@ -153,6 +153,18 @@ bool StringEndsWith(const char *aString, char aChar); */ bool StringEndsWith(const char *aString, const char *aSubString, StringMatchMode aMode = kStringExactMatch); +/** + * Checks whether or not two null-terminated strings match exactly. + * + * @param[in] aFirstString A pointer to the first string. + * @param[in] aSecondString A pointer to the second string. + * + * @retval TRUE If @p aFirstString matches @p aSecondString. + * @retval FALSE If @p aFirstString does not match @p aSecondString. + * + */ +bool StringMatch(const char *aFirstString, const char *aSecondString); + /** * Checks whether or not two null-terminated strings match. * @@ -164,7 +176,7 @@ bool StringEndsWith(const char *aString, const char *aSubString, StringMatchMode * @retval FALSE If @p aFirstString does not match @p aSecondString using match mode @p aMode. * */ -bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatchMode aMode = kStringExactMatch); +bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatchMode aMode); /** * Copies a string into a given target buffer with a given size if it fits. diff --git a/src/core/diags/factory_diags.cpp b/src/core/diags/factory_diags.cpp index d5a0ee288..b6fe8645b 100644 --- a/src/core/diags/factory_diags.cpp +++ b/src/core/diags/factory_diags.cpp @@ -45,6 +45,7 @@ #include "common/as_core_type.hpp" #include "common/code_utils.hpp" #include "common/locator_getters.hpp" +#include "common/string.hpp" #include "instance/instance.hpp" #include "radio/radio.hpp" #include "utils/parse_cmdline.hpp" @@ -125,7 +126,7 @@ Error Diags::ProcessEcho(uint8_t aArgsLength, char *aArgs[]) { Output("%s\r\n", aArgs[0]); } - else if ((aArgsLength == 2) && (strcmp(aArgs[0], "-n") == 0)) + else if ((aArgsLength == 2) && StringMatch(aArgs[0], "-n")) { static constexpr uint8_t kReservedLen = 1; // 1 byte '\0' static constexpr uint16_t kOutputLen = OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE; @@ -297,7 +298,7 @@ Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[]) VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs); - if (strcmp(aArgs[0], "stop") == 0) + if (StringMatch(aArgs[0], "stop")) { otPlatAlarmMilliStop(&GetInstance()); mRepeatActive = false; @@ -414,7 +415,7 @@ Error Diags::ProcessStats(uint8_t aArgsLength, char *aArgs[]) VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); - if ((aArgsLength == 1) && (strcmp(aArgs[0], "clear") == 0)) + if ((aArgsLength == 1) && StringMatch(aArgs[0], "clear")) { mStats.Clear(); Output("stats cleared\r\n"); @@ -486,12 +487,12 @@ Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[]) VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs); - if (strcmp(aArgs[0], "sleep") == 0) + if (StringMatch(aArgs[0], "sleep")) { SuccessOrExit(error = Get().Sleep()); Output("set radio from receive to sleep \r\nstatus 0x%02x\r\n", error); } - else if (strcmp(aArgs[0], "receive") == 0) + else if (StringMatch(aArgs[0], "receive")) { SuccessOrExit(error = Get().Receive(mChannel)); SuccessOrExit(error = Get().SetTransmitPower(mTxPower)); @@ -500,7 +501,7 @@ Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[]) Output("set radio from sleep to receive on channel %d\r\nstatus 0x%02x\r\n", mChannel, error); } - else if (strcmp(aArgs[0], "state") == 0) + else if (StringMatch(aArgs[0], "state")) { otRadioState state = Get().GetState(); @@ -607,11 +608,11 @@ Error Diags::ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[]) VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs); - if (strcmp(aArgs[0], "start") == 0) + if (StringMatch(aArgs[0], "start")) { SuccessOrExit(error = otPlatDiagRadioTransmitCarrier(&GetInstance(), true)); } - else if (strcmp(aArgs[0], "stop") == 0) + else if (StringMatch(aArgs[0], "stop")) { SuccessOrExit(error = otPlatDiagRadioTransmitCarrier(&GetInstance(), false)); } @@ -628,11 +629,11 @@ Error Diags::ProcessStream(uint8_t aArgsLength, char *aArgs[]) VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs); - if (strcmp(aArgs[0], "start") == 0) + if (StringMatch(aArgs[0], "start")) { error = otPlatDiagRadioTransmitStream(&GetInstance(), true); } - else if (strcmp(aArgs[0], "stop") == 0) + else if (StringMatch(aArgs[0], "stop")) { error = otPlatDiagRadioTransmitStream(&GetInstance(), false); } @@ -722,11 +723,11 @@ Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[]) SuccessOrExit(error = GetRawPowerSetting(setting)); Output("%s\r\n", setting.ToString().AsCString()); } - else if (strcmp(aArgs[0], "enable") == 0) + else if (StringMatch(aArgs[0], "enable")) { SuccessOrExit(error = otPlatDiagRadioRawPowerSettingEnable(&GetInstance(), true)); } - else if (strcmp(aArgs[0], "disable") == 0) + else if (StringMatch(aArgs[0], "disable")) { SuccessOrExit(error = otPlatDiagRadioRawPowerSettingEnable(&GetInstance(), false)); } @@ -750,21 +751,21 @@ Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[]) bool level; otGpioMode mode; - if ((aArgsLength == 2) && (strcmp(aArgs[0], "get") == 0)) + if ((aArgsLength == 2) && StringMatch(aArgs[0], "get")) { SuccessOrExit(error = ParseLong(aArgs[1], value)); gpio = static_cast(value); SuccessOrExit(error = otPlatDiagGpioGet(gpio, &level)); Output("%d\r\n", level); } - else if ((aArgsLength == 3) && (strcmp(aArgs[0], "set") == 0)) + else if ((aArgsLength == 3) && StringMatch(aArgs[0], "set")) { SuccessOrExit(error = ParseLong(aArgs[1], value)); gpio = static_cast(value); SuccessOrExit(error = ParseBool(aArgs[2], level)); SuccessOrExit(error = otPlatDiagGpioSet(gpio, level)); } - else if ((aArgsLength >= 2) && (strcmp(aArgs[0], "mode") == 0)) + else if ((aArgsLength >= 2) && StringMatch(aArgs[0], "mode")) { SuccessOrExit(error = ParseLong(aArgs[1], value)); gpio = static_cast(value); @@ -781,11 +782,11 @@ Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[]) Output("out\r\n"); } } - else if ((aArgsLength == 3) && (strcmp(aArgs[2], "in") == 0)) + else if ((aArgsLength == 3) && StringMatch(aArgs[2], "in")) { SuccessOrExit(error = otPlatDiagGpioSetMode(gpio, OT_GPIO_MODE_INPUT)); } - else if ((aArgsLength == 3) && (strcmp(aArgs[2], "out") == 0)) + else if ((aArgsLength == 3) && StringMatch(aArgs[2], "out")) { SuccessOrExit(error = otPlatDiagGpioSetMode(gpio, OT_GPIO_MODE_OUTPUT)); } @@ -882,7 +883,7 @@ Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[]) // This `rcp` command is for debugging and testing only, building only when NDEBUG is not defined // so that it will be excluded from release build. #if OPENTHREAD_RADIO && !defined(NDEBUG) - if (aArgsLength > 0 && !strcmp(aArgs[0], "rcp")) + if (aArgsLength > 0 && StringMatch(aArgs[0], "rcp")) { aArgs++; aArgsLength--; @@ -897,7 +898,7 @@ Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[]) for (const Command &command : sCommands) { - if (strcmp(aArgs[0], command.mName) == 0) + if (StringMatch(aArgs[0], command.mName)) { error = (this->*command.mCommand)(aArgsLength - 1, (aArgsLength > 1) ? &aArgs[1] : nullptr); ExitNow(); diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index 0b4c66e32..c75500c9e 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -172,7 +172,7 @@ bool Client::Service::Matches(const Service &aOther) const // for use by `LinkedList::FindMatching()` to search within the // `mServices` list. - return (strcmp(GetName(), aOther.GetName()) == 0) && (strcmp(GetInstanceName(), aOther.GetInstanceName()) == 0); + return StringMatch(GetName(), aOther.GetName()) && StringMatch(GetInstanceName(), aOther.GetInstanceName()); } //--------------------------------------------------------------------- diff --git a/src/core/radio/trel_interface.cpp b/src/core/radio/trel_interface.cpp index dc94b4511..5441f0c37 100644 --- a/src/core/radio/trel_interface.cpp +++ b/src/core/radio/trel_interface.cpp @@ -271,14 +271,14 @@ Error Interface::ParsePeerInfoTxtData(const Peer::Info &aInfo, continue; } - if (strcmp(entry.mKey, kTxtRecordExtAddressKey) == 0) + if (StringMatch(entry.mKey, kTxtRecordExtAddressKey)) { VerifyOrExit(!parsedExtAddress, error = kErrorParse); VerifyOrExit(entry.mValueLength == sizeof(Mac::ExtAddress), error = kErrorParse); aExtAddress.Set(entry.mValue); parsedExtAddress = true; } - else if (strcmp(entry.mKey, kTxtRecordExtPanIdKey) == 0) + else if (StringMatch(entry.mKey, kTxtRecordExtPanIdKey)) { VerifyOrExit(!parsedExtPanId, error = kErrorParse); VerifyOrExit(entry.mValueLength == sizeof(MeshCoP::ExtendedPanId), error = kErrorParse); diff --git a/src/core/utils/parse_cmdline.cpp b/src/core/utils/parse_cmdline.cpp index 00b96047b..c05a0d621 100644 --- a/src/core/utils/parse_cmdline.cpp +++ b/src/core/utils/parse_cmdline.cpp @@ -317,7 +317,7 @@ Error ParseAsHexStringSegment(const char *&aString, uint16_t &aSize, uint8_t *aB uint16_t Arg::GetLength(void) const { return IsEmpty() ? 0 : static_cast(strlen(mString)); } -bool Arg::operator==(const char *aString) const { return !IsEmpty() && (strcmp(mString, aString) == 0); } +bool Arg::operator==(const char *aString) const { return !IsEmpty() && StringMatch(mString, aString); } void Arg::CopyArgsToStringArray(Arg aArgs[], char *aStrings[]) { From 5e34ab2eabba99ac3220b382008ec4d2223472be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 08:54:10 -0700 Subject: [PATCH 126/160] github-actions: bump actions/upload-artifact from 4.3.1 to 4.3.6 (#10639) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.1 to 4.3.6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/5d5d22a31266ced268874388b861e4b58bb5c2f3...834a144ee995460fba8ed112a2fc961b36a5ec5a) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/fuzz.yml | 2 +- .github/workflows/otbr.yml | 12 +++++------ .github/workflows/otns.yml | 12 +++++------ .github/workflows/posix.yml | 18 ++++++++--------- .github/workflows/scorecards.yml | 2 +- .github/workflows/simulation-1.1.yml | 28 +++++++++++++------------- .github/workflows/simulation-1.2.yml | 30 ++++++++++++++-------------- .github/workflows/toranj.yml | 2 +- .github/workflows/unit.yml | 2 +- 9 files changed, 54 insertions(+), 54 deletions(-) diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index b0d80661c..c4ad11e2e 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -61,7 +61,7 @@ jobs: fuzz-seconds: 1800 dry-run: false - name: Upload Crash - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: failure() with: name: artifacts diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml index c3e58c13b..018a7f282 100644 --- a/.github/workflows/otbr.yml +++ b/.github/workflows/otbr.yml @@ -86,12 +86,12 @@ jobs: export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE" echo "CI_ENV=${CI_ENV}" sudo -E ./script/test cert_suite ./tests/scripts/thread-cert/backbone/*.py || (sudo chmod a+r ot_testing/* && false) - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-thread-1-3-backbone-docker path: /tmp/coverage/ retention-days: 1 - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: thread-1-3-backbone-results @@ -104,7 +104,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-thread-1-3-backbone path: tmp/coverage.info @@ -208,12 +208,12 @@ jobs: export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE" echo "CI_ENV=${CI_ENV}" sudo -E ./script/test cert_suite ${{ matrix.cert_scripts }} || (sudo chmod a+r ot_testing/* && false) - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-br-docker-${{ matrix.description }}-${{ matrix.otbr_mdns }}-${{matrix.otbr_trel}} path: /tmp/coverage/ retention-days: 1 - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: br-results-${{ matrix.description }}-${{ matrix.otbr_mdns }}-${{matrix.otbr_trel}} @@ -226,7 +226,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-br-${{ matrix.description }}-${{ matrix.otbr_mdns }}-${{matrix.otbr_trel}} path: tmp/coverage.info diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml index 74ddb0746..a624a972b 100644 --- a/.github/workflows/otns.yml +++ b/.github/workflows/otns.yml @@ -82,7 +82,7 @@ jobs: cd /tmp/otns ./script/test py-unittests ) - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: unittests-pcaps @@ -92,7 +92,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-otns-unittests path: tmp/coverage.info @@ -122,7 +122,7 @@ jobs: cd /tmp/otns ./script/test py-examples ) - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: examples-pcaps @@ -132,7 +132,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-otns-examples path: tmp/coverage.info @@ -184,7 +184,7 @@ jobs: cd /tmp/otns ./script/test stress-tests ${{ matrix.suite }} ) - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: stress-tests-${{ matrix.suite }}-pcaps @@ -194,7 +194,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-otns-stress-tests-${{ matrix.suite }} path: tmp/coverage.info diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index 3f251a8d6..a8175e0fd 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -76,7 +76,7 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED_RCP=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() && env.CRASHED_RCP == '1' }} with: name: core-expect-rcp @@ -85,7 +85,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-expects-linux-1 path: tmp/coverage.info @@ -109,13 +109,13 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED_TUN=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() && env.CRASHED_TUN == '1' }} with: name: core-expect-linux path: | ./ot-core-dump/* - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: syslog-expect-linux @@ -123,7 +123,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-expects-linux-2 path: tmp/coverage.info @@ -156,7 +156,7 @@ jobs: - name: Run run: | MAX_JOBS=$(getconf _NPROCESSORS_ONLN) ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: thread-cert @@ -164,7 +164,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-thread-cert path: tmp/coverage.info @@ -214,7 +214,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-pty-linux-${{ matrix.OT_DAEMON }} path: tmp/coverage.info @@ -282,7 +282,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-rcp-stack-reset path: tmp/coverage.info diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index f177b6dd6..54d4824c6 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -87,7 +87,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v3.1.0 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v3.1.0 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml index ed491312c..b4e4bf4a1 100644 --- a/.github/workflows/simulation-1.1.yml +++ b/.github/workflows/simulation-1.1.yml @@ -76,7 +76,7 @@ jobs: - name: Run run: | ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: packet-verification-pcaps @@ -86,7 +86,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-packet-verification path: tmp/coverage.info @@ -122,7 +122,7 @@ jobs: - name: Run run: | ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: cli-ftd-thread-cert @@ -130,7 +130,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-cli-ftd path: tmp/coverage.info @@ -173,7 +173,7 @@ jobs: - name: Run run: | ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: cli-mtd-thread-cert @@ -181,7 +181,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-cli-mtd-${{ matrix.message_use_heap }} path: tmp/coverage.info @@ -217,7 +217,7 @@ jobs: - name: Run run: | ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: cli-time-sync-thread-cert @@ -225,7 +225,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-cli-time-sync path: tmp/coverage.info @@ -259,7 +259,7 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED_CLI=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() && env.CRASHED_CLI == '1' }} with: name: core-expect-cli @@ -268,7 +268,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-expects path: tmp/coverage.info @@ -318,7 +318,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-ot-commissioner path: tmp/coverage.info @@ -350,7 +350,7 @@ jobs: - name: Run run: | ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: ot_testing @@ -358,7 +358,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-multiple-instance path: tmp/coverage.info @@ -386,7 +386,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-simulation-local-host path: tmp/coverage.info diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml index 1ac5c1c0d..e2a20d39f 100644 --- a/.github/workflows/simulation-1.2.yml +++ b/.github/workflows/simulation-1.2.yml @@ -95,12 +95,12 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }}-pcaps path: "*.pcap" - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() && env.CRASHED == '1' }} with: name: core-packet-verification-thread-1-3 @@ -109,7 +109,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage "${{ matrix.compiler.gcov }}" - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }} path: tmp/coverage.info @@ -166,14 +166,14 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: packet-verification-low-power-pcaps path: | *.pcap *.json - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() && env.CRASHED == '1' }} with: name: core-packet-verification-low-power @@ -182,7 +182,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-packet-verification-low-power path: tmp/coverage.info @@ -220,7 +220,7 @@ jobs: - name: Run run: | ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: packet-verification-1.1-on-1.3-pcaps @@ -230,7 +230,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-packet-verification-1-1-on-1-3 path: tmp/coverage.info @@ -266,7 +266,7 @@ jobs: run: | ulimit -c unlimited ./script/test cert_suite ./tests/scripts/thread-cert/addon_test_channel_manager_autocsl*.py - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: channel-manager-csl @@ -274,7 +274,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-channel-manager-csl path: tmp/coverage.info @@ -310,7 +310,7 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() && env.CRASHED == '1' }} with: name: core-expect-1-3 @@ -319,7 +319,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-expects path: tmp/coverage.info @@ -369,12 +369,12 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: name: thread-1-3-posix-pcaps path: "*.pcap" - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() && env.CRASHED == '1' }} with: name: core-thread-1-3-posix @@ -383,7 +383,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-thread-1-3-posix path: tmp/coverage.info diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml index ac2cb1597..96cc0fb7c 100644 --- a/.github/workflows/toranj.yml +++ b/.github/workflows/toranj.yml @@ -111,7 +111,7 @@ jobs: if: "matrix.TORANJ_RADIO != 'multi'" run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: "matrix.TORANJ_RADIO != 'multi'" with: name: cov-toranj-cli-${{ matrix.TORANJ_RADIO }} diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 3604c718e..c66c8e463 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -93,7 +93,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: cov-unit-tests path: tmp/coverage.info From 3c6b8a3dd202c83f88d6a38c328b382311380b94 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Tue, 27 Aug 2024 11:26:31 +0800 Subject: [PATCH 127/160] [time] unify macros for time unit conversion (#10636) This commit unifies macros used for time unit conversion so that we don't need to define it on different components. --- examples/platforms/simulation/alarm.c | 24 ++++++++----------- examples/platforms/simulation/radio.c | 7 ++---- .../simulation/virtual_time/alarm-sim.c | 7 +++--- include/openthread/instance.h | 2 +- include/openthread/platform/time.h | 5 ++++ src/posix/platform/alarm.cpp | 20 ++++++++-------- src/posix/platform/hdlc_interface.cpp | 20 ++++++++-------- src/posix/platform/multicast_routing.cpp | 4 ++-- src/posix/platform/platform-posix.h | 13 ---------- src/posix/platform/radio.cpp | 6 ++--- src/posix/platform/spi_interface.cpp | 4 ++-- 11 files changed, 48 insertions(+), 64 deletions(-) diff --git a/examples/platforms/simulation/alarm.c b/examples/platforms/simulation/alarm.c index d826d111d..3a1518064 100644 --- a/examples/platforms/simulation/alarm.c +++ b/examples/platforms/simulation/alarm.c @@ -51,14 +51,10 @@ timer_t sMicroTimer; #include #include #include +#include #include "lib/platform/exit_code.h" -#define MS_PER_S 1000 -#define NS_PER_US 1000 -#define US_PER_MS 1000 -#define US_PER_S 1000000 - #define DEFAULT_TIMEOUT_IN_SEC 10 // seconds #ifdef CLOCK_MONOTONIC_RAW @@ -146,7 +142,7 @@ uint64_t platformGetNow(void) VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO); - return (uint64_t)now.tv_sec * sSpeedUpFactor * US_PER_S + (uint64_t)now.tv_nsec * sSpeedUpFactor / NS_PER_US; + return (uint64_t)now.tv_sec * sSpeedUpFactor * OT_US_PER_S + (uint64_t)now.tv_nsec * sSpeedUpFactor / OT_NS_PER_US; } #else uint64_t platformGetNow(void) @@ -158,11 +154,11 @@ uint64_t platformGetNow(void) assert(err == 0); - return (uint64_t)tv.tv_sec * sSpeedUpFactor * US_PER_S + (uint64_t)tv.tv_usec * sSpeedUpFactor; + return (uint64_t)tv.tv_sec * sSpeedUpFactor * OT_US_PER_S + (uint64_t)tv.tv_usec * sSpeedUpFactor; } #endif // defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC) -uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(platformGetNow() / US_PER_MS); } +uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(platformGetNow() / OT_US_PER_MS); } void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { @@ -193,8 +189,8 @@ void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) struct itimerspec its; uint32_t diff = sUsAlarm - otPlatAlarmMicroGetNow(); - its.it_value.tv_sec = diff / US_PER_S; - its.it_value.tv_nsec = (diff % US_PER_S) * NS_PER_US; + its.it_value.tv_sec = diff / OT_US_PER_S; + its.it_value.tv_nsec = (diff % OT_US_PER_S) * OT_NS_PER_US; its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; @@ -229,7 +225,7 @@ void otPlatAlarmMicroStop(otInstance *aInstance) void platformAlarmUpdateTimeout(struct timeval *aTimeout) { - uint64_t remaining = DEFAULT_TIMEOUT_IN_SEC * US_PER_S; // in usec. + uint64_t remaining = DEFAULT_TIMEOUT_IN_SEC * OT_US_PER_S; // in usec. assert(aTimeout != NULL); @@ -237,7 +233,7 @@ void platformAlarmUpdateTimeout(struct timeval *aTimeout) { uint32_t msRemaining = calculateDuration(sMsAlarm, otPlatAlarmMilliGetNow()); - remaining = ((uint64_t)msRemaining) * US_PER_MS; + remaining = ((uint64_t)msRemaining) * OT_US_PER_MS; } if (sIsUsRunning) @@ -264,8 +260,8 @@ void platformAlarmUpdateTimeout(struct timeval *aTimeout) remaining = 1; } - aTimeout->tv_sec = (time_t)(remaining / US_PER_S); - aTimeout->tv_usec = remaining % US_PER_S; + aTimeout->tv_sec = (time_t)(remaining / OT_US_PER_S); + aTimeout->tv_usec = remaining % OT_US_PER_S; } } diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index 9e547e1c2..430440bc5 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -47,9 +47,6 @@ #include "utils/mac_frame.h" #include "utils/soft_source_match_table.h" -#define MS_PER_S 1000 -#define US_PER_MS 1000 - enum { IEEE802154_ACK_LENGTH = 5, @@ -814,8 +811,8 @@ void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct ti { uint32_t remaining = sEnergyScanEndTime - now; - tv.tv_sec = remaining / MS_PER_S; - tv.tv_usec = (remaining % MS_PER_S) * US_PER_MS; + tv.tv_sec = remaining / OT_MS_PER_S; + tv.tv_usec = (remaining % OT_MS_PER_S) * OT_US_PER_MS; } if (timercmp(&tv, aTimeout, <)) diff --git a/examples/platforms/simulation/virtual_time/alarm-sim.c b/examples/platforms/simulation/virtual_time/alarm-sim.c index 7c2cea1e5..b0d786218 100644 --- a/examples/platforms/simulation/virtual_time/alarm-sim.c +++ b/examples/platforms/simulation/virtual_time/alarm-sim.c @@ -37,8 +37,7 @@ #include #include #include - -#define US_PER_MS 1000 +#include extern uint64_t sNow; // microseconds @@ -59,7 +58,7 @@ uint64_t platformAlarmGetNow(void) { return sNow; } void platformAlarmAdvanceNow(uint64_t aDelta) { sNow += aDelta; } -uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(sNow / US_PER_MS); } +uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(sNow / OT_US_PER_MS); } void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { @@ -108,7 +107,7 @@ uint64_t platformAlarmGetNext(void) else { remaining = (uint64_t)milli; - remaining *= US_PER_MS; + remaining *= OT_US_PER_MS; } } diff --git a/include/openthread/instance.h b/include/openthread/instance.h index bbc4d4d72..d3cdebb12 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (436) +#define OPENTHREAD_API_VERSION (437) /** * @addtogroup api-instance diff --git a/include/openthread/platform/time.h b/include/openthread/platform/time.h index 1766cc559..7ce36519a 100644 --- a/include/openthread/platform/time.h +++ b/include/openthread/platform/time.h @@ -41,6 +41,11 @@ extern "C" { #endif +#define OT_MS_PER_S 1000 ///< Number of milliseconds per second +#define OT_US_PER_MS 1000 ///< Number of microseconds per millisecond +#define OT_US_PER_S 1000000 ///< Number of microseconds per second +#define OT_NS_PER_US 1000 ///< Number of nanoseconds per microsecond + /** * @addtogroup plat-time * diff --git a/src/posix/platform/alarm.cpp b/src/posix/platform/alarm.cpp index a739a56b9..3c2f7012d 100644 --- a/src/posix/platform/alarm.cpp +++ b/src/posix/platform/alarm.cpp @@ -83,7 +83,7 @@ uint64_t otPlatTimeGet(void) VerifyOrDie(clock_gettime(OT_POSIX_CLOCK_ID, &now) == 0, OT_EXIT_FAILURE); - return static_cast(now.tv_sec) * US_PER_S + static_cast(now.tv_nsec) / NS_PER_US; + return static_cast(now.tv_sec) * OT_US_PER_S + static_cast(now.tv_nsec) / OT_NS_PER_US; } #endif // !OPENTHREAD_POSIX_VIRTUAL_TIME @@ -128,7 +128,7 @@ void platformAlarmInit(uint32_t aSpeedUpFactor, int aRealTimeSignal) } } -uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(platformAlarmGetNow() / US_PER_MS); } +uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(platformAlarmGetNow() / OT_US_PER_MS); } void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { @@ -161,8 +161,8 @@ void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) struct itimerspec its; uint32_t diff = sUsAlarm - otPlatAlarmMicroGetNow(); - its.it_value.tv_sec = diff / US_PER_S; - its.it_value.tv_nsec = (diff % US_PER_S) * NS_PER_US; + its.it_value.tv_sec = diff / OT_US_PER_S; + its.it_value.tv_nsec = (diff % OT_US_PER_S) * OT_NS_PER_US; its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; @@ -204,10 +204,10 @@ void platformAlarmUpdateTimeout(struct timeval *aTimeout) if (sIsMsRunning) { - remaining = (int32_t)(sMsAlarm - (uint32_t)(now / US_PER_MS)); + remaining = (int32_t)(sMsAlarm - (uint32_t)(now / OT_US_PER_MS)); VerifyOrExit(remaining > 0); - remaining *= US_PER_MS; - remaining -= (now % US_PER_MS); + remaining *= OT_US_PER_MS; + remaining -= (now % OT_US_PER_MS); } #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE @@ -237,10 +237,10 @@ void platformAlarmUpdateTimeout(struct timeval *aTimeout) remaining = 1; } - if (remaining < static_cast(aTimeout->tv_sec) * US_PER_S + static_cast(aTimeout->tv_usec)) + if (remaining < static_cast(aTimeout->tv_sec) * OT_US_PER_S + static_cast(aTimeout->tv_usec)) { - aTimeout->tv_sec = static_cast(remaining / US_PER_S); - aTimeout->tv_usec = static_cast(remaining % US_PER_S); + aTimeout->tv_sec = static_cast(remaining / OT_US_PER_S); + aTimeout->tv_usec = static_cast(remaining % OT_US_PER_S); } } } diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp index a1a6c8c9a..235af9ac8 100644 --- a/src/posix/platform/hdlc_interface.cpp +++ b/src/posix/platform/hdlc_interface.cpp @@ -283,8 +283,8 @@ otError HdlcInterface::WaitForFrame(uint64_t aTimeoutUs) #if OPENTHREAD_POSIX_VIRTUAL_TIME struct VirtualTimeEvent event; - timeout.tv_sec = static_cast(aTimeoutUs / US_PER_S); - timeout.tv_usec = static_cast(aTimeoutUs % US_PER_S); + timeout.tv_sec = static_cast(aTimeoutUs / OT_US_PER_S); + timeout.tv_usec = static_cast(aTimeoutUs % OT_US_PER_S); virtualTimeSendSleepEvent(&timeout); virtualTimeReceiveEvent(&event); @@ -304,8 +304,8 @@ otError HdlcInterface::WaitForFrame(uint64_t aTimeoutUs) break; } #else // OPENTHREAD_POSIX_VIRTUAL_TIME - timeout.tv_sec = static_cast(aTimeoutUs / US_PER_S); - timeout.tv_usec = static_cast(aTimeoutUs % US_PER_S); + timeout.tv_sec = static_cast(aTimeoutUs / OT_US_PER_S); + timeout.tv_usec = static_cast(aTimeoutUs % OT_US_PER_S); fd_set read_fds; fd_set error_fds; @@ -392,7 +392,7 @@ otError HdlcInterface::WaitForWritable(void) otError error = OT_ERROR_NONE; struct timeval timeout = {kMaxWaitTime / 1000, (kMaxWaitTime % 1000) * 1000}; uint64_t now = otPlatTimeGet(); - uint64_t end = now + kMaxWaitTime * US_PER_MS; + uint64_t end = now + kMaxWaitTime * OT_US_PER_MS; fd_set writeFds; fd_set errorFds; int rval; @@ -432,8 +432,8 @@ otError HdlcInterface::WaitForWritable(void) { uint64_t remain = end - now; - timeout.tv_sec = static_cast(remain / US_PER_S); - timeout.tv_usec = static_cast(remain % US_PER_S); + timeout.tv_sec = static_cast(remain / OT_US_PER_S); + timeout.tv_usec = static_cast(remain % OT_US_PER_S); } else { @@ -744,10 +744,10 @@ otError HdlcInterface::ResetConnection(void) if (mRadioUrl.HasParam("uart-reset")) { - usleep(static_cast(kRemoveRcpDelay) * US_PER_MS); + usleep(static_cast(kRemoveRcpDelay) * OT_US_PER_MS); CloseFile(); - end = otPlatTimeGet() + kResetTimeout * US_PER_MS; + end = otPlatTimeGet() + kResetTimeout * OT_US_PER_MS; do { mSockFd = OpenFile(mRadioUrl); @@ -755,7 +755,7 @@ otError HdlcInterface::ResetConnection(void) { ExitNow(); } - usleep(static_cast(kOpenFileDelay) * US_PER_MS); + usleep(static_cast(kOpenFileDelay) * OT_US_PER_MS); } while (end > otPlatTimeGet()); LogCrit("Failed to reopen UART connection after resetting the RCP device."); diff --git a/src/posix/platform/multicast_routing.cpp b/src/posix/platform/multicast_routing.cpp index ffac62157..16aab11d5 100644 --- a/src/posix/platform/multicast_routing.cpp +++ b/src/posix/platform/multicast_routing.cpp @@ -400,7 +400,7 @@ void MulticastRoutingManager::ExpireMulticastForwardingCache(void) uint64_t now = otPlatTimeGet(); struct mf6cctl mf6cctl; - VerifyOrExit(now >= mLastExpireTime + kMulticastForwardingCacheExpiringInterval * US_PER_S); + VerifyOrExit(now >= mLastExpireTime + kMulticastForwardingCacheExpiringInterval * OT_US_PER_S); mLastExpireTime = now; @@ -409,7 +409,7 @@ void MulticastRoutingManager::ExpireMulticastForwardingCache(void) for (MulticastForwardingCache &mfc : mMulticastForwardingCacheTable) { - if (mfc.IsValid() && mfc.mLastUseTime + kMulticastForwardingCacheExpireTimeout * US_PER_S < now) + if (mfc.IsValid() && mfc.mLastUseTime + kMulticastForwardingCacheExpireTimeout * OT_US_PER_S < now) { if (!UpdateMulticastRouteInfo(mfc)) { diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h index 822f3f48a..177106db7 100644 --- a/src/posix/platform/platform-posix.h +++ b/src/posix/platform/platform-posix.h @@ -127,19 +127,6 @@ void platformAlarmProcess(otInstance *aInstance); */ int32_t platformAlarmGetNext(void); -#ifndef MS_PER_S -#define MS_PER_S 1000 -#endif -#ifndef US_PER_MS -#define US_PER_MS 1000 -#endif -#ifndef US_PER_S -#define US_PER_S (MS_PER_S * US_PER_MS) -#endif -#ifndef NS_PER_US -#define NS_PER_US 1000 -#endif - /** * Advances the alarm time by @p aDelta. * diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index b76d0aa08..6bc33c2f0 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -348,11 +348,11 @@ void platformRadioUpdateFdSet(otSysMainloopContext *aContext) { uint64_t remain = deadline - now; - if (remain < (static_cast(aContext->mTimeout.tv_sec) * US_PER_S + + if (remain < (static_cast(aContext->mTimeout.tv_sec) * OT_US_PER_S + static_cast(aContext->mTimeout.tv_usec))) { - aContext->mTimeout.tv_sec = static_cast(remain / US_PER_S); - aContext->mTimeout.tv_usec = static_cast(remain % US_PER_S); + aContext->mTimeout.tv_sec = static_cast(remain / OT_US_PER_S); + aContext->mTimeout.tv_usec = static_cast(remain % OT_US_PER_S); } } else diff --git a/src/posix/platform/spi_interface.cpp b/src/posix/platform/spi_interface.cpp index 2040cd58e..4c196f195 100644 --- a/src/posix/platform/spi_interface.cpp +++ b/src/posix/platform/spi_interface.cpp @@ -748,8 +748,8 @@ otError SpiInterface::WaitForFrame(uint64_t aTimeoutUs) int ret; context.mMaxFd = -1; - context.mTimeout.tv_sec = static_cast((end - now) / US_PER_S); - context.mTimeout.tv_usec = static_cast((end - now) % US_PER_S); + context.mTimeout.tv_sec = static_cast((end - now) / OT_US_PER_S); + context.mTimeout.tv_usec = static_cast((end - now) % OT_US_PER_S); FD_ZERO(&context.mReadFdSet); FD_ZERO(&context.mWriteFdSet); From abb6934cddc61ac242eb8d7fa41fd68d31a4cc4a Mon Sep 17 00:00:00 2001 From: Rongli Sun Date: Tue, 27 Aug 2024 11:31:46 +0800 Subject: [PATCH 128/160] [border-agent] directly respond to MGMT_COMMISSIONER_GET from non-active commissioner (#10632) --- src/core/meshcop/border_agent.cpp | 35 ++++++++------- src/core/meshcop/border_agent.hpp | 2 +- src/core/thread/network_data_leader.cpp | 46 +++++++++++++++++++ src/core/thread/network_data_leader.hpp | 10 +++++ src/core/thread/network_data_leader_ftd.cpp | 50 ++++----------------- 5 files changed, 86 insertions(+), 57 deletions(-) diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index 6ae830f50..a4c184eec 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -468,7 +468,7 @@ void BorderAgent::HandleTmf(Coap::Message &aMessage, c template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriCommissionerGet)); + HandleTmfDatasetGet(aMessage, aMessageInfo, kUriCommissionerGet); } template <> @@ -479,7 +479,7 @@ void BorderAgent::HandleTmf(Coap::Message &aMessage, const template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - HandleTmfDatasetGet(aMessage, aMessageInfo, Dataset::kActive); + HandleTmfDatasetGet(aMessage, aMessageInfo, kUriActiveGet); mCounters.mMgmtActiveGets++; } @@ -490,7 +490,7 @@ template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - HandleTmfDatasetGet(aMessage, aMessageInfo, Dataset::kPending); + HandleTmfDatasetGet(aMessage, aMessageInfo, kUriPendingGet); mCounters.mMgmtPendingGets++; } @@ -610,18 +610,14 @@ Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::Mes return error; } -void BorderAgent::HandleTmfDatasetGet(Coap::Message &aMessage, - const Ip6::MessageInfo &aMessageInfo, - Dataset::Type aType) +void BorderAgent::HandleTmfDatasetGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri) { Error error = kErrorNone; Coap::Message *response = nullptr; if (mState == kStateAccepted) { - Uri uri = (aType == Dataset::kActive) ? kUriActiveGet : kUriPendingGet; - - IgnoreError(ForwardToLeader(aMessage, aMessageInfo, uri)); + IgnoreError(ForwardToLeader(aMessage, aMessageInfo, aUri)); ExitNow(); } @@ -629,23 +625,32 @@ void BorderAgent::HandleTmfDatasetGet(Coap::Message &aMessage, // the Security Policy flags (O-bit) should be ignore to allow // the commissioner candidate to get the full Operational Dataset. - if (aType == Dataset::kActive) + switch (aUri) { + case kUriActiveGet: response = Get().ProcessGetRequest(aMessage, DatasetManager::kIgnoreSecurityPolicyFlags); - } - else - { + break; + + case kUriPendingGet: response = Get().ProcessGetRequest(aMessage, DatasetManager::kIgnoreSecurityPolicyFlags); + break; + + case kUriCommissionerGet: + response = Get().ProcessCommissionerGetRequest(aMessage); + break; + + default: + break; } VerifyOrExit(response != nullptr, error = kErrorParse); SuccessOrExit(error = Get().SendMessage(*response, aMessageInfo)); - LogInfo("Sent %sGet response to non-active commissioner", Dataset::TypeToString(aType)); + LogInfo("Sent %s response to non-active commissioner", PathForUri(aUri)); exit: - LogWarnOnError(error, "send Active/PendingGet response"); + LogWarnOnError(error, "send Active/Pending/CommissionerGet response"); FreeMessageOnError(response, error); } diff --git a/src/core/meshcop/border_agent.hpp b/src/core/meshcop/border_agent.hpp index 1cc57e55f..35ee6b79b 100644 --- a/src/core/meshcop/border_agent.hpp +++ b/src/core/meshcop/border_agent.hpp @@ -298,7 +298,7 @@ class BorderAgent : public InstanceLocator, private NonCopyable template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - void HandleTmfDatasetGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Dataset::Type aType); + void HandleTmfDatasetGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri); void HandleTimeout(void); #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp index ead2a95a7..4a75f13af 100644 --- a/src/core/thread/network_data_leader.cpp +++ b/src/core/thread/network_data_leader.cpp @@ -581,6 +581,52 @@ void Leader::GetCommissioningDataset(MeshCoP::CommissioningDataset &aDataset) co return; } +Coap::Message *Leader::ProcessCommissionerGetRequest(const Coap::Message &aMessage) const +{ + Error error = kErrorNone; + Coap::Message *response = nullptr; + OffsetRange offsetRange; + + response = Get().NewPriorityResponseMessage(aMessage); + VerifyOrExit(response != nullptr, error = kErrorNoBufs); + + if (Tlv::FindTlvValueOffsetRange(aMessage, MeshCoP::Tlv::kGet, offsetRange) == kErrorNone) + { + // Append the requested sub-TLV types given in Get TLV. + + while (!offsetRange.IsEmpty()) + { + uint8_t type; + const MeshCoP::Tlv *subTlv; + + IgnoreError(aMessage.Read(offsetRange, type)); + offsetRange.AdvanceOffset(sizeof(type)); + + subTlv = FindCommissioningDataSubTlv(type); + + if (subTlv != nullptr) + { + SuccessOrExit(error = subTlv->AppendTo(*response)); + } + } + } + else + { + // Append all sub-TLVs in the Commissioning Data. + + const CommissioningDataTlv *dataTlv = FindCommissioningData(); + + if (dataTlv != nullptr) + { + SuccessOrExit(error = response->AppendBytes(dataTlv->GetValue(), dataTlv->GetLength())); + } + } + +exit: + FreeAndNullMessageOnError(response, error); + return response; +} + Error Leader::FindBorderAgentRloc(uint16_t &aRloc16) const { return ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::kBorderAgentLocator, aRloc16); diff --git a/src/core/thread/network_data_leader.hpp b/src/core/thread/network_data_leader.hpp index 4cb109eef..4301bfaa7 100644 --- a/src/core/thread/network_data_leader.hpp +++ b/src/core/thread/network_data_leader.hpp @@ -188,6 +188,16 @@ class Leader : public MutableNetworkData, private NonCopyable */ void GetCommissioningDataset(MeshCoP::CommissioningDataset &aDataset) const; + /** + * Processes a MGMT_COMMISSIONER_GET request message and prepares the response. + * + * @param[in] aRequest The MGMT_COMMISSIONER_GET request message. + * + * @returns The prepared response, or `nullptr` if fails to parse the request or cannot allocate message. + * + */ + Coap::Message *ProcessCommissionerGetRequest(const Coap::Message &aMessage) const; + /** * Searches for given sub-TLV in Commissioning Data TLV. * diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp index 3768d385d..11699f6f5 100644 --- a/src/core/thread/network_data_leader_ftd.cpp +++ b/src/core/thread/network_data_leader_ftd.cpp @@ -317,53 +317,21 @@ template <> void Leader::HandleTmf(Coap::Message &aMessage, template <> void Leader::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { + Error error = kErrorNone; Coap::Message *response = nullptr; - OffsetRange offsetRange; - VerifyOrExit(Get().IsLeader() && !mWaitingForNetDataSync); - - response = Get().NewPriorityResponseMessage(aMessage); - VerifyOrExit(response != nullptr); - - if (Tlv::FindTlvValueOffsetRange(aMessage, MeshCoP::Tlv::kGet, offsetRange) == kErrorNone) - { - // Append the requested sub-TLV types given in Get TLV. - - while (!offsetRange.IsEmpty()) - { - uint8_t type; - const MeshCoP::Tlv *subTlv; - - IgnoreError(aMessage.Read(offsetRange, type)); - offsetRange.AdvanceOffset(sizeof(type)); - - subTlv = FindCommissioningDataSubTlv(type); - - if (subTlv != nullptr) - { - SuccessOrExit(subTlv->AppendTo(*response)); - } - } - } - else - { - // Append all sub-TLVs in the Commissioning Data. - - CommissioningDataTlv *dataTlv = FindCommissioningData(); - - if (dataTlv != nullptr) - { - SuccessOrExit(response->AppendBytes(dataTlv->GetValue(), dataTlv->GetLength())); - } - } + VerifyOrExit(Get().IsLeader() && !mWaitingForNetDataSync, error = kErrorInvalidState); - SuccessOrExit(Get().SendMessage(*response, aMessageInfo)); - response = nullptr; // `SendMessage` takes ownership on success + response = ProcessCommissionerGetRequest(aMessage); + VerifyOrExit(response != nullptr, error = kErrorParse); + SuccessOrExit(error = Get().SendMessage(*response, aMessageInfo)); - LogInfo("Sent %s response", UriToString()); + LogInfo("Sent %s response to %s", UriToString(), + aMessageInfo.GetPeerAddr().ToString().AsCString()); exit: - FreeMessage(response); + LogWarnOnError(error, "send CommissionerGet response"); + FreeMessageOnError(response, error); } void Leader::SendCommissioningSetResponse(const Coap::Message &aRequest, From 8f2ddf93c33f873f4c9886e4f56f8abf50eb0bd0 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 27 Aug 2024 08:18:45 -0700 Subject: [PATCH 129/160] [thread-cert] relax TLV type checks in `pktverify` (#10627) This commit modifies thread-cert scripts utilizing `pktverify` to adopt a more flexible approach to TLV type checking. Specifically, it replaces strict equality (`==`) or strict subset (`<`) checks with a subset or equal check (`<=`) when verifying the presence of TLVs in a message. This adjustment ensures that test scripts adhere to the principle of ignoring extra or unknown TLVs, thereby future-proofing them against potential protocol updates that might introduce new TLVs. --- .../thread-cert/Cert_5_1_01_RouterAttach.py | 6 ++-- .../Cert_5_1_04_RouterAddressReallocation.py | 2 +- .../Cert_5_1_05_RouterAddressTimeout.py | 10 +++---- .../thread-cert/Cert_5_1_06_RemoveRouterId.py | 6 ++-- .../Cert_5_1_12_NewRouterNeighborSync.py | 4 +-- .../thread-cert/Cert_5_1_13_RouterReset.py | 4 +-- .../thread-cert/Cert_5_2_01_REEDAttach.py | 10 +++---- .../Cert_5_2_03_LeaderReject2Hops.py | 8 ++--- .../thread-cert/Cert_5_2_04_REEDUpgrade.py | 2 +- .../thread-cert/Cert_5_2_05_AddressQuery.py | 6 ++-- .../Cert_5_2_06_RouterDowngrade.py | 2 +- .../Cert_5_2_07_REEDSynchronization.py | 2 +- .../thread-cert/Cert_5_3_03_AddressQuery.py | 2 +- .../Cert_5_3_07_DuplicateAddress.py | 2 +- .../thread-cert/Cert_5_5_02_LeaderReboot.py | 20 ++++++------- .../Cert_5_5_03_SplitMergeChildren.py | 18 +++++------ .../Cert_5_5_04_SplitMergeRouters.py | 2 +- .../Cert_5_5_07_SplitMergeThreeWay.py | 14 ++++----- ...1_NetworkDataRegisterBeforeAttachLeader.py | 8 ++--- ...2_NetworkDataRegisterBeforeAttachRouter.py | 8 ++--- ...03_NetworkDataRegisterAfterAttachLeader.py | 6 ++-- ...04_NetworkDataRegisterAfterAttachRouter.py | 6 ++-- ...05_NetworkDataRegisterAfterAttachRouter.py | 6 ++-- .../Cert_5_6_06_NetworkDataExpiration.py | 18 +++++------ .../Cert_5_6_09_NetworkDataForwarding.py | 16 +++++----- .../Cert_5_7_01_CoapDiagCommands.py | 22 +++++++------- .../Cert_5_8_04_SecurityPolicyTLV.py | 8 ++--- .../thread-cert/Cert_6_1_02_REEDAttach.py | 6 ++-- .../Cert_6_1_03_RouterAttachConnectivity.py | 2 +- .../Cert_6_1_04_REEDAttachConnectivity.py | 2 +- .../Cert_6_1_05_REEDAttachConnectivity.py | 4 +-- .../Cert_6_1_07_RouterAttachLinkQuality.py | 2 +- .../thread-cert/Cert_6_2_02_NewPartition.py | 2 +- .../Cert_6_3_02_NetworkDataUpdate.py | 6 ++-- .../Cert_6_5_01_ChildResetReattach.py | 2 +- .../Cert_6_5_02_ChildResetReattach.py | 2 +- .../Cert_6_5_03_ChildResetSynchronize.py | 2 +- .../Cert_7_1_01_BorderRouterAsLeader.py | 8 ++--- .../Cert_7_1_02_BorderRouterAsRouter.py | 4 +-- .../Cert_7_1_03_BorderRouterAsLeader.py | 6 ++-- .../Cert_7_1_04_BorderRouterAsRouter.py | 4 +-- .../Cert_7_1_05_BorderRouterAsRouter.py | 6 ++-- .../Cert_7_1_06_BorderRouterAsLeader.py | 4 +-- .../Cert_7_1_07_BorderRouterAsLeader.py | 6 ++-- .../thread-cert/Cert_8_1_02_Commissioning.py | 2 +- .../thread-cert/Cert_8_1_06_Commissioning.py | 2 +- .../thread-cert/Cert_8_2_01_JoinerRouter.py | 2 +- .../thread-cert/Cert_8_2_02_JoinerRouter.py | 2 +- .../thread-cert/Cert_8_2_05_JoinerRouter.py | 6 ++-- .../Cert_8_3_01_CommissionerPetition.py | 30 +++++++++---------- .../Cert_9_2_01_MGMTCommissionerGet.py | 8 ++--- .../Cert_9_2_02_MGMTCommissionerSet.py | 16 +++++----- .../Cert_9_2_03_ActiveDatasetGet.py | 6 ++-- .../thread-cert/Cert_9_2_07_DelayTimer.py | 6 ++-- .../Cert_9_2_08_PersistentDatasets.py | 4 +-- .../thread-cert/Cert_9_2_12_Announce.py | 4 +-- .../thread-cert/Cert_9_2_13_EnergyScan.py | 4 +-- .../Cert_9_2_15_PendingPartition.py | 8 ++--- .../Cert_9_2_16_ActivePendingPartition.py | 8 ++--- .../scripts/thread-cert/Cert_9_2_17_Orphan.py | 2 +- .../Cert_9_2_18_RollBackActiveTimestamp.py | 8 ++--- .../Cert_9_2_19_PendingDatasetGet.py | 4 +-- .../thread-cert/pktverify/packet_filter.py | 2 +- 63 files changed, 205 insertions(+), 205 deletions(-) diff --git a/tests/scripts/thread-cert/Cert_5_1_01_RouterAttach.py b/tests/scripts/thread-cert/Cert_5_1_01_RouterAttach.py index 0c8739948..d0ca9b67d 100755 --- a/tests/scripts/thread-cert/Cert_5_1_01_RouterAttach.py +++ b/tests/scripts/thread-cert/Cert_5_1_01_RouterAttach.py @@ -245,7 +245,7 @@ def verify(self, pv): filter(lambda p: { NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV - } == set(p.coap.tlv.type)\ + } <= set(p.coap.tlv.type)\ ).\ must_next() @@ -265,7 +265,7 @@ def verify(self, pv): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_ACK and\ p.thread_address.tlv.status == 0\ ).\ @@ -289,7 +289,7 @@ def verify(self, pv): LEADER_DATA_TLV, ROUTE64_TLV, SOURCE_ADDRESS_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ p.ipv6.hlim == 255).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_5_1_04_RouterAddressReallocation.py b/tests/scripts/thread-cert/Cert_5_1_04_RouterAddressReallocation.py index 183710243..c0cc2bbc0 100755 --- a/tests/scripts/thread-cert/Cert_5_1_04_RouterAddressReallocation.py +++ b/tests/scripts/thread-cert/Cert_5_1_04_RouterAddressReallocation.py @@ -256,7 +256,7 @@ def verify(self, pv): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_ACK and\ p.thread_address.tlv.rloc16 == _pkt_as.thread_address.tlv.rloc16 and\ diff --git a/tests/scripts/thread-cert/Cert_5_1_05_RouterAddressTimeout.py b/tests/scripts/thread-cert/Cert_5_1_05_RouterAddressTimeout.py index 3eef9c9d7..0d7d682c3 100755 --- a/tests/scripts/thread-cert/Cert_5_1_05_RouterAddressTimeout.py +++ b/tests/scripts/thread-cert/Cert_5_1_05_RouterAddressTimeout.py @@ -117,7 +117,7 @@ def verify(self, pv): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_ACK and\ p.thread_address.tlv.status == 0 ).\ @@ -196,7 +196,7 @@ def verify(self, pv): NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, NL_STATUS_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_POST and\ p.thread_address.tlv.rloc16 == _pkt_as.thread_address.tlv.rloc16 @@ -209,7 +209,7 @@ def verify(self, pv): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_ACK and\ p.thread_address.tlv.rloc16 != _pkt_as2.thread_address.tlv.rloc16 and\ @@ -288,7 +288,7 @@ def verify(self, pv): NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, NL_STATUS_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_POST and\ p.thread_address.tlv.rloc16 == _pkt_as3.thread_address.tlv.rloc16 @@ -301,7 +301,7 @@ def verify(self, pv): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_ACK and\ p.thread_address.tlv.rloc16 == _pkt_as3.thread_address.tlv.rloc16 and\ diff --git a/tests/scripts/thread-cert/Cert_5_1_06_RemoveRouterId.py b/tests/scripts/thread-cert/Cert_5_1_06_RemoveRouterId.py index 05a77b75e..738bc2b97 100755 --- a/tests/scripts/thread-cert/Cert_5_1_06_RemoveRouterId.py +++ b/tests/scripts/thread-cert/Cert_5_1_06_RemoveRouterId.py @@ -111,7 +111,7 @@ def verify(self, pv): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_ACK and\ p.thread_address.tlv.status == 0 ).\ @@ -162,7 +162,7 @@ def verify(self, pv): NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, NL_STATUS_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_POST and\ p.thread_address.tlv.rloc16 == _pkt_as.thread_address.tlv.rloc16 @@ -175,7 +175,7 @@ def verify(self, pv): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_ACK and\ p.thread_address.tlv.rloc16 != _pkt_as.thread_address.tlv.rloc16 and\ diff --git a/tests/scripts/thread-cert/Cert_5_1_12_NewRouterNeighborSync.py b/tests/scripts/thread-cert/Cert_5_1_12_NewRouterNeighborSync.py index 69470a4c5..589911949 100755 --- a/tests/scripts/thread-cert/Cert_5_1_12_NewRouterNeighborSync.py +++ b/tests/scripts/thread-cert/Cert_5_1_12_NewRouterNeighborSync.py @@ -122,7 +122,7 @@ def verify(self, pv): LEADER_DATA_TLV, ROUTE64_TLV, SOURCE_ADDRESS_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ p.ipv6.hlim == 255).\ must_next() @@ -134,7 +134,7 @@ def verify(self, pv): LEADER_DATA_TLV, ROUTE64_TLV, SOURCE_ADDRESS_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ p.ipv6.hlim == 255).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_5_1_13_RouterReset.py b/tests/scripts/thread-cert/Cert_5_1_13_RouterReset.py index a1c55ac35..4c0fc9bbc 100755 --- a/tests/scripts/thread-cert/Cert_5_1_13_RouterReset.py +++ b/tests/scripts/thread-cert/Cert_5_1_13_RouterReset.py @@ -122,7 +122,7 @@ def verify(self, pv): LEADER_DATA_TLV, ROUTE64_TLV, SOURCE_ADDRESS_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ p.ipv6.hlim == 255 ).\ must_next() @@ -133,7 +133,7 @@ def verify(self, pv): LEADER_DATA_TLV, ROUTE64_TLV, SOURCE_ADDRESS_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ p.ipv6.hlim == 255 ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_5_2_01_REEDAttach.py b/tests/scripts/thread-cert/Cert_5_2_01_REEDAttach.py index 27f4e5e9c..5c7b88606 100755 --- a/tests/scripts/thread-cert/Cert_5_2_01_REEDAttach.py +++ b/tests/scripts/thread-cert/Cert_5_2_01_REEDAttach.py @@ -144,7 +144,7 @@ def verify(self, pv): LEADER_DATA_TLV, ROUTE64_TLV, SOURCE_ADDRESS_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ p.ipv6.hlim == 255 ).\ must_next() @@ -243,7 +243,7 @@ def verify(self, pv): filter(lambda p: { NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV - } == set(p.coap.tlv.type)\ + } <= set(p.coap.tlv.type)\ ).\ must_next() @@ -263,7 +263,7 @@ def verify(self, pv): filter(lambda p: { NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV - } == set(p.coap.tlv.type)\ + } <= set(p.coap.tlv.type)\ ).\ must_next() @@ -274,7 +274,7 @@ def verify(self, pv): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_ACK and\ p.thread_address.tlv.status == 0\ ).\ @@ -286,7 +286,7 @@ def verify(self, pv): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_ACK and\ p.thread_address.tlv.status == 0\ ).\ diff --git a/tests/scripts/thread-cert/Cert_5_2_03_LeaderReject2Hops.py b/tests/scripts/thread-cert/Cert_5_2_03_LeaderReject2Hops.py index 6a261bfa7..4daf51d72 100755 --- a/tests/scripts/thread-cert/Cert_5_2_03_LeaderReject2Hops.py +++ b/tests/scripts/thread-cert/Cert_5_2_03_LeaderReject2Hops.py @@ -350,7 +350,7 @@ def verify(self, pv: PacketVerifier): filter(lambda p: { NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV - } == set(p.coap.tlv.type) + } <= set(p.coap.tlv.type) ).\ must_next() @@ -370,7 +370,7 @@ def verify(self, pv: PacketVerifier): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_ACK and\ p.thread_address.tlv.status == ADDR_SOL_SUCCESS\ ).\ @@ -387,7 +387,7 @@ def verify(self, pv: PacketVerifier): LEADER_DATA_TLV, ROUTE64_TLV, SOURCE_ADDRESS_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ len(p.mle.tlv.route64.cost) == 32 and\ p.ipv6.hlim == 255 ).\ @@ -403,7 +403,7 @@ def verify(self, pv: PacketVerifier): filter(lambda p: { NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV - } == set(p.coap.tlv.type) + } <= set(p.coap.tlv.type) ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py b/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py index 5a0506837..2591c8cad 100755 --- a/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py +++ b/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py @@ -238,7 +238,7 @@ def verify(self, pv): filter(lambda p: { NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV - } == set(p.coap.tlv.type) + } <= set(p.coap.tlv.type) ).\ must_not_next() diff --git a/tests/scripts/thread-cert/Cert_5_2_05_AddressQuery.py b/tests/scripts/thread-cert/Cert_5_2_05_AddressQuery.py index 903650a15..4422cd3f4 100755 --- a/tests/scripts/thread-cert/Cert_5_2_05_AddressQuery.py +++ b/tests/scripts/thread-cert/Cert_5_2_05_AddressQuery.py @@ -302,7 +302,7 @@ def verify(self, pv): NL_TARGET_EID_TLV, NL_RLOC16_TLV, NL_ML_EID_TLV - } == set(p.thread_address.tlv.type) + } <= set(p.thread_address.tlv.type) ).\ must_next() pkts.filter_ipv6_src_dst(REED_MLEID, MED_MLEID).\ @@ -330,7 +330,7 @@ def verify(self, pv): NL_TARGET_EID_TLV, NL_RLOC16_TLV, NL_ML_EID_TLV - } == set(p.thread_address.tlv.type) + } <= set(p.thread_address.tlv.type) ).\ must_next() pkts.filter_ipv6_src_dst(REED2001, MED2001).\ @@ -358,7 +358,7 @@ def verify(self, pv): NL_TARGET_EID_TLV, NL_RLOC16_TLV, NL_ML_EID_TLV - } == set(p.thread_address.tlv.type) + } <= set(p.thread_address.tlv.type) ).\ must_next() pkts.filter_ipv6_src_dst(REED2002, MED2002).\ diff --git a/tests/scripts/thread-cert/Cert_5_2_06_RouterDowngrade.py b/tests/scripts/thread-cert/Cert_5_2_06_RouterDowngrade.py index a8576e241..7c89d33ea 100755 --- a/tests/scripts/thread-cert/Cert_5_2_06_RouterDowngrade.py +++ b/tests/scripts/thread-cert/Cert_5_2_06_RouterDowngrade.py @@ -301,7 +301,7 @@ def verify(self, pv): filter(lambda p: { NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.thread_address.tlv.rloc16 == ROUTER_1_RLOC16 ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_5_2_07_REEDSynchronization.py b/tests/scripts/thread-cert/Cert_5_2_07_REEDSynchronization.py index 038fe6f50..6f16ab39e 100755 --- a/tests/scripts/thread-cert/Cert_5_2_07_REEDSynchronization.py +++ b/tests/scripts/thread-cert/Cert_5_2_07_REEDSynchronization.py @@ -198,7 +198,7 @@ def verify(self, pv): filter(lambda p: { NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV - } == set(p.coap.tlv.type) + } <= set(p.coap.tlv.type) ).\ must_not_next() diff --git a/tests/scripts/thread-cert/Cert_5_3_03_AddressQuery.py b/tests/scripts/thread-cert/Cert_5_3_03_AddressQuery.py index 403a39784..eb55ecd0e 100755 --- a/tests/scripts/thread-cert/Cert_5_3_03_AddressQuery.py +++ b/tests/scripts/thread-cert/Cert_5_3_03_AddressQuery.py @@ -212,7 +212,7 @@ def verify(self, pv): NL_ML_EID_TLV, NL_RLOC16_TLV, NL_TARGET_EID_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.thread_address.tlv.target_eid == ROUTER_3_MLEID and\ p.coap.code == COAP_CODE_POST ).\ diff --git a/tests/scripts/thread-cert/Cert_5_3_07_DuplicateAddress.py b/tests/scripts/thread-cert/Cert_5_3_07_DuplicateAddress.py index 0fd38b638..7a1cb77bf 100755 --- a/tests/scripts/thread-cert/Cert_5_3_07_DuplicateAddress.py +++ b/tests/scripts/thread-cert/Cert_5_3_07_DuplicateAddress.py @@ -208,7 +208,7 @@ def verify(self, pv): filter(lambda p: { NL_ML_EID_TLV, NL_TARGET_EID_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.thread_address.tlv.target_eid == IPV6_ADDR ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_5_5_02_LeaderReboot.py b/tests/scripts/thread-cert/Cert_5_5_02_LeaderReboot.py index 80aba8774..52804cffd 100755 --- a/tests/scripts/thread-cert/Cert_5_5_02_LeaderReboot.py +++ b/tests/scripts/thread-cert/Cert_5_5_02_LeaderReboot.py @@ -105,24 +105,24 @@ def verify(self, pv): _rpkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next() _lpkts = leader_pkts.range(_rpkts.index) _lpkts.filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type)) _rpkts.filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type)) # Step 4: Router_1 MUST attempt to reattach to its original partition by # sending MLE Parent Requests to the All-Routers multicast # address (FFxx::xx) with a hop limit of 255. MUST make two separate attempts for i in range(1, 3): _rpkts.filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set( + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 1) lreset_start = _rpkts.index # Step 6:Router_1 MUST attempt to attach to any other Partition # within range by sending a MLE Parent Request. _rpkts.filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set(p.mle.tlv.type)) + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set(p.mle.tlv.type)) lreset_stop = _rpkts.index # Step 3: The Leader MUST stop sending MLE advertisements. @@ -135,35 +135,35 @@ def verify(self, pv): # begin transmitting MLE Advertisements with _rpkts.save_index(): _rpkts.filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type)) # Step 8: Router_1 MUST respond with an MLE Child Update Response, # with the updated TLVs of the new partition _rpkts.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) # Step 9: The Leader MUST send properly formatted MLE Parent # Requests to the All-Routers multicast address _lpkts.range(lreset_stop).filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set(p.mle.tlv.type)) + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set(p.mle.tlv.type)) # Step 10: Router_1 MUST send an MLE Parent Response _rpkts.filter_mle_cmd(MLE_PARENT_RESPONSE).must_next().must_verify( lambda p: { SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, LINK_LAYER_FRAME_COUNTER_TLV, RESPONSE_TLV, CHALLENGE_TLV, LINK_MARGIN_TLV, CONNECTIVITY_TLV, VERSION_TLV - } < set(p.mle.tlv.type)) + } <= set(p.mle.tlv.type)) # Step 11: Leader send MLE Child ID Request _lpkts.filter_mle_cmd(MLE_CHILD_ID_REQUEST).must_next().must_verify( lambda p: { RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MODE_TLV, TIMEOUT_TLV, VERSION_TLV, TLV_REQUEST_TLV, ADDRESS16_TLV, NETWORK_DATA_TLV, ROUTE64_TLV, ACTIVE_TIMESTAMP_TLV - } < set(p.mle.tlv.type)) + } <= set(p.mle.tlv.type)) #Step 12: Router_1 send MLE Child ID Response _rpkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ADDRESS16_TLV, NETWORK_DATA_TLV, ROUTE64_TLV} < set( + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ADDRESS16_TLV, NETWORK_DATA_TLV, ROUTE64_TLV} <= set( p.mle.tlv.type)) #Step 13: Leader send an Address Solicit Request diff --git a/tests/scripts/thread-cert/Cert_5_5_03_SplitMergeChildren.py b/tests/scripts/thread-cert/Cert_5_5_03_SplitMergeChildren.py index 06f77f65d..8c8b40c07 100755 --- a/tests/scripts/thread-cert/Cert_5_5_03_SplitMergeChildren.py +++ b/tests/scripts/thread-cert/Cert_5_5_03_SplitMergeChildren.py @@ -143,15 +143,15 @@ def verify(self, pv): # Step 2: The Leader and Router_1 MUST send properly formatted MLE Advertisements pkts.filter_wpan_src64(LEADER).filter_LLANMA().filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type)) _pkt = pkts.filter_wpan_src64(ROUTER_1).filter_LLANMA().filter_mle_cmd(MLE_ADVERTISEMENT).must_next() _pkt.must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type) and p.ipv6.hlim == 255) + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type) and p.ipv6.hlim == 255) # Step 4: Router_1 MUST attempt to reattach to its original partition by # sending MLE Parent Requests to the All-Routers multicast address _router1_pkts.range(pkts.index).filter_LLARMA().filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set( + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 1 and p.ipv6.hlim == 255) lreset_start = _router1_pkts.index @@ -159,7 +159,7 @@ def verify(self, pv): # within range by sending a MLE Parent Request. _router1_pkts.filter_LLARMA().filter_mle_cmd(MLE_PARENT_REQUEST).filter( lambda p: p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 0).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set(p.mle.tlv.type + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set(p.mle.tlv.type ) and p.ipv6.hlim == 255) lreset_stop = _router1_pkts.index @@ -174,7 +174,7 @@ def verify(self, pv): # begin transmitting MLE Advertisements with _router1_pkts.save_index(): _router1_pkts.filter_LLANMA().filter_mle_cmd(MLE_ADVERTISEMENT).filter( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type) and p.mle.tlv. + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type) and p.mle.tlv. leader_data.partition_id != _pkt.mle.tlv.leader_data.partition_id and p.mle.tlv.leader_data. data_version != _pkt.mle.tlv.leader_data.data_version and p.mle.tlv.leader_data.stable_data_version != _pkt.mle.tlv.leader_data.stable_data_version and p.ipv6.hlim == 255).must_next() @@ -182,12 +182,12 @@ def verify(self, pv): # Step 9: Router_1 MUST respond with an MLE Child Update Response, # with the updated TLVs of the new partition _router1_pkts.filter_wpan_dst64(MED_2).filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) # Step 10: The Leader MUST send properly formatted MLE Parent # Requests to the All-Routers multicast address _lpkts.filter_LLARMA().filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set(p.mle.tlv.type + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set(p.mle.tlv.type ) and p.ipv6.hlim == 255) # Step 11: Leader send MLE Child ID Request to Router_2 @@ -195,11 +195,11 @@ def verify(self, pv): lambda p: { RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MODE_TLV, TIMEOUT_TLV, VERSION_TLV, TLV_REQUEST_TLV, ADDRESS16_TLV, NETWORK_DATA_TLV, ROUTE64_TLV, ACTIVE_TIMESTAMP_TLV - } < set(p.mle.tlv.type)) + } <= set(p.mle.tlv.type)) # Step 12: Leader send MLE ADVERTISEMENT _lpkts.filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type) and p.ipv6.hlim == 255) + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type) and p.ipv6.hlim == 255) # Step 13: Router_1 send an Address Solicit Request _router1_pkts.filter_coap_request(ADDR_SOL_URI).must_next().must_verify( diff --git a/tests/scripts/thread-cert/Cert_5_5_04_SplitMergeRouters.py b/tests/scripts/thread-cert/Cert_5_5_04_SplitMergeRouters.py index 2bcc87652..d507bf0f8 100755 --- a/tests/scripts/thread-cert/Cert_5_5_04_SplitMergeRouters.py +++ b/tests/scripts/thread-cert/Cert_5_5_04_SplitMergeRouters.py @@ -126,7 +126,7 @@ def verify(self, pv): # Step 2: The Leader MUST send properly formatted MLE Advertisements router1_pkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next() leader_pkts.range(router1_pkts.index).filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, ROUTE64_TLV, LEADER_DATA_TLV} == set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, ROUTE64_TLV, LEADER_DATA_TLV} <= set(p.mle.tlv.type)) router1_pkts.filter_mle_cmd(MLE_PARENT_REQUEST).must_next() lreset_start = router1_pkts.index diff --git a/tests/scripts/thread-cert/Cert_5_5_07_SplitMergeThreeWay.py b/tests/scripts/thread-cert/Cert_5_5_07_SplitMergeThreeWay.py index fbb9dee48..ce0c8a316 100755 --- a/tests/scripts/thread-cert/Cert_5_5_07_SplitMergeThreeWay.py +++ b/tests/scripts/thread-cert/Cert_5_5_07_SplitMergeThreeWay.py @@ -127,25 +127,25 @@ def verify(self, pv): leader_pkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next() _lpkts = leader_pkts.copy() _lpkts.filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type)) router1_pkts.range(leader_pkts.index).filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type)) # Step 4: Each router forms a partition with the lowest possible partition ID # Step 5: Router_1 MUST send MLE Parent Requests and MUST make two separate attempts router1_pkts.filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set( + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 1) lreset_start = router1_pkts.index router1_pkts.filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set( + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 1) # Step 7: Router_1 MUST attempt to attach to any other Partition # within range by sending a MLE Parent Request. router1_pkts.filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set(p.mle.tlv.type)) + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set(p.mle.tlv.type)) lreset_stop = router1_pkts.index # Step 3: The Leader MUST stop sending MLE advertisements. @@ -157,12 +157,12 @@ def verify(self, pv): # Step 8: Router_1 take over leader role of a new Partition and begin transmitting # MLE Advertisements router1_pkts.copy().filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type)) # Step 9: The Leader MUST send properly formatted MLE Parent Requests to the # All-Routers multicast address _lpkts.filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set(p.mle.tlv.type)) + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set(p.mle.tlv.type)) # Step 10: Router_1 MUST send an MLE Parent Response router1_pkts.filter_mle_cmd(MLE_PARENT_RESPONSE).must_next().must_verify( diff --git a/tests/scripts/thread-cert/Cert_5_6_01_NetworkDataRegisterBeforeAttachLeader.py b/tests/scripts/thread-cert/Cert_5_6_01_NetworkDataRegisterBeforeAttachLeader.py index 044df4892..d898a9b4e 100755 --- a/tests/scripts/thread-cert/Cert_5_6_01_NetworkDataRegisterBeforeAttachLeader.py +++ b/tests/scripts/thread-cert/Cert_5_6_01_NetworkDataRegisterBeforeAttachLeader.py @@ -129,14 +129,14 @@ def verify(self, pv): lambda p: { RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MODE_TLV, TIMEOUT_TLV, VERSION_TLV, TLV_REQUEST_TLV, ADDRESS16_TLV, NETWORK_DATA_TLV, ROUTE64_TLV - } < set(p.mle.tlv.type)) + } <= set(p.mle.tlv.type)) _rpkts_med = _rpkts.copy() _rpkts_sed = _rpkts.copy() # Step 6: The DUT MUST send an MLE Child ID Response to SED_1, # containing only stable Network Data _rpkts_sed.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).filter_wpan_dst64(SED).must_next().must_verify( - lambda p: {MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV} == set(p.thread_nwd.tlv.type) and + lambda p: {MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV} <= set(p.thread_nwd.tlv.type) and {Ipv6Addr('2001:2:0:1::')} == set(p.thread_nwd.tlv.prefix) and p.thread_nwd.tlv.border_router.flag.p == [1] and p.thread_nwd.tlv.border_router.flag.s == [1] and p.thread_nwd.tlv.border_router.flag.r == [1] and p .thread_nwd.tlv.border_router.flag.o == [1] and p.thread_nwd.tlv.stable == [1, 1, 1]) @@ -153,10 +153,10 @@ def verify(self, pv): # Response to each of MED_1 and SED_1 _rpkts_med.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).must_next().must_verify( lambda p: p.wpan.dst64 == MED and - {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) _rpkts_sed.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).must_next().must_verify( lambda p: p.wpan.dst64 == SED and - {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) # Step 11: MED_1 and SED_1 MUST respond to each ICMPv6 Echo Request # with an ICMPv6 Echo Reply diff --git a/tests/scripts/thread-cert/Cert_5_6_02_NetworkDataRegisterBeforeAttachRouter.py b/tests/scripts/thread-cert/Cert_5_6_02_NetworkDataRegisterBeforeAttachRouter.py index cfb983ce6..df67e4f26 100755 --- a/tests/scripts/thread-cert/Cert_5_6_02_NetworkDataRegisterBeforeAttachRouter.py +++ b/tests/scripts/thread-cert/Cert_5_6_02_NetworkDataRegisterBeforeAttachRouter.py @@ -121,14 +121,14 @@ def verify(self, pv): # Step 1: The DUT MUST send properly formatted MLE Advertisements _lpkts.filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {LEADER_DATA_TLV, ROUTE64_TLV, SOURCE_ADDRESS_TLV} == set(p.mle.tlv.type)) + lambda p: {LEADER_DATA_TLV, ROUTE64_TLV, SOURCE_ADDRESS_TLV} <= set(p.mle.tlv.type)) # Step 3: The DUT MUST properly attach Router_1 device to the network, # and transmit Network Data during the attach phase in the # Child ID Response frame of the Network Data TLV _lpkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next().must_verify(lambda p: p.wpan.dst64 == ROUTER and { SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ACTIVE_TIMESTAMP_TLV, ADDRESS16_TLV, NETWORK_DATA_TLV - } < set(p.mle.tlv.type)) + } <= set(p.mle.tlv.type)) # Step 5: The DUT Automatically sends a CoAP Response frame and # MLE Data Response message @@ -159,9 +159,9 @@ def verify(self, pv): # Step 10: The DUT MUST send a unicast MLE Child Update # Response to each of MED_1 and SED_1 _lpkts_med.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).filter_wpan_dst64(MED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) _lpkts_sed.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).filter_wpan_dst64(SED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/Cert_5_6_03_NetworkDataRegisterAfterAttachLeader.py b/tests/scripts/thread-cert/Cert_5_6_03_NetworkDataRegisterAfterAttachLeader.py index e5c6667e4..10e824875 100755 --- a/tests/scripts/thread-cert/Cert_5_6_03_NetworkDataRegisterAfterAttachLeader.py +++ b/tests/scripts/thread-cert/Cert_5_6_03_NetworkDataRegisterAfterAttachLeader.py @@ -134,12 +134,12 @@ def verify(self, pv): # Step 5: The DUT MUST send a unicast MLE Child Update # Response to MED_1 _rpkts_med.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).filter_wpan_dst64(MED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) # Step 6: The DUT MUST send a unicast MLE Child Update # Request to SED_1 _rpkts_sed.filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).filter_wpan_dst64(SED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} == set( + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} <= set( p.mle.tlv.type) and {Ipv6Addr('2001:2:0:1::')} == set(p.thread_nwd.tlv.prefix) and p.thread_nwd.tlv. border_router.flag.p == [1] and p.thread_nwd.tlv.border_router.flag.s == [1] and p.thread_nwd.tlv. border_router.flag.r == [1] and p.thread_nwd.tlv.border_router.flag.o == [1] and p.thread_nwd.tlv.stable == @@ -148,7 +148,7 @@ def verify(self, pv): # Step 8: The DUT MUST send a unicast MLE Child Update # Response to SED_1 _rpkts_sed.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).filter_wpan_dst64(SED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/Cert_5_6_04_NetworkDataRegisterAfterAttachRouter.py b/tests/scripts/thread-cert/Cert_5_6_04_NetworkDataRegisterAfterAttachRouter.py index ab3d9ca76..c43c92b67 100755 --- a/tests/scripts/thread-cert/Cert_5_6_04_NetworkDataRegisterAfterAttachRouter.py +++ b/tests/scripts/thread-cert/Cert_5_6_04_NetworkDataRegisterAfterAttachRouter.py @@ -141,12 +141,12 @@ def verify(self, pv): # Step 7: The DUT MUST send a unicast MLE Child Update # Response to MED_1 _lpkts_med.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).filter_wpan_dst64(MED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) # Step 9: The DUT MUST send a unicast MLE Child Update # Request to SED_1 _lpkts_sed.filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).filter_wpan_dst64(SED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} == set( + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} <= set( p.mle.tlv.type) and {Ipv6Addr('2001:2:0:1::')} == set(p.thread_nwd.tlv.prefix) and p.thread_nwd.tlv. border_router.flag.p == [1] and p.thread_nwd.tlv.border_router.flag.s == [1] and p.thread_nwd.tlv. border_router.flag.r == [1] and p.thread_nwd.tlv.border_router.flag.o == [1] and p.thread_nwd.tlv.stable == @@ -155,7 +155,7 @@ def verify(self, pv): # Step 11: The DUT MUST send a unicast MLE Child Update # Response to SED_1 _lpkts_sed.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).filter_wpan_dst64(SED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/Cert_5_6_05_NetworkDataRegisterAfterAttachRouter.py b/tests/scripts/thread-cert/Cert_5_6_05_NetworkDataRegisterAfterAttachRouter.py index 232ee23b2..b5f085f0b 100755 --- a/tests/scripts/thread-cert/Cert_5_6_05_NetworkDataRegisterAfterAttachRouter.py +++ b/tests/scripts/thread-cert/Cert_5_6_05_NetworkDataRegisterAfterAttachRouter.py @@ -147,12 +147,12 @@ def verify(self, pv): # Step 7: The DUT MUST send a unicast MLE Child Update # Response to MED_1 _lpkts_med.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).filter_wpan_dst64(MED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) # Step 8: The DUT MUST send a unicast MLE Child Update # Request to SED_1 _lpkts_sed.filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).filter_wpan_dst64(SED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} == set( + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} <= set( p.mle.tlv.type) and {Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:3::')} == set( p.thread_nwd.tlv.prefix) and p.thread_nwd.tlv.border_router.flag.p == [1, 1] and p.thread_nwd.tlv. border_router.flag.s == [1, 1] and p.thread_nwd.tlv.border_router.flag.r == [1, 0] and p.thread_nwd.tlv. @@ -161,7 +161,7 @@ def verify(self, pv): # Step 10: The DUT MUST send a unicast MLE Child Update # Response to SED_1 _lpkts_sed.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).filter_wpan_dst64(SED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/Cert_5_6_06_NetworkDataExpiration.py b/tests/scripts/thread-cert/Cert_5_6_06_NetworkDataExpiration.py index d29640a48..7a2c224a5 100755 --- a/tests/scripts/thread-cert/Cert_5_6_06_NetworkDataExpiration.py +++ b/tests/scripts/thread-cert/Cert_5_6_06_NetworkDataExpiration.py @@ -163,29 +163,29 @@ def verify(self, pv): lambda p: { NWD_COMMISSIONING_DATA_TLV, NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV - } == set(p.thread_nwd.tlv.type) and { + } <= set(p.thread_nwd.tlv.type) and { Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:2::'), Ipv6Addr('2001:2:0:3::') } == set(p.thread_nwd.tlv.prefix) and p.thread_nwd.tlv.stable == [0, 1, 1, 1, 0, 0, 0, 1, 1, 1]) # Step 7: The DUT MUST send a unicast MLE Child Update Response to MED_1 _lpkts_med.filter_wpan_dst64(MED).filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) # Step 8: The DUT MUST send a unicast MLE Child Update Request to SED_1 _lpkts_sed.filter_wpan_dst64(SED).filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} == set( + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} <= set( p.mle.tlv.type) and { NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV - } == set(p.thread_nwd.tlv.type) and { + } <= set(p.thread_nwd.tlv.type) and { Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:3::') } == set(p.thread_nwd.tlv.prefix) and {0xFFFE, 0xFFFE} == set(p.thread_nwd.tlv.border_router_16)) # Step 10: The DUT MUST send a unicast MLE Child Update Response to SED_1 _pkt = _lpkts_sed.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).filter_wpan_dst64(SED).must_next() _pkt.must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) # Step 12: The DUT updates Router ID Set and removes Router_1 # from Network Data TLV after Router_1 power off @@ -197,7 +197,7 @@ def verify(self, pv): lambda p: { NWD_COMMISSIONING_DATA_TLV, NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV - } == set(p.thread_nwd.tlv.type) and + } <= set(p.thread_nwd.tlv.type) and {Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:2::'), Ipv6Addr('2001:2:0:3::')} == set(p.thread_nwd.tlv.prefix) and p.mle.tlv.leader_data.data_version == (_pkt.mle.tlv.leader_data.data_version + 1) % 256 and p.mle.tlv.leader_data.stable_data_version == @@ -205,20 +205,20 @@ def verify(self, pv): # Step 15: The DUT MUST send a unicast MLE Child Update Response to MED_1 _lpkts_med.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).filter_wpan_dst64(MED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type) and p.mle.tlv.leader_data.data_version == (_pkt.mle.tlv.leader_data.data_version + 1) % 256 and p.mle.tlv. leader_data.stable_data_version == (_pkt.mle.tlv.leader_data.stable_data_version + 1) % 256) # Step 16: The DUT MUST send a unicast MLE Child Update Request to SED_1 _lpkts_sed.filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).filter_wpan_dst64(SED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} == set( + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.leader_data.data_version == (_pkt.mle.tlv.leader_data.data_version + 1) % 256 and p.mle.tlv.leader_data.stable_data_version == (_pkt.mle.tlv.leader_data.stable_data_version + 1) % 256) # Step 18: The DUT MUST send a unicast MLE Child Update Response to SED_1 _lpkts_sed.filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).filter_wpan_dst64(SED).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} < set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/Cert_5_6_09_NetworkDataForwarding.py b/tests/scripts/thread-cert/Cert_5_6_09_NetworkDataForwarding.py index 619999621..3aa3db1d4 100755 --- a/tests/scripts/thread-cert/Cert_5_6_09_NetworkDataForwarding.py +++ b/tests/scripts/thread-cert/Cert_5_6_09_NetworkDataForwarding.py @@ -154,9 +154,9 @@ def verify(self, pv): # Step 5: The DUT MUST send a unicast MLE Child Update # Request to SED_1 _rpkts.filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).filter_wpan_dst64(SED).must_next( - ).must_verify(lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} == set( + ).must_verify(lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} <= set( p.mle.tlv.type - ) and {NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_HAS_ROUTER_TLV} == set( + ) and {NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_HAS_ROUTER_TLV} <= set( p.thread_nwd.tlv.type) and {Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:2::')} == set( p.thread_nwd.tlv.prefix) and {0xFFFE, 0xFFFE} == set(p.thread_nwd.tlv.border_router_16)) @@ -179,7 +179,7 @@ def verify(self, pv): lambda p: { NWD_COMMISSIONING_DATA_TLV, NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_HAS_ROUTER_TLV - } == set(p.thread_nwd.tlv.type) and { + } <= set(p.thread_nwd.tlv.type) and { Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:2::') } == set(p.thread_nwd.tlv.prefix) and p.thread_nwd.tlv.border_router.flag.p == [0, 1] and p.thread_nwd. tlv.border_router.flag.s == [1, 1] and p.thread_nwd.tlv.border_router.flag.r == [1, 1] and p.thread_nwd @@ -187,9 +187,9 @@ def verify(self, pv): # Step 10: The DUT MUST send a unicast MLE Child Update Request to SED_1 _rpkts.filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).filter_wpan_dst64(SED).must_next( - ).must_verify(lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} == set( + ).must_verify(lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} <= set( p.mle.tlv.type - ) and {NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_HAS_ROUTER_TLV} == set( + ) and {NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_HAS_ROUTER_TLV} <= set( p.thread_nwd.tlv.type) and {Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:2::')} == set( p.thread_nwd.tlv.prefix) and {0xFFFE, 0xFFFE} == set(p.thread_nwd.tlv.border_router_16)) @@ -205,14 +205,14 @@ def verify(self, pv): lambda p: { NWD_COMMISSIONING_DATA_TLV, NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_HAS_ROUTER_TLV - } == set(p.thread_nwd.tlv.type) and {Ipv6Addr('2001:2:0:1::'), + } <= set(p.thread_nwd.tlv.type) and {Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:2::')} == set(p.thread_nwd.tlv.prefix)) # Step 14: The DUT MUST send a unicast MLE Child Update Request to SED_1 _rpkts.filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).filter_wpan_dst64(SED).must_next( - ).must_verify(lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} == set( + ).must_verify(lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} <= set( p.mle.tlv.type - ) and {NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_HAS_ROUTER_TLV} == set( + ) and {NWD_PREFIX_TLV, NWD_BORDER_ROUTER_TLV, NWD_6LOWPAN_ID_TLV, NWD_PREFIX_TLV, NWD_HAS_ROUTER_TLV} <= set( p.thread_nwd.tlv.type) and {Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:2::')} == set( p.thread_nwd.tlv.prefix) and {0xFFFE, 0xFFFE} == set(p.thread_nwd.tlv.border_router_16)) diff --git a/tests/scripts/thread-cert/Cert_5_7_01_CoapDiagCommands.py b/tests/scripts/thread-cert/Cert_5_7_01_CoapDiagCommands.py index a2b0c402d..88c4dbf24 100755 --- a/tests/scripts/thread-cert/Cert_5_7_01_CoapDiagCommands.py +++ b/tests/scripts/thread-cert/Cert_5_7_01_CoapDiagCommands.py @@ -230,7 +230,7 @@ def verify(self, pv): DG_NETWORK_DATA_TLV, DG_IPV6_ADDRESS_LIST_TLV, DG_CHANNEL_PAGES_TLV - } == set(p.thread_diagnostic.tlv.type) + } <= set(p.thread_diagnostic.tlv.type) ).\ must_next() pkts.filter_wpan_src64(DUT).\ @@ -246,7 +246,7 @@ def verify(self, pv): DG_NETWORK_DATA_TLV, DG_IPV6_ADDRESS_LIST_TLV, DG_CHANNEL_PAGES_TLV - } == set(p.thread_diagnostic.tlv.type) + } <= set(p.thread_diagnostic.tlv.type) ).\ must_next() @@ -263,7 +263,7 @@ def verify(self, pv): filter(lambda p: { DG_TYPE_LIST_TLV, DG_MAC_COUNTERS_TLV - } == set(p.thread_diagnostic.tlv.type) + } <= set(p.thread_diagnostic.tlv.type) ).\ must_next() pkts.filter_wpan_src64(DUT).\ @@ -271,7 +271,7 @@ def verify(self, pv): filter_coap_ack(DIAG_GET_URI).\ filter(lambda p: { DG_MAC_COUNTERS_TLV - } == set(p.thread_diagnostic.tlv.type) + } <= set(p.thread_diagnostic.tlv.type) ).\ must_next() @@ -288,7 +288,7 @@ def verify(self, pv): filter(lambda p: { DG_TYPE_LIST_TLV, DG_TIMEOUT_TLV - } == set(p.thread_diagnostic.tlv.type) + } <= set(p.thread_diagnostic.tlv.type) ).\ must_next() pkts.filter_wpan_src64(DUT).\ @@ -311,7 +311,7 @@ def verify(self, pv): DG_TYPE_LIST_TLV, DG_BATTERY_LEVEL_TLV, DG_SUPPLY_VOLTAGE_TLV - } == set(p.thread_diagnostic.tlv.type) + } <= set(p.thread_diagnostic.tlv.type) ).\ must_next() pkts.filter_wpan_src64(DUT).\ @@ -334,7 +334,7 @@ def verify(self, pv): filter(lambda p: { DG_TYPE_LIST_TLV, DG_CHILD_TABLE_TLV - } == set(p.thread_diagnostic.tlv.type) + } <= set(p.thread_diagnostic.tlv.type) ).\ must_next() @@ -344,7 +344,7 @@ def verify(self, pv): filter_coap_ack(DIAG_GET_URI).\ filter(lambda p: { DG_CHILD_TABLE_TLV - } == set(p.thread_diagnostic.tlv.type) + } <= set(p.thread_diagnostic.tlv.type) ).\ must_next() else: @@ -366,7 +366,7 @@ def verify(self, pv): filter(lambda p: { DG_TYPE_LIST_TLV, DG_MAC_COUNTERS_TLV - } == set(p.thread_diagnostic.tlv.type) + } <= set(p.thread_diagnostic.tlv.type) ).\ must_next() pkts.filter_wpan_src64(DUT).\ @@ -389,7 +389,7 @@ def verify(self, pv): filter(lambda p: { DG_TYPE_LIST_TLV, DG_MAC_COUNTERS_TLV - } == set(p.thread_diagnostic.tlv.type) + } <= set(p.thread_diagnostic.tlv.type) ).\ must_next() pkts.filter_wpan_src64(DUT).\ @@ -397,7 +397,7 @@ def verify(self, pv): filter_coap_ack(DIAG_GET_URI).\ filter(lambda p: { DG_MAC_COUNTERS_TLV - } == set(p.thread_diagnostic.tlv.type) + } <= set(p.thread_diagnostic.tlv.type) ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_5_8_04_SecurityPolicyTLV.py b/tests/scripts/thread-cert/Cert_5_8_04_SecurityPolicyTLV.py index 8620314c5..328206bb1 100755 --- a/tests/scripts/thread-cert/Cert_5_8_04_SecurityPolicyTLV.py +++ b/tests/scripts/thread-cert/Cert_5_8_04_SecurityPolicyTLV.py @@ -236,7 +236,7 @@ def verify(self, pv): NM_COMMISSIONER_SESSION_ID_TLV, NM_ACTIVE_TIMESTAMP_TLV, NM_SECURITY_POLICY_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.active_tstamp == 15 and\ (p.thread_meshcop.tlv.sec_policy_o == 0 or p.thread_meshcop.tlv.unknown == '0e1077')).\ @@ -289,7 +289,7 @@ def verify(self, pv): NM_COMMISSIONER_SESSION_ID_TLV, NM_ACTIVE_TIMESTAMP_TLV, NM_SECURITY_POLICY_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.active_tstamp == 20 and\ (p.thread_meshcop.tlv.sec_policy_n == 0 or p.thread_meshcop.tlv.unknown == '0e10b7')).\ @@ -327,7 +327,7 @@ def verify(self, pv): NM_COMMISSIONER_SESSION_ID_TLV, NM_ACTIVE_TIMESTAMP_TLV, NM_SECURITY_POLICY_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.active_tstamp == 25 and\ (p.thread_meshcop.tlv.sec_policy_b == 0 or p.thread_meshcop.tlv.unknown == '0e10f7')).\ @@ -373,7 +373,7 @@ def verify(self, pv): NM_COMMISSIONER_SESSION_ID_TLV, NM_ACTIVE_TIMESTAMP_TLV, NM_SECURITY_POLICY_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.active_tstamp == 30 and\ (p.thread_meshcop.tlv.sec_policy_r == 0 or p.thread_meshcop.tlv.unknown == '0e10d7')).\ diff --git a/tests/scripts/thread-cert/Cert_6_1_02_REEDAttach.py b/tests/scripts/thread-cert/Cert_6_1_02_REEDAttach.py index 1bbb2d96f..9858b201b 100755 --- a/tests/scripts/thread-cert/Cert_6_1_02_REEDAttach.py +++ b/tests/scripts/thread-cert/Cert_6_1_02_REEDAttach.py @@ -272,7 +272,7 @@ def verify(self, pv): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.coap.code == COAP_CODE_ACK and\ p.thread_address.tlv.status == 0\ ).\ @@ -305,7 +305,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV - } < set(p.mle.tlv.type) + } <= set(p.mle.tlv.type) ).\ must_next() @@ -318,7 +318,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, MODE_TLV, LEADER_DATA_TLV - } < set(p.mle.tlv.type) + } <= set(p.mle.tlv.type) ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_6_1_03_RouterAttachConnectivity.py b/tests/scripts/thread-cert/Cert_6_1_03_RouterAttachConnectivity.py index 3da6d068f..1d7c54851 100755 --- a/tests/scripts/thread-cert/Cert_6_1_03_RouterAttachConnectivity.py +++ b/tests/scripts/thread-cert/Cert_6_1_03_RouterAttachConnectivity.py @@ -108,7 +108,7 @@ def verify(self, pv): # All-Routers multicast address _ed_pkts.filter_mle_cmd(MLE_PARENT_REQUEST).filter_ipv6_dst( LINK_LOCAL_ALL_ROUTERS_MULTICAST_ADDRESS).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set(p.mle.tlv.type + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set(p.mle.tlv.type ) and p.mle.tlv.scan_mask.r == 1) # Step 3: Router_2, Router_3 Respond with MLE Parent Response diff --git a/tests/scripts/thread-cert/Cert_6_1_04_REEDAttachConnectivity.py b/tests/scripts/thread-cert/Cert_6_1_04_REEDAttachConnectivity.py index 34be7e9d9..302dd2473 100755 --- a/tests/scripts/thread-cert/Cert_6_1_04_REEDAttachConnectivity.py +++ b/tests/scripts/thread-cert/Cert_6_1_04_REEDAttachConnectivity.py @@ -276,7 +276,7 @@ def verify(self, pv): NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.thread_address.tlv.status == 0 ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_6_1_05_REEDAttachConnectivity.py b/tests/scripts/thread-cert/Cert_6_1_05_REEDAttachConnectivity.py index 10ed131c8..c324a7d64 100755 --- a/tests/scripts/thread-cert/Cert_6_1_05_REEDAttachConnectivity.py +++ b/tests/scripts/thread-cert/Cert_6_1_05_REEDAttachConnectivity.py @@ -117,13 +117,13 @@ def verify(self, pv): for num in range(self.num_parent_requests): _ed_pkts.filter_mle_cmd(MLE_PARENT_REQUEST).filter_ipv6_dst( LINK_LOCAL_ALL_ROUTERS_MULTICAST_ADDRESS).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set( + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.scan_mask.r == 1) # Step 3: REED_1 and REED_2 No response to Parent Request # Step 4: DUT Send MLE Parent Request with Scan Mask set to Routers AND REEDs _ed_pkts.filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set( + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 1) # Step 5: The DUT MUST send a MLE Child ID Request diff --git a/tests/scripts/thread-cert/Cert_6_1_07_RouterAttachLinkQuality.py b/tests/scripts/thread-cert/Cert_6_1_07_RouterAttachLinkQuality.py index 64cba5086..e191b5f82 100755 --- a/tests/scripts/thread-cert/Cert_6_1_07_RouterAttachLinkQuality.py +++ b/tests/scripts/thread-cert/Cert_6_1_07_RouterAttachLinkQuality.py @@ -99,7 +99,7 @@ def verify(self, pv): # All-Routers multicast address _ed_pkts.filter_mle_cmd(MLE_PARENT_REQUEST).filter_ipv6_dst( LINK_LOCAL_ALL_ROUTERS_MULTICAST_ADDRESS).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set( + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 0) # Step 5: The DUT MUST send a MLE Child ID Request to Router_1 diff --git a/tests/scripts/thread-cert/Cert_6_2_02_NewPartition.py b/tests/scripts/thread-cert/Cert_6_2_02_NewPartition.py index a7fc7a675..be67a9e1f 100755 --- a/tests/scripts/thread-cert/Cert_6_2_02_NewPartition.py +++ b/tests/scripts/thread-cert/Cert_6_2_02_NewPartition.py @@ -111,7 +111,7 @@ def verify(self, pv): _ed_pkts.filter_mle_cmd(MLE_ADVERTISEMENT).must_not_next() _ed_pkts.filter_mle_cmd(MLE_CHILD_ID_REQUEST).must_not_next() _ed_pkts.filter_wpan_dst64(ROUTER_1).filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, SOURCE_ADDRESS_TLV, LEADER_DATA_TLV} < set(p.mle.tlv.type)) + lambda p: {MODE_TLV, SOURCE_ADDRESS_TLV, LEADER_DATA_TLV} <= set(p.mle.tlv.type)) # Step 8: The DUT MUST respond with ICMPv6 Echo Reply _ed_pkts.filter('ipv6.dst == {ROUTER_1_MLEID} and ipv6.src == {ED_MLEID}', diff --git a/tests/scripts/thread-cert/Cert_6_3_02_NetworkDataUpdate.py b/tests/scripts/thread-cert/Cert_6_3_02_NetworkDataUpdate.py index 4d8ba3464..d4ab14734 100755 --- a/tests/scripts/thread-cert/Cert_6_3_02_NetworkDataUpdate.py +++ b/tests/scripts/thread-cert/Cert_6_3_02_NetworkDataUpdate.py @@ -113,16 +113,16 @@ def verify(self, pv): # Step 3: The DUT MUST send a MLE Child Update Request to the Leader _epkts.range(pkts.index).filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).must_next().must_verify( lambda p: p.wpan.dst64 == LEADER and {LEADER_DATA_TLV, ADDRESS_REGISTRATION_TLV, MODE_TLV, TIMEOUT_TLV - } < set(p.mle.tlv.type)) + } <= set(p.mle.tlv.type)) # Step 10: The DUT MUST send a MLE Data Request frame to # request the updated Network Data _epkts.filter_mle_cmd(MLE_DATA_REQUEST).must_next().must_verify( - lambda p: {TLV_REQUEST_TLV, NETWORK_DATA_TLV} < set(p.mle.tlv.type)) + lambda p: {TLV_REQUEST_TLV, NETWORK_DATA_TLV} <= set(p.mle.tlv.type)) # Step 12: The DUT MUST send a MLE Child Update Request to the Leader _epkts.filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).filter_wpan_dst64(LEADER).must_next().must_verify( - lambda p: {ADDRESS_REGISTRATION_TLV, MODE_TLV, TIMEOUT_TLV} < set(p.mle.tlv.type)) + lambda p: {ADDRESS_REGISTRATION_TLV, MODE_TLV, TIMEOUT_TLV} <= set(p.mle.tlv.type)) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/Cert_6_5_01_ChildResetReattach.py b/tests/scripts/thread-cert/Cert_6_5_01_ChildResetReattach.py index df62743b3..15b68e807 100755 --- a/tests/scripts/thread-cert/Cert_6_5_01_ChildResetReattach.py +++ b/tests/scripts/thread-cert/Cert_6_5_01_ChildResetReattach.py @@ -97,7 +97,7 @@ def verify(self, pv): # Step 3: Send MLE Child Update Request to Leader _ed_pkts.filter_mle_cmd(MLE_CHILD_ID_REQUEST).must_next() _ed_pkts.filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV} < set(p.mle.tlv.type)) + lambda p: {MODE_TLV} <= set(p.mle.tlv.type)) # Step 5: DUT reattaches to Leader _ed_pkts.filter_mle_cmd(MLE_CHILD_ID_REQUEST).must_next().must_verify( diff --git a/tests/scripts/thread-cert/Cert_6_5_02_ChildResetReattach.py b/tests/scripts/thread-cert/Cert_6_5_02_ChildResetReattach.py index 05e4355f8..ecbfc8b0f 100755 --- a/tests/scripts/thread-cert/Cert_6_5_02_ChildResetReattach.py +++ b/tests/scripts/thread-cert/Cert_6_5_02_ChildResetReattach.py @@ -150,7 +150,7 @@ def verify(self, pv): filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).\ filter(lambda p: { MODE_TLV - } < set(p.mle.tlv.type) + } <= set(p.mle.tlv.type) ).\ must_next() if self.TOPOLOGY[MTD]['mode'] == '-': diff --git a/tests/scripts/thread-cert/Cert_6_5_03_ChildResetSynchronize.py b/tests/scripts/thread-cert/Cert_6_5_03_ChildResetSynchronize.py index d92539013..67d971a05 100755 --- a/tests/scripts/thread-cert/Cert_6_5_03_ChildResetSynchronize.py +++ b/tests/scripts/thread-cert/Cert_6_5_03_ChildResetSynchronize.py @@ -95,7 +95,7 @@ def verify(self, pv): _ed_pkts.filter_mle_cmd(MLE_CHILD_ID_REQUEST).must_next() _leader_pkts.range(_ed_pkts.index).filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next() _ed_pkts.filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV} < set(p.mle.tlv.type)) + lambda p: {MODE_TLV} <= set(p.mle.tlv.type)) # Step 4: Leader send an MLE Child Update Response _leader_pkts.range(_ed_pkts.index).filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).must_next() diff --git a/tests/scripts/thread-cert/Cert_7_1_01_BorderRouterAsLeader.py b/tests/scripts/thread-cert/Cert_7_1_01_BorderRouterAsLeader.py index 7b90d12b9..c0d306b8f 100755 --- a/tests/scripts/thread-cert/Cert_7_1_01_BorderRouterAsLeader.py +++ b/tests/scripts/thread-cert/Cert_7_1_01_BorderRouterAsLeader.py @@ -146,7 +146,7 @@ def verify(self, pv): ADDRESS16_TLV, NETWORK_DATA_TLV, ROUTE64_TLV - } < set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ p.mle.tlv.mode.network_data == 1 ).\ must_next() @@ -208,7 +208,7 @@ def verify(self, pv): MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV - } == set(p.thread_nwd.tlv.type) and\ + } <= set(p.thread_nwd.tlv.type) and\ [Ipv6Addr(PREFIX_2001[:-3])] == p.thread_nwd.tlv.prefix and\ p.thread_nwd.tlv.border_router.flag.p == [1] and\ p.thread_nwd.tlv.border_router.flag.s == [1] and\ @@ -235,7 +235,7 @@ def verify(self, pv): ADDRESS16_TLV, NETWORK_DATA_TLV, ADDRESS_REGISTRATION_TLV - } < set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ p.mle.tlv.mode.network_data == 1 ).\ must_next() @@ -286,7 +286,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, MODE_TLV, ADDRESS_REGISTRATION_TLV - } < set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ set(p.mle.tlv.addr_reg_iid) < set(_pkt.mle.tlv.addr_reg_iid) ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_7_1_02_BorderRouterAsRouter.py b/tests/scripts/thread-cert/Cert_7_1_02_BorderRouterAsRouter.py index 61495ea0a..a4286ea7e 100755 --- a/tests/scripts/thread-cert/Cert_7_1_02_BorderRouterAsRouter.py +++ b/tests/scripts/thread-cert/Cert_7_1_02_BorderRouterAsRouter.py @@ -125,7 +125,7 @@ def verify(self, pv): # with the server’s information (Prefix, Border Router) to the Leader _rpkts.filter_mle_cmd(MLE_CHILD_ID_REQUEST).must_next() _rpkts.filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type)) _rpkts.filter_coap_request(SVR_DATA_URI).must_next().must_verify( lambda p: p.wpan.dst16 == pv.vars['LEADER_RLOC16'] and { Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:2::') @@ -136,7 +136,7 @@ def verify(self, pv): # Step 3: Automatically transmits a 2.04 Changed CoAP response to the DUT # Step 4: The DUT MUST multicast the MLE Data Response message sent by the Leader _rpkts.filter_mle_cmd(MLE_DATA_RESPONSE).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} == set(p.mle.tlv. + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} <= set(p.mle.tlv. type)) # Step 5: MED_1 to attach to DUT and request complete network data diff --git a/tests/scripts/thread-cert/Cert_7_1_03_BorderRouterAsLeader.py b/tests/scripts/thread-cert/Cert_7_1_03_BorderRouterAsLeader.py index 03d440ebd..aa643731d 100755 --- a/tests/scripts/thread-cert/Cert_7_1_03_BorderRouterAsLeader.py +++ b/tests/scripts/thread-cert/Cert_7_1_03_BorderRouterAsLeader.py @@ -177,7 +177,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, MODE_TLV, ADDRESS_REGISTRATION_TLV - } < set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ p.mle.tlv.addr_reg_iid is not nullField and\ set(_pkt.mle.tlv.addr_reg_iid) > set(p.mle.tlv.addr_reg_iid) ).\ @@ -197,7 +197,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ACTIVE_TIMESTAMP_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ [Ipv6Addr(PREFIX_2001[:-3])] == p.thread_nwd.tlv.prefix and\ p.thread_nwd.tlv.border_router.flag.p == [1] and\ p.thread_nwd.tlv.border_router.flag.s == [1] and\ @@ -228,7 +228,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, MODE_TLV, ADDRESS_REGISTRATION_TLV - } < set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ p.mle.tlv.addr_reg_iid is not nullField and\ set(_pkt.mle.tlv.addr_reg_iid) > set(p.mle.tlv.addr_reg_iid) ).\ diff --git a/tests/scripts/thread-cert/Cert_7_1_04_BorderRouterAsRouter.py b/tests/scripts/thread-cert/Cert_7_1_04_BorderRouterAsRouter.py index c5490a455..fe5279860 100755 --- a/tests/scripts/thread-cert/Cert_7_1_04_BorderRouterAsRouter.py +++ b/tests/scripts/thread-cert/Cert_7_1_04_BorderRouterAsRouter.py @@ -125,7 +125,7 @@ def verify(self, pv): # with the server’s information (Prefix, Border Router) to the Leader _rpkts.filter_mle_cmd(MLE_CHILD_ID_REQUEST).must_next() _rpkts.filter_mle_cmd(MLE_ADVERTISEMENT).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} == set(p.mle.tlv.type)) + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ROUTE64_TLV} <= set(p.mle.tlv.type)) _pkt = _rpkts.filter_coap_request(SVR_DATA_URI).must_next() _pkt.must_verify(lambda p: p.wpan.dst16 == pv.vars['LEADER_RLOC16'] and { Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:2::') @@ -138,7 +138,7 @@ def verify(self, pv): # Step 4: Automatically transmits a 2.04 Changed CoAP response to the DUT # Step 5: The DUT MUST send a multicast MLE Data Response _rpkts.filter_mle_cmd(MLE_DATA_RESPONSE).must_next().must_verify( - lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} == set( + lambda p: {SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV} <= set( p.mle.tlv.type) and {Ipv6Addr('2001:2:0:1::'), Ipv6Addr('2001:2:0:2::')} == set( p.thread_nwd.tlv.prefix) and p.thread_nwd.tlv.border_router.flag.p == [1, 1] and p.thread_nwd.tlv. border_router.flag.s == [1, 1] and p.thread_nwd.tlv.border_router.flag.r == [1, 1] and p.thread_nwd.tlv. diff --git a/tests/scripts/thread-cert/Cert_7_1_05_BorderRouterAsRouter.py b/tests/scripts/thread-cert/Cert_7_1_05_BorderRouterAsRouter.py index 3ee96e1bc..f46c3ebc3 100755 --- a/tests/scripts/thread-cert/Cert_7_1_05_BorderRouterAsRouter.py +++ b/tests/scripts/thread-cert/Cert_7_1_05_BorderRouterAsRouter.py @@ -188,7 +188,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, MODE_TLV, ADDRESS_REGISTRATION_TLV - } < set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ len(p.mle.tlv.addr_reg_iid) >= 3 and\ set(p.mle.tlv.addr_reg_iid) < set(_pkt.mle.tlv.addr_reg_iid) ).\ @@ -214,7 +214,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ACTIVE_TIMESTAMP_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ { Ipv6Addr(PREFIX_2001[:-3]), Ipv6Addr(PREFIX_2003[:-3]) @@ -246,7 +246,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, MODE_TLV, ADDRESS_REGISTRATION_TLV - } < set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ len(p.mle.tlv.addr_reg_iid) >= 2 and\ set(p.mle.tlv.addr_reg_iid) < set(_pkt.mle.tlv.addr_reg_iid) ).\ diff --git a/tests/scripts/thread-cert/Cert_7_1_06_BorderRouterAsLeader.py b/tests/scripts/thread-cert/Cert_7_1_06_BorderRouterAsLeader.py index 36e0f9c97..9bde88aa6 100755 --- a/tests/scripts/thread-cert/Cert_7_1_06_BorderRouterAsLeader.py +++ b/tests/scripts/thread-cert/Cert_7_1_06_BorderRouterAsLeader.py @@ -287,7 +287,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ACTIVE_TIMESTAMP_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ [Ipv6Addr(PREFIX_2001[:-3])] == p.thread_nwd.tlv.prefix and\ p.thread_nwd.tlv.stable == [1, 1, 1] and\ @@ -459,7 +459,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ACTIVE_TIMESTAMP_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ [Ipv6Addr(PREFIX_2001[:-3])] == p.thread_nwd.tlv.prefix and\ p.mle.tlv.leader_data.data_version == diff --git a/tests/scripts/thread-cert/Cert_7_1_07_BorderRouterAsLeader.py b/tests/scripts/thread-cert/Cert_7_1_07_BorderRouterAsLeader.py index 49e000bc7..44fdc44ab 100755 --- a/tests/scripts/thread-cert/Cert_7_1_07_BorderRouterAsLeader.py +++ b/tests/scripts/thread-cert/Cert_7_1_07_BorderRouterAsLeader.py @@ -313,7 +313,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ACTIVE_TIMESTAMP_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ is_sublist([Ipv6Addr(PREFIX_1[:-3])], p.thread_nwd.tlv.prefix) and\ is_sublist([1, 1, 1], p.thread_nwd.tlv.stable) and\ is_sublist([1], getattr(p.thread_nwd.tlv, '6co').flag.c) and\ @@ -429,7 +429,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ACTIVE_TIMESTAMP_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ is_sublist([1, 1, 1, 1, 1, 1], p.thread_nwd.tlv.stable) and\ is_sublist([1, 1], getattr(p.thread_nwd.tlv, '6co').flag.c) and\ @@ -548,7 +548,7 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, ACTIVE_TIMESTAMP_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ is_sublist([Ipv6Addr(PREFIX_1[:-3]), Ipv6Addr(PREFIX_2[:-3])], p.thread_nwd.tlv.prefix) and\ is_sublist([1, 1, 1, 1, 1], p.thread_nwd.tlv.stable) and\ diff --git a/tests/scripts/thread-cert/Cert_8_1_02_Commissioning.py b/tests/scripts/thread-cert/Cert_8_1_02_Commissioning.py index a80770036..c28fca899 100755 --- a/tests/scripts/thread-cert/Cert_8_1_02_Commissioning.py +++ b/tests/scripts/thread-cert/Cert_8_1_02_Commissioning.py @@ -82,7 +82,7 @@ def verify(self, pv): lambda p: { NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_NAME_TLV, NM_STEERING_DATA_TLV, NM_COMMISSIONER_UDP_PORT_TLV, NM_JOINER_UDP_PORT_TLV, NM_DISCOVERY_RESPONSE_TLV - } == set(p.thread_meshcop.tlv.type)) + } <= set(p.thread_meshcop.tlv.type)) # 2. Joiner_1 sends an initial DTLS-ClientHello handshake record to the Commissioner _cpkts2.range(_cpkts.index).filter(lambda p: p.dtls.handshake.type == [HANDSHAKE_CLIENT_HELLO]).must_next() diff --git a/tests/scripts/thread-cert/Cert_8_1_06_Commissioning.py b/tests/scripts/thread-cert/Cert_8_1_06_Commissioning.py index d00092985..bf167aef3 100755 --- a/tests/scripts/thread-cert/Cert_8_1_06_Commissioning.py +++ b/tests/scripts/thread-cert/Cert_8_1_06_Commissioning.py @@ -123,7 +123,7 @@ def verify(self, pv): NM_COMMISSIONER_UDP_PORT_TLV, NM_JOINER_UDP_PORT_TLV, NM_DISCOVERY_RESPONSE_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.discovery_rsp_ver == COMMISSIONER_VERSION ).\ diff --git a/tests/scripts/thread-cert/Cert_8_2_01_JoinerRouter.py b/tests/scripts/thread-cert/Cert_8_2_01_JoinerRouter.py index b2951ee46..c00df8f0d 100755 --- a/tests/scripts/thread-cert/Cert_8_2_01_JoinerRouter.py +++ b/tests/scripts/thread-cert/Cert_8_2_01_JoinerRouter.py @@ -119,7 +119,7 @@ def verify(self, pv): lambda p: { NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_NAME_TLV, NM_STEERING_DATA_TLV, NM_COMMISSIONER_UDP_PORT_TLV, NM_JOINER_UDP_PORT_TLV, NM_DISCOVERY_RESPONSE_TLV - } == set(p.thread_meshcop.tlv.type)) + } <= set(p.thread_meshcop.tlv.type)) # 2. Joiner_1 sends an initial DTLS-ClientHello handshake record to the Commissioner pkts.filter(lambda p: p.dtls.handshake.type == [HANDSHAKE_CLIENT_HELLO]).must_next() diff --git a/tests/scripts/thread-cert/Cert_8_2_02_JoinerRouter.py b/tests/scripts/thread-cert/Cert_8_2_02_JoinerRouter.py index 7ef880b28..c51a63093 100755 --- a/tests/scripts/thread-cert/Cert_8_2_02_JoinerRouter.py +++ b/tests/scripts/thread-cert/Cert_8_2_02_JoinerRouter.py @@ -111,7 +111,7 @@ def verify(self, pv): lambda p: { NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_NAME_TLV, NM_STEERING_DATA_TLV, NM_COMMISSIONER_UDP_PORT_TLV, NM_JOINER_UDP_PORT_TLV, NM_DISCOVERY_RESPONSE_TLV - } == set(p.thread_meshcop.tlv.type)) + } <= set(p.thread_meshcop.tlv.type)) # 2. Joiner_1 sends an initial DTLS-ClientHello handshake record to the Commissioner pkts.filter(lambda p: p.dtls.handshake.type == [HANDSHAKE_CLIENT_HELLO]).must_next() diff --git a/tests/scripts/thread-cert/Cert_8_2_05_JoinerRouter.py b/tests/scripts/thread-cert/Cert_8_2_05_JoinerRouter.py index a8f01b2ff..f4c03773f 100755 --- a/tests/scripts/thread-cert/Cert_8_2_05_JoinerRouter.py +++ b/tests/scripts/thread-cert/Cert_8_2_05_JoinerRouter.py @@ -161,7 +161,7 @@ def verify(self, pv): NM_COMMISSIONER_UDP_PORT_TLV, NM_JOINER_UDP_PORT_TLV, NM_DISCOVERY_RESPONSE_TLV - } == set(p.thread_meshcop.tlv.type) + } <= set(p.thread_meshcop.tlv.type) ).\ must_next() @@ -198,7 +198,7 @@ def verify(self, pv): NM_JOINER_UDP_PORT_TLV, NM_JOINER_IID_TLV, NM_JOINER_ROUTER_LOCATOR_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.udp_port == [_ch_pkt.udp.dstport] and\ p.thread_meshcop.tlv.jr_locator == JOINER_ROUTER_RLOC16 ).\ @@ -223,7 +223,7 @@ def verify(self, pv): NM_JOINER_IID_TLV, NM_JOINER_ROUTER_LOCATOR_TLV, NM_JOINER_ROUTER_KEK_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.udp_port == [_ch_pkt.udp.dstport] and\ p.thread_meshcop.tlv.jr_locator == JOINER_ROUTER_RLOC16 ).\ diff --git a/tests/scripts/thread-cert/Cert_8_3_01_CommissionerPetition.py b/tests/scripts/thread-cert/Cert_8_3_01_CommissionerPetition.py index 4c30bfc22..d9e017060 100755 --- a/tests/scripts/thread-cert/Cert_8_3_01_CommissionerPetition.py +++ b/tests/scripts/thread-cert/Cert_8_3_01_CommissionerPetition.py @@ -122,7 +122,7 @@ def verify(self, pv): filter_coap_request(LEAD_PET_URI).\ filter(lambda p: { NM_COMMISSIONER_ID_TLV - } == set(p.coap.tlv.type)\ + } <= set(p.coap.tlv.type)\ ).\ must_next() @@ -159,15 +159,15 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, ACTIVE_TIMESTAMP_TLV, LEADER_DATA_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ { NWD_COMMISSIONING_DATA_TLV - } == set(p.thread_nwd.tlv.type) and\ + } <= set(p.thread_nwd.tlv.type) and\ { NM_BORDER_AGENT_LOCATOR_TLV, NM_COMMISSIONER_SESSION_ID_TLV, NM_STEERING_DATA_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.mle.tlv.leader_data.data_version == (_pkt.mle.tlv.leader_data.data_version + 1) % 256 and\ p.thread_nwd.tlv.stable == [0] @@ -186,7 +186,7 @@ def verify(self, pv): filter(lambda p: { NM_STATE_TLV, NM_COMMISSIONER_SESSION_ID_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.thread_meshcop.tlv.state == MESHCOP_ACCEPT ).\ must_next() @@ -218,7 +218,7 @@ def verify(self, pv): filter(lambda p: { NM_STEERING_DATA_TLV, NM_COMMISSIONER_SESSION_ID_TLV - } == set(p.coap.tlv.type) + } <= set(p.coap.tlv.type) ).\ must_next() @@ -252,15 +252,15 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, ACTIVE_TIMESTAMP_TLV, LEADER_DATA_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ { NWD_COMMISSIONING_DATA_TLV - } == set(p.thread_nwd.tlv.type) and\ + } <= set(p.thread_nwd.tlv.type) and\ { NM_BORDER_AGENT_LOCATOR_TLV, NM_COMMISSIONER_SESSION_ID_TLV, NM_STEERING_DATA_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.mle.tlv.leader_data.data_version == (_dr_pkt.mle.tlv.leader_data.data_version + 1) % 256 and\ p.thread_nwd.tlv.stable == [0] @@ -280,7 +280,7 @@ def verify(self, pv): filter(lambda p: { NM_STATE_TLV, NM_COMMISSIONER_SESSION_ID_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.thread_meshcop.tlv.state == MESHCOP_REJECT ).\ must_next() @@ -302,7 +302,7 @@ def verify(self, pv): filter_coap_ack(LEAD_KA_URI).\ filter(lambda p: { NM_STATE_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.thread_meshcop.tlv.state == MESHCOP_REJECT ).\ must_next() @@ -314,13 +314,13 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, ACTIVE_TIMESTAMP_TLV, LEADER_DATA_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ { NWD_COMMISSIONING_DATA_TLV - } == set(p.thread_nwd.tlv.type) and\ + } <= set(p.thread_nwd.tlv.type) and\ { NM_COMMISSIONER_SESSION_ID_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ (p.mle.tlv.leader_data.data_version - _dr_pkt2.mle.tlv.leader_data.data_version) % 256 <= 127 and\ p.thread_nwd.tlv.stable == [0] @@ -337,7 +337,7 @@ def verify(self, pv): filter_coap_request(LEAD_PET_URI).\ filter(lambda p: { NM_COMMISSIONER_ID_TLV - } == set(p.coap.tlv.type)\ + } <= set(p.coap.tlv.type)\ ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_9_2_01_MGMTCommissionerGet.py b/tests/scripts/thread-cert/Cert_9_2_01_MGMTCommissionerGet.py index 9dd812098..9a2226a8d 100755 --- a/tests/scripts/thread-cert/Cert_9_2_01_MGMTCommissionerGet.py +++ b/tests/scripts/thread-cert/Cert_9_2_01_MGMTCommissionerGet.py @@ -146,7 +146,7 @@ def verify(self, pv): NM_COMMISSIONER_SESSION_ID_TLV, NM_BORDER_AGENT_LOCATOR_TLV, NM_STEERING_DATA_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.ba_locator is not nullField and\ p.thread_meshcop.tlv.commissioner_sess_id is not nullField and\ p.thread_meshcop.tlv.steering_data is not nullField @@ -183,7 +183,7 @@ def verify(self, pv): filter(lambda p: { NM_COMMISSIONER_SESSION_ID_TLV, NM_STEERING_DATA_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.commissioner_sess_id is not nullField and\ p.thread_meshcop.tlv.steering_data is not nullField ).\ @@ -218,7 +218,7 @@ def verify(self, pv): filter_coap_ack(MGMT_COMMISSIONER_GET_URI).\ filter(lambda p: { NM_COMMISSIONER_SESSION_ID_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.commissioner_sess_id is not nullField and\ p.thread_meshcop.tlv.pan_id is nullField ).\ @@ -253,7 +253,7 @@ def verify(self, pv): filter_coap_ack(MGMT_COMMISSIONER_GET_URI).\ filter(lambda p: { NM_BORDER_AGENT_LOCATOR_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.ba_locator is not nullField and\ p.thread_meshcop.tlv.net_name is nullField ).\ diff --git a/tests/scripts/thread-cert/Cert_9_2_02_MGMTCommissionerSet.py b/tests/scripts/thread-cert/Cert_9_2_02_MGMTCommissionerSet.py index 7ece17af2..ea6e4ef36 100755 --- a/tests/scripts/thread-cert/Cert_9_2_02_MGMTCommissionerSet.py +++ b/tests/scripts/thread-cert/Cert_9_2_02_MGMTCommissionerSet.py @@ -185,7 +185,7 @@ def verify(self, pv): filter(lambda p: { NM_COMMISSIONER_SESSION_ID_TLV, NM_STEERING_DATA_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.steering_data == Bytes('ff') ).\ must_next() @@ -218,15 +218,15 @@ def verify(self, pv): SOURCE_ADDRESS_TLV, ACTIVE_TIMESTAMP_TLV, LEADER_DATA_TLV - } == set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ { NWD_COMMISSIONING_DATA_TLV - } == set(p.thread_nwd.tlv.type) and\ + } <= set(p.thread_nwd.tlv.type) and\ { NM_BORDER_AGENT_LOCATOR_TLV, NM_COMMISSIONER_SESSION_ID_TLV, NM_STEERING_DATA_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_nwd.tlv.stable == [0] ).\ must_next() @@ -244,7 +244,7 @@ def verify(self, pv): filter(lambda p: { NM_COMMISSIONER_SESSION_ID_TLV, NM_BORDER_AGENT_LOCATOR_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.ba_locator == 0x0400 ).\ must_next() @@ -278,7 +278,7 @@ def verify(self, pv): NM_COMMISSIONER_SESSION_ID_TLV, NM_STEERING_DATA_TLV, NM_BORDER_AGENT_LOCATOR_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.ba_locator == 0x0400 and\ p.thread_meshcop.tlv.steering_data == Bytes('ff') ).\ @@ -311,7 +311,7 @@ def verify(self, pv): filter(lambda p: { NM_COMMISSIONER_SESSION_ID_TLV, NM_STEERING_DATA_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.commissioner_sess_id == 0xFFFF and\ p.thread_meshcop.tlv.steering_data == Bytes('ff') ).\ @@ -346,7 +346,7 @@ def verify(self, pv): NM_COMMISSIONER_SESSION_ID_TLV, NM_STEERING_DATA_TLV, NM_CHANNEL_TLV - } == set(p.thread_meshcop.tlv.type) and\ + } <= set(p.thread_meshcop.tlv.type) and\ p.thread_meshcop.tlv.steering_data == Bytes('ff') ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_9_2_03_ActiveDatasetGet.py b/tests/scripts/thread-cert/Cert_9_2_03_ActiveDatasetGet.py index 66ac7d8a3..59ab1555a 100755 --- a/tests/scripts/thread-cert/Cert_9_2_03_ActiveDatasetGet.py +++ b/tests/scripts/thread-cert/Cert_9_2_03_ActiveDatasetGet.py @@ -162,7 +162,7 @@ def verify(self, pv): NM_PAN_ID_TLV, NM_PSKC_TLV, NM_SECURITY_POLICY_TLV - } == set(p.thread_meshcop.tlv.type) + } <= set(p.thread_meshcop.tlv.type) ).\ must_next() @@ -199,7 +199,7 @@ def verify(self, pv): NM_CHANNEL_MASK_TLV, NM_NETWORK_MESH_LOCAL_PREFIX_TLV, NM_NETWORK_NAME_TLV - } == set(p.thread_meshcop.tlv.type) + } <= set(p.thread_meshcop.tlv.type) ).\ must_next() @@ -240,7 +240,7 @@ def verify(self, pv): NM_CHANNEL_TLV, NM_NETWORK_MESH_LOCAL_PREFIX_TLV, NM_NETWORK_NAME_TLV, - } == set(p.thread_meshcop.tlv.type) + } <= set(p.thread_meshcop.tlv.type) ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_9_2_07_DelayTimer.py b/tests/scripts/thread-cert/Cert_9_2_07_DelayTimer.py index 0460f1dc2..b4c51668f 100755 --- a/tests/scripts/thread-cert/Cert_9_2_07_DelayTimer.py +++ b/tests/scripts/thread-cert/Cert_9_2_07_DelayTimer.py @@ -159,7 +159,7 @@ def verify(self, pv): # Step 4: Leader MUST send a unicast MLE Child ID Response to the Router _lpkts.filter_wpan_dst64(ROUTER).filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next( - ).must_verify(lambda p: {ACTIVE_OPERATION_DATASET_TLV, ACTIVE_TIMESTAMP_TLV} < set(p.mle.tlv.type) and { + ).must_verify(lambda p: {ACTIVE_OPERATION_DATASET_TLV, ACTIVE_TIMESTAMP_TLV} <= set(p.mle.tlv.type) and { NM_CHANNEL_TLV, NM_CHANNEL_MASK_TLV, NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_KEY_TLV, NM_NETWORK_MESH_LOCAL_PREFIX_TLV, NM_NETWORK_NAME_TLV, NM_PAN_ID_TLV, NM_PSKC_TLV, NM_SECURITY_POLICY_TLV } <= set(p.thread_meshcop.tlv.type) and p.mle.tlv.active_tstamp == LEADER_ACTIVE_TIMESTAMP) @@ -177,7 +177,7 @@ def verify(self, pv): # Step 10: Leader MUST send a unicast MLE Data Response to the Router _lpkts.filter_wpan_dst64(ROUTER).filter_mle_cmd(MLE_DATA_RESPONSE).must_next( - ).must_verify(lambda p: {ACTIVE_OPERATION_DATASET_TLV, ACTIVE_TIMESTAMP_TLV} < set(p.mle.tlv.type) and { + ).must_verify(lambda p: {ACTIVE_OPERATION_DATASET_TLV, ACTIVE_TIMESTAMP_TLV} <= set(p.mle.tlv.type) and { NM_CHANNEL_TLV, NM_CHANNEL_MASK_TLV, NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_KEY_TLV, NM_NETWORK_MESH_LOCAL_PREFIX_TLV, NM_NETWORK_NAME_TLV, NM_PAN_ID_TLV, NM_PSKC_TLV, NM_SECURITY_POLICY_TLV } <= set(p.thread_meshcop.tlv.type) and p.mle.tlv.active_tstamp == ROUTER_ACTIVE_TIMESTAMP) @@ -198,7 +198,7 @@ def verify(self, pv): # Step 16: Leader MUST send a unicast MLE Data Response to the Router _lpkts.filter_wpan_dst64(ROUTER).filter_mle_cmd(MLE_DATA_RESPONSE).must_next().must_verify( - lambda p: {ACTIVE_TIMESTAMP_TLV, PENDING_TIMESTAMP_TLV} < set(p.mle.tlv.type) and p.mle.tlv.active_tstamp + lambda p: {ACTIVE_TIMESTAMP_TLV, PENDING_TIMESTAMP_TLV} <= set(p.mle.tlv.type) and p.mle.tlv.active_tstamp == ROUTER_ACTIVE_TIMESTAMP and p.mle.tlv.pending_tstamp == ROUTER_PENDING_TIMESTAMP) # Step 18: The DUT MUST send MGMT_PENDING_SET.rsp to the Commissioner diff --git a/tests/scripts/thread-cert/Cert_9_2_08_PersistentDatasets.py b/tests/scripts/thread-cert/Cert_9_2_08_PersistentDatasets.py index d64ff047c..a42188169 100755 --- a/tests/scripts/thread-cert/Cert_9_2_08_PersistentDatasets.py +++ b/tests/scripts/thread-cert/Cert_9_2_08_PersistentDatasets.py @@ -285,7 +285,7 @@ def verify(self, pv): filter(lambda p: { TLV_REQUEST_TLV, NETWORK_DATA_TLV - } < set(p.mle.tlv.type) and\ + } <= set(p.mle.tlv.type) and\ p.thread_nwd.tlv.type is nullField and\ p.mle.tlv.active_tstamp == LEADER_ACTIVE_TIMESTAMP ).\ @@ -302,7 +302,7 @@ def verify(self, pv): ACTIVE_TIMESTAMP_TLV, PENDING_TIMESTAMP_TLV, PENDING_OPERATION_DATASET_TLV - } < set(p.mle.tlv.type) + } <= set(p.mle.tlv.type) ).\ must_next() diff --git a/tests/scripts/thread-cert/Cert_9_2_12_Announce.py b/tests/scripts/thread-cert/Cert_9_2_12_Announce.py index f0dd65ef2..f6db932e3 100755 --- a/tests/scripts/thread-cert/Cert_9_2_12_Announce.py +++ b/tests/scripts/thread-cert/Cert_9_2_12_Announce.py @@ -152,7 +152,7 @@ def verify(self, pv): pkts.filter_wpan_src64(LEADER_2).filter_ipv6_dst(LINK_LOCAL_ALL_NODES_MULTICAST_ADDRESS).filter_mle_cmd( MLE_ANNOUNCE).must_next().must_verify( - lambda p: {CHANNEL_TLV, PAN_ID_TLV, ACTIVE_TIMESTAMP_TLV} == set(p.mle.tlv.type) and p.wpan.dst_pan == + lambda p: {CHANNEL_TLV, PAN_ID_TLV, ACTIVE_TIMESTAMP_TLV} <= set(p.mle.tlv.type) and p.wpan.dst_pan == 0xffff and p.wpan.aux_sec.key_id_mode == 0x2 and p.wpan.aux_sec.key_source == 0x00000000ffffffff) # Step 5: MED MUST send a MLE Child ID Request on its new channel @@ -163,7 +163,7 @@ def verify(self, pv): pkts.filter_wpan_src64(MED).filter_ipv6_dst(LINK_LOCAL_ALL_NODES_MULTICAST_ADDRESS).filter_mle_cmd( MLE_ANNOUNCE).must_next().must_verify( - lambda p: {CHANNEL_TLV, PAN_ID_TLV, ACTIVE_TIMESTAMP_TLV} == set(p.mle.tlv.type) and p.wpan.dst_pan == + lambda p: {CHANNEL_TLV, PAN_ID_TLV, ACTIVE_TIMESTAMP_TLV} <= set(p.mle.tlv.type) and p.wpan.dst_pan == 0xffff and p.wpan.aux_sec.key_id_mode == 0x2 and p.wpan.aux_sec.key_source == 0x00000000ffffffff) # Step 6: MED MUST respond with an ICMPv6 Echo Reply diff --git a/tests/scripts/thread-cert/Cert_9_2_13_EnergyScan.py b/tests/scripts/thread-cert/Cert_9_2_13_EnergyScan.py index 3e9df4781..58e28e01a 100755 --- a/tests/scripts/thread-cert/Cert_9_2_13_EnergyScan.py +++ b/tests/scripts/thread-cert/Cert_9_2_13_EnergyScan.py @@ -112,12 +112,12 @@ def verify(self, pv): # Step 3: The DUT MUST send MGMT_ED_REPORT.ans to the Commissioner and report energy measurements _pkts.filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_request(MGMT_ED_REPORT).must_next().must_verify( - lambda p: {NM_CHANNEL_MASK_TLV, NM_ENERGY_LIST_TLV} == set(p.thread_meshcop.tlv.type) and p.thread_meshcop. + lambda p: {NM_CHANNEL_MASK_TLV, NM_ENERGY_LIST_TLV} <= set(p.thread_meshcop.tlv.type) and p.thread_meshcop. tlv.chan_mask_mask == '0000a000' and len(p.thread_meshcop.tlv.energy_list) == 2) # Step 5: The DUT MUST send MGMT_ED_REPORT.ans to the Commissioner and report energy measurements _pkts.filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_request(MGMT_ED_REPORT).must_next().must_verify( - lambda p: {NM_CHANNEL_MASK_TLV, NM_ENERGY_LIST_TLV} == set(p.thread_meshcop.tlv.type) and p.thread_meshcop. + lambda p: {NM_CHANNEL_MASK_TLV, NM_ENERGY_LIST_TLV} <= set(p.thread_meshcop.tlv.type) and p.thread_meshcop. tlv.chan_mask_mask == '0000a000' and len(p.thread_meshcop.tlv.energy_list) == 2) # Step 6: The DUT MUST respond with ICMPv6 Echo Reply diff --git a/tests/scripts/thread-cert/Cert_9_2_15_PendingPartition.py b/tests/scripts/thread-cert/Cert_9_2_15_PendingPartition.py index a0e2bccda..51326222d 100755 --- a/tests/scripts/thread-cert/Cert_9_2_15_PendingPartition.py +++ b/tests/scripts/thread-cert/Cert_9_2_15_PendingPartition.py @@ -174,24 +174,24 @@ def verify(self, pv): # Step 5: Router_2 begins attach process by sending a multicast MLE Parent Request # The first MLE Parent Request sent MUST NOT be sent to all routers and REEDS _router2_pkts.range(pkts.index).filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set( + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 0) # Step 7: Router_2 MUST send a MLE Child ID Request to Router_1 _router2_pkts.filter_mle_cmd(MLE_CHILD_ID_REQUEST).must_next().must_verify(lambda p: { RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MODE_TLV, TIMEOUT_TLV, VERSION_TLV, TLV_REQUEST_TLV - } < set(p.mle.tlv.type) and ADDRESS_REGISTRATION_TLV not in p.mle.tlv.type) + } <= set(p.mle.tlv.type) and ADDRESS_REGISTRATION_TLV not in p.mle.tlv.type) # Step 14: Router_2 begins attach process by sending a multicast MLE Parent Request # The first MLE Parent Request sent MUST NOT be sent to all routers and REEDS _router2_pkts.filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set( + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 0) # Step 16: Router_2 MUST send a MLE Child ID Request to Router_1 _router2_pkts.filter_mle_cmd(MLE_CHILD_ID_REQUEST).must_next().must_verify(lambda p: { RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MODE_TLV, TIMEOUT_TLV, VERSION_TLV, TLV_REQUEST_TLV - } < set(p.mle.tlv.type) and ADDRESS_REGISTRATION_TLV not in p.mle.tlv.type) + } <= set(p.mle.tlv.type) and ADDRESS_REGISTRATION_TLV not in p.mle.tlv.type) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/Cert_9_2_16_ActivePendingPartition.py b/tests/scripts/thread-cert/Cert_9_2_16_ActivePendingPartition.py index 8515659d5..727eb8be3 100755 --- a/tests/scripts/thread-cert/Cert_9_2_16_ActivePendingPartition.py +++ b/tests/scripts/thread-cert/Cert_9_2_16_ActivePendingPartition.py @@ -184,24 +184,24 @@ def verify(self, pv): # Step 5: Router_2 begins attach process by sending a multicast MLE Parent Request # The first MLE Parent Request sent MUST NOT be sent to all routers and REEDS _router2_pkts.range(pkts.index).filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set( + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 0) # Step 7: Router_2 MUST send a MLE Child ID Request to Router_1 _router2_pkts.filter_mle_cmd(MLE_CHILD_ID_REQUEST).must_next().must_verify(lambda p: { RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MODE_TLV, TIMEOUT_TLV, VERSION_TLV, TLV_REQUEST_TLV - } < set(p.mle.tlv.type) and ADDRESS_REGISTRATION_TLV not in p.mle.tlv.type) + } <= set(p.mle.tlv.type) and ADDRESS_REGISTRATION_TLV not in p.mle.tlv.type) # Step 14: Router_2 begins attach process by sending a multicast MLE Parent Request # The first MLE Parent Request sent MUST NOT be sent to all routers and REEDS _router2_pkts.filter_mle_cmd(MLE_PARENT_REQUEST).must_next().must_verify( - lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} == set( + lambda p: {MODE_TLV, CHALLENGE_TLV, SCAN_MASK_TLV, VERSION_TLV} <= set( p.mle.tlv.type) and p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 0) # Step 16: Router_2 MUST send a MLE Child ID Request to Router_1 _router2_pkts.filter_mle_cmd(MLE_CHILD_ID_REQUEST).must_next().must_verify(lambda p: { RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MODE_TLV, TIMEOUT_TLV, VERSION_TLV, TLV_REQUEST_TLV - } < set(p.mle.tlv.type) and ADDRESS_REGISTRATION_TLV not in p.mle.tlv.type) + } <= set(p.mle.tlv.type) and ADDRESS_REGISTRATION_TLV not in p.mle.tlv.type) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/Cert_9_2_17_Orphan.py b/tests/scripts/thread-cert/Cert_9_2_17_Orphan.py index 9fce65e29..9a641fb1a 100755 --- a/tests/scripts/thread-cert/Cert_9_2_17_Orphan.py +++ b/tests/scripts/thread-cert/Cert_9_2_17_Orphan.py @@ -129,7 +129,7 @@ def verify(self, pv): # Step 6: ED MUST send a MLE Announce Message # The Destination PAN ID (0xFFFF) in the IEEE 802.15.4 MAC and MUST be secured using Key ID Mode 2. _epkts.filter_mle_cmd(MLE_ANNOUNCE).must_next().must_verify( - lambda p: {CHANNEL_TLV, PAN_ID_TLV, ACTIVE_TIMESTAMP_TLV} == set( + lambda p: {CHANNEL_TLV, PAN_ID_TLV, ACTIVE_TIMESTAMP_TLV} <= set( p.mle.tlv.type) and p.wpan.dst_pan == 0xffff and p.wpan.aux_sec.key_id_mode == 0x2) # Step 8: ED MUST attempt to attach on the Secondary channel, diff --git a/tests/scripts/thread-cert/Cert_9_2_18_RollBackActiveTimestamp.py b/tests/scripts/thread-cert/Cert_9_2_18_RollBackActiveTimestamp.py index 5c809ea23..a5b2db255 100755 --- a/tests/scripts/thread-cert/Cert_9_2_18_RollBackActiveTimestamp.py +++ b/tests/scripts/thread-cert/Cert_9_2_18_RollBackActiveTimestamp.py @@ -196,7 +196,7 @@ def verify(self, pv): MLE_DATA_RESPONSE).must_next() _pkt.must_verify(lambda p: { SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV, PENDING_TIMESTAMP_TLV - } == set(p.mle.tlv.type) and {NM_COMMISSIONER_SESSION_ID_TLV, NM_BORDER_AGENT_LOCATOR_TLV} <= set( + } <= set(p.mle.tlv.type) and {NM_COMMISSIONER_SESSION_ID_TLV, NM_BORDER_AGENT_LOCATOR_TLV} <= set( p.thread_meshcop.tlv.type) and p.thread_nwd.tlv.stable == [0]) # Step 9: Router MUST send a unicast MLE Data Request to the Leader @@ -209,7 +209,7 @@ def verify(self, pv): lambda p: { SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV, PENDING_TIMESTAMP_TLV, PENDING_OPERATION_DATASET_TLV - } == set(p.mle.tlv.type) and { + } <= set(p.mle.tlv.type) and { NM_COMMISSIONER_SESSION_ID_TLV, NM_BORDER_AGENT_LOCATOR_TLV, NM_ACTIVE_TIMESTAMP_TLV, NM_NETWORK_NAME_TLV, NM_NETWORK_KEY_TLV } <= set(p.thread_meshcop.tlv.type) and p.thread_nwd.tlv.stable == [0]) @@ -222,7 +222,7 @@ def verify(self, pv): MLE_DATA_RESPONSE).must_next().must_verify( lambda p: { SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV, PENDING_TIMESTAMP_TLV - } == set(p.mle.tlv.type) and p.mle.tlv.leader_data.data_version == _pkt.mle.tlv.leader_data. + } <= set(p.mle.tlv.type) and p.mle.tlv.leader_data.data_version == _pkt.mle.tlv.leader_data. data_version and p.mle.tlv.leader_data.stable_data_version == _pkt.mle.tlv.leader_data. stable_data_version and {NM_COMMISSIONER_SESSION_ID_TLV, NM_BORDER_AGENT_LOCATOR_TLV} <= set( p.thread_meshcop.tlv.type) and p.thread_nwd.tlv.stable == [0]) @@ -231,7 +231,7 @@ def verify(self, pv): pkts.filter_wpan_src64(ROUTER_1).filter_wpan_dst64(SED).filter_mle_cmd( MLE_CHILD_UPDATE_REQUEST).must_next().must_verify(lambda p: { SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV, PENDING_TIMESTAMP_TLV - } == set(p.mle.tlv.type) and p.mle.tlv.leader_data.data_version == _pkt.mle.tlv.leader_data.data_version) + } <= set(p.mle.tlv.type) and p.mle.tlv.leader_data.data_version == _pkt.mle.tlv.leader_data.data_version) # Step 13: SED MUST send a unicast MLE Data Request to Router_1 _pkts_sed.filter_wpan_src64(SED).filter_wpan_dst64(ROUTER_1).filter_mle_cmd(MLE_DATA_REQUEST).must_next( diff --git a/tests/scripts/thread-cert/Cert_9_2_19_PendingDatasetGet.py b/tests/scripts/thread-cert/Cert_9_2_19_PendingDatasetGet.py index ab0842d57..34079f178 100755 --- a/tests/scripts/thread-cert/Cert_9_2_19_PendingDatasetGet.py +++ b/tests/scripts/thread-cert/Cert_9_2_19_PendingDatasetGet.py @@ -228,7 +228,7 @@ def verify(self, pv): NM_PENDING_TIMESTAMP_TLV, NM_PSKC_TLV, NM_SECURITY_POLICY_TLV - } == set(p.thread_meshcop.tlv.type) + } <= set(p.thread_meshcop.tlv.type) ).\ must_next() @@ -259,7 +259,7 @@ def verify(self, pv): filter(lambda p: { NM_DELAY_TIMER_TLV, NM_PAN_ID_TLV - } == set(p.coap.tlv.type) and\ + } <= set(p.coap.tlv.type) and\ p.thread_meshcop.tlv.pan_id == [0xafce] and\ p.thread_meshcop.tlv.delay_timer < 60000 ).\ diff --git a/tests/scripts/thread-cert/pktverify/packet_filter.py b/tests/scripts/thread-cert/pktverify/packet_filter.py index 307330448..e9122ea2e 100644 --- a/tests/scripts/thread-cert/pktverify/packet_filter.py +++ b/tests/scripts/thread-cert/pktverify/packet_filter.py @@ -363,7 +363,7 @@ def filter_mle_advertisement(self, role: str, **kwargs): return self.filter_LLANMA(). \ filter_mle_cmd(consts.MLE_ADVERTISEMENT). \ - filter(lambda p: tlv_set == + filter(lambda p: tlv_set <= set(p.mle.tlv.type) and \ p.ipv6.hlim == 255, **kwargs ) From 4465c9b57a765b47854f5402a91ec89c0277b987 Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Wed, 28 Aug 2024 00:41:55 +0800 Subject: [PATCH 130/160] [tests] optimize multi-ail test case to run three BRs on two infra link (#10637) This commit: 1. update the topology to make 3 BRs running on 2 AILs; 2. specify the test name as `test_multi_backbone_infra`, which is focusing on verifying the multiple backbone framework works good 3. fix bug that previously pinging throught ethernet does not actually work. For 3, previous `br1.ping(br2_infra_link_local, interface=br1_infra_link_local)` actually send ping command via ot-ctl instead of the docker bash. The new `br1.ping(br2_onlink_ula, backbone=True)` actually send a docker bash command to ping another node via ethernet. Here we choose to use onlink ULA address (9100::) instead of the link local address because only "ping [link-local-addr]%eth0" (by specifying the zone index) can work for link-local address, but this is not currently supported by `LinuxHost.ping_ether()` method. --- .../border_router/test_multi_ail.py | 90 ++++++++++--------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/tests/scripts/thread-cert/border_router/test_multi_ail.py b/tests/scripts/thread-cert/border_router/test_multi_ail.py index 66642fd7f..e2d9174dd 100755 --- a/tests/scripts/thread-cert/border_router/test_multi_ail.py +++ b/tests/scripts/thread-cert/border_router/test_multi_ail.py @@ -38,21 +38,19 @@ IPV4_CIDR_ADDR_CMD = f'ip addr show {config.BACKBONE_IFNAME} | grep -w inet | grep -Eo "[0-9.]+/[0-9]+"' -class TwoBorderRoutersOnTwoInfrastructures(thread_cert.TestCase): +class ThreeBRs_TwoInfra(thread_cert.TestCase): """ Test that two border routers on different infrastructures can ping each other via Thread interface. Topology: - -------(backbone0.0)-------- | ---------(backbone0.1)------- - | | - BR1 (Leader) .............. BR2 (Router) + ----(backbone0.0)---- | -------(backbone0.1)------ + | | | + BR1 ............. BR2 ........ BR3 """ USE_MESSAGE_FACTORY = False - - BR1 = 1 - BR2 = 2 + BR1, BR2, BR3 = range(1, 4) TOPOLOGY = { BR1: { @@ -65,56 +63,66 @@ class TwoBorderRoutersOnTwoInfrastructures(thread_cert.TestCase): BR2: { 'name': 'BR_2', 'backbone_network_id': 1, - 'allowlist': [BR1], + 'allowlist': [BR1, BR3], + 'is_otbr': True, + 'version': '1.3', + }, + BR3: { + 'name': 'BR_3', + 'backbone_network_id': 1, + 'allowlist': [BR2], 'is_otbr': True, 'version': '1.3', } } - def test(self): + def test_multi_backbone_infra(self): + """This test ensures that the multiple backbone infra works as expected.""" br1: OtbrNode = self.nodes[self.BR1] br2: OtbrNode = self.nodes[self.BR2] + br3: OtbrNode = self.nodes[self.BR3] # start nodes - br1.start() - self.simulator.go(2) - br2.start() + for br in [br1, br2, br3]: + br.start() self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) - # check roles - self.assertEqual('leader', br1.get_state()) - self.assertEqual('router', br2.get_state()) - - # check two BRs AIL are in different subnets + # check BR1 and BR2 are not on the same subnet, but BR2 and BR3 are on the same subnet br1_infra_ip_addr = br1.bash(IPV4_CIDR_ADDR_CMD) br2_infra_ip_addr = br2.bash(IPV4_CIDR_ADDR_CMD) + br3_infra_ip_addr = br3.bash(IPV4_CIDR_ADDR_CMD) + assert len(br1_infra_ip_addr) == 1 + assert len(br2_infra_ip_addr) == 1 + assert len(br3_infra_ip_addr) == 1 - self.assertEqual(len(br1_infra_ip_addr), 1) - self.assertEqual(len(br2_infra_ip_addr), 1) self.assertNotEqual(ipaddress.ip_network(br1_infra_ip_addr[0].strip(), strict=False), ipaddress.ip_network(br2_infra_ip_addr[0].strip(), strict=False)) - - # Ping test - br1_thread_link_local = br1.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL) - br2_thread_link_local = br2.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL) - br1_infra_link_local = br1.get_ip6_address(config.ADDRESS_TYPE.BACKBONE_LINK_LOCAL) - br2_infra_link_local = br2.get_ip6_address(config.ADDRESS_TYPE.BACKBONE_LINK_LOCAL) - - # ping each other using Thread link-local address - self.assertTrue(br1.ping(br2_thread_link_local)) - self.assertTrue(br2.ping(br1_thread_link_local)) - - # ping each other using Infra link-local address - self.assertFalse(br1.ping(br2_infra_link_local, interface=br1_infra_link_local)) - self.assertFalse(br2.ping(br1_infra_link_local, interface=br2_infra_link_local)) - - # br peers - self.assertEqual(br1.get_br_peers_rloc16s(), [br2.get_addr16()]) - self.assertEqual(br2.get_br_peers_rloc16s(), [br1.get_addr16()]) - - # br routers - self.assertEqual(br1.get_br_routers_ip_addresses(), []) - self.assertEqual(br2.get_br_routers_ip_addresses(), []) + self.assertEqual(ipaddress.ip_network(br2_infra_ip_addr[0].strip(), strict=False), + ipaddress.ip_network(br3_infra_ip_addr[0].strip(), strict=False)) + + # ping each other using Thread MLEID + br1_mleid = br1.get_ip6_address(config.ADDRESS_TYPE.ML_EID) + br2_mleid = br2.get_ip6_address(config.ADDRESS_TYPE.ML_EID) + br3_mleid = br3.get_ip6_address(config.ADDRESS_TYPE.ML_EID) + + self.assertTrue(br1.ping(br2_mleid)) + self.assertTrue(br1.ping(br3_mleid)) + self.assertTrue(br2.ping(br1_mleid)) + self.assertTrue(br2.ping(br3_mleid)) + self.assertTrue(br3.ping(br1_mleid)) + self.assertTrue(br3.ping(br2_mleid)) + + # ping each other using Infra ULA + br1_onlink_ula = br1.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0] + br2_onlink_ula = br2.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0] + br3_onlink_ula = br3.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0] + + self.assertFalse(br1.ping(br2_onlink_ula, backbone=True)) + self.assertFalse(br1.ping(br3_onlink_ula, backbone=True)) + self.assertFalse(br2.ping(br1_onlink_ula, backbone=True)) + self.assertFalse(br3.ping(br1_onlink_ula, backbone=True)) + self.assertTrue(br2.ping(br3_onlink_ula, backbone=True)) + self.assertTrue(br3.ping(br2_onlink_ula, backbone=True)) if __name__ == '__main__': From e3f0a7cc37fff434dc6e7b115c9bb9f318dc25ab Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 27 Aug 2024 11:13:32 -0700 Subject: [PATCH 131/160] [radio-spinel] fix tracking of current MAC frame counter (#10635) This commit fixes an issue in `RadioSpinel` related to the tracking of the MAC frame counter used during RCP restoration. Previously, `RadioSpinel` tracked the last seen counter on any secured received/transmitted frame. However, the frame counter should only be tracked for frames that use Key ID Mode 1 and where the included Key ID in the frame matches the current Key ID being used. The `SubMac` module also tracks the current MAC frame counter, and it does perform Key ID Mode and Key ID checks. This commit adds a new public API `otLinkGetFrameCounter()` to get the current frame counter. This is used by `RadioSpinel` to get the frame counter instead of having `RadioSpinel` track the frame counter itself. --- include/openthread/instance.h | 2 +- include/openthread/link.h | 10 ++++++++++ src/core/api/link_api.cpp | 5 +++++ src/lib/spinel/radio_spinel.cpp | 10 ++++------ src/lib/spinel/radio_spinel.hpp | 1 - 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index d3cdebb12..d26bdf851 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (437) +#define OPENTHREAD_API_VERSION (438) /** * @addtogroup api-instance diff --git a/include/openthread/link.h b/include/openthread/link.h index 15bc0d860..bd42cff20 100644 --- a/include/openthread/link.h +++ b/include/openthread/link.h @@ -690,6 +690,16 @@ uint8_t otLinkGetMaxFrameRetriesIndirect(otInstance *aInstance); */ void otLinkSetMaxFrameRetriesIndirect(otInstance *aInstance, uint8_t aMaxFrameRetriesIndirect); +/** + * Gets the current MAC frame counter value. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + * @returns The current MAC frame counter value. + * + */ +uint32_t otLinkGetFrameCounter(otInstance *aInstance); + /** * Gets the address mode of MAC filter. * diff --git a/src/core/api/link_api.cpp b/src/core/api/link_api.cpp index 95ec35c42..4d8484745 100644 --- a/src/core/api/link_api.cpp +++ b/src/core/api/link_api.cpp @@ -188,6 +188,11 @@ void otLinkSetMaxFrameRetriesIndirect(otInstance *aInstance, uint8_t aMaxFrameRe #endif // OPENTHREAD_FTD +uint32_t otLinkGetFrameCounter(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetFrameCounter(); +} + #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE otMacFilterAddressMode otLinkFilterGetAddressMode(otInstance *aInstance) diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 49d88cbb8..0bd60e4b2 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -703,7 +704,6 @@ otError RadioSpinel::ParseRadioFrame(otRadioFrame &aFrame, if (flags & SPINEL_MD_FLAG_ACKED_SEC) { mMacFrameCounterSet = true; - mMacFrameCounter = aFrame.mInfo.mRxInfo.mAckFrameCounter; } #endif } @@ -926,7 +926,6 @@ otError RadioSpinel::SetMacFrameCounter(uint32_t aMacFrameCounter, bool aSetIfLa aMacFrameCounter, aSetIfLarger)); #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 mMacFrameCounterSet = true; - mMacFrameCounter = aMacFrameCounter; #endif exit: @@ -1578,7 +1577,6 @@ void RadioSpinel::HandleTransmitDone(uint32_t aCommand, #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 mMacFrameCounterSet = true; - mMacFrameCounter = frameCounter; #endif } @@ -2150,7 +2148,7 @@ void RadioSpinel::RestoreProperties(void) if (mMacFrameCounterSet) { - // There is a chance that radio/RCP has used some counters after `mMacFrameCounter` (for enh ack) and they + // There is a chance that radio/RCP has used some counters after otLinkGetFrameCounter() (for enh ack) and they // are in queue to be sent to host (not yet processed by host RadioSpinel). Here we add some guard jump // when we restore the frame counter. // Consider the worst case: the radio/RCP continuously receives the shortest data frame and replies with the @@ -2162,8 +2160,8 @@ void RadioSpinel::RestoreProperties(void) // CounterGuard: 2000ms(Timeout) / [(28bytes(Data) + 29bytes(Ack)) * 32us/byte + 192us(Ifs)] = 992 static constexpr uint16_t kFrameCounterGuard = 1000; - SuccessOrDie( - Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S, mMacFrameCounter + kFrameCounterGuard)); + SuccessOrDie(Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S, + otLinkGetFrameCounter(mInstance) + kFrameCounterGuard)); } for (int i = 0; i < mSrcMatchShortEntryCount; ++i) diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 36acc7773..1e1590880 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -1316,7 +1316,6 @@ class RadioSpinel : private Logger int8_t mCcaEnergyDetectThreshold; int8_t mTransmitPower; int8_t mFemLnaGain; - uint32_t mMacFrameCounter; bool mCoexEnabled : 1; bool mSrcMatchEnabled : 1; From a30cbda8ae3ebb02f2557aa9c1ea460729324c08 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 27 Aug 2024 11:14:50 -0700 Subject: [PATCH 132/160] [mle] include Link Margin TLV in Child Update messages (#10626) This commit adds code to include the Link Margin TLV in MLE Child Update Request or Response messages sent by a parent to a child. This allows the child to learn and update its "link quality out" to its parent. The change is designed to be backward compatible. Child devices running older firmware will simply disregard this additional TLV. When processing the message, the presence of the Link Margin TLV is checked (it is optional). --- src/core/thread/mle.cpp | 24 ++++++++++++++++++++++++ src/core/thread/mle_router.cpp | 11 ++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index efe9e593b..6b64959f1 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -3394,6 +3394,7 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo) RxChallenge challenge; TlvList requestedTlvList; TlvList tlvList; + uint8_t linkMarginOut; SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, sourceAddress)); @@ -3436,6 +3437,17 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo) SuccessOrExit(error = HandleLeaderData(aRxInfo)); + switch (Tlv::Find(aRxInfo.mMessage, linkMarginOut)) + { + case kErrorNone: + mParent.SetLinkQualityOut(LinkQualityForLinkMargin(linkMarginOut)); + break; + case kErrorNotFound: + break; + default: + ExitNow(error = kErrorParse); + } + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE { Mac::CslAccuracy cslAccuracy; @@ -3494,6 +3506,7 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) uint32_t mleFrameCounter; uint16_t sourceAddress; uint32_t timeout; + uint8_t linkMarginOut; Log(kMessageReceive, kTypeChildUpdateResponseAsChild, aRxInfo.mMessageInfo.GetPeerAddr()); @@ -3616,6 +3629,17 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) OT_ASSERT(false); } + switch (Tlv::Find(aRxInfo.mMessage, linkMarginOut)) + { + case kErrorNone: + mParent.SetLinkQualityOut(LinkQualityForLinkMargin(linkMarginOut)); + break; + case kErrorNotFound: + break; + default: + ExitNow(error = kErrorParse); + } + aRxInfo.mClass = response.IsEmpty() ? RxInfo::kPeerMessage : RxInfo::kAuthoritativeMessage; exit: diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index fcb4595de..087be21bc 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -2211,6 +2211,7 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) child->SetDeviceMode(mode); tlvList.Add(Tlv::kMode); + tlvList.Add(Tlv::kLinkMargin); // Parent MUST include Leader Data TLV in Child Update Response tlvList.Add(Tlv::kLeaderData); @@ -2921,7 +2922,11 @@ Error MleRouter::SendChildUpdateRequest(Child &aChild) SuccessOrExit(error = message->AppendNetworkDataTlv(aChild.GetNetworkDataType())); SuccessOrExit(error = message->AppendActiveAndPendingTimestampTlvs()); - if (!aChild.IsStateValid()) + if (aChild.IsStateValid()) + { + SuccessOrExit(error = message->AppendLinkMarginTlv(aChild.GetLinkInfo().GetLinkMargin())); + } + else { SuccessOrExit(error = message->AppendTlvRequestTlv(kTlvs)); @@ -3029,6 +3034,10 @@ void MleRouter::SendChildUpdateResponse(Child *aChild, SuccessOrExit(error = message->AppendTimeoutTlv(aChild->GetTimeout())); break; + case Tlv::kLinkMargin: + SuccessOrExit(error = message->AppendLinkMarginTlv(aChild->GetLinkInfo().GetLinkMargin())); + break; + case Tlv::kSupervisionInterval: SuccessOrExit(error = message->AppendSupervisionIntervalTlv(aChild->GetSupervisionInterval())); break; From 95a4c338e0f85bd36fbb117d7aab253191380506 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Wed, 28 Aug 2024 09:35:04 +0800 Subject: [PATCH 133/160] [diag] support send security processed frames (#10640) This commit extends the `diag frame` command by adding an argument `-s` to indicate whether the frame has been encrypted or not, so that the diag commands can be used to test frames having security processed in the host. --- src/core/diags/README.md | 7 +++++- src/core/diags/factory_diags.cpp | 39 +++++++++++++++++++++++++++--- src/core/diags/factory_diags.hpp | 1 + tests/scripts/expect/cli-diags.exp | 7 ++++++ 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/core/diags/README.md b/src/core/diags/README.md index ee447b3f9..7af2f6084 100644 --- a/src/core/diags/README.md +++ b/src/core/diags/README.md @@ -10,6 +10,7 @@ The diagnostics module supports common diagnostics features that are listed belo - [diag start](#diag-start) - [diag channel](#diag-channel) - [diag cw](#diag-cw-start) +- [diag frame](#diag-frame) - [diag stream](#diag-stream-start) - [diag power](#diag-power) - [diag powersettings](#diag-powersettings) @@ -77,10 +78,14 @@ Stop transmitting continuous carrier wave. Done ``` -### diag frame \ +### diag frame + +Usage: `diag frame [-s] ` Set the frame (hex encoded) to be used by `diag send` and `diag repeat`. The frame may be overwritten by `diag send` and `diag repeat`. +Specify `-s` to skip security processing in radio layer. + ```bash > diag frame 11223344 Done diff --git a/src/core/diags/factory_diags.cpp b/src/core/diags/factory_diags.cpp index b6fe8645b..b0691e10d 100644 --- a/src/core/diags/factory_diags.cpp +++ b/src/core/diags/factory_diags.cpp @@ -33,6 +33,7 @@ #include "factory_diags.hpp" #include "common/error.hpp" +#include "openthread/platform/radio.h" #if OPENTHREAD_CONFIG_DIAG_ENABLE @@ -216,18 +217,47 @@ Diags::Diags(Instance &aInstance) mStats.Clear(); } +void Diags::ResetTxPacket(void) +{ + mTxPacket->mInfo.mTxInfo.mTxDelayBaseTime = 0; + mTxPacket->mInfo.mTxInfo.mTxDelay = 0; + mTxPacket->mInfo.mTxInfo.mMaxCsmaBackoffs = 0; + mTxPacket->mInfo.mTxInfo.mMaxFrameRetries = 0; + mTxPacket->mInfo.mTxInfo.mRxChannelAfterTxDone = mChannel; + mTxPacket->mInfo.mTxInfo.mTxPower = OT_RADIO_POWER_INVALID; + mTxPacket->mInfo.mTxInfo.mIsHeaderUpdated = false; + mTxPacket->mInfo.mTxInfo.mIsARetx = false; + mTxPacket->mInfo.mTxInfo.mCsmaCaEnabled = false; + mTxPacket->mInfo.mTxInfo.mCslPresent = false; + mTxPacket->mInfo.mTxInfo.mIsSecurityProcessed = false; +} + Error Diags::ProcessFrame(uint8_t aArgsLength, char *aArgs[]) { - Error error = kErrorNone; - uint16_t size = OT_RADIO_FRAME_MAX_SIZE; + Error error = kErrorNone; + uint16_t size = OT_RADIO_FRAME_MAX_SIZE; + bool securityProcessed = false; + + if (aArgsLength >= 1) + { + if (StringMatch(aArgs[0], "-s")) + { + securityProcessed = true; + aArgs++; + aArgsLength--; + } + } VerifyOrExit(aArgsLength == 1, error = kErrorInvalidArgs); SuccessOrExit(error = Utils::CmdLineParser::ParseAsHexString(aArgs[0], size, mTxPacket->mPsdu)); VerifyOrExit(size <= OT_RADIO_FRAME_MAX_SIZE, error = kErrorInvalidArgs); VerifyOrExit(size >= OT_RADIO_FRAME_MIN_SIZE, error = kErrorInvalidArgs); - mTxPacket->mLength = size; - mIsTxPacketSet = true; + + ResetTxPacket(); + mTxPacket->mInfo.mTxInfo.mIsSecurityProcessed = securityProcessed; + mTxPacket->mLength = size; + mIsTxPacketSet = true; exit: AppendErrorResult(error); @@ -468,6 +498,7 @@ void Diags::TransmitPacket(void) if (!mIsTxPacketSet) { + ResetTxPacket(); mTxPacket->mLength = mTxLen; for (uint8_t i = 0; i < mTxLen; i++) diff --git a/src/core/diags/factory_diags.hpp b/src/core/diags/factory_diags.hpp index 01864786d..db9fd8583 100644 --- a/src/core/diags/factory_diags.hpp +++ b/src/core/diags/factory_diags.hpp @@ -210,6 +210,7 @@ class Diags : public InstanceLocator, private NonCopyable void TransmitPacket(void); void Output(const char *aFormat, ...); void AppendErrorResult(Error aError); + void ResetTxPacket(void); static Error ParseLong(char *aString, long &aLong); static Error ParseBool(char *aString, bool &aBool); diff --git a/tests/scripts/expect/cli-diags.exp b/tests/scripts/expect/cli-diags.exp index b9ab58071..3f26628d0 100755 --- a/tests/scripts/expect/cli-diags.exp +++ b/tests/scripts/expect/cli-diags.exp @@ -152,6 +152,13 @@ send "diag repeat 1\n" expect "length 0x7f" expect "Done" +send_user "send frame with security processed\n" +send "diag frame -s 112233\n" +expect "Done" +send "diag send 1\n" +expect "length 0x3" +expect "Done" + send "diag repeat stop\n" expect "Done" From 706013fa7b71017f5916fc134be6309f3495ff7f Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Wed, 28 Aug 2024 10:58:17 +0800 Subject: [PATCH 134/160] [git-tool] explicit disable rebase (#10645) This commit adds `--no-rebase` to `git` command when applying dependencies because recent git versions doesn't have a default rebase strategy anymore. --- script/git-tool | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/git-tool b/script/git-tool index c6fb695f0..f0f057ee8 100755 --- a/script/git-tool +++ b/script/git-tool @@ -46,7 +46,7 @@ apply_dependencies() echo "${dependency}" depends_on_pr="$(echo "${dependency}" | tr -d '\r\n' | cut -d# -f2)" echo "pr: #${depends_on_pr}" - git pull --no-edit origin "pull/${depends_on_pr}/merge" + git pull --no-edit --no-rebase origin "pull/${depends_on_pr}/merge" done < <(grep -E "^Depends-On: *${project_name}" || true) } From f91610f3f3b49d5a4342e57145e73e36bf980d9c Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 28 Aug 2024 08:06:01 -0700 Subject: [PATCH 135/160] [mle] include Supervision TLV only from sleepy child (#10628) This commit updates the code to include the Supervision Interval TLV in MLE messages sent from a child only when the child mode indicates it is sleepy (`!IsRxOnWhenIdle()`). The Network Diagnostic Child TLV is also updated to use zero for the "Supervision Interval" field when the child is not sleepy. The `test-023-mesh-diag.py` is updated to validate this new behavior. The parent node still tracks the received supervision interval from a child in the `Child` entry. This parameter indicates the interval that would be used if the child were to be supervised. --- src/core/thread/mle.cpp | 17 ++++++++++++++--- src/core/thread/mle.hpp | 1 + src/core/thread/network_diagnostic_tlvs.cpp | 2 +- tests/toranj/cli/test-023-mesh-diag.py | 4 ++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 6b64959f1..e2a48f240 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -1776,7 +1776,7 @@ Error Mle::SendChildIdRequest(void) SuccessOrExit(error = message->AppendModeTlv(mDeviceMode)); SuccessOrExit(error = message->AppendTimeoutTlv(mTimeout)); SuccessOrExit(error = message->AppendVersionTlv()); - SuccessOrExit(error = message->AppendSupervisionIntervalTlv(Get().GetInterval())); + SuccessOrExit(error = message->AppendSupervisionIntervalTlvIfSleepyChild()); if (!IsFullThreadDevice()) { @@ -2056,7 +2056,7 @@ Error Mle::SendChildUpdateRequest(ChildUpdateRequestMode aMode) SuccessOrExit(error = message->AppendSourceAddressTlv()); SuccessOrExit(error = message->AppendLeaderDataTlv()); SuccessOrExit(error = message->AppendTimeoutTlv((aMode == kAppendZeroTimeout) ? 0 : mTimeout)); - SuccessOrExit(error = message->AppendSupervisionIntervalTlv(Get().GetInterval())); + SuccessOrExit(error = message->AppendSupervisionIntervalTlvIfSleepyChild()); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (Get().IsCslEnabled()) { @@ -2152,7 +2152,7 @@ Error Mle::SendChildUpdateResponse(const TlvList &aTlvList, break; case Tlv::kSupervisionInterval: - SuccessOrExit(error = message->AppendSupervisionIntervalTlv(Get().GetInterval())); + SuccessOrExit(error = message->AppendSupervisionIntervalTlvIfSleepyChild()); break; #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE @@ -4690,6 +4690,17 @@ Error Mle::TxMessage::AppendAddressEntry(const Ip6::Address &aAddress) return error; } +Error Mle::TxMessage::AppendSupervisionIntervalTlvIfSleepyChild(void) +{ + Error error = kErrorNone; + + VerifyOrExit(!Get().IsRxOnWhenIdle()); + error = AppendSupervisionIntervalTlv(Get().GetInterval()); + +exit: + return error; +} + Error Mle::TxMessage::AppendSupervisionIntervalTlv(uint16_t aInterval) { return Tlv::Append(*this, aInterval); diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 24afe67a9..2c49f5900 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -1036,6 +1036,7 @@ class Mle : public InstanceLocator, private NonCopyable Error AppendLinkMarginTlv(uint8_t aLinkMargin); Error AppendVersionTlv(void); Error AppendAddressRegistrationTlv(AddressRegistrationMode aMode = kAppendAllAddresses); + Error AppendSupervisionIntervalTlvIfSleepyChild(void); Error AppendSupervisionIntervalTlv(uint16_t aInterval); Error AppendXtalAccuracyTlv(void); Error AppendActiveTimestampTlv(void); diff --git a/src/core/thread/network_diagnostic_tlvs.cpp b/src/core/thread/network_diagnostic_tlvs.cpp index 715730f2d..9d4fc6651 100644 --- a/src/core/thread/network_diagnostic_tlvs.cpp +++ b/src/core/thread/network_diagnostic_tlvs.cpp @@ -58,7 +58,7 @@ void ChildTlv::InitFrom(const Child &aChild) mTimeout = BigEndian::HostSwap32(aChild.GetTimeout()); mAge = BigEndian::HostSwap32(Time::MsecToSec(TimerMilli::GetNow() - aChild.GetLastHeard())); mConnectionTime = BigEndian::HostSwap32(aChild.GetConnectionTime()); - mSupervisionInterval = BigEndian::HostSwap16(aChild.GetSupervisionInterval()); + mSupervisionInterval = aChild.IsRxOnWhenIdle() ? 0 : BigEndian::HostSwap16(aChild.GetSupervisionInterval()); mLinkMargin = aChild.GetLinkInfo().GetLinkMargin(); mAverageRssi = aChild.GetLinkInfo().GetAverageRss(); mLastRssi = aChild.GetLinkInfo().GetLastRss(); diff --git a/tests/toranj/cli/test-023-mesh-diag.py b/tests/toranj/cli/test-023-mesh-diag.py index a62475c25..418813e43 100755 --- a/tests/toranj/cli/test-023-mesh-diag.py +++ b/tests/toranj/cli/test-023-mesh-diag.py @@ -136,9 +136,13 @@ childtable = r2.cli('meshdiag childtable', r1_rloc) verify(len([line for line in childtable if line.startswith('rloc16')]) == 2) +verify(len([line for line in childtable if ' supvn:0 ' in line]) == 1) +verify(len([line for line in childtable if ' supvn:' in line and 'supvn:0' not in line]) == 1) childtable = r1.cli('meshdiag childtable', r3_rloc) verify(len([line for line in childtable if line.startswith('rloc16')]) == 3) +verify(len([line for line in childtable if ' supvn:0 ' in line]) == 1) +verify(len([line for line in childtable if ' supvn:' in line and 'supvn:0' not in line]) == 2) childtable = r1.cli('meshdiag childtable', r2_rloc) verify(len([line for line in childtable if line.startswith('rloc16')]) == 0) From 3a525f3b577cdef8848c342c86bd88eebc7e90dd Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Thu, 29 Aug 2024 01:54:19 +0800 Subject: [PATCH 136/160] [radio] otPlatRadioGetNow fallback to otPlatTimeGet (#10646) This commit makes the default otPlatRadioGetNow implementation fallback to to otPlatTimeGet so that the default one is a correct implementation in single chip architecture. --- src/core/radio/radio_platform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp index 3556d2629..6ce3c48a2 100644 --- a/src/core/radio/radio_platform.cpp +++ b/src/core/radio/radio_platform.cpp @@ -253,7 +253,7 @@ OT_TOOL_WEAK uint64_t otPlatRadioGetNow(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - return UINT64_MAX; + return otPlatTimeGet(); } OT_TOOL_WEAK uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance) From 45c5fe475b281b80c426f2e9e80433dc5992a403 Mon Sep 17 00:00:00 2001 From: Suvesh Pratapa <66088488+suveshpratapa@users.noreply.github.com> Date: Wed, 28 Aug 2024 14:38:31 -0400 Subject: [PATCH 137/160] [posix] add radio URL parameter `uart-init-deassert` to deassert DTR and RTS on init when flow control is disabled (#10644) This lets us preserve legacy behavior where `uart-flow-control` was not required even with hardware flow control enabled. --- src/posix/platform/hdlc_interface.cpp | 3 ++- src/posix/platform/radio_url.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp index 235af9ac8..321bfbae2 100644 --- a/src/posix/platform/hdlc_interface.cpp +++ b/src/posix/platform/hdlc_interface.cpp @@ -600,8 +600,9 @@ int HdlcInterface::OpenFile(const Url::Url &aRadioUrl) { tios.c_cflag |= CRTSCTS; } - else + else if (aRadioUrl.HasParam("uart-init-deassert")) { + // When flow control is disabled, deassert DTR and RTS on init #ifndef __APPLE__ int flags; #endif diff --git a/src/posix/platform/radio_url.cpp b/src/posix/platform/radio_url.cpp index 57e1df0c2..c91baacba 100644 --- a/src/posix/platform/radio_url.cpp +++ b/src/posix/platform/radio_url.cpp @@ -84,6 +84,7 @@ const char *otSysGetRadioUrlHelpString(void) " uart-stop[=number-of-bits] Uart stop bit, default is 1.\n" \ " uart-baudrate[=baudrate] Uart baud rate, default is 115200.\n" \ " uart-flow-control Enable flow control, disabled by default.\n" \ + " uart-init-deassert Deassert lines on init when flow control is disabled.\n" \ " uart-reset Reset connection after hard resetting RCP(USB CDC ACM).\n" \ "\n" #else From f9349c14868b14d935164f4bbcaa08d474a15c6d Mon Sep 17 00:00:00 2001 From: Rongli Sun Date: Fri, 30 Aug 2024 05:13:39 +0800 Subject: [PATCH 138/160] [border-agent] not forward MGMT_GET/SET commands directly to leader (#10652) This commit doesn't forward the MGMT*GET/SET commands directly to leader any more, and enforces the use of UDP Proxy for Thread Management Commands. --- src/core/meshcop/border_agent.cpp | 22 ---------------------- src/core/meshcop/border_agent.hpp | 3 --- src/core/thread/tmf.cpp | 3 --- 3 files changed, 28 deletions(-) diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index a4c184eec..6c67c6a5b 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -471,34 +471,18 @@ void BorderAgent::HandleTmf(Coap::Message &aMessage, const HandleTmfDatasetGet(aMessage, aMessageInfo, kUriCommissionerGet); } -template <> -void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriCommissionerSet)); -} - template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { HandleTmfDatasetGet(aMessage, aMessageInfo, kUriActiveGet); mCounters.mMgmtActiveGets++; } -template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriActiveSet)); -} - template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { HandleTmfDatasetGet(aMessage, aMessageInfo, kUriPendingGet); mCounters.mMgmtPendingGets++; } -template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriPendingSet)); -} - template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { @@ -615,12 +599,6 @@ void BorderAgent::HandleTmfDatasetGet(Coap::Message &aMessage, const Ip6::Messag Error error = kErrorNone; Coap::Message *response = nullptr; - if (mState == kStateAccepted) - { - IgnoreError(ForwardToLeader(aMessage, aMessageInfo, aUri)); - ExitNow(); - } - // When processing `MGMT_GET` request directly on Border Agent, // the Security Policy flags (O-bit) should be ignore to allow // the commissioner candidate to get the full Operational Dataset. diff --git a/src/core/meshcop/border_agent.hpp b/src/core/meshcop/border_agent.hpp index 35ee6b79b..8e08c22bf 100644 --- a/src/core/meshcop/border_agent.hpp +++ b/src/core/meshcop/border_agent.hpp @@ -349,11 +349,8 @@ DeclareTmfHandler(BorderAgent, kUriCommissionerPetition); DeclareTmfHandler(BorderAgent, kUriCommissionerKeepAlive); DeclareTmfHandler(BorderAgent, kUriRelayTx); DeclareTmfHandler(BorderAgent, kUriCommissionerGet); -DeclareTmfHandler(BorderAgent, kUriCommissionerSet); DeclareTmfHandler(BorderAgent, kUriActiveGet); -DeclareTmfHandler(BorderAgent, kUriActiveSet); DeclareTmfHandler(BorderAgent, kUriPendingGet); -DeclareTmfHandler(BorderAgent, kUriPendingSet); DeclareTmfHandler(BorderAgent, kUriProxyTx); } // namespace MeshCoP diff --git a/src/core/thread/tmf.cpp b/src/core/thread/tmf.cpp index a0bb80f34..430f18694 100644 --- a/src/core/thread/tmf.cpp +++ b/src/core/thread/tmf.cpp @@ -311,11 +311,8 @@ bool SecureAgent::HandleResource(const char *aUriPath, Message &aMessage, const Case(kUriCommissionerKeepAlive, MeshCoP::BorderAgent); Case(kUriRelayTx, MeshCoP::BorderAgent); Case(kUriCommissionerGet, MeshCoP::BorderAgent); - Case(kUriCommissionerSet, MeshCoP::BorderAgent); Case(kUriActiveGet, MeshCoP::BorderAgent); - Case(kUriActiveSet, MeshCoP::BorderAgent); Case(kUriPendingGet, MeshCoP::BorderAgent); - Case(kUriPendingSet, MeshCoP::BorderAgent); Case(kUriProxyTx, MeshCoP::BorderAgent); #endif From d60aaab22e273b6fd74f6d5ff90754a349472c53 Mon Sep 17 00:00:00 2001 From: Yang Sun Date: Sun, 1 Sep 2024 00:51:12 +0800 Subject: [PATCH 139/160] [dtls] notify `kDisconnectedLocalClosed` when disconnect locally (#10653) --- src/core/meshcop/secure_transport.cpp | 24 ++++++++++++++---------- src/core/meshcop/secure_transport.hpp | 1 + 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/core/meshcop/secure_transport.cpp b/src/core/meshcop/secure_transport.cpp index 3cb0333f7..59f859c96 100644 --- a/src/core/meshcop/secure_transport.cpp +++ b/src/core/meshcop/secure_transport.cpp @@ -484,7 +484,7 @@ int SecureTransport::SetApplicationSecureKeys(void) void SecureTransport::Close(void) { - Disconnect(); + Disconnect(kDisconnectedLocalClosed); SetState(kStateClosed); mTimerSet = false; @@ -494,12 +494,15 @@ void SecureTransport::Close(void) mTimer.Stop(); } -void SecureTransport::Disconnect(void) +void SecureTransport::Disconnect(void) { Disconnect(kDisconnectedLocalClosed); } + +void SecureTransport::Disconnect(ConnectEvent aEvent) { VerifyOrExit(IsStateConnectingOrConnected()); mbedtls_ssl_close_notify(&mSsl); SetState(kStateCloseNotify); + mConnectEvent = aEvent; mTimer.Start(kGuardTimeNewConnectionMilli); mMessageInfo.Clear(); @@ -1057,9 +1060,10 @@ void SecureTransport::HandleTimer(void) void SecureTransport::Process(void) { - uint8_t buf[OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN]; - bool shouldDisconnect = false; - int rval; + uint8_t buf[OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN]; + bool shouldDisconnect = false; + int rval; + ConnectEvent event; while (IsStateConnectingOrConnected()) { @@ -1093,7 +1097,7 @@ void SecureTransport::Process(void) { case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: mbedtls_ssl_close_notify(&mSsl); - mConnectEvent = kDisconnectedPeerClosed; + event = kDisconnectedPeerClosed; ExitNow(shouldDisconnect = true); OT_UNREACHABLE_CODE(break); @@ -1102,7 +1106,7 @@ void SecureTransport::Process(void) case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE: mbedtls_ssl_close_notify(&mSsl); - mConnectEvent = kDisconnectedError; + event = kDisconnectedError; ExitNow(shouldDisconnect = true); OT_UNREACHABLE_CODE(break); @@ -1111,7 +1115,7 @@ void SecureTransport::Process(void) { mbedtls_ssl_send_alert_message(&mSsl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC); - mConnectEvent = kDisconnectedError; + event = kDisconnectedError; ExitNow(shouldDisconnect = true); } @@ -1122,7 +1126,7 @@ void SecureTransport::Process(void) { mbedtls_ssl_send_alert_message(&mSsl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); - mConnectEvent = kDisconnectedError; + event = kDisconnectedError; ExitNow(shouldDisconnect = true); } @@ -1142,7 +1146,7 @@ void SecureTransport::Process(void) if (shouldDisconnect) { - Disconnect(); + Disconnect(event); } } diff --git a/src/core/meshcop/secure_transport.hpp b/src/core/meshcop/secure_transport.hpp index a4f577775..1d0acd8e8 100644 --- a/src/core/meshcop/secure_transport.hpp +++ b/src/core/meshcop/secure_transport.hpp @@ -595,6 +595,7 @@ class SecureTransport : public InstanceLocator Error HandleSecureTransportSend(const uint8_t *aBuf, uint16_t aLength, Message::SubType aMessageSubType); void Process(void); + void Disconnect(ConnectEvent aEvent); #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) static const char *StateToString(State aState); From 98d1aed973c7a9f232bbae019d1c6b87c175d1ed Mon Sep 17 00:00:00 2001 From: Rongli Sun Date: Tue, 3 Sep 2024 05:02:51 +0800 Subject: [PATCH 140/160] [border-agent] apply pskc if changed (#10641) This commit applies new pskc into secure transport when it's changed via active/pending dataset, without impacting existing secure session if any or ephemeralkey mode if it's activated --- src/core/meshcop/border_agent.cpp | 38 ++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index 6c67c6a5b..3a61a0ea9 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -308,19 +308,41 @@ Error BorderAgent::SetId(const Id &aId) void BorderAgent::HandleNotifierEvents(Events aEvents) { - VerifyOrExit(aEvents.ContainsAny(kEventThreadRoleChanged | kEventCommissionerStateChanged)); - + if ((aEvents.ContainsAny(kEventThreadRoleChanged | kEventCommissionerStateChanged))) + { #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD - VerifyOrExit(Get().IsDisabled()); + VerifyOrExit(Get().IsDisabled()); #endif - if (Get().IsAttached()) - { - Start(); + if (Get().IsAttached()) + { + Start(); + } + else + { + Stop(); + } } - else + + if (aEvents.ContainsAny(kEventPskcChanged)) { - Stop(); + VerifyOrExit(mState != kStateStopped); + +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + // No-op if Ephemeralkey mode is activated, new pskc will be applied + // when Ephemeralkey mode is deactivated. + VerifyOrExit(!mUsingEphemeralKey); +#endif + + { + Pskc pskc; + Get().GetPskc(pskc); + + // If there is secure session already established, it won't be impacted, + // new pskc will be applied for next connection. + SuccessOrExit(Get().SetPsk(pskc.m8, Pskc::kSize)); + pskc.Clear(); + } } exit: From e19c775ce3740331069c78899908db0a3a3d29da Mon Sep 17 00:00:00 2001 From: Handa Wang <7058128+superwhd@users.noreply.github.com> Date: Tue, 3 Sep 2024 05:05:54 +0800 Subject: [PATCH 141/160] [doc] fix typos in comments (#10664) Done by `script/code-spell` with some manual fixes. --- .code-spell-ignore | 5 ++++- include/openthread/ble_secure.h | 2 +- include/openthread/instance.h | 2 +- include/openthread/platform/multipan.h | 2 +- src/core/coap/coap.hpp | 2 +- src/core/common/heap_string.hpp | 2 +- src/core/common/string.hpp | 2 +- src/core/radio/ble_secure.hpp | 2 +- src/core/radio/radio.hpp | 2 +- src/core/thread/link_metrics.hpp | 2 +- src/core/thread/network_data_types.hpp | 4 ++-- src/lib/spinel/radio_spinel.hpp | 2 +- src/lib/spinel/spinel.h | 2 +- src/posix/platform/netif.cpp | 2 +- tests/scripts/thread-cert/command.py | 2 +- tests/scripts/thread-cert/test_crypto.py | 2 +- 16 files changed, 20 insertions(+), 17 deletions(-) diff --git a/.code-spell-ignore b/.code-spell-ignore index c3c1a3a08..ae1c08b1f 100644 --- a/.code-spell-ignore +++ b/.code-spell-ignore @@ -1,10 +1,12 @@ +aafter aanother acount addrss afile aline -anumber +alocator ans +anumber aother aparent apending @@ -14,6 +16,7 @@ ect intialize nd ot +re-use shashes ue unknwn diff --git a/include/openthread/ble_secure.h b/include/openthread/ble_secure.h index ca0e8706f..8561ef834 100644 --- a/include/openthread/ble_secure.h +++ b/include/openthread/ble_secure.h @@ -334,7 +334,7 @@ otError otBleSecureConnect(otInstance *aInstance); void otBleSecureDisconnect(otInstance *aInstance); /** - * Indicates whether or not the TLS session is active (connected or conneting). + * Indicates whether or not the TLS session is active (connected or connecting). * * @param[in] aInstance A pointer to an OpenThread instance. * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index d26bdf851..42268ac64 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (438) +#define OPENTHREAD_API_VERSION (439) /** * @addtogroup api-instance diff --git a/include/openthread/platform/multipan.h b/include/openthread/platform/multipan.h index 69cd9839c..9af61e6d5 100644 --- a/include/openthread/platform/multipan.h +++ b/include/openthread/platform/multipan.h @@ -36,7 +36,7 @@ * Currently we support two types of multipan RCP: * - Full multipan: RCP operates in parallel on both networks (for example using more than one transceiver) * - Switching RCP: RCP can communicate only with one network at a time and requires network switching mechanism. - * Switching can be automatic (for example time based, radio sleep based) or manually contolled by + * Switching can be automatic (for example time based, radio sleep based) or manually controlled by * the host. * * Full multipan RCP and Automatic Switching RCP do not require any special care from the host side. diff --git a/src/core/coap/coap.hpp b/src/core/coap/coap.hpp index 968c845ca..a552021c8 100644 --- a/src/core/coap/coap.hpp +++ b/src/core/coap/coap.hpp @@ -91,7 +91,7 @@ class TxParameters : public otCoapTxParameters public: /** - * Coverts a pointer to `otCoapTxParameters` to `Coap::TxParamters` + * Converts a pointer to `otCoapTxParameters` to `Coap::TxParamters` * * If the pointer is `nullptr`, the default parameters are used instead. * diff --git a/src/core/common/heap_string.hpp b/src/core/common/heap_string.hpp index d06c97bd5..adfa549c1 100644 --- a/src/core/common/heap_string.hpp +++ b/src/core/common/heap_string.hpp @@ -46,7 +46,7 @@ namespace Heap { /** * Represents a heap allocated string. * - * The buffer to store the string is allocated from heap and is manged by the `Heap::String` class itself, e.g., it may + * The buffer to store the string is allocated from heap and is managed by the `Heap::String` class itself, e.g., it may * be reused and/or freed and reallocated when the string is set. The `Heap::String` destructor will always free the * allocated buffer. * diff --git a/src/core/common/string.hpp b/src/core/common/string.hpp index 360be44b5..293923214 100644 --- a/src/core/common/string.hpp +++ b/src/core/common/string.hpp @@ -352,7 +352,7 @@ Error ParseDigit(char aDigitChar, uint8_t &aValue); Error ParseHexDigit(char aHexChar, uint8_t &aValue); /** - * Coverts a boolean to "yes" or "no" string. + * Converts a boolean to "yes" or "no" string. * * @param[in] aBool A boolean value to convert. * diff --git a/src/core/radio/ble_secure.hpp b/src/core/radio/ble_secure.hpp index 4ddf351bb..d2ef628d7 100644 --- a/src/core/radio/ble_secure.hpp +++ b/src/core/radio/ble_secure.hpp @@ -142,7 +142,7 @@ class BleSecure : public InstanceLocator, private NonCopyable void Disconnect(void); /** - * Indicates whether or not the TLS session is active (connected or conneting). + * Indicates whether or not the TLS session is active (connected or connecting). * * @retval TRUE If TLS session is active. * @retval FALSE If TLS session is not active. diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index 72d9f4157..8eb43fd1a 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -77,7 +77,7 @@ static constexpr uint64_t kMaxCslTimeout = OPENTHREAD_CONFIG_MAC_CSL_MAX_TIMEOUT * Implements the radio statistics logic. * * The radio statistics are the time when the radio in TX/RX/radio state. - * Since this class collects these statistics from pure software level and no platform API is involved, a simplied + * Since this class collects these statistics from pure software level and no platform API is involved, a simplified * model is used to calculate the time of different radio states. The data may not be very accurate, but it's * sufficient to provide a general understanding of the proportion of time a device is in different radio states. * diff --git a/src/core/thread/link_metrics.hpp b/src/core/thread/link_metrics.hpp index ebe923f8b..a6db50e5c 100644 --- a/src/core/thread/link_metrics.hpp +++ b/src/core/thread/link_metrics.hpp @@ -270,7 +270,7 @@ class Initiator : public InstanceLocator, private NonCopyable /** * Implements the Thread Link Metrics Subject. * - * The Subject reponds queries with reports, handles Link Metrics Management Requests and Link Probe Messages. + * The Subject responds queries with reports, handles Link Metrics Management Requests and Link Probe Messages. * */ class Subject : public InstanceLocator, private NonCopyable diff --git a/src/core/thread/network_data_types.hpp b/src/core/thread/network_data_types.hpp index af63ff5d7..08190b319 100644 --- a/src/core/thread/network_data_types.hpp +++ b/src/core/thread/network_data_types.hpp @@ -153,7 +153,7 @@ typedef Array Rlocs; inline bool IsRoutePreferenceValid(int8_t aPref) { return Preference::IsValid(aPref); } /** - * Coverts a route preference to a 2-bit unsigned value. + * Converts a route preference to a 2-bit unsigned value. * * The @p aPref MUST be valid (value from `RoutePreference` enumeration), or the behavior is undefined. * @@ -165,7 +165,7 @@ inline bool IsRoutePreferenceValid(int8_t aPref) { return Preference::IsValid(aP inline uint8_t RoutePreferenceToValue(int8_t aPref) { return Preference::To2BitUint(aPref); } /** - * Coverts a 2-bit unsigned value to a route preference. + * Converts a 2-bit unsigned value to a route preference. * * @param[in] aValue The 2-bit unsigned value to convert from. Note that only the first two bits of @p aValue * are used and the rest of bits are ignored. diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 1e1590880..241d7aed6 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -172,7 +172,7 @@ class RadioSpinel : private Logger * directly do a hardware reset. * @param[in] aSpinelDriver A pointer to the spinel driver instance that this object depends on. * @param[in] aRequiredRadioCaps The required radio capabilities. RadioSpinel will check if RCP has - * the required capabilities during initiailization. + * the required capabilities during initialization. * @param[in] aEnableRcpTimeSync TRUE to enable RCP time sync, FALSE to not enable. * */ diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index e1cb888ba..5015720da 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -522,7 +522,7 @@ enum /// Generic failure to associate with other peers. /** - * This status error should not be used by implementors if + * This status error should not be used by implementers if * enough information is available to determine that one of the * later join failure status codes would be more accurate. * diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp index 37eb4cb05..edad3d708 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -226,7 +226,7 @@ ot::Posix::Resolver gResolver; #else // on some platforms (Linux, but others might be made to work), we do not get information about multicast // group joining via AF_NETLINK or AF_ROUTE sockets. on those platform, we must listen for IPv6 ICMP -// MLDv2 messages to know when mulicast memberships change +// MLDv2 messages to know when multicast memberships change // https://stackoverflow.com/questions/37346289/using-netlink-is-it-possible-to-listen-whenever-multicast-group-membership-is-ch #define OPENTHREAD_POSIX_USE_MLD_MONITOR 1 #endif // defined(RTM_NEWMADDR) || defined(__NetBSD__) diff --git a/tests/scripts/thread-cert/command.py b/tests/scripts/thread-cert/command.py index 419840f02..ed966eae9 100644 --- a/tests/scripts/thread-cert/command.py +++ b/tests/scripts/thread-cert/command.py @@ -559,7 +559,7 @@ def check_compressed_address_registration_tlv(command_msg, cid, iid, cid_present command_msg (MleMessage) : The Mle message to check. cid (int): The context id of the domain prefix. iid (string): The Interface Identifier. - cid_present_once(boolean): True if cid entry should apprear only once in AR Tlv. + cid_present_once(boolean): True if cid entry should appear only once in AR Tlv. False otherwise. ''' found = False diff --git a/tests/scripts/thread-cert/test_crypto.py b/tests/scripts/thread-cert/test_crypto.py index eafc83910..61d04261a 100755 --- a/tests/scripts/thread-cert/test_crypto.py +++ b/tests/scripts/thread-cert/test_crypto.py @@ -195,7 +195,7 @@ def test_should_encrypt_and_decrypt_random_data_content_when_proper_methods_are_ class TestCryptoMaterialCreator(unittest.TestCase): - """ Key generaion was described in Thread specification. + """ Key generation was described in Thread specification. Read more: Thread 1.1.0 Specification Candidate Final - 7.1.4 Key Generation From f908b5e3151a7bab9d6a69cb72dcb947df693140 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 07:57:42 -0700 Subject: [PATCH 142/160] github-actions: bump actions/setup-python from 5.1.1 to 5.2.0 (#10660) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.1.1 to 5.2.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/39cd14951b08e74b54015e9e001cdefcf80e669f...f677139bbe7f9c59b41e40162b753c062f5d49a3) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/otns.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml index a624a972b..e8b9d1fd3 100644 --- a/.github/workflows/otns.yml +++ b/.github/workflows/otns.yml @@ -67,7 +67,7 @@ jobs: with: go-version: "1.20" - name: Set up Python - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: "3.9" - name: Bootstrap @@ -107,7 +107,7 @@ jobs: with: go-version: "1.20" - name: Set up Python - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: "3.9" - name: Bootstrap @@ -169,7 +169,7 @@ jobs: with: go-version: "1.20" - name: Set up Python - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: "3.9" - name: Bootstrap From 5b33afd1881488743fecb342467e5a2758453695 Mon Sep 17 00:00:00 2001 From: Handa Wang <7058128+superwhd@users.noreply.github.com> Date: Wed, 4 Sep 2024 01:34:08 +0800 Subject: [PATCH 143/160] [docs] add the missing section for `dns upstream` command (#10665) --- src/cli/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/cli/README.md b/src/cli/README.md index 23f9f1664..0b7c15f98 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -1473,6 +1473,27 @@ Service instance label is provided first, followed by the service name (note tha The parameters after `service-name` are optional. Any unspecified (or zero) value for these optional parameters is replaced by the value from the current default config (`dns config`). +### dns server upstream \[enable|disable\] + +Enable/Disable the upstream DNS feature. If no argument is provided, it prints whether the upstream DNS feature is enabled. + +`OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE` is required. + +Enable the upstream DNS feature. + +``` +> dns server upstream enable +Done +``` + +Get whether the upstream DNS feature is enabled. + +``` +> dns server upstream +Enabled +Done +``` + ### dns compression \[enable|disable\] Enable/Disable the "DNS name compression" mode. From aed9cd1a307c05fe2f5b6b4b5677baaabce3d38e Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Wed, 4 Sep 2024 01:36:45 +0800 Subject: [PATCH 144/160] [csl] add `Mac::Frame` methods to process the CSL IE (#10651) This commit provides unified methods for checking whether the CSL IE is present and getting the CSL IE. --- src/core/mac/data_poll_sender.cpp | 10 +- src/core/mac/mac.cpp | 12 +-- src/core/mac/mac_frame.cpp | 25 ++++- src/core/mac/mac_frame.hpp | 149 +++++++++++++++----------- src/core/mac/sub_mac_csl_receiver.cpp | 2 +- src/core/thread/mesh_forwarder.cpp | 2 +- 6 files changed, 119 insertions(+), 81 deletions(-) diff --git a/src/core/mac/data_poll_sender.cpp b/src/core/mac/data_poll_sender.cpp index 80e18f7a5..ef3d4b662 100644 --- a/src/core/mac/data_poll_sender.cpp +++ b/src/core/mac/data_poll_sender.cpp @@ -255,16 +255,14 @@ void DataPollSender::HandlePollSent(Mac::TxFrame &aFrame, Error aError) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter, - (aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts - : kMaxPollRetxAttempts); + aFrame.HasCslIe() ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts); #else LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter, kMaxPollRetxAttempts); #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - if (mPollTxFailureCounter < - ((aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts)) + if (mPollTxFailureCounter < (aFrame.HasCslIe() ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts)) #else if (mPollTxFailureCounter < kMaxPollRetxAttempts) #endif @@ -344,7 +342,7 @@ void DataPollSender::ProcessTxDone(const Mac::TxFrame &aFrame, const Mac::RxFram VerifyOrExit(aFrame.GetSecurityEnabled()); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - if (aFrame.mInfo.mTxInfo.mIsARetx && (aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr)) + if (aFrame.mInfo.mTxInfo.mIsARetx && aFrame.HasCslIe()) { // For retransmission frame, use a data poll to resync its parent with correct CSL phase sendDataPoll = true; @@ -583,7 +581,7 @@ Mac::TxFrame *DataPollSender::PrepareDataRequest(Mac::TxFrames &aTxFrames) Mac::Frame::kSecurityEncMic32, Mac::Frame::kKeyIdMode1, nullptr); #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - if (frame->GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) + if (frame->HasCslIe()) { // Disable frame retransmission when the data poll has CSL IE included aTxFrames.SetMaxFrameRetries(0); diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index d1fbd0a8e..98b06ed1d 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -1316,7 +1316,7 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError) ProcessCsl(*aAckFrame, dstAddr); #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - if (!mRxOnWhenIdle && aFrame.GetHeaderIe(CslIe::kHeaderIeId) != nullptr) + if (!mRxOnWhenIdle && aFrame.HasCslIe()) { Get().ResetKeepAliveTimer(); } @@ -2356,19 +2356,17 @@ bool Mac::IsCslSupported(void) const #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr) { - const uint8_t *cur; - Child *child; - const CslIe *csl; + Child *child; + const CslIe *csl; VerifyOrExit(aFrame.IsVersion2015() && aFrame.GetSecurityEnabled()); - cur = aFrame.GetHeaderIe(CslIe::kHeaderIeId); - VerifyOrExit(cur != nullptr); + csl = aFrame.GetCslIe(); + VerifyOrExit(csl != nullptr); child = Get().FindChild(aSrcAddr, Child::kInStateAnyExceptInvalid); VerifyOrExit(child != nullptr); - csl = reinterpret_cast(cur + sizeof(HeaderIe)); VerifyOrExit(csl->GetPeriod() >= kMinCslIePeriod); child->SetCslPeriod(csl->GetPeriod()); diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 035319b27..0c6f59e9f 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -1232,19 +1232,34 @@ const uint8_t *Frame::GetThreadIe(uint8_t aSubType) const #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE void Frame::SetCslIe(uint16_t aCslPeriod, uint16_t aCslPhase) { - uint8_t *cur = GetHeaderIe(CslIe::kHeaderIeId); - CslIe *csl; + CslIe *csl = GetCslIe(); - VerifyOrExit(cur != nullptr); - - csl = reinterpret_cast(cur + sizeof(HeaderIe)); + VerifyOrExit(csl != nullptr); csl->SetPeriod(aCslPeriod); csl->SetPhase(aCslPhase); + exit: return; } + +bool Frame::HasCslIe(void) const { return GetHeaderIe(CslIe::kHeaderIeId) != nullptr; } #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE) +const CslIe *Frame::GetCslIe(void) const +{ + const uint8_t *cur; + const CslIe *csl = nullptr; + + cur = GetHeaderIe(CslIe::kHeaderIeId); + VerifyOrExit(cur != nullptr); + csl = reinterpret_cast(cur + sizeof(HeaderIe)); + +exit: + return csl; +} +#endif + #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE void Frame::SetEnhAckProbingIe(const uint8_t *aValue, uint8_t aLen) { diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index eef63aae4..4ce924103 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -134,6 +134,67 @@ class HeaderIe } OT_TOOL_PACKED_END; +/** + * Implements CSL IE data structure. + * + */ +OT_TOOL_PACKED_BEGIN +class CslIe +{ +public: + static constexpr uint8_t kHeaderIeId = 0x1a; + static constexpr uint8_t kIeContentSize = sizeof(uint16_t) * 2; + + /** + * Returns the CSL Period. + * + * @returns the CSL Period. + * + */ + uint16_t GetPeriod(void) const { return LittleEndian::HostSwap16(mPeriod); } + + /** + * Sets the CSL Period. + * + * @param[in] aPeriod The CSL Period. + * + */ + void SetPeriod(uint16_t aPeriod) { mPeriod = LittleEndian::HostSwap16(aPeriod); } + + /** + * Returns the CSL Phase. + * + * @returns the CSL Phase. + * + */ + uint16_t GetPhase(void) const { return LittleEndian::HostSwap16(mPhase); } + + /** + * Sets the CSL Phase. + * + * @param[in] aPhase The CSL Phase. + * + */ + void SetPhase(uint16_t aPhase) { mPhase = LittleEndian::HostSwap16(aPhase); } + +private: + uint16_t mPhase; + uint16_t mPeriod; +} OT_TOOL_PACKED_END; + +/** + * Implements Termination2 IE. + * + * Is empty for template specialization. + * + */ +class Termination2Ie +{ +public: + static constexpr uint8_t kHeaderIeId = 0x7f; + static constexpr uint8_t kIeContentSize = 0; +}; + #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || \ OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE /** @@ -1023,8 +1084,35 @@ class Frame : public otRadioFrame * */ void SetCslIe(uint16_t aCslPeriod, uint16_t aCslPhase); + + /** + * Indicates whether or not the frame contains CSL IE. + * + * @retval TRUE If the frame contains CSL IE. + * @retval FALSE If the frame doesn't contain CSL IE. + * + */ + bool HasCslIe(void) const; #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE) + /** + * Returns a pointer to a CSL IE. + * + * @returns A pointer to the CSL IE, `nullptr` if not found. + * + */ + const CslIe *GetCslIe(void) const; + + /** + * Returns a pointer to a CSL IE. + * + * @returns A pointer to the CSL IE, `nullptr` if not found. + * + */ + CslIe *GetCslIe(void) { return AsNonConst(AsConst(this)->GetCslIe()); } +#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE) + #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE /** * Finds Enhanced ACK Probing (Vendor Specific) IE and set its value. @@ -1766,67 +1854,6 @@ class BeaconPayload otExtendedPanId mExtendedPanId; } OT_TOOL_PACKED_END; -/** - * Implements CSL IE data structure. - * - */ -OT_TOOL_PACKED_BEGIN -class CslIe -{ -public: - static constexpr uint8_t kHeaderIeId = 0x1a; - static constexpr uint8_t kIeContentSize = sizeof(uint16_t) * 2; - - /** - * Returns the CSL Period. - * - * @returns the CSL Period. - * - */ - uint16_t GetPeriod(void) const { return LittleEndian::HostSwap16(mPeriod); } - - /** - * Sets the CSL Period. - * - * @param[in] aPeriod The CSL Period. - * - */ - void SetPeriod(uint16_t aPeriod) { mPeriod = LittleEndian::HostSwap16(aPeriod); } - - /** - * Returns the CSL Phase. - * - * @returns the CSL Phase. - * - */ - uint16_t GetPhase(void) const { return LittleEndian::HostSwap16(mPhase); } - - /** - * Sets the CSL Phase. - * - * @param[in] aPhase The CSL Phase. - * - */ - void SetPhase(uint16_t aPhase) { mPhase = LittleEndian::HostSwap16(aPhase); } - -private: - uint16_t mPhase; - uint16_t mPeriod; -} OT_TOOL_PACKED_END; - -/** - * Implements Termination2 IE. - * - * Is empty for template specialization. - * - */ -class Termination2Ie -{ -public: - static constexpr uint8_t kHeaderIeId = 0x7f; - static constexpr uint8_t kIeContentSize = 0; -}; - /** * @} * diff --git a/src/core/mac/sub_mac_csl_receiver.cpp b/src/core/mac/sub_mac_csl_receiver.cpp index 85611f95b..e890687d6 100644 --- a/src/core/mac/sub_mac_csl_receiver.cpp +++ b/src/core/mac/sub_mac_csl_receiver.cpp @@ -60,7 +60,7 @@ void SubMac::UpdateCslLastSyncTimestamp(TxFrame &aFrame, RxFrame *aAckFrame) { // Actual synchronization timestamp should be from the sent frame instead of the current time. // Assuming the error here since it is bounded and has very small effect on the final window duration. - if (aAckFrame != nullptr && aFrame.GetHeaderIe(CslIe::kHeaderIeId) != nullptr) + if (aAckFrame != nullptr && aFrame.HasCslIe()) { mCslLastSync = TimeMicro(GetLocalTime()); } diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 004bbe834..085b40188 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -1138,7 +1138,7 @@ Neighbor *MeshForwarder::UpdateNeighborOnSentFrame(Mac::TxFrame &aFrame, #endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - if ((aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) && aIsDataPoll) + if (aFrame.HasCslIe() && aIsDataPoll) { failLimit = kFailedCslDataPollTransmissions; } From 928074c538c9e3f6522c3b89d0d451a15568a7b6 Mon Sep 17 00:00:00 2001 From: Mason Tran Date: Tue, 3 Sep 2024 14:08:08 -0400 Subject: [PATCH 145/160] [tests] add a expect test for `coaps disconnect` (#10655) --- tests/scripts/expect/cli-coaps.exp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/scripts/expect/cli-coaps.exp b/tests/scripts/expect/cli-coaps.exp index 1b0c3db86..d9f7433bd 100755 --- a/tests/scripts/expect/cli-coaps.exp +++ b/tests/scripts/expect/cli-coaps.exp @@ -96,6 +96,9 @@ send "coaps start something_invalid\n" expect "Error 7: InvalidArgs" switch_node 2 +send "coaps disconnect\n" +expect_line "Done" +expect "coaps disconnected" send "coaps stop\n" expect_line "Done" From 2493658d6a087b787298e6bfc747230425e40b63 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Wed, 4 Sep 2024 04:05:58 +0800 Subject: [PATCH 146/160] [config] disable uptime logging when multi-instance enabled (#10659) This commit soften enabling log uptime in simulation and posix platform so that it won't be enabled when multiple instance is also enabled. --- .../platforms/simulation/openthread-core-simulation-config.h | 2 +- src/posix/platform/openthread-core-posix-config.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/platforms/simulation/openthread-core-simulation-config.h b/examples/platforms/simulation/openthread-core-simulation-config.h index 517b5b5b2..1b702788a 100644 --- a/examples/platforms/simulation/openthread-core-simulation-config.h +++ b/examples/platforms/simulation/openthread-core-simulation-config.h @@ -124,7 +124,7 @@ #endif #ifndef OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME -#define OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME 1 +#define OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME !OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE #endif #ifndef OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_SERVICES diff --git a/src/posix/platform/openthread-core-posix-config.h b/src/posix/platform/openthread-core-posix-config.h index a837ddc96..cfe1bc0ab 100644 --- a/src/posix/platform/openthread-core-posix-config.h +++ b/src/posix/platform/openthread-core-posix-config.h @@ -137,7 +137,7 @@ #endif #ifndef OPENTHREAD_CONFIG_UPTIME_ENABLE -#define OPENTHREAD_CONFIG_UPTIME_ENABLE 1 +#define OPENTHREAD_CONFIG_UPTIME_ENABLE !OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE #endif #ifndef OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME From 01cb5b08cf1dde2397de036b393d92c77c64cb80 Mon Sep 17 00:00:00 2001 From: Handa Wang <7058128+superwhd@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:47:07 +0800 Subject: [PATCH 147/160] [posix] decouple `resolver.cpp` from `netif.cpp` (#10662) This commit introduces `platformResolver*` APIs so that `system.cpp` can treat resolver as an independent module. Reasons for this refactor: - Simplify the integration on Android platform. - The functionality of resolver is not related to the functionality of netif. --- src/posix/platform/netif.cpp | 15 --------------- src/posix/platform/platform-posix.h | 22 ++++++++++++++++++++++ src/posix/platform/resolver.cpp | 8 +++++++- src/posix/platform/system.cpp | 9 +++++++++ 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp index edad3d708..edbe8c416 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -215,10 +215,6 @@ static otIp6Prefix sAddedExternalRoutes[kMaxExternalRoutesNum]; static constexpr uint32_t kNat64RoutePriority = 100; ///< Priority for route to NAT64 CIDR, 100 means a high priority. #endif -#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE -ot::Posix::Resolver gResolver; -#endif - #if defined(RTM_NEWMADDR) || defined(__NetBSD__) // on some BSDs (mac OS, FreeBSD), we get RTM_NEWMADDR/RTM_DELMADDR messages, so we don't need to monitor using MLD // on NetBSD, MLD monitoring simply doesn't work @@ -2285,9 +2281,6 @@ void platformNetifSetUp(void) #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE nat64Init(); #endif -#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE - gResolver.Init(); -#endif } void platformNetifTearDown(void) {} @@ -2345,10 +2338,6 @@ void platformNetifUpdateFdSet(otSysMainloopContext *aContext) FD_SET(sMLDMonitorFd, &aContext->mErrorFdSet); #endif -#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE - gResolver.UpdateFdSet(*aContext); -#endif - if (sTunFd > aContext->mMaxFd) { aContext->mMaxFd = sTunFd; @@ -2411,10 +2400,6 @@ void platformNetifProcess(const otSysMainloopContext *aContext) } #endif -#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE - gResolver.Process(*aContext); -#endif - exit: return; } diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h index 177106db7..d0c89987a 100644 --- a/src/posix/platform/platform-posix.h +++ b/src/posix/platform/platform-posix.h @@ -461,6 +461,28 @@ void platformSpinelManagerProcess(otInstance *aInstance, const otSysMainloopCont */ void platformSpinelManagerUpdateFdSet(otSysMainloopContext *aContext); +/** + * Initializes the resolver used by OpenThread. + * + */ +void platformResolverInit(void); + +/** + * Updates the file descriptor sets with file descriptors used by the resolver. + * + * @param[in] aContext A pointer to the mainloop context. + * + */ +void platformResolverUpdateFdSet(otSysMainloopContext *aContext); + +/** + * Performs the resolver processing. + * + * @param[in] aContext A pointer to the mainloop context. + * + */ +void platformResolverProcess(const otSysMainloopContext *aContext); + #ifdef __cplusplus } #endif diff --git a/src/posix/platform/resolver.cpp b/src/posix/platform/resolver.cpp index 642d77137..9aaa4ba3f 100644 --- a/src/posix/platform/resolver.cpp +++ b/src/posix/platform/resolver.cpp @@ -56,7 +56,7 @@ constexpr char kResolvConfFullPath[] = "/etc/resolv.conf"; constexpr char kNameserverItem[] = "nameserver"; } // namespace -extern ot::Posix::Resolver gResolver; +ot::Posix::Resolver gResolver; namespace ot { namespace Posix { @@ -293,6 +293,12 @@ void Resolver::Process(const otSysMainloopContext &aContext) } // namespace Posix } // namespace ot +void platformResolverProcess(const otSysMainloopContext *aContext) { gResolver.Process(*aContext); } + +void platformResolverUpdateFdSet(otSysMainloopContext *aContext) { gResolver.UpdateFdSet(*aContext); } + +void platformResolverInit(void) { gResolver.Init(); } + void otPlatDnsStartUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery) { OT_UNUSED_VARIABLE(aInstance); diff --git a/src/posix/platform/system.cpp b/src/posix/platform/system.cpp index 5f3640a56..176a15fbb 100644 --- a/src/posix/platform/system.cpp +++ b/src/posix/platform/system.cpp @@ -156,6 +156,9 @@ void platformInitRcpMode(otPlatformConfig *aPlatformConfig) #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE platformNetifInit(aPlatformConfig); #endif +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + platformResolverInit(); +#endif #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE @@ -408,6 +411,9 @@ void otSysMainloopUpdate(otInstance *aInstance, otSysMainloopContext *aMainloop) #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelUpdateFdSet(aMainloop); #endif +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + platformResolverUpdateFdSet(aMainloop); +#endif if (otTaskletsArePending(aInstance)) { @@ -476,6 +482,9 @@ void otSysMainloopProcess(otInstance *aInstance, const otSysMainloopContext *aMa #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE platformNetifProcess(aMainloop); #endif +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + platformResolverProcess(aMainloop); +#endif } bool IsSystemDryRun(void) { return gDryRun; } From c36c0ed7609d43cad0158426b13822e880627de3 Mon Sep 17 00:00:00 2001 From: Li Cao Date: Wed, 4 Sep 2024 22:48:59 +0800 Subject: [PATCH 148/160] [ncp] add NCP based implementation (empty) of platform InfraIf APIs (#10638) This implementation will be used on all platforms for NCP nodes (simulation, any vendor platforms). However the simulation platform already has a different implementation. To avoid conflicts, the commit adds a control flag in simulation config so that we can choose whether to enable the simulation implementation. --- examples/platforms/simulation/CMakeLists.txt | 7 +++ examples/platforms/simulation/infra_if.c | 4 +- .../platforms/simulation/platform-config.h | 10 +++ .../simulation/platform-simulation.h | 4 +- examples/platforms/simulation/system.c | 8 +-- script/test | 4 ++ src/ncp/CMakeLists.txt | 8 +++ src/ncp/ncp_config.h | 10 +++ src/ncp/platform/infra_if.cpp | 62 +++++++++++++++++++ 9 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 src/ncp/platform/infra_if.cpp diff --git a/examples/platforms/simulation/CMakeLists.txt b/examples/platforms/simulation/CMakeLists.txt index 4e4ffbae7..3030de47a 100644 --- a/examples/platforms/simulation/CMakeLists.txt +++ b/examples/platforms/simulation/CMakeLists.txt @@ -45,6 +45,13 @@ if(OT_SIMULATION_MAX_NETWORK_SIZE) target_compile_definitions(ot-simulation-config INTERFACE "OPENTHREAD_SIMULATION_MAX_NETWORK_SIZE=${OT_SIMULATION_MAX_NETWORK_SIZE}") endif() +option(OT_SIMULATION_INFRA_IF "enable simulation infra if" ON) +if (OT_SIMULATION_INFRA_IF) + target_compile_definitions(ot-simulation-config INTERFACE "OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF=1") +else() + target_compile_definitions(ot-simulation-config INTERFACE "OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF=0") +endif() + if(NOT OT_PLATFORM_CONFIG) set(OT_PLATFORM_CONFIG "openthread-core-simulation-config.h" PARENT_SCOPE) endif() diff --git a/examples/platforms/simulation/infra_if.c b/examples/platforms/simulation/infra_if.c index e8db5f84f..4b904d04d 100644 --- a/examples/platforms/simulation/infra_if.c +++ b/examples/platforms/simulation/infra_if.c @@ -36,7 +36,7 @@ #include "simul_utils.h" #include "utils/code_utils.h" -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && !OPENTHREAD_RADIO +#if OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #define DEBUG_LOG 0 @@ -335,4 +335,4 @@ OT_TOOL_WEAK void otPlatInfraIfRecvIcmp6Nd(otInstance *aInstance, exit(1); } -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && !OPENTHREAD_RADIO +#endif // OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/examples/platforms/simulation/platform-config.h b/examples/platforms/simulation/platform-config.h index 79caa4a4d..3e953d14c 100644 --- a/examples/platforms/simulation/platform-config.h +++ b/examples/platforms/simulation/platform-config.h @@ -116,3 +116,13 @@ #ifndef OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX #define OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX 0 #endif + +/** + * @def OPENTHREAD_SIMULATION_IMPLMENT_INFRA_IF + * + * Define as 1 for the simulation platform to provide implementation of `otPlatInfra` APIs. + * + */ +#ifndef OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF +#define OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF 1 +#endif diff --git a/examples/platforms/simulation/platform-simulation.h b/examples/platforms/simulation/platform-simulation.h index fcdd1c1eb..1a12e9e91 100644 --- a/examples/platforms/simulation/platform-simulation.h +++ b/examples/platforms/simulation/platform-simulation.h @@ -305,7 +305,7 @@ void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const #endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE /** * Initializes the platform infra-if module. @@ -339,7 +339,7 @@ void platformInfraIfUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aM */ void platformInfraIfProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet); -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#endif // OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX diff --git a/examples/platforms/simulation/system.c b/examples/platforms/simulation/system.c index 772681393..5c1f5d36a 100644 --- a/examples/platforms/simulation/system.c +++ b/examples/platforms/simulation/system.c @@ -200,7 +200,7 @@ void otSysInit(int aArgCount, char *aArgVector[]) #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelInit(speedUpFactor); #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE platformInfraIfInit(); #endif platformRandomInit(); @@ -214,7 +214,7 @@ void otSysDeinit(void) #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelDeinit(); #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE platformInfraIfDeinit(); #endif platformLoggingDeinit(); @@ -239,7 +239,7 @@ void otSysProcessDrivers(otInstance *aInstance) #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd); #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE platformInfraIfUpdateFdSet(&read_fds, &write_fds, &max_fd); #endif #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX @@ -276,7 +276,7 @@ void otSysProcessDrivers(otInstance *aInstance) #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelProcess(aInstance, &read_fds, &write_fds); #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE platformInfraIfProcess(aInstance, &read_fds, &write_fds); #endif #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX diff --git a/script/test b/script/test index ceab700ef..0149b6f62 100755 --- a/script/test +++ b/script/test @@ -148,6 +148,10 @@ build_simulation() options+=("${ot_extra_options[@]}") fi + if [[ ${OT_NODE_TYPE} == rcp* ]]; then + options+=("-DOT_SIMULATION_INFRA_IF=OFF") + fi + OT_CMAKE_BUILD_DIR="${OT_BUILDDIR}/openthread-simulation-${version}" "${OT_SRCDIR}"/script/cmake-build simulation "${options[@]}" if [[ ${VIRTUAL_TIME} == 1 ]] && [[ ${OT_NODE_TYPE} == rcp* ]]; then diff --git a/src/ncp/CMakeLists.txt b/src/ncp/CMakeLists.txt index 25d7e2de1..7bcab9dd4 100644 --- a/src/ncp/CMakeLists.txt +++ b/src/ncp/CMakeLists.txt @@ -39,6 +39,7 @@ set(COMMON_SOURCES ncp_base_radio.cpp ncp_spi.cpp ncp_hdlc.cpp + platform/infra_if.cpp ) set(OT_NCP_VENDOR_HOOK_SOURCE "" CACHE STRING "set vendor hook source file for NCP") @@ -47,6 +48,13 @@ if(OT_NCP_VENDOR_HOOK_SOURCE) list(APPEND COMMON_SOURCES ${OT_NCP_VENDOR_HOOK_SOURCE_DIR}${OT_NCP_VENDOR_HOOK_SOURCE}) endif() +option(OT_NCP_INFRA_IF "enable NCP Infra If support") +if (OT_NCP_INFRA_IF) + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE=1") +else() + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE=0") +endif() + set(COMMON_NCP_SOURCES ${COMMON_SOURCES} ncp_base_ftd.cpp diff --git a/src/ncp/ncp_config.h b/src/ncp/ncp_config.h index bc54fb52a..dea5d14a8 100644 --- a/src/ncp/ncp_config.h +++ b/src/ncp/ncp_config.h @@ -179,6 +179,16 @@ #define OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL 0 #endif +/** + * @def OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE + * + * Define to 1 to enable the NCP based implementation of platform InfraIf APIs. + * + */ +#ifndef OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE +#define OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE 0 +#endif + /** * @def OPENTHREAD_ENABLE_NCP_VENDOR_HOOK * diff --git a/src/ncp/platform/infra_if.cpp b/src/ncp/platform/infra_if.cpp new file mode 100644 index 000000000..8dd176c7d --- /dev/null +++ b/src/ncp/platform/infra_if.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024, 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. + */ + +#include + +#include "ncp/ncp_base.hpp" + +#if OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE +bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddress) +{ + OT_UNUSED_VARIABLE(aInfraIfIndex); + OT_UNUSED_VARIABLE(aAddress); + + return true; +} + +otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, + const otIp6Address *aDestAddress, + const uint8_t *aBuffer, + uint16_t aBufferLength) +{ + OT_UNUSED_VARIABLE(aInfraIfIndex); + OT_UNUSED_VARIABLE(aDestAddress); + OT_UNUSED_VARIABLE(aBuffer); + OT_UNUSED_VARIABLE(aBufferLength); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex) +{ + OT_UNUSED_VARIABLE(aInfraIfIndex); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +#endif // OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE From 12cf1207d231b7656e1a0509a77f0581f45eaab3 Mon Sep 17 00:00:00 2001 From: Przemyslaw Bida Date: Mon, 12 Aug 2024 09:50:03 +0200 Subject: [PATCH 149/160] [tcat] Fix handling certificate path in tcat client. (#10597) Commit fixes propagation of certificate path from `cert-path` option to `scan` command. --- tools/tcat_ble_client/bbtc.py | 2 +- tools/tcat_ble_client/cli/base_commands.py | 7 ++++--- tools/tcat_ble_client/cli/cli.py | 14 +++++++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/tools/tcat_ble_client/bbtc.py b/tools/tcat_ble_client/bbtc.py index 2824e5aca..a9be1f539 100755 --- a/tools/tcat_ble_client/bbtc.py +++ b/tools/tcat_ble_client/bbtc.py @@ -96,7 +96,7 @@ async def main(): quit_with_reason('TLS handshake failure') ds = ThreadDataset() - cli = CLI(ds, ble_sstream) + cli = CLI(ds, args, ble_sstream) loop = asyncio.get_running_loop() print('Enter \'help\' to see available commands' ' or \'exit\' to exit the application.') while True: diff --git a/tools/tcat_ble_client/cli/base_commands.py b/tools/tcat_ble_client/cli/base_commands.py index 411bc821e..b086ee0d3 100644 --- a/tools/tcat_ble_client/cli/base_commands.py +++ b/tools/tcat_ble_client/cli/base_commands.py @@ -251,10 +251,11 @@ async def execute_default(self, args, context): print(f'Connecting to {device}') ble_stream = await BleStream.create(device.address, BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, BBTC_RX_CHAR_UUID) ble_sstream = BleStreamSecure(ble_stream) + cert_path = context['cmd_args'].cert_path if context['cmd_args'] else 'auth' ble_sstream.load_cert( - certfile=path.join('auth', 'commissioner_cert.pem'), - keyfile=path.join('auth', 'commissioner_key.pem'), - cafile=path.join('auth', 'ca_cert.pem'), + certfile=path.join(cert_path, 'commissioner_cert.pem'), + keyfile=path.join(cert_path, 'commissioner_key.pem'), + cafile=path.join(cert_path, 'ca_cert.pem'), ) print('Setting up secure channel...') diff --git a/tools/tcat_ble_client/cli/cli.py b/tools/tcat_ble_client/cli/cli.py index 01e33bac3..9835c950a 100644 --- a/tools/tcat_ble_client/cli/cli.py +++ b/tools/tcat_ble_client/cli/cli.py @@ -25,9 +25,9 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ - import readline import shlex +from argparse import ArgumentParser from ble.ble_stream_secure import BleStreamSecure from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, GetDeviceIdCommand, GetExtPanIDCommand, GetNetworkNameCommand, GetProvisioningUrlCommand, PingCommand, @@ -39,7 +39,10 @@ class CLI: - def __init__(self, dataset: ThreadDataset, ble_sstream: Optional[BleStreamSecure] = None): + def __init__(self, + dataset: ThreadDataset, + cmd_args: Optional[ArgumentParser] = None, + ble_sstream: Optional[BleStreamSecure] = None): self._commands = { 'help': HelpCommand(), 'hello': HelloCommand(), @@ -54,7 +57,12 @@ def __init__(self, dataset: ThreadDataset, ble_sstream: Optional[BleStreamSecure 'thread': ThreadStateCommand(), 'scan': ScanCommand(), } - self._context = {'ble_sstream': ble_sstream, 'dataset': dataset, 'commands': self._commands} + self._context = { + 'ble_sstream': ble_sstream, + 'dataset': dataset, + 'commands': self._commands, + 'cmd_args': cmd_args + } readline.set_completer(self.completer) readline.parse_and_bind('tab: complete') From db6393251a25b9952a4907137092acf9c7f254aa Mon Sep 17 00:00:00 2001 From: Przemyslaw Bida Date: Mon, 12 Aug 2024 09:55:15 +0200 Subject: [PATCH 150/160] [tcat] Add timeout while connecting over BLE. (#10597) Adding timeout while handling ble connection establishement in TCAT. --- tools/tcat_ble_client/ble/ble_stream.py | 4 ++++ tools/tcat_ble_client/ble/ble_stream_secure.py | 10 ++++++++-- tools/tcat_ble_client/cli/base_commands.py | 10 ++++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/tools/tcat_ble_client/ble/ble_stream.py b/tools/tcat_ble_client/ble/ble_stream.py index 3e22da8f4..45abb2f54 100644 --- a/tools/tcat_ble_client/ble/ble_stream.py +++ b/tools/tcat_ble_client/ble/ble_stream.py @@ -91,3 +91,7 @@ async def recv(self, bufsize, recv_timeout=0.2): self.__receive_buffer = self.__receive_buffer[bufsize:] logger.debug(f'retrieved {message}') return message + + async def disconnect(self): + if self.client.is_connected: + await self.client.disconnect() diff --git a/tools/tcat_ble_client/ble/ble_stream_secure.py b/tools/tcat_ble_client/ble/ble_stream_secure.py index ab8858c29..de2b42d8a 100644 --- a/tools/tcat_ble_client/ble/ble_stream_secure.py +++ b/tools/tcat_ble_client/ble/ble_stream_secure.py @@ -34,6 +34,7 @@ from tlv.tlv import TLV from tlv.tcat_tlv import TcatTLVType +from time import time import utils logger = logging.getLogger(__name__) @@ -60,7 +61,7 @@ def load_cert(self, certfile='', keyfile='', cafile=''): if cafile: self.ssl_context.load_verify_locations(cafile=cafile) - async def do_handshake(self): + async def do_handshake(self, timeout=30.0): is_debug = logger.getEffectiveLevel() <= logging.DEBUG self.ssl_object = self.ssl_context.wrap_bio( incoming=self.incoming, @@ -68,7 +69,8 @@ async def do_handshake(self): server_side=False, server_hostname=None, ) - while True: + start = time() + while (time() - start) < timeout: try: if not is_debug: print('.', end='') @@ -97,6 +99,10 @@ async def do_handshake(self): if output: self.incoming.write(output) await asyncio.sleep(0.02) + else: + print('TLS Connection timed out.') + return False + return True async def send(self, bytes): self.ssl_object.write(bytes) diff --git a/tools/tcat_ble_client/cli/base_commands.py b/tools/tcat_ble_client/cli/base_commands.py index b086ee0d3..5bef2a021 100644 --- a/tools/tcat_ble_client/cli/base_commands.py +++ b/tools/tcat_ble_client/cli/base_commands.py @@ -257,8 +257,10 @@ async def execute_default(self, args, context): keyfile=path.join(cert_path, 'commissioner_key.pem'), cafile=path.join(cert_path, 'ca_cert.pem'), ) - print('Setting up secure channel...') - await ble_sstream.do_handshake() - print('Done') - context['ble_sstream'] = ble_sstream + if await ble_sstream.do_handshake(): + print('Done') + context['ble_sstream'] = ble_sstream + else: + print('Secure channel not established.') + await ble_stream.disconnect() From e63e9ce86b4f22f95155e1e2e9da1d300389e7c6 Mon Sep 17 00:00:00 2001 From: Handa Wang <7058128+superwhd@users.noreply.github.com> Date: Fri, 6 Sep 2024 06:12:56 +0800 Subject: [PATCH 151/160] [posix] support specifying DNS server for resolver (#10663) This commit adds 2 system APIs for resolver for integration on Android platform. - `otSysUpstreamDnsServerSetResolvConfEnabled` is for enabling/disabling retrieving the DNS servers from `resolve.conf`. - `otSysUpstreamDnsSetServerList` for specifying the DNS servers on the infra link. --- .../include/openthread/openthread-system.h | 19 ++++++++++++ src/posix/platform/resolver.cpp | 30 +++++++++++++++++++ src/posix/platform/resolver.hpp | 19 ++++++++++++ 3 files changed, 68 insertions(+) diff --git a/src/posix/platform/include/openthread/openthread-system.h b/src/posix/platform/include/openthread/openthread-system.h index 3c72ad0b5..8a36fd3d7 100644 --- a/src/posix/platform/include/openthread/openthread-system.h +++ b/src/posix/platform/include/openthread/openthread-system.h @@ -43,6 +43,7 @@ #include #include +#include #include #include "lib/spinel/coprocessor_type.h" @@ -311,6 +312,24 @@ bool otSysInfraIfIsRunning(void); */ void otSysCliInitUsingDaemon(otInstance *aInstance); +/** + * Sets whether to retrieve upstream DNS servers from "resolv.conf". + * + * @param[in] aEnabled TRUE if enable retrieving upstream DNS servers from "resolv.conf", FALSE otherwise. + * + */ +void otSysUpstreamDnsServerSetResolvConfEnabled(bool aEnabled); + +/** + * Sets the upstream DNS server list. + * + * @param[in] aUpstreamDnsServers A pointer to the list of upstream DNS server addresses. Each address could be an IPv6 + * address or an IPv4-mapped IPv6 address. + * @param[in] aNumServers The number of upstream DNS servers. + * + */ +void otSysUpstreamDnsSetServerList(const otIp6Address *aUpstreamDnsServers, int aNumServers); + #ifdef __cplusplus } // end of extern "C" #endif diff --git a/src/posix/platform/resolver.cpp b/src/posix/platform/resolver.cpp index 9aaa4ba3f..9aa53196a 100644 --- a/src/posix/platform/resolver.cpp +++ b/src/posix/platform/resolver.cpp @@ -32,6 +32,8 @@ #include #include +#include +#include #include #include #include @@ -85,6 +87,8 @@ void Resolver::LoadDnsServerListFromConf(void) std::string line; std::ifstream fp; + VerifyOrExit(mIsResolvConfEnabled); + mUpstreamDnsServerCount = 0; fp.open(kResolvConfFullPath); @@ -110,6 +114,8 @@ void Resolver::LoadDnsServerListFromConf(void) } mUpstreamDnsServerListFreshness = otPlatTimeGet(); +exit: + return; } void Resolver::Query(otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery) @@ -290,6 +296,23 @@ void Resolver::Process(const otSysMainloopContext &aContext) } } +void Resolver::SetUpstreamDnsServers(const otIp6Address *aUpstreamDnsServers, int aNumServers) +{ + mUpstreamDnsServerCount = 0; + + for (int i = 0; i < aNumServers && i < kMaxUpstreamServerCount; ++i) + { + otIp4Address ip4Address; + + // TODO: support DNS servers with IPv6 addresses + if (otIp4FromIp4MappedIp6Address(&aUpstreamDnsServers[i], &ip4Address) == OT_ERROR_NONE) + { + mUpstreamDnsServerList[mUpstreamDnsServerCount] = ip4Address.mFields.m32; + mUpstreamDnsServerCount++; + } + } +} + } // namespace Posix } // namespace ot @@ -313,4 +336,11 @@ void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery gResolver.Cancel(aTxn); } +void otSysUpstreamDnsServerSetResolvConfEnabled(bool aEnabled) { gResolver.SetResolvConfEnabled(aEnabled); } + +void otSysUpstreamDnsSetServerList(const otIp6Address *aUpstreamDnsServers, int aNumServers) +{ + gResolver.SetUpstreamDnsServers(aUpstreamDnsServers, aNumServers); +} + #endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE diff --git a/src/posix/platform/resolver.hpp b/src/posix/platform/resolver.hpp index fae0cf9be..c7b145ff2 100644 --- a/src/posix/platform/resolver.hpp +++ b/src/posix/platform/resolver.hpp @@ -90,6 +90,24 @@ class Resolver : public Logger */ void Process(const otSysMainloopContext &aContext); + /** + * Sets whether to retrieve upstream DNS servers from "resolv.conf". + * + * @param[in] aEnabled TRUE if enable retrieving upstream DNS servers from "resolv.conf", FALSE otherwise. + * + */ + void SetResolvConfEnabled(bool aEnabled) { mIsResolvConfEnabled = aEnabled; } + + /** + * Sets the upstream DNS servers. + * + * @param[in] aUpstreamDnsServers A pointer to the list of upstream DNS server addresses. Each address could be an + * IPv6 address or an IPv4-mapped IPv6 address. + * @param[in] aNumServers The number of upstream DNS servers. + * + */ + void SetUpstreamDnsServers(const otIp6Address *aUpstreamDnsServers, int aNumServers); + private: static constexpr uint64_t kDnsServerListNullCacheTimeoutMs = 1 * 60 * 1000; // 1 minute static constexpr uint64_t kDnsServerListCacheTimeoutMs = 10 * 60 * 1000; // 10 minutes @@ -110,6 +128,7 @@ class Resolver : public Logger void TryRefreshDnsServerList(void); void LoadDnsServerListFromConf(void); + bool mIsResolvConfEnabled = true; int mUpstreamDnsServerCount = 0; in_addr_t mUpstreamDnsServerList[kMaxUpstreamServerCount]; uint64_t mUpstreamDnsServerListFreshness = 0; From 4459c54069bb8573579aa4e84c3c6cb6ea82b1cf Mon Sep 17 00:00:00 2001 From: Esko Dijk Date: Fri, 6 Sep 2024 04:59:39 +0200 Subject: [PATCH 152/160] [tcat][ble] fixes to connection state mgmt and Disconnect cmd (#10619) - Fixes to connection state management and handling of Disconnect command TLV - specifically, this now ensures that TCAT remains on (started) after a commissioner disconnects. Earlier, there was the problem that the 2nd commissioner couldn't connect anymore. - specifically, in ble_secure.cpp the check for if (mTcatAgent.IsEnabled()) is removed, since the err = mTcatAgent.Connected(mTls) will already check this and raise an error if not enabled. If not enabled, the Device is in a wrong state to handle TCAT Commissioner commands so now it closes the connection right away. That's better than to leave the Commissioner in limbo on the TLS connection. The Commissioner can now retry again and all will be well again. - timeout of at most 10 seconds on UDP write operation in simulation mode (if longer, the TCAT device isn't reachable and the Commissioner now shows the error to the user.) Earlier, it got stuck forever. - Corrects some copy/paste errors in API definitions in comments; adds comments where needed to explain. - adds whitespace at some places to align format with rest of code - improved some of the --debug output for the UDP simulation mode of the TCAT Commissioner. --- src/core/meshcop/tcat_agent.cpp | 6 ++++- src/core/meshcop/tcat_agent.hpp | 26 +++++++++---------- src/core/radio/ble_secure.cpp | 20 +++++++------- src/core/radio/ble_secure.hpp | 8 +++--- tools/tcat_ble_client/bbtc.py | 7 ++--- .../tcat_ble_client/ble/ble_stream_secure.py | 1 + tools/tcat_ble_client/ble/udp_stream.py | 18 ++++++++----- tools/tcat_ble_client/cli/base_commands.py | 4 +-- 8 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index 8f2c7c2e7..847a005c6 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -393,7 +393,8 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg switch (tlv.GetType()) { case kTlvDisconnect: - error = kErrorAbort; + error = kErrorAbort; + response = true; // true - to avoid response-with-status being sent. break; case kTlvSetActiveOperationalDataset: @@ -415,9 +416,11 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg response = true; error = kErrorNone; break; + case kTlvDecommission: error = HandleDecomission(); break; + case kTlvPing: error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length, response); break; @@ -433,6 +436,7 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg case kTlvGetProvisioningURL: error = HandleGetProvisioningUrl(aOutgoingMessage, response); break; + default: error = kErrorInvalidCommand; } diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp index a76008513..15578ffbc 100644 --- a/src/core/meshcop/tcat_agent.hpp +++ b/src/core/meshcop/tcat_agent.hpp @@ -272,7 +272,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable }; /** - * Initializes the Joiner object. + * Initializes the TCAT agent object. * * @param[in] aInstance A reference to the OpenThread instance. * @@ -280,20 +280,20 @@ class TcatAgent : public InstanceLocator, private NonCopyable explicit TcatAgent(Instance &aInstance); /** - * Enables the TCAT protocol. + * Enables the TCAT agent. * * @param[in] aAppDataReceiveCallback A pointer to a function that is called when the user data is received. * @param[in] aHandler A pointer to a function that is called when the join operation completes. * @param[in] aContext A context pointer. * - * @retval kErrorNone Successfully started the TCAT agent. - * @retval kErrorInvalidArgs The aVendorInfo is invalid. + * @retval kErrorNone Successfully started the TCAT agent. + * @retval kErrorFailed Failed to start due to missing vendor info. * */ Error Start(AppDataReceiveCallback aAppDataReceiveCallback, JoinCallback aHandler, void *aContext); /** - * Stops the TCAT protocol. + * Stops the TCAT agent. * */ void Stop(void); @@ -318,19 +318,19 @@ class TcatAgent : public InstanceLocator, private NonCopyable /** * Indicates whether or not the TCAT agent is connected. * - * @retval TRUE The TCAT agent is connected. + * @retval TRUE The TCAT agent is connected with a TCAT commissioner. * @retval FALSE The TCAT agent is not connected. * */ bool IsConnected(void) const { return mState == kStateConnected; } /** - * Indicates whether or not a command class is authorized. + * Indicates whether or not a TCAT command class is authorized for use. * - * @param[in] aCommandClass Command class to subject for authorization check. + * @param[in] aCommandClass Command class to subject to authorization check. * - * @retval TRUE The command class is authorized. - * @retval FALSE The command class is not authorized. + * @retval TRUE The command class is authorized for use by the present TCAT commissioner. + * @retval FALSE The command class is not authorized for use. * */ bool IsCommandClassAuthorized(CommandClass aCommandClass) const; @@ -338,11 +338,11 @@ class TcatAgent : public InstanceLocator, private NonCopyable /** * Gets TCAT advertisement data. * - * @param[out] aLen Advertisement length. + * @param[out] aLen Advertisement data length (up to OT_TCAT_ADVERTISEMENT_MAX_LEN). * @param[out] aAdvertisementData Advertisement data. * - * @retval kErrorNone Successfully started the TCAT agent. - * @retval kErrorInvalidArgs The aVendorInfo is invalid or provided incorrect parameters. + * @retval kErrorNone Successfully retrieved the TCAT advertisement data. + * @retval kErrorInvalidArgs The data could not be retrieved, or aAdvertisementData is null. * */ Error GetAdvertisementData(uint16_t &aLen, uint8_t *aAdvertisementData); diff --git a/src/core/radio/ble_secure.cpp b/src/core/radio/ble_secure.cpp index e18c3fd07..f1047ef01 100644 --- a/src/core/radio/ble_secure.cpp +++ b/src/core/radio/ble_secure.cpp @@ -330,21 +330,19 @@ void BleSecure::HandleTlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEv { if (aEvent == MeshCoP::SecureTransport::kConnected) { + Error err; + if (mReceivedMessage == nullptr) { mReceivedMessage = Get().Allocate(Message::kTypeBle); } + err = mTcatAgent.Connected(mTls); - if (mTcatAgent.IsEnabled()) + if (err != kErrorNone) { - Error err = mTcatAgent.Connected(mTls); - - if (err != kErrorNone) - { - mTls.Close(); - LogWarn("Rejected TCAT Commissioner, error: %s", ErrorToString(err)); - ExitNow(); - } + mTls.Disconnect(); // must not use Close(), so that next Commissioner can connect + LogWarn("Rejected TCAT Commissioner, error: %s", ErrorToString(err)); + ExitNow(); } } else @@ -449,8 +447,10 @@ void BleSecure::HandleTlsReceive(uint8_t *aBuf, uint16_t aLength) if (error == kErrorAbort) { + // kErrorAbort indicates that a Disconnect command TLV has been received. Disconnect(); - Stop(); + // BleSecure is not stopped here, it must remain active in advertising state and + // must be ready to receive a next TCAT commissioner. ExitNow(); } } diff --git a/src/core/radio/ble_secure.hpp b/src/core/radio/ble_secure.hpp index d2ef628d7..7be1201d1 100644 --- a/src/core/radio/ble_secure.hpp +++ b/src/core/radio/ble_secure.hpp @@ -169,13 +169,13 @@ class BleSecure : public InstanceLocator, private NonCopyable bool IsTcatEnabled(void) const { return mTcatAgent.IsEnabled(); } /** - * Indicates whether or not a TCAT command class is authorized. + * Indicates whether or not a TCAT command class is authorized for use. * * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aCommandClass A command class to check. + * @param[in] aCommandClass A command class to subject to authorization check. * - * @retval TRUE The command class is authorized. - * @retval FALSE The command class is not authorized. + * @retval TRUE The command class is authorized for use by the present TCAT commissioner. + * @retval FALSE The command class is not authorized for use. * */ bool IsCommandClassAuthorized(CommandClass aCommandClass) const diff --git a/tools/tcat_ble_client/bbtc.py b/tools/tcat_ble_client/bbtc.py index a9be1f539..5ad735943 100755 --- a/tools/tcat_ble_client/bbtc.py +++ b/tools/tcat_ble_client/bbtc.py @@ -102,9 +102,6 @@ async def main(): while True: user_input = await loop.run_in_executor(None, lambda: input('> ')) if user_input.lower() == 'exit': - print('Disconnecting...') - if ble_sstream is not None: - await ble_sstream.close() break try: result: CommandResult = await cli.evaluate_input(user_input) @@ -113,6 +110,10 @@ async def main(): except Exception as e: logger.error(e) + print('Disconnecting...') + if ble_sstream is not None: + await ble_sstream.close() + async def get_device_by_args(args): device = None diff --git a/tools/tcat_ble_client/ble/ble_stream_secure.py b/tools/tcat_ble_client/ble/ble_stream_secure.py index de2b42d8a..97ac0d653 100644 --- a/tools/tcat_ble_client/ble/ble_stream_secure.py +++ b/tools/tcat_ble_client/ble/ble_stream_secure.py @@ -140,6 +140,7 @@ async def send_with_resp(self, bytes): async def close(self): if self.ssl_object.session is not None: + logger.debug('sending Disconnect command TLV') data = TLV(TcatTLVType.DISCONNECT.value, bytes()).to_bytes() await self.send(data) diff --git a/tools/tcat_ble_client/ble/udp_stream.py b/tools/tcat_ble_client/ble/udp_stream.py index c6ba599a0..aef5fa60a 100644 --- a/tools/tcat_ble_client/ble/udp_stream.py +++ b/tools/tcat_ble_client/ble/udp_stream.py @@ -28,25 +28,31 @@ import logging import socket +import select logger = logging.getLogger(__name__) class UdpStream: BASE_PORT = 10000 + MAX_SERVER_TIMEOUT_SEC = 10 def __init__(self, address, node_id): self.__receive_buffer = b'' self.__last_recv_time = None self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.socket.setblocking(False) self.address = (address, self.BASE_PORT + node_id) async def send(self, data): - logger.debug(f'sending {data}') - self.socket.sendto(data, self.address) - return len(data) + logger.debug(f'sending {len(data)} bytes: {data}') + return self.socket.sendto(data, self.address) async def recv(self, bufsize): - message = self.socket.recv(bufsize) - logger.debug(f'retrieved {message}') - return message + ready = select.select([self.socket], [], [], self.MAX_SERVER_TIMEOUT_SEC) + if ready[0]: + data = self.socket.recv(bufsize) + logger.debug(f'received {len(data)} bytes: {data}') + return data + else: + raise Exception('simulation UdpStream recv timeout - likely, TCAT is stopped on TCAT Device') diff --git a/tools/tcat_ble_client/cli/base_commands.py b/tools/tcat_ble_client/cli/base_commands.py index 5bef2a021..669f78ccf 100644 --- a/tools/tcat_ble_client/cli/base_commands.py +++ b/tools/tcat_ble_client/cli/base_commands.py @@ -181,7 +181,7 @@ async def execute_default(self, args, context): data = TLV(TcatTLVType.PING.value, to_send).to_bytes() elapsed_time = time() response = await bless.send_with_resp(data) - elapsed_time = time() - elapsed_time + elapsed_time = 1e3 * (time() - elapsed_time) if not response: return @@ -189,7 +189,7 @@ async def execute_default(self, args, context): if tlv_response.value != to_send: print("Received malformed response.") - print(f"Roundtrip time {elapsed_time} s.") + print(f"Roundtrip time: {elapsed_time} ms") return CommandResultTLV(tlv_response) From a887530e0d55d768d0b17a05715f2da17bd9b7c6 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Sat, 7 Sep 2024 03:21:03 +0800 Subject: [PATCH 153/160] [pcap] fix pcap callback for TX frames (#10678) This commit fixes the pcap callback for TX frames: * Report the tx frame on each tx started callback, so that retransmissions can be captured. * Set the RSSI to be invalid for TX frames. --- src/core/mac/sub_mac.cpp | 10 +++++----- src/ncp/ncp_base_mtd.cpp | 7 ++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 94916f6d7..28b170f9a 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -473,11 +473,6 @@ void SubMac::BeginTransmit(void) SetState(kStateTransmit); - if (mPcapCallback.IsSet()) - { - mPcapCallback.Invoke(&mTransmitFrame, true); - } - error = Get().Transmit(mTransmitFrame); if (error == kErrorInvalidState && mTransmitFrame.mInfo.mTxInfo.mTxDelay > 0) @@ -497,6 +492,11 @@ void SubMac::BeginTransmit(void) void SubMac::HandleTransmitStarted(TxFrame &aFrame) { + if (mPcapCallback.IsSet()) + { + mPcapCallback.Invoke(&aFrame, true); + } + if (ShouldHandleAckTimeout() && aFrame.GetAckRequest()) { StartTimer(kAckTimeout); diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 3c1faf7a6..4f7c80569 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -4600,9 +4600,10 @@ void NcpBase::HandlePcapFrame(const otRadioFrame *aFrame, bool aIsTx) SuccessOrExit(mEncoder.WriteData(aFrame->mPsdu, aFrame->mLength)); // Append metadata (rssi, etc) - SuccessOrExit(mEncoder.WriteInt8(aFrame->mInfo.mRxInfo.mRssi)); // RSSI - SuccessOrExit(mEncoder.WriteInt8(-128)); // Noise floor (Currently unused) - SuccessOrExit(mEncoder.WriteUint16(flags)); // Flags + SuccessOrExit( + mEncoder.WriteInt8((aIsTx ? static_cast(OT_RADIO_RSSI_INVALID) : aFrame->mInfo.mRxInfo.mRssi))); // RSSI + SuccessOrExit(mEncoder.WriteInt8(-128)); // Noise floor (Currently unused) + SuccessOrExit(mEncoder.WriteUint16(flags)); // Flags SuccessOrExit(mEncoder.OpenStruct()); // PHY-data // Empty for now From bb5e9b791af56e4802a00d7b5ce1e8eb01558b98 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Sat, 7 Sep 2024 04:18:25 +0800 Subject: [PATCH 154/160] [spinel] coorect description of STREAM_RAW (#10680) This commit corrects the description of STREAM_RAW. - timestamp should be type `X`. - channel can be both RX and TX channel. --- src/lib/spinel/spinel.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index 5015720da..169b12ab8 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -3578,10 +3578,9 @@ enum * The format of PHY-specific data for a Thread device contains the following * optional fields: - * `C` : 802.15.4 channel (Receive channel) + * `C` : 802.15.4 channel * `C` : IEEE 802.15.4 LQI - * `L` : The timestamp milliseconds - * `S` : The timestamp microseconds, offset to mMsec + * `X` : The timestamp in microseconds * * Frames written to this stream with `CMD_PROP_VALUE_SET` will be sent out * over the radio. This allows the caller to use the radio directly. From 2283b799b020197124cf82f7a83f8194cbefaecc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:22:34 -0700 Subject: [PATCH 155/160] github-actions: bump docker/login-action from 3.2.0 to 3.3.0 (#10687) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/0d4c9c5ea7693da7b068278f7b52bda2a190a446...9780b0c442fbb1117ed29e0efdff1e18412f7567) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f062d6898..b4f3074ea 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -94,7 +94,7 @@ jobs: - name: Login to DockerHub if: success() && github.repository == 'openthread/openthread' && github.event_name != 'pull_request' - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} From 7673194af6c4ede079afc8a9af24f4b0493373a2 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 9 Sep 2024 10:57:07 -0700 Subject: [PATCH 156/160] [mac] move `HeaderIe` definitions into separate files (#10683) This commit moves all `HeaderIe` related definitions from `mac_frame.hpp/cpp` into newly added `mac_header_ie.hpp/cpp` source files. This commit does not make any changes to the code. --- src/core/BUILD.gn | 3 + src/core/CMakeLists.txt | 2 + src/core/mac/mac_frame.cpp | 7 - src/core/mac/mac_frame.hpp | 271 +-------------------------- src/core/mac/mac_header_ie.cpp | 47 +++++ src/core/mac/mac_header_ie.hpp | 332 +++++++++++++++++++++++++++++++++ 6 files changed, 385 insertions(+), 277 deletions(-) create mode 100644 src/core/mac/mac_header_ie.cpp create mode 100644 src/core/mac/mac_header_ie.hpp diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 2d5260800..9512c1d65 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -499,6 +499,8 @@ openthread_core_files = [ "mac/mac_filter.hpp", "mac/mac_frame.cpp", "mac/mac_frame.hpp", + "mac/mac_header_ie.cpp", + "mac/mac_header_ie.hpp", "mac/mac_links.cpp", "mac/mac_links.hpp", "mac/mac_types.cpp", @@ -772,6 +774,7 @@ openthread_radio_sources = [ "instance/instance.cpp", "mac/link_raw.cpp", "mac/mac_frame.cpp", + "mac/mac_header_ie.cpp", "mac/mac_types.cpp", "mac/sub_mac.cpp", "mac/sub_mac_callbacks.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8e712c8fd..0089c42ec 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -140,6 +140,7 @@ set(COMMON_SOURCES mac/mac.cpp mac/mac_filter.cpp mac/mac_frame.cpp + mac/mac_header_ie.cpp mac/mac_links.cpp mac/mac_types.cpp mac/sub_mac.cpp @@ -289,6 +290,7 @@ set(RADIO_COMMON_SOURCES instance/instance.cpp mac/link_raw.cpp mac/mac_frame.cpp + mac/mac_header_ie.cpp mac/mac_types.cpp mac/sub_mac.cpp mac/sub_mac_callbacks.cpp diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 0c6f59e9f..8400a194f 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -47,13 +47,6 @@ namespace ot { namespace Mac { -void HeaderIe::Init(uint16_t aId, uint8_t aLen) -{ - Init(); - SetId(aId); - SetLength(aLen); -} - void Frame::InitMacHeader(Type aType, Version aVersion, const Addresses &aAddrs, diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 4ce924103..d51e45caa 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -40,6 +40,7 @@ #include "common/const_cast.hpp" #include "common/encoding.hpp" #include "common/numeric_limits.hpp" +#include "mac/mac_header_ie.hpp" #include "mac/mac_types.hpp" #include "meshcop/network_name.hpp" @@ -53,276 +54,6 @@ namespace Mac { * */ -/** - * Implements IEEE 802.15.4 IE (Information Element) header generation and parsing. - * - */ -OT_TOOL_PACKED_BEGIN -class HeaderIe -{ -public: - /** - * Initializes the Header IE. - * - */ - void Init(void) { mFields.m16 = 0; } - - /** - * Initializes the Header IE with Id and Length. - * - * @param[in] aId The IE Element Id. - * @param[in] aLen The IE content length. - * - */ - void Init(uint16_t aId, uint8_t aLen); - - /** - * Returns the IE Element Id. - * - * @returns the IE Element Id. - * - */ - uint16_t GetId(void) const { return (LittleEndian::HostSwap16(mFields.m16) & kIdMask) >> kIdOffset; } - - /** - * Sets the IE Element Id. - * - * @param[in] aId The IE Element Id. - * - */ - void SetId(uint16_t aId) - { - mFields.m16 = LittleEndian::HostSwap16((LittleEndian::HostSwap16(mFields.m16) & ~kIdMask) | - ((aId << kIdOffset) & kIdMask)); - } - - /** - * Returns the IE content length. - * - * @returns the IE content length. - * - */ - uint8_t GetLength(void) const { return mFields.m8[0] & kLengthMask; } - - /** - * Sets the IE content length. - * - * @param[in] aLength The IE content length. - * - */ - void SetLength(uint8_t aLength) { mFields.m8[0] = (mFields.m8[0] & ~kLengthMask) | (aLength & kLengthMask); } - -private: - // Header IE format: - // - // +-----------+------------+--------+ - // | Bits: 0-6 | 7-14 | 15 | - // +-----------+------------+--------+ - // | Length | Element ID | Type=0 | - // +-----------+------------+--------+ - - static constexpr uint8_t kSize = 2; - static constexpr uint8_t kIdOffset = 7; - static constexpr uint8_t kLengthMask = 0x7f; - static constexpr uint16_t kIdMask = 0x00ff << kIdOffset; - - union OT_TOOL_PACKED_FIELD - { - uint8_t m8[kSize]; - uint16_t m16; - } mFields; - -} OT_TOOL_PACKED_END; - -/** - * Implements CSL IE data structure. - * - */ -OT_TOOL_PACKED_BEGIN -class CslIe -{ -public: - static constexpr uint8_t kHeaderIeId = 0x1a; - static constexpr uint8_t kIeContentSize = sizeof(uint16_t) * 2; - - /** - * Returns the CSL Period. - * - * @returns the CSL Period. - * - */ - uint16_t GetPeriod(void) const { return LittleEndian::HostSwap16(mPeriod); } - - /** - * Sets the CSL Period. - * - * @param[in] aPeriod The CSL Period. - * - */ - void SetPeriod(uint16_t aPeriod) { mPeriod = LittleEndian::HostSwap16(aPeriod); } - - /** - * Returns the CSL Phase. - * - * @returns the CSL Phase. - * - */ - uint16_t GetPhase(void) const { return LittleEndian::HostSwap16(mPhase); } - - /** - * Sets the CSL Phase. - * - * @param[in] aPhase The CSL Phase. - * - */ - void SetPhase(uint16_t aPhase) { mPhase = LittleEndian::HostSwap16(aPhase); } - -private: - uint16_t mPhase; - uint16_t mPeriod; -} OT_TOOL_PACKED_END; - -/** - * Implements Termination2 IE. - * - * Is empty for template specialization. - * - */ -class Termination2Ie -{ -public: - static constexpr uint8_t kHeaderIeId = 0x7f; - static constexpr uint8_t kIeContentSize = 0; -}; - -#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || \ - OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE -/** - * Implements vendor specific Header IE generation and parsing. - * - */ -OT_TOOL_PACKED_BEGIN -class VendorIeHeader -{ -public: - static constexpr uint8_t kHeaderIeId = 0x00; - static constexpr uint8_t kIeContentSize = sizeof(uint8_t) * 4; - - /** - * Returns the Vendor OUI. - * - * @returns The Vendor OUI. - * - */ - uint32_t GetVendorOui(void) const { return LittleEndian::ReadUint24(mOui); } - - /** - * Sets the Vendor OUI. - * - * @param[in] aVendorOui A Vendor OUI. - * - */ - void SetVendorOui(uint32_t aVendorOui) { LittleEndian::WriteUint24(aVendorOui, mOui); } - - /** - * Returns the Vendor IE sub-type. - * - * @returns The Vendor IE sub-type. - * - */ - uint8_t GetSubType(void) const { return mSubType; } - - /** - * Sets the Vendor IE sub-type. - * - * @param[in] aSubType The Vendor IE sub-type. - * - */ - void SetSubType(uint8_t aSubType) { mSubType = aSubType; } - -private: - static constexpr uint8_t kOuiSize = 3; - - uint8_t mOui[kOuiSize]; - uint8_t mSubType; -} OT_TOOL_PACKED_END; - -#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE -/** - * Implements Time Header IE generation and parsing. - * - */ -OT_TOOL_PACKED_BEGIN -class TimeIe : public VendorIeHeader -{ -public: - static constexpr uint32_t kVendorOuiNest = 0x18b430; - static constexpr uint8_t kVendorIeTime = 0x01; - static constexpr uint8_t kHeaderIeId = VendorIeHeader::kHeaderIeId; - static constexpr uint8_t kIeContentSize = VendorIeHeader::kIeContentSize + sizeof(uint8_t) + sizeof(uint64_t); - - /** - * Initializes the time IE. - * - */ - void Init(void) - { - SetVendorOui(kVendorOuiNest); - SetSubType(kVendorIeTime); - } - - /** - * Returns the time sync sequence. - * - * @returns the time sync sequence. - * - */ - uint8_t GetSequence(void) const { return mSequence; } - - /** - * Sets the tine sync sequence. - * - * @param[in] aSequence The time sync sequence. - * - */ - void SetSequence(uint8_t aSequence) { mSequence = aSequence; } - - /** - * Returns the network time. - * - * @returns the network time, in microseconds. - * - */ - uint64_t GetTime(void) const { return LittleEndian::HostSwap64(mTime); } - - /** - * Sets the network time. - * - * @param[in] aTime The network time. - * - */ - void SetTime(uint64_t aTime) { mTime = LittleEndian::HostSwap64(aTime); } - -private: - uint8_t mSequence; - uint64_t mTime; -} OT_TOOL_PACKED_END; -#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE - -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE -class ThreadIe -{ -public: - static constexpr uint8_t kHeaderIeId = VendorIeHeader::kHeaderIeId; - static constexpr uint8_t kIeContentSize = VendorIeHeader::kIeContentSize; - static constexpr uint32_t kVendorOuiThreadCompanyId = 0xeab89b; - static constexpr uint8_t kEnhAckProbingIe = 0x00; -}; -#endif - -#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || - // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE - /** * Implements IEEE 802.15.4 MAC frame generation and parsing. * diff --git a/src/core/mac/mac_header_ie.cpp b/src/core/mac/mac_header_ie.cpp new file mode 100644 index 000000000..405a6f099 --- /dev/null +++ b/src/core/mac/mac_header_ie.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016-2024, 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 IEEE 802.15.4 header IE generation and parsing. + */ + +#include "mac_header_ie.hpp" + +namespace ot { +namespace Mac { + +void HeaderIe::Init(uint16_t aId, uint8_t aLen) +{ + Init(); + SetId(aId); + SetLength(aLen); +} + +} // namespace Mac +} // namespace ot diff --git a/src/core/mac/mac_header_ie.hpp b/src/core/mac/mac_header_ie.hpp new file mode 100644 index 000000000..5e25dc9cd --- /dev/null +++ b/src/core/mac/mac_header_ie.hpp @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2016-2024, 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 generating and processing IEEE 802.15.4 IE (Information Element). + */ + +#ifndef MAC_HEADER_IE_HPP_ +#define MAC_HEADER_IE_HPP_ + +#include "openthread-core-config.h" + +#include "common/as_core_type.hpp" +#include "common/encoding.hpp" +#include "common/numeric_limits.hpp" +#include "mac/mac_types.hpp" + +namespace ot { +namespace Mac { + +/** + * @addtogroup core-mac + * + * @{ + * + */ + +/** + * Implements IEEE 802.15.4 IE (Information Element) header generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class HeaderIe +{ +public: + /** + * Initializes the Header IE. + * + */ + void Init(void) { mFields.m16 = 0; } + + /** + * Initializes the Header IE with Id and Length. + * + * @param[in] aId The IE Element Id. + * @param[in] aLen The IE content length. + * + */ + void Init(uint16_t aId, uint8_t aLen); + + /** + * Returns the IE Element Id. + * + * @returns the IE Element Id. + * + */ + uint16_t GetId(void) const { return (LittleEndian::HostSwap16(mFields.m16) & kIdMask) >> kIdOffset; } + + /** + * Sets the IE Element Id. + * + * @param[in] aId The IE Element Id. + * + */ + void SetId(uint16_t aId) + { + mFields.m16 = LittleEndian::HostSwap16((LittleEndian::HostSwap16(mFields.m16) & ~kIdMask) | + ((aId << kIdOffset) & kIdMask)); + } + + /** + * Returns the IE content length. + * + * @returns the IE content length. + * + */ + uint8_t GetLength(void) const { return mFields.m8[0] & kLengthMask; } + + /** + * Sets the IE content length. + * + * @param[in] aLength The IE content length. + * + */ + void SetLength(uint8_t aLength) { mFields.m8[0] = (mFields.m8[0] & ~kLengthMask) | (aLength & kLengthMask); } + +private: + // Header IE format: + // + // +-----------+------------+--------+ + // | Bits: 0-6 | 7-14 | 15 | + // +-----------+------------+--------+ + // | Length | Element ID | Type=0 | + // +-----------+------------+--------+ + + static constexpr uint8_t kSize = 2; + static constexpr uint8_t kIdOffset = 7; + static constexpr uint8_t kLengthMask = 0x7f; + static constexpr uint16_t kIdMask = 0x00ff << kIdOffset; + + union OT_TOOL_PACKED_FIELD + { + uint8_t m8[kSize]; + uint16_t m16; + } mFields; + +} OT_TOOL_PACKED_END; + +/** + * Implements CSL IE data structure. + * + */ +OT_TOOL_PACKED_BEGIN +class CslIe +{ +public: + static constexpr uint8_t kHeaderIeId = 0x1a; + static constexpr uint8_t kIeContentSize = sizeof(uint16_t) * 2; + + /** + * Returns the CSL Period. + * + * @returns the CSL Period. + * + */ + uint16_t GetPeriod(void) const { return LittleEndian::HostSwap16(mPeriod); } + + /** + * Sets the CSL Period. + * + * @param[in] aPeriod The CSL Period. + * + */ + void SetPeriod(uint16_t aPeriod) { mPeriod = LittleEndian::HostSwap16(aPeriod); } + + /** + * Returns the CSL Phase. + * + * @returns the CSL Phase. + * + */ + uint16_t GetPhase(void) const { return LittleEndian::HostSwap16(mPhase); } + + /** + * Sets the CSL Phase. + * + * @param[in] aPhase The CSL Phase. + * + */ + void SetPhase(uint16_t aPhase) { mPhase = LittleEndian::HostSwap16(aPhase); } + +private: + uint16_t mPhase; + uint16_t mPeriod; +} OT_TOOL_PACKED_END; + +/** + * Implements Termination2 IE. + * + * Is empty for template specialization. + * + */ +class Termination2Ie +{ +public: + static constexpr uint8_t kHeaderIeId = 0x7f; + static constexpr uint8_t kIeContentSize = 0; +}; + +#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || \ + OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +/** + * Implements vendor specific Header IE generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class VendorIeHeader +{ +public: + static constexpr uint8_t kHeaderIeId = 0x00; + static constexpr uint8_t kIeContentSize = sizeof(uint8_t) * 4; + + /** + * Returns the Vendor OUI. + * + * @returns The Vendor OUI. + * + */ + uint32_t GetVendorOui(void) const { return LittleEndian::ReadUint24(mOui); } + + /** + * Sets the Vendor OUI. + * + * @param[in] aVendorOui A Vendor OUI. + * + */ + void SetVendorOui(uint32_t aVendorOui) { LittleEndian::WriteUint24(aVendorOui, mOui); } + + /** + * Returns the Vendor IE sub-type. + * + * @returns The Vendor IE sub-type. + * + */ + uint8_t GetSubType(void) const { return mSubType; } + + /** + * Sets the Vendor IE sub-type. + * + * @param[in] aSubType The Vendor IE sub-type. + * + */ + void SetSubType(uint8_t aSubType) { mSubType = aSubType; } + +private: + static constexpr uint8_t kOuiSize = 3; + + uint8_t mOui[kOuiSize]; + uint8_t mSubType; +} OT_TOOL_PACKED_END; + +#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE +/** + * Implements Time Header IE generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class TimeIe : public VendorIeHeader +{ +public: + static constexpr uint32_t kVendorOuiNest = 0x18b430; + static constexpr uint8_t kVendorIeTime = 0x01; + static constexpr uint8_t kHeaderIeId = VendorIeHeader::kHeaderIeId; + static constexpr uint8_t kIeContentSize = VendorIeHeader::kIeContentSize + sizeof(uint8_t) + sizeof(uint64_t); + + /** + * Initializes the time IE. + * + */ + void Init(void) + { + SetVendorOui(kVendorOuiNest); + SetSubType(kVendorIeTime); + } + + /** + * Returns the time sync sequence. + * + * @returns the time sync sequence. + * + */ + uint8_t GetSequence(void) const { return mSequence; } + + /** + * Sets the tine sync sequence. + * + * @param[in] aSequence The time sync sequence. + * + */ + void SetSequence(uint8_t aSequence) { mSequence = aSequence; } + + /** + * Returns the network time. + * + * @returns the network time, in microseconds. + * + */ + uint64_t GetTime(void) const { return LittleEndian::HostSwap64(mTime); } + + /** + * Sets the network time. + * + * @param[in] aTime The network time. + * + */ + void SetTime(uint64_t aTime) { mTime = LittleEndian::HostSwap64(aTime); } + +private: + uint8_t mSequence; + uint64_t mTime; +} OT_TOOL_PACKED_END; +#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE + +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +class ThreadIe +{ +public: + static constexpr uint8_t kHeaderIeId = VendorIeHeader::kHeaderIeId; + static constexpr uint8_t kIeContentSize = VendorIeHeader::kIeContentSize; + static constexpr uint32_t kVendorOuiThreadCompanyId = 0xeab89b; + static constexpr uint8_t kEnhAckProbingIe = 0x00; +}; +#endif + +#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || + // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + +/** + * @} + * + */ + +} // namespace Mac +} // namespace ot + +#endif // MAC_HEADER_IE_HPP_ From 07a1b7bd985d8d1cd9fab5d45bc62ae8fc1358c9 Mon Sep 17 00:00:00 2001 From: Marcin Kajor <98948394+markaj-nordic@users.noreply.github.com> Date: Tue, 10 Sep 2024 21:33:27 +0200 Subject: [PATCH 157/160] [mle] add API to configure the store frame counter ahead (#10576) Currently the OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD is hard-coded in the OT core, yet sometimes it is desired to modify this value which requires re-building of the OT libraries and forces re-certification of the end product. Implement `otThreadSetStoreFrameCounterAhead` and `otThreadGetStoreFrameCounterAhead` to allow API clients to configure the store frame counter ahead parameter at run-time. This extension offloads product makers from the need of re-certification in case the store frame counter ahead must be tuned. Signed-off-by: Marcin Kajor --- etc/cmake/options.cmake | 1 + include/openthread/instance.h | 2 +- include/openthread/thread.h | 27 +++++++++++++++++++++++++++ src/core/api/thread_api.cpp | 12 ++++++++++++ src/core/config/mle.h | 10 ++++++++++ src/core/thread/mle.cpp | 5 +++-- src/core/thread/mle.hpp | 34 ++++++++++++++++++++++++++++------ 7 files changed, 82 insertions(+), 9 deletions(-) diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 8825fb14d..9c39b89cb 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -204,6 +204,7 @@ ot_option(OT_DNS_UPSTREAM_QUERY OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE "All ot_option(OT_DNSSD_DISCOVERY_PROXY OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE "DNS-SD discovery proxy") ot_option(OT_DNSSD_SERVER OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE "DNS-SD server") ot_option(OT_DUA OPENTHREAD_CONFIG_DUA_ENABLE "Domain Unicast Address (DUA)") +ot_option(OT_DYNAMIC_STORE_FRAME_AHEAD_COUNTER OPENTHREAD_CONFIG_DYNAMIC_STORE_FRAME_AHEAD_COUNTER_ENABLE "dynamic store frame ahead counter") ot_option(OT_ECDSA OPENTHREAD_CONFIG_ECDSA_ENABLE "ECDSA") ot_option(OT_EXTERNAL_HEAP OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE "external heap") ot_option(OT_FIREWALL OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE "firewall") diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 42268ac64..eba6c33d4 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (439) +#define OPENTHREAD_API_VERSION (440) /** * @addtogroup api-instance diff --git a/include/openthread/thread.h b/include/openthread/thread.h index f31d46b6c..25de6e63c 100644 --- a/include/openthread/thread.h +++ b/include/openthread/thread.h @@ -1167,6 +1167,33 @@ otError otThreadDetachGracefully(otInstance *aInstance, otDetachGracefullyCallba */ void otConvertDurationInSecondsToString(uint32_t aDuration, char *aBuffer, uint16_t aSize); +/** + * Sets the store frame counter ahead. + * + * Requires `OPENTHREAD_CONFIG_DYNAMIC_STORE_FRAME_AHEAD_COUNTER_ENABLE` to be enabled. + * + * The OpenThread stack stores the MLE and MAC security frame counter values in non-volatile storage, + * ensuring they persist across device resets. These saved values are set to be ahead of their current + * values by the "frame counter ahead" value. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aStoreFrameCounterAhead The store frame counter ahead to set. + * + */ +void otThreadSetStoreFrameCounterAhead(otInstance *aInstance, uint32_t aStoreFrameCounterAhead); + +/** + * Gets the store frame counter ahead. + * + * Requires `OPENTHREAD_CONFIG_DYNAMIC_STORE_FRAME_AHEAD_COUNTER_ENABLE` to be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The current store frame counter ahead. + * + */ +uint32_t otThreadGetStoreFrameCounterAhead(otInstance *aInstance); + /** * @} * diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp index d09771862..1fa4dc9bc 100644 --- a/src/core/api/thread_api.cpp +++ b/src/core/api/thread_api.cpp @@ -510,6 +510,18 @@ otError otThreadDetachGracefully(otInstance *aInstance, otDetachGracefullyCallba return AsCoreType(aInstance).Get().DetachGracefully(aCallback, aContext); } +#if OPENTHREAD_CONFIG_DYNAMIC_STORE_FRAME_AHEAD_COUNTER_ENABLE +void otThreadSetStoreFrameCounterAhead(otInstance *aInstance, uint32_t aStoreFrameCounterAhead) +{ + return AsCoreType(aInstance).Get().SetStoreFrameCounterAhead(aStoreFrameCounterAhead); +} + +uint32_t otThreadGetStoreFrameCounterAhead(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetStoreFrameCounterAhead(); +} +#endif + #endif // OPENTHREAD_FTD || OPENTHREAD_MTD #if OPENTHREAD_CONFIG_UPTIME_ENABLE diff --git a/src/core/config/mle.h b/src/core/config/mle.h index d09a3cc80..964012838 100644 --- a/src/core/config/mle.h +++ b/src/core/config/mle.h @@ -348,6 +348,16 @@ #define OPENTHREAD_CONFIG_MLE_LINK_METRICS_SERIES_MTD 2 #endif +/** + * @def OPENTHREAD_CONFIG_DYNAMIC_STORE_FRAME_AHEAD_COUNTER_ENABLE + * + * Enable setting the store frame counter ahead. + * + */ +#ifndef OPENTHREAD_CONFIG_DYNAMIC_STORE_FRAME_AHEAD_COUNTER_ENABLE +#define OPENTHREAD_CONFIG_DYNAMIC_STORE_FRAME_AHEAD_COUNTER_ENABLE 0 +#endif + /** * @} * diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index e2a48f240..ff90ba098 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -100,6 +100,7 @@ Mle::Mle(Instance &aInstance) , mAttachCounter(0) , mAnnounceDelay(kAnnounceTimeout) , mAlternatePanId(Mac::kPanIdBroadcast) + , mStoreFrameCounterAhead(kDefaultStoreFrameCounterAhead) , mTimeout(kDefaultChildTimeout) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE , mCslTimeout(kDefaultCslTimeout) @@ -501,8 +502,8 @@ Error Mle::Store(void) } networkInfo.SetKeySequence(Get().GetCurrentKeySequence()); - networkInfo.SetMleFrameCounter(Get().GetMleFrameCounter() + kStoreFrameCounterAhead); - networkInfo.SetMacFrameCounter(Get().GetMaximumMacFrameCounter() + kStoreFrameCounterAhead); + networkInfo.SetMleFrameCounter(Get().GetMleFrameCounter() + mStoreFrameCounterAhead); + networkInfo.SetMacFrameCounter(Get().GetMaximumMacFrameCounter() + mStoreFrameCounterAhead); networkInfo.SetDeviceMode(mDeviceMode.Get()); SuccessOrExit(error = Get().Save(networkInfo)); diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 2c49f5900..8cbb4c03a 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -737,6 +737,27 @@ class Mle : public InstanceLocator, private NonCopyable return (&aAddress == &mLinkLocalAllThreadNodes) || (&aAddress == &mRealmLocalAllThreadNodes); } +#if OPENTHREAD_CONFIG_DYNAMIC_STORE_FRAME_AHEAD_COUNTER_ENABLE + /** + * Sets the store frame counter ahead. + * + * @param[in] aStoreFrameCounterAhead The store frame counter ahead to set. + * + */ + void SetStoreFrameCounterAhead(uint32_t aStoreFrameCounterAhead) + { + mStoreFrameCounterAhead = aStoreFrameCounterAhead; + } + + /** + * Gets the current store frame counter ahead. + * + * @returns The current store frame counter ahead. + * + */ + uint32_t GetStoreFrameCounterAhead(void) { return mStoreFrameCounterAhead; } +#endif // OPENTHREAD_CONFIG_DYNAMIC_STORE_FRAME_AHEAD_COUNTER_ENABLE + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE /** * Gets the CSL timeout. @@ -829,12 +850,12 @@ class Mle : public InstanceLocator, private NonCopyable static constexpr uint8_t kMaxServiceAlocs = OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_MAX_ALOCS; #endif - static constexpr uint8_t kMleHopLimit = 255; - static constexpr uint8_t kMleSecurityTagSize = 4; - static constexpr uint32_t kStoreFrameCounterAhead = OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD; - static constexpr uint8_t kMaxIpAddressesToRegister = OPENTHREAD_CONFIG_MLE_IP_ADDRS_TO_REGISTER; - static constexpr uint32_t kDefaultChildTimeout = OPENTHREAD_CONFIG_MLE_CHILD_TIMEOUT_DEFAULT; - static constexpr uint32_t kDefaultCslTimeout = OPENTHREAD_CONFIG_CSL_TIMEOUT; + static constexpr uint8_t kMleHopLimit = 255; + static constexpr uint8_t kMleSecurityTagSize = 4; + static constexpr uint32_t kDefaultStoreFrameCounterAhead = OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD; + static constexpr uint8_t kMaxIpAddressesToRegister = OPENTHREAD_CONFIG_MLE_IP_ADDRS_TO_REGISTER; + static constexpr uint32_t kDefaultChildTimeout = OPENTHREAD_CONFIG_MLE_CHILD_TIMEOUT_DEFAULT; + static constexpr uint32_t kDefaultCslTimeout = OPENTHREAD_CONFIG_CSL_TIMEOUT; //------------------------------------------------------------------------------------------------------------------ // Enumerations @@ -1431,6 +1452,7 @@ class Mle : public InstanceLocator, private NonCopyable uint16_t mAttachCounter; uint16_t mAnnounceDelay; uint16_t mAlternatePanId; + uint32_t mStoreFrameCounterAhead; uint32_t mTimeout; #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE uint32_t mCslTimeout; From c0690e78fcb0a780770687a70eac74858efe5d7c Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Wed, 11 Sep 2024 04:39:38 +0800 Subject: [PATCH 158/160] [build] set default Thread version to 1.4 (#10690) This commit updates the default Thread version to 1.4, so that most latest features will be enabled by default. This commit also explicitly enable the epskc feature just like other features in the build script. --- etc/cmake/options.cmake | 2 +- examples/config/ot-core-config-check-size-br.h | 2 +- examples/config/ot-core-config-check-size-ftd.h | 2 +- examples/config/ot-core-config-check-size-mtd.h | 2 +- script/cmake-build | 3 ++- .../thread-cert/border_router/test_publish_meshcop_service.py | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 9c39b89cb..f16694652 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -287,7 +287,7 @@ endif() # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - set(OT_THREAD_VERSION_VALUES "1.1" "1.2" "1.3" "1.3.1" "1.4") -set(OT_THREAD_VERSION "1.3" CACHE STRING "set Thread version") +set(OT_THREAD_VERSION "1.4" CACHE STRING "set Thread version") set_property(CACHE OT_THREAD_VERSION PROPERTY STRINGS "${OT_THREAD_VERSION_VALUES}") list(FIND OT_THREAD_VERSION_VALUES "${OT_THREAD_VERSION}" ot_index) if(ot_index EQUAL -1) diff --git a/examples/config/ot-core-config-check-size-br.h b/examples/config/ot-core-config-check-size-br.h index d8b2b130e..b8bf96fea 100644 --- a/examples/config/ot-core-config-check-size-br.h +++ b/examples/config/ot-core-config-check-size-br.h @@ -35,7 +35,7 @@ #ifndef OT_CORE_CONFIG_CHECK_SIZE_BR_H_ #define OT_CORE_CONFIG_CHECK_SIZE_BR_H_ -#define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_3 +#define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_4 #define OPENTHREAD_CONFIG_ASSERT_ENABLE 1 #define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1 diff --git a/examples/config/ot-core-config-check-size-ftd.h b/examples/config/ot-core-config-check-size-ftd.h index bdd69d055..3cda3271b 100644 --- a/examples/config/ot-core-config-check-size-ftd.h +++ b/examples/config/ot-core-config-check-size-ftd.h @@ -35,7 +35,7 @@ #ifndef OT_CORE_CONFIG_CHECK_SIZE_FTD_H_ #define OT_CORE_CONFIG_CHECK_SIZE_FTD_H_ -#define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_3 +#define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_4 #define OPENTHREAD_CONFIG_ASSERT_ENABLE 1 #define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 0 diff --git a/examples/config/ot-core-config-check-size-mtd.h b/examples/config/ot-core-config-check-size-mtd.h index d5ca74cbc..310cfce9d 100644 --- a/examples/config/ot-core-config-check-size-mtd.h +++ b/examples/config/ot-core-config-check-size-mtd.h @@ -35,7 +35,7 @@ #ifndef OT_CORE_CONFIG_CHECK_SIZE_MTD_H_ #define OT_CORE_CONFIG_CHECK_SIZE_MTD_H_ -#define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_3 +#define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_4 #define OPENTHREAD_CONFIG_ASSERT_ENABLE 1 #define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 0 diff --git a/script/cmake-build b/script/cmake-build index f8e227c5d..2756d3d44 100755 --- a/script/cmake-build +++ b/script/cmake-build @@ -72,7 +72,9 @@ readonly OT_PLATFORMS OT_POSIX_SIM_COMMON_OPTIONS=( "-DOT_ANYCAST_LOCATOR=ON" + "-DOT_BLE_TCAT=ON" "-DOT_BORDER_AGENT=ON" + "-DOT_BORDER_AGENT_EPSKC=ON" "-DOT_BORDER_AGENT_ID=ON" "-DOT_BORDER_ROUTER=ON" "-DOT_CHANNEL_MANAGER=ON" @@ -109,7 +111,6 @@ OT_POSIX_SIM_COMMON_OPTIONS=( "-DOT_SRP_CLIENT=ON" "-DOT_SRP_SERVER=ON" "-DOT_UPTIME=ON" - "-DOT_BLE_TCAT=ON" ) readonly OT_POSIX_SIM_COMMON_OPTIONS diff --git a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py index 2b522d44c..03b6add98 100755 --- a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py +++ b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py @@ -233,7 +233,7 @@ def check_meshcop_service_by_data(self, br, service_data): self.assertEqual(bool(state_bitmap >> 11 & 1), br.ephemeral_key_enabled) # ePSKc is supported or not self.assertEqual(service_data['txt']['nn'], br.get_network_name()) self.assertEqual(service_data['txt']['rv'], '1') - self.assertIn(service_data['txt']['tv'], ['1.1.0', '1.1.1', '1.2.0', '1.3.0']) + self.assertIn(service_data['txt']['tv'], ['1.1.0', '1.1.1', '1.2.0', '1.3.0', '1.4.0']) def discover_services(self, host, type): instance_names = host.browse_mdns_services(type) From 2f51cababfee1cf2654af3a68d0459d87e4b7231 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Wed, 11 Sep 2024 07:25:32 -0700 Subject: [PATCH 159/160] [github-actions] migrate version 1.3 checks to 1.4 (#10694) --- .github/workflows/otbr.yml | 4 +- .github/workflows/otci.yml | 2 +- ...{simulation-1.2.yml => simulation-1.4.yml} | 46 +++++++++---------- script/check-arm-build | 2 +- script/check-gn-build | 4 +- script/check-simulation-build-cmake | 22 ++++----- script/make-pretty | 6 +-- script/test | 20 ++++---- src/cli/cli.cpp | 2 +- .../border_router/nat64/test_upstream_dns.py | 4 +- .../border_router/test_firewall.py | 6 +-- .../border_router/test_multi_ail.py | 6 +-- tests/scripts/thread-cert/config.py | 1 + tests/scripts/thread-cert/node.py | 12 ++--- .../thread-cert/pktverify/packet_verifier.py | 2 +- tests/scripts/thread-cert/thread_cert.py | 6 +-- 16 files changed, 73 insertions(+), 72 deletions(-) rename .github/workflows/{simulation-1.2.yml => simulation-1.4.yml} (94%) diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml index 018a7f282..2fe3243d8 100644 --- a/.github/workflows/otbr.yml +++ b/.github/workflows/otbr.yml @@ -51,7 +51,7 @@ jobs: REFERENCE_DEVICE: 1 VIRTUAL_TIME: 0 PACKET_VERIFICATION: 1 - THREAD_VERSION: 1.3 + THREAD_VERSION: 1.4 INTER_OP: 1 COVERAGE: 1 MULTIPLY: 1 @@ -170,7 +170,7 @@ jobs: REFERENCE_DEVICE: 1 VIRTUAL_TIME: 0 PACKET_VERIFICATION: ${{ matrix.packet_verification }} - THREAD_VERSION: 1.3 + THREAD_VERSION: 1.4 INTER_OP: 1 COVERAGE: 1 MULTIPLY: 1 diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml index c6342231f..0ec7c13ef 100644 --- a/.github/workflows/otci.yml +++ b/.github/workflows/otci.yml @@ -73,7 +73,7 @@ jobs: PYTHONPATH=./tests/scripts/thread-cert pytype tools/otci - name: Build run: | - ./script/cmake-build simulation -DOT_THREAD_VERSION=1.3 -DOT_DUA=ON -DOT_MLR=ON -DOT_BACKBONE_ROUTER=ON \ + ./script/cmake-build simulation -DOT_THREAD_VERSION=1.4 -DOT_DUA=ON -DOT_MLR=ON -DOT_BACKBONE_ROUTER=ON \ -DOT_CSL_RECEIVER=ON -DOT_SIMULATION_VIRTUAL_TIME=${VIRTUAL_TIME} - name: Install OTCI Python Library run: | diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.4.yml similarity index 94% rename from .github/workflows/simulation-1.2.yml rename to .github/workflows/simulation-1.4.yml index e2a20d39f..f1c85bc14 100644 --- a/.github/workflows/simulation-1.2.yml +++ b/.github/workflows/simulation-1.4.yml @@ -26,7 +26,7 @@ # POSSIBILITY OF SUCH DAMAGE. # -name: Simulation 1.3 +name: Simulation 1.4 on: push: @@ -45,15 +45,15 @@ permissions: # added using https://github.com/step-security/secure-workflows jobs: - thread-1-3: - name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }} + thread-1-4: + name: thread-1-4-${{ matrix.compiler.c }}-${{ matrix.arch }} runs-on: ubuntu-20.04 env: CFLAGS: -${{ matrix.arch }} CXXFLAGS: -${{ matrix.arch }} LDFLAGS: -${{ matrix.arch }} COVERAGE: 1 - THREAD_VERSION: 1.3 + THREAD_VERSION: 1.4 VIRTUAL_TIME: 1 INTER_OP: 1 INTER_OP_BBR: 1 @@ -98,12 +98,12 @@ jobs: - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: - name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }}-pcaps + name: thread-1-4-${{ matrix.compiler.c }}-${{ matrix.arch }}-pcaps path: "*.pcap" - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() && env.CRASHED == '1' }} with: - name: core-packet-verification-thread-1-3 + name: core-packet-verification-thread-1-4 path: | ./ot-core-dump/* - name: Generate Coverage @@ -111,7 +111,7 @@ jobs: ./script/test generate_coverage "${{ matrix.compiler.gcov }}" - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: - name: cov-thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }} + name: cov-thread-1-4-${{ matrix.compiler.c }}-${{ matrix.arch }} path: tmp/coverage.info retention-days: 1 @@ -122,7 +122,7 @@ jobs: VIRTUAL_TIME: 1 COVERAGE: 1 PACKET_VERIFICATION: 1 - THREAD_VERSION: 1.3 + THREAD_VERSION: 1.4 MAC_FILTER: 1 INTER_OP: 1 INTER_OP_BBR: 0 @@ -188,13 +188,13 @@ jobs: path: tmp/coverage.info retention-days: 1 - packet-verification-1-1-on-1-3: + packet-verification-1-1-on-1-4: runs-on: ubuntu-20.04 env: REFERENCE_DEVICE: 1 VIRTUAL_TIME: 1 PACKET_VERIFICATION: 1 - THREAD_VERSION: 1.3 + THREAD_VERSION: 1.4 INTER_OP_BBR: 1 MULTIPLY: 3 steps: @@ -223,7 +223,7 @@ jobs: - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: - name: packet-verification-1.1-on-1.3-pcaps + name: packet-verification-1.1-on-1.4-pcaps path: | *.pcap *.json @@ -232,7 +232,7 @@ jobs: ./script/test generate_coverage gcc - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: - name: cov-packet-verification-1-1-on-1-3 + name: cov-packet-verification-1-1-on-1-4 path: tmp/coverage.info retention-days: 1 @@ -243,7 +243,7 @@ jobs: CXXFLAGS: -m32 LDFLAGS: -m32 COVERAGE: 1 - THREAD_VERSION: 1.3 + THREAD_VERSION: 1.4 VIRTUAL_TIME: 1 steps: - name: Harden Runner @@ -284,7 +284,7 @@ jobs: runs-on: ubuntu-20.04 env: COVERAGE: 1 - THREAD_VERSION: 1.3 + THREAD_VERSION: 1.4 VIRTUAL_TIME: 0 steps: - name: Harden Runner @@ -313,7 +313,7 @@ jobs: - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() && env.CRASHED == '1' }} with: - name: core-expect-1-3 + name: core-expect-1-4 path: | ./ot-core-dump/* - name: Generate Coverage @@ -325,13 +325,13 @@ jobs: path: tmp/coverage.info retention-days: 1 - thread-1-3-posix: + thread-1-4-posix: runs-on: ubuntu-20.04 env: COVERAGE: 1 PYTHONUNBUFFERED: 1 READLINE: readline - THREAD_VERSION: 1.3 + THREAD_VERSION: 1.4 OT_NODE_TYPE: rcp USE_MTD: 1 VIRTUAL_TIME: 1 @@ -372,12 +372,12 @@ jobs: - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() }} with: - name: thread-1-3-posix-pcaps + name: thread-1-4-posix-pcaps path: "*.pcap" - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 if: ${{ failure() && env.CRASHED == '1' }} with: - name: core-thread-1-3-posix + name: core-thread-1-4-posix path: | ./ot-core-dump/* - name: Generate Coverage @@ -385,17 +385,17 @@ jobs: ./script/test generate_coverage gcc - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: - name: cov-thread-1-3-posix + name: cov-thread-1-4-posix path: tmp/coverage.info retention-days: 1 upload-coverage: needs: - - thread-1-3 + - thread-1-4 - packet-verification-low-power - - packet-verification-1-1-on-1-3 + - packet-verification-1-1-on-1-4 - expects - - thread-1-3-posix + - thread-1-4-posix runs-on: ubuntu-20.04 steps: - name: Harden Runner diff --git a/script/check-arm-build b/script/check-arm-build index c44095829..4806d5afc 100755 --- a/script/check-arm-build +++ b/script/check-arm-build @@ -73,7 +73,7 @@ build_nrf52840() "-DOT_SNTP_CLIENT=ON" "-DOT_SRP_CLIENT=ON" "-DOT_SRP_SERVER=ON" - "-DOT_THREAD_VERSION=1.3" + "-DOT_THREAD_VERSION=1.4" "-DOT_TIME_SYNC=ON" "-DOT_UDP_FORWARD=ON" "-DOT_UPTIME=ON" diff --git a/script/check-gn-build b/script/check-gn-build index 68b17a7b7..9c44e2057 100755 --- a/script/check-gn-build +++ b/script/check-gn-build @@ -43,10 +43,10 @@ main() ninja -C gn-out test -f gn-out/obj/src/core/libopenthread-ftd.a - # Check GN build for OT1.3 + # Check GN build for OT1.4 rm gn-out -r || true mkdir gn-out - echo 'openthread_config_thread_version = "1.3"' >gn-out/args.gn + echo 'openthread_config_thread_version = "1.4"' >gn-out/args.gn gn gen --check gn-out gn args gn-out --list ninja -C gn-out diff --git a/script/check-simulation-build-cmake b/script/check-simulation-build-cmake index eaee3716b..a60e811d8 100755 --- a/script/check-simulation-build-cmake +++ b/script/check-simulation-build-cmake @@ -142,7 +142,7 @@ build_all_features() -DOT_SIMULATION_VIRTUAL_TIME=ON \ -DOT_OTNS=ON - # Thread 1.3 options + # Thread 1.4 options local options=( "-DOT_BACKBONE_ROUTER=ON" "-DOT_BORDER_ROUTING=ON" @@ -152,35 +152,35 @@ build_all_features() "-DOT_MLR=ON" "-DOT_OTNS=ON" "-DOT_SIMULATION_VIRTUAL_TIME=ON" - "-DOT_THREAD_VERSION=1.3" + "-DOT_THREAD_VERSION=1.4" ) - # Build Thread 1.3 with full features + # Build Thread 1.4 with full features reset_source CFLAGS="${cppflags[*]} ${CFLAGS}" CXXFLAGS="${cppflags[*]} ${CXXFLAGS}" \ "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_DUA=ON - # Build Thread 1.3 with external heap and msg pool using heap + # Build Thread 1.4 with external heap and msg pool using heap reset_source CFLAGS="${cppflags[*]} ${CFLAGS} -DOPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE=1" \ CXXFLAGS="${cppflags[*]} ${CXXFLAGS} -DOPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE=1" \ "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_DUA=ON - # Build Thread 1.3 with full features and no log + # Build Thread 1.4 with full features and no log reset_source CFLAGS="${cppflags[*]} ${CFLAGS}" CXXFLAGS="${cppflags[*]} ${CXXFLAGS}" \ "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_DUA=ON -DOT_LOG_OUTPUT=NONE - # Build Thread 1.3 with full features and full logs + # Build Thread 1.4 with full features and full logs reset_source CFLAGS="${cppflags[*]} ${CFLAGS}" CXXFLAGS="${cppflags[*]} ${CXXFLAGS}" \ "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_DUA=ON -DOT_FULL_LOGS=ON - # Build Thread 1.3 Backbone Router without DUA ND Proxying + # Build Thread 1.4 Backbone Router without DUA ND Proxying reset_source "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_BACKBONE_ROUTER_DUA_NDPROXYING=OFF - # Build Thread 1.3 Backbone Router without Multicast Routing + # Build Thread 1.4 Backbone Router without Multicast Routing reset_source "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_BACKBONE_ROUTER_MULTICAST_ROUTING=OFF @@ -190,11 +190,11 @@ build_all_features() -DOT_THREAD_VERSION=1.1 \ -DOT_VENDOR_EXTENSION=../../src/core/instance/extension_example.cpp - # Build Thread 1.3 with no additional features + # Build Thread 1.4 with no additional features reset_source - "$(dirname "$0")"/cmake-build simulation -DOT_THREAD_VERSION=1.3 + "$(dirname "$0")"/cmake-build simulation -DOT_THREAD_VERSION=1.4 - # Build Thread 1.3 with full features and OT_ASSERT=OFF + # Build Thread 1.4 with full features and OT_ASSERT=OFF reset_source "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_DUA=ON -DOT_ASSERT=OFF diff --git a/script/make-pretty b/script/make-pretty index 67885d8b0..fa8e8d280 100755 --- a/script/make-pretty +++ b/script/make-pretty @@ -137,7 +137,7 @@ OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_SRP_ADV_PROXY=ON' '-DOT_SRP_CLIENT=ON' '-DOT_SRP_SERVER=ON' - '-DOT_THREAD_VERSION=1.3' + '-DOT_THREAD_VERSION=1.4' '-DOT_TREL=ON' '-DOT_COVERAGE=ON' '-DOT_LOG_LEVEL_DYNAMIC=ON' @@ -174,7 +174,7 @@ do_clang_tidy_fix() (mkdir -p ./build/cmake-tidy \ && cd ./build/cmake-tidy \ - && THREAD_VERSION=1.3 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ + && THREAD_VERSION=1.4 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ && ../../script/clang-tidy -j"$OT_BUILD_JOBS" "${OT_CLANG_TIDY_FIX_DIRS[@]}" -fix) } @@ -186,7 +186,7 @@ do_clang_tidy_check() (mkdir -p ./build/cmake-tidy \ && cd ./build/cmake-tidy \ - && THREAD_VERSION=1.3 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ + && THREAD_VERSION=1.4 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ && ../../script/clang-tidy -j"$OT_BUILD_JOBS" "${OT_CLANG_TIDY_FIX_DIRS[@]}") } diff --git a/script/test b/script/test index 0149b6f62..be04e237e 100755 --- a/script/test +++ b/script/test @@ -56,7 +56,7 @@ readonly OT_NODE_TYPE OT_NATIVE_IP="${OT_NATIVE_IP:-0}" readonly OT_NATIVE_IP -THREAD_VERSION="${THREAD_VERSION:-1.3}" +THREAD_VERSION="${THREAD_VERSION:-1.4}" readonly THREAD_VERSION INTER_OP="${INTER_OP:-0}" @@ -263,7 +263,7 @@ do_unit() do_unit_version "${THREAD_VERSION}" if [[ ${THREAD_VERSION} != "1.1" && ${INTER_OP_BBR} == 1 ]]; then - do_unit_version "1.3-bbr" + do_unit_version "1.4-bbr" fi } @@ -282,7 +282,7 @@ do_cert() esac if [[ ${THREAD_VERSION} != "1.1" ]]; then - export top_builddir_1_3_bbr="${OT_BUILDDIR}/openthread-simulation-1.3-bbr" + export top_builddir_1_4_bbr="${OT_BUILDDIR}/openthread-simulation-1.4-bbr" if [[ ${INTER_OP} == "1" ]]; then export top_builddir_1_1="${OT_BUILDDIR}/openthread-simulation-1.1" fi @@ -301,7 +301,7 @@ do_cert_suite() export top_srcdir="${OT_SRCDIR}" if [[ ${THREAD_VERSION} != "1.1" ]]; then - export top_builddir_1_3_bbr="${OT_BUILDDIR}/openthread-simulation-1.3-bbr" + export top_builddir_1_4_bbr="${OT_BUILDDIR}/openthread-simulation-1.4-bbr" if [[ ${INTER_OP} == "1" ]]; then export top_builddir_1_1="${OT_BUILDDIR}/openthread-simulation-1.1" fi @@ -505,9 +505,9 @@ ENVIRONMENTS: VERBOSE 1 to build or test verbosely. The default is 0. VIRTUAL_TIME 1 for virtual time, otherwise real time. The default value is 0 when running expect tests, otherwise default value is 1. - THREAD_VERSION 1.1 for Thread 1.1 stack, 1.3 for Thread 1.3 stack. The default is 1.3. - INTER_OP 1 to build 1.1 together. Only works when THREAD_VERSION is 1.3. The default is 0. - INTER_OP_BBR 1 to build bbr version together. Only works when THREAD_VERSION is 1.3. The default is 1. + THREAD_VERSION 1.1 for Thread 1.1 stack, 1.4 for Thread 1.4 stack. The default is 1.4. + INTER_OP 1 to build 1.1 together. Only works when THREAD_VERSION is 1.4. The default is 0. + INTER_OP_BBR 1 to build bbr version together. Only works when THREAD_VERSION is 1.4. The default is 1. COMMANDS: clean Clean built files to prepare for new build. @@ -539,7 +539,7 @@ EXAMPLES: THREAD_VERSION=1.1 VIRTUAL_TIME=0 $0 clean build cert tests/scripts/thread-cert/Cert_5_1_01_RouterAttach.py THREAD_VERSION=1.1 VIRTUAL_TIME=0 $0 cert tests/scripts/thread-cert/Cert_5_1_02_ChildAddressTimeout.py - # Test Thread 1.3 with real time, use 'INTER_OP=1' when the case needs both versions. + # Test Thread 1.4 with real time, use 'INTER_OP=1' when the case needs both versions. VIRTUAL_TIME=0 $0 clean build cert tests/scripts/thread-cert/v1_2_test_enhanced_keep_alive.py INTER_OP=1 VIRTUAL_TIME=0 $0 clean build cert tests/scripts/thread-cert/v1_2_router_5_1_1.py INTER_OP=1 VIRTUAL_TIME=0 $0 clean build cert_suite tests/scripts/thread-cert/v1_2_* @@ -646,7 +646,7 @@ envsetup() if [[ ${THREAD_VERSION} != "1.1" ]]; then export RADIO_DEVICE_1_1="${OT_BUILDDIR}/openthread-simulation-1.1/examples/apps/ncp/ot-rcp" export OT_CLI_PATH_1_1="${OT_BUILDDIR}/openthread-posix-1.1/src/posix/ot-cli" - export OT_CLI_PATH_BBR="${OT_BUILDDIR}/openthread-posix-1.3-bbr/src/posix/ot-cli" + export OT_CLI_PATH_BBR="${OT_BUILDDIR}/openthread-posix-1.4-bbr/src/posix/ot-cli" fi fi @@ -690,7 +690,7 @@ main() fi [[ ${VIRTUAL_TIME} == 1 ]] && echo "Using virtual time" || echo "Using real time" - [[ ${THREAD_VERSION} != "1.1" ]] && echo "Using Thread 1.3 stack" || echo "Using Thread 1.1 stack" + [[ ${THREAD_VERSION} != "1.1" ]] && echo "Using Thread 1.4 stack" || echo "Using Thread 1.1 stack" while [[ $# != 0 ]]; do case "$1" in diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index be6e144b8..10994ca3c 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -3661,7 +3661,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otDeviceProperties props; bool value; - uint8_t index; + size_t index; for (index = 0; index < OT_ARRAY_LENGTH(kPowerSupplyStrings); index++) { diff --git a/tests/scripts/thread-cert/border_router/nat64/test_upstream_dns.py b/tests/scripts/thread-cert/border_router/nat64/test_upstream_dns.py index 617ea0f19..84707d280 100755 --- a/tests/scripts/thread-cert/border_router/nat64/test_upstream_dns.py +++ b/tests/scripts/thread-cert/border_router/nat64/test_upstream_dns.py @@ -72,12 +72,12 @@ class UpstreamDns(thread_cert.TestCase): 'name': 'BR', 'allowlist': [ROUTER], 'is_otbr': True, - 'version': '1.3', + 'version': '1.4', }, ROUTER: { 'name': 'Router', 'allowlist': [BR], - 'version': '1.3', + 'version': '1.4', }, HOST: { 'name': 'Host', diff --git a/tests/scripts/thread-cert/border_router/test_firewall.py b/tests/scripts/thread-cert/border_router/test_firewall.py index 54d0a1b2b..d037d9084 100755 --- a/tests/scripts/thread-cert/border_router/test_firewall.py +++ b/tests/scripts/thread-cert/border_router/test_firewall.py @@ -63,17 +63,17 @@ class Firewall(thread_cert.TestCase): 'name': 'BR_1', 'allowlist': [ROUTER1, ROUTER2], 'is_otbr': True, - 'version': '1.3', + 'version': '1.4', }, ROUTER1: { 'name': 'Router_1', 'allowlist': [BR1], - 'version': '1.3', + 'version': '1.4', }, ROUTER2: { 'name': 'Router_2', 'allowlist': [BR1], - 'version': '1.3', + 'version': '1.4', }, HOST: { 'name': 'Host', diff --git a/tests/scripts/thread-cert/border_router/test_multi_ail.py b/tests/scripts/thread-cert/border_router/test_multi_ail.py index e2d9174dd..f037dde67 100755 --- a/tests/scripts/thread-cert/border_router/test_multi_ail.py +++ b/tests/scripts/thread-cert/border_router/test_multi_ail.py @@ -58,21 +58,21 @@ class ThreeBRs_TwoInfra(thread_cert.TestCase): 'backbone_network_id': 0, 'allowlist': [BR2], 'is_otbr': True, - 'version': '1.3', + 'version': '1.4', }, BR2: { 'name': 'BR_2', 'backbone_network_id': 1, 'allowlist': [BR1, BR3], 'is_otbr': True, - 'version': '1.3', + 'version': '1.4', }, BR3: { 'name': 'BR_3', 'backbone_network_id': 1, 'allowlist': [BR2], 'is_otbr': True, - 'version': '1.3', + 'version': '1.4', } } diff --git a/tests/scripts/thread-cert/config.py b/tests/scripts/thread-cert/config.py index 3532690e6..1a8606e58 100644 --- a/tests/scripts/thread-cert/config.py +++ b/tests/scripts/thread-cert/config.py @@ -160,6 +160,7 @@ class ADDRESS_TYPE(Enum): THREAD_VERSION_1_1 = 2 THREAD_VERSION_1_2 = 3 THREAD_VERSION_1_3 = 4 +THREAD_VERSION_1_4 = 5 PACKET_VERIFICATION_NONE = 0 PACKET_VERIFICATION_DEFAULT = 1 diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index da1d96b1f..6da0f635b 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -625,8 +625,8 @@ def __init_sim(self, nodeid, mode): if self.version != '1.1' and self.is_bbr: if 'OT_CLI_PATH_BBR' in os.environ: cmd = os.environ['OT_CLI_PATH_BBR'] - elif 'top_builddir_1_3_bbr' in os.environ: - srcdir = os.environ['top_builddir_1_3_bbr'] + elif 'top_builddir_1_4_bbr' in os.environ: + srcdir = os.environ['top_builddir_1_4_bbr'] cmd = '%s/examples/apps/cli/ot-cli-%s' % (srcdir, mode) # Load Thread device of the testing environment version (may be 1.1 or 1.2) @@ -692,13 +692,13 @@ def __init_ncp_sim(self, nodeid, mode): # Load Thread 1.2 BBR device when testing Thread 1.2 scenarios # which requires device with Backbone functionality. if self.version != '1.1' and self.is_bbr: - if 'OT_NCP_PATH_1_3_BBR' in os.environ: + if 'OT_NCP_PATH_1_4_BBR' in os.environ: cmd = 'spinel-cli.py -p "%s%s" -n' % ( - os.environ['OT_NCP_PATH_1_3_BBR'], + os.environ['OT_NCP_PATH_1_4_BBR'], args, ) - elif 'top_builddir_1_3_bbr' in os.environ: - srcdir = os.environ['top_builddir_1_3_bbr'] + elif 'top_builddir_1_4_bbr' in os.environ: + srcdir = os.environ['top_builddir_1_4_bbr'] cmd = '%s/examples/apps/ncp/ot-ncp-%s' % (srcdir, mode) cmd = 'spinel-cli.py -p "%s%s" -n' % ( cmd, diff --git a/tests/scripts/thread-cert/pktverify/packet_verifier.py b/tests/scripts/thread-cert/pktverify/packet_verifier.py index 6873cf90c..7b5a1c93e 100644 --- a/tests/scripts/thread-cert/pktverify/packet_verifier.py +++ b/tests/scripts/thread-cert/pktverify/packet_verifier.py @@ -176,7 +176,7 @@ def _add_initial_vars(self): for i, topo in self.test_info.topology.items(): name = self.test_info.get_node_name(i) if topo['version']: - self._vars[name + '_VERSION'] = {'1.1': 2, '1.2': 3, '1.3': 4}[topo['version']] + self._vars[name + '_VERSION'] = {'1.1': 2, '1.2': 3, '1.3': 4, '1.4': 5}[topo['version']] def verify_attached(self, child: str, parent: str = None, child_type: str = 'FTD', pkts=None) -> VerifyResult: """ diff --git a/tests/scripts/thread-cert/thread_cert.py b/tests/scripts/thread-cert/thread_cert.py index e09b769fc..27f8d826d 100644 --- a/tests/scripts/thread-cert/thread_cert.py +++ b/tests/scripts/thread-cert/thread_cert.py @@ -511,7 +511,7 @@ def _parse_params(self, params: Optional[dict]) -> dict: if params.get('is_bbr') or params.get('is_otbr'): # BBRs must not use thread version 1.1 - version = params.get('version', '1.3') + version = params.get('version', '1.4') assert version != '1.1', params params['version'] = version params.setdefault('bbr_registration_jitter', config.DEFAULT_BBR_REGISTRATION_JITTER) @@ -520,9 +520,9 @@ def _parse_params(self, params: Optional[dict]) -> dict: assert params.get('version', '') == '', params params['version'] = '' - # use 1.3 node for 1.2 tests + # use 1.4 node for 1.2 tests if params.get('version') == '1.2': - params['version'] = '1.3' + params['version'] = '1.4' is_ftd = (not params.get('is_mtd') and not params.get('is_host')) From 2aeb8b833ba760ec29d5f340dd1ce7bcb61c5d56 Mon Sep 17 00:00:00 2001 From: Tom Rebbert <109624508+trebbert-lutron@users.noreply.github.com> Date: Wed, 11 Sep 2024 08:27:37 -0600 Subject: [PATCH 160/160] [mle] prioritize equivalent pending dataset update over MLE announce (#10631) Avoid processing announce messages with equivalent data that is stored in a pending dataset update. --- src/core/meshcop/dataset_manager.cpp | 29 ++++++++++++++++++++++++++++ src/core/meshcop/dataset_manager.hpp | 22 +++++++++++++++++++++ src/core/thread/mle.cpp | 19 ++++++++++++++++++ src/core/thread/mle.hpp | 1 + 4 files changed, 71 insertions(+) diff --git a/src/core/meshcop/dataset_manager.cpp b/src/core/meshcop/dataset_manager.cpp index a90ae2ef7..af83a25bf 100644 --- a/src/core/meshcop/dataset_manager.cpp +++ b/src/core/meshcop/dataset_manager.cpp @@ -885,6 +885,35 @@ PendingDatasetManager::PendingDatasetManager(Instance &aInstance) { } +Error PendingDatasetManager::ReadActiveTimestamp(Timestamp &aTimestamp) const +{ + Error error = kErrorNotFound; + Dataset dataset; + + SuccessOrExit(Read(dataset)); + + SuccessOrExit(dataset.Read(aTimestamp)); + error = kErrorNone; + +exit: + return error; +} + +Error PendingDatasetManager::ReadRemainingDelay(uint32_t &aRemainingDelay) const +{ + Error error = kErrorNone; + TimeMilli now = TimerMilli::GetNow(); + + aRemainingDelay = 0; + + VerifyOrExit(mDelayTimer.IsRunning(), error = kErrorNotFound); + VerifyOrExit(mDelayTimer.GetFireTime() > now); + aRemainingDelay = mDelayTimer.GetFireTime() - now; + +exit: + return error; +} + void PendingDatasetManager::StartDelayTimer(void) { Dataset dataset; diff --git a/src/core/meshcop/dataset_manager.hpp b/src/core/meshcop/dataset_manager.hpp index 9c76df517..8092f03fe 100644 --- a/src/core/meshcop/dataset_manager.hpp +++ b/src/core/meshcop/dataset_manager.hpp @@ -441,6 +441,28 @@ class PendingDatasetManager : public DatasetManager, private NonCopyable */ explicit PendingDatasetManager(Instance &aInstance); + /** + * Reads the Active Timestamp in the Pending Operational Dataset. + * + * @param[out] aTimestamp A reference to return the read timestamp. + * + * @retval kErrorNone The active timestamp was successfully fetched. + * @retval kErrorNotFound The pending dataset is not currently valid. + * + */ + Error ReadActiveTimestamp(Timestamp &aTimestamp) const; + + /** + * Reads the remaining delay time in ms. + * + * @param[out] aRemainingDelay A reference to return the remaining delay time. + * + * @retval kErrorNone The remaining delay time was successfully fetched. + * @retval kErrorNotFound The pending dataset is not currently valid. + * + */ + Error ReadRemainingDelay(uint32_t &aRemainingDelay) const; + #if OPENTHREAD_FTD /** * Starts the Leader functions for maintaining the Active Operational Dataset. diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index ff90ba098..61313a314 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -3663,6 +3663,7 @@ void Mle::HandleAnnounce(RxInfo &aRxInfo) Error error = kErrorNone; ChannelTlvValue channelTlvValue; MeshCoP::Timestamp timestamp; + MeshCoP::Timestamp pendingActiveTimestamp; uint8_t channel; uint16_t panId; bool isFromOrphan; @@ -3707,6 +3708,24 @@ void Mle::HandleAnnounce(RxInfo &aRxInfo) VerifyOrExit(!channelAndPanIdMatch); } + if (Get().ReadActiveTimestamp(pendingActiveTimestamp) == kErrorNone) + { + // Ignore the Announce and take no action, if a pending + // dataset exists with an equal or more recent timestamp, + // and it will be applied soon. + + if (pendingActiveTimestamp >= timestamp) + { + uint32_t remainingDelay; + + if ((Get().ReadRemainingDelay(remainingDelay) == kErrorNone) && + (remainingDelay < kAnnounceBackoffForPendingDataset)) + { + ExitNow(); + } + } + } + if (mAttachState == kAttachStateProcessAnnounce) { VerifyOrExit(mAlternateTimestamp < timestamp.GetSeconds()); diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 8cbb4c03a..8c3a6b870 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -811,6 +811,7 @@ class Mle : public InstanceLocator, private NonCopyable static constexpr uint32_t kMulticastRetxDelay = 5000; // Base delay for MLE multicast retx static constexpr uint32_t kMulticastRetxDelayMin = kMulticastRetxDelay * 9 / 10; // 0.9 * base delay static constexpr uint32_t kMulticastRetxDelayMax = kMulticastRetxDelay * 11 / 10; // 1.1 * base delay + static constexpr uint32_t kAnnounceBackoffForPendingDataset = 60000; // Max delay left to block Announce processing. static constexpr uint8_t kMaxTxCount = 3; // Max tx count for MLE message static constexpr uint8_t kMaxCriticalTxCount = 6; // Max tx count for critical MLE message