Matter SDK Coverage Report
Current view: top level - lib/address_resolve - AddressResolve_DefaultImpl.cpp (source / functions) Coverage Total Hit
Test: SHA:f84fe08d06f240e801b5d923f8a938a9938ca110 Lines: 32.4 % 188 61
Test Date: 2025-02-22 08:08:07 Functions: 43.8 % 16 7

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2022 Project CHIP Authors
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : 
      18              : #include <lib/address_resolve/AddressResolve_DefaultImpl.h>
      19              : 
      20              : #include <lib/address_resolve/TracingStructs.h>
      21              : #include <tracing/macros.h>
      22              : #include <transport/raw/PeerAddress.h>
      23              : 
      24              : namespace chip {
      25              : namespace AddressResolve {
      26              : namespace Impl {
      27              : namespace {
      28              : 
      29              : static constexpr System::Clock::Timeout kInvalidTimeout{ System::Clock::Timeout::max() };
      30              : 
      31              : } // namespace
      32              : 
      33            5 : void NodeLookupHandle::ResetForLookup(System::Clock::Timestamp now, const NodeLookupRequest & request)
      34              : {
      35            5 :     mRequestStartTime = now;
      36            5 :     mRequest          = request;
      37            5 :     mResults          = NodeLookupResults();
      38            5 : }
      39              : 
      40           29 : void NodeLookupHandle::LookupResult(const ResolveResult & result)
      41              : {
      42              :     MATTER_LOG_NODE_DISCOVERED(Tracing::DiscoveryInfoType::kIntermediateResult, &GetRequest().GetPeerId(), &result);
      43              : 
      44           29 :     auto score = Dnssd::IPAddressSorter::ScoreIpAddress(result.address.GetIPAddress(), result.address.GetInterface());
      45           29 :     [[maybe_unused]] bool success = mResults.UpdateResults(result, score);
      46              : 
      47              : #if CHIP_PROGRESS_LOGGING
      48              :     char addr_string[Transport::PeerAddress::kMaxToStringSize];
      49           29 :     result.address.ToString(addr_string);
      50              : 
      51           29 :     const PeerId peerId = GetRequest().GetPeerId();
      52           29 :     if (success)
      53              :     {
      54           20 :         ChipLogProgress(Discovery, "%s: new best score: %u (for " ChipLogFormatPeerId ")", addr_string, to_underlying(score),
      55              :                         ChipLogValuePeerId(peerId));
      56              :     }
      57              :     else
      58              :     {
      59            9 :         ChipLogProgress(Discovery, "%s: score has not improved: %u (for " ChipLogFormatPeerId ")", addr_string,
      60              :                         to_underlying(score), ChipLogValuePeerId(peerId));
      61              :     }
      62              : #endif
      63           29 : }
      64              : 
      65            0 : System::Clock::Timeout NodeLookupHandle::NextEventTimeout(System::Clock::Timestamp now)
      66              : {
      67            0 :     const System::Clock::Timestamp elapsed = now - mRequestStartTime;
      68              : 
      69            0 :     if (elapsed < mRequest.GetMinLookupTime())
      70              :     {
      71            0 :         return mRequest.GetMinLookupTime() - elapsed;
      72              :     }
      73              : 
      74            0 :     if (HasLookupResult())
      75              :     {
      76              :         // We can get here if we got our result before our min lookup time had
      77              :         // elapsed, but time has passed between then and this attempt to re-arm
      78              :         // the timer, such that now we are past our min lookup time.  For
      79              :         // example, this can happen because the timer is a bit delayed in firing
      80              :         // but is now being re-scheduled due to a cancellation of a lookup or
      81              :         // start of a new lookup.  Or it could happen because
      82              :         // OnOperationalNodeResolved got called close to our min lookup time,
      83              :         // and we crossed that line while going through mActiveLookups and
      84              :         // before we got to calling ReArmTimer.
      85              :         //
      86              :         // In this case, we should just fire the timer ASAP, since our min
      87              :         // lookup time has elapsed and we have results.
      88            0 :         return System::Clock::Timeout::zero();
      89              :     }
      90              : 
      91            0 :     if (elapsed < mRequest.GetMaxLookupTime())
      92              :     {
      93            0 :         return mRequest.GetMaxLookupTime() - elapsed;
      94              :     }
      95              : 
      96            0 :     ChipLogError(Discovery, "Unexpected timeout: lookup should have been cleaned already.");
      97            0 :     return System::Clock::Timeout::zero();
      98              : }
      99              : 
     100            0 : NodeLookupAction NodeLookupHandle::NextAction(System::Clock::Timestamp now)
     101              : {
     102            0 :     const System::Clock::Timestamp elapsed = now - mRequestStartTime;
     103              : 
     104            0 :     ChipLogProgress(Discovery, "Checking node lookup status for " ChipLogFormatPeerId " after %lu ms",
     105              :                     ChipLogValuePeerId(mRequest.GetPeerId()), static_cast<unsigned long>(elapsed.count()));
     106              : 
     107              :     // We are still within the minimal search time. Wait for more results.
     108            0 :     if (elapsed < mRequest.GetMinLookupTime())
     109              :     {
     110            0 :         ChipLogProgress(Discovery, "Keeping DNSSD lookup active");
     111            0 :         return NodeLookupAction::KeepSearching();
     112              :     }
     113              : 
     114              :     // Minimal time to search reached. If any IP available, ready to return it.
     115            0 :     if (HasLookupResult())
     116              :     {
     117            0 :         auto result = TakeLookupResult();
     118            0 :         return NodeLookupAction::Success(result);
     119              :     }
     120              : 
     121              :     // Give up if the maximum search time has been reached
     122            0 :     if (elapsed >= mRequest.GetMaxLookupTime())
     123              :     {
     124            0 :         return NodeLookupAction::Error(CHIP_ERROR_TIMEOUT);
     125              :     }
     126              : 
     127            0 :     return NodeLookupAction::KeepSearching();
     128              : }
     129              : 
     130           48 : bool NodeLookupResults::UpdateResults(const ResolveResult & result, const Dnssd::IPAddressSorter::IpScore newScore)
     131              : {
     132           48 :     Transport::PeerAddress addressWithAdjustedInterface = result.address;
     133           48 :     if (!addressWithAdjustedInterface.GetIPAddress().IsIPv6LinkLocal())
     134              :     {
     135              :         // Only use the DNS-SD resolution's InterfaceID for addresses that are IPv6 LLA.
     136              :         // For all other addresses, we should rely on the device's routing table to route messages sent.
     137              :         // Forcing messages down an InterfaceId might fail. For example, in bridged networks like Thread,
     138              :         // mDNS advertisements are not usually received on the same interface the peer is reachable on.
     139           42 :         addressWithAdjustedInterface.SetInterface(Inet::InterfaceId::Null());
     140           42 :         ChipLogDetail(Discovery, "Lookup clearing interface for non LL address");
     141              :     }
     142              : 
     143           48 :     uint8_t insertAtIndex = 0;
     144          125 :     for (; insertAtIndex < kNodeLookupResultsLen; insertAtIndex++)
     145              :     {
     146          119 :         if (insertAtIndex >= count)
     147              :         {
     148              :             // This is a new entry.
     149           23 :             break;
     150              :         }
     151              : 
     152           96 :         auto & oldAddress = results[insertAtIndex].address;
     153              : 
     154           96 :         if (oldAddress == addressWithAdjustedInterface)
     155              :         {
     156              :             // this address is already in our list.
     157           12 :             return false;
     158              :         }
     159              : 
     160           84 :         auto oldScore = Dnssd::IPAddressSorter::ScoreIpAddress(oldAddress.GetIPAddress(), oldAddress.GetInterface());
     161           84 :         if (newScore > oldScore)
     162              :         {
     163              :             // This is a score update, it will replace a previous entry.
     164            7 :             break;
     165              :         }
     166              :     }
     167              : 
     168           36 :     if (insertAtIndex == kNodeLookupResultsLen)
     169              :     {
     170            6 :         return false;
     171              :     }
     172              : 
     173              :     // we are guaranteed no duplicates here:
     174              :     // - insertAtIndex MUST be with some score that is `< newScore`, so all
     175              :     //   addresses with a `newScore` were duplicate-checked
     176              : 
     177              :     // Move the following valid entries one level down.
     178           52 :     for (auto i = count; i > insertAtIndex; i--)
     179              :     {
     180           22 :         if (i >= kNodeLookupResultsLen)
     181              :         {
     182            4 :             continue;
     183              :         }
     184              : 
     185           18 :         results[i] = results[i - 1];
     186              :     }
     187              : 
     188              :     // If the number of valid entries is less than the size of the array there is an additional entry.
     189           30 :     if (count < kNodeLookupResultsLen)
     190              :     {
     191           26 :         count++;
     192              :     }
     193              : 
     194           30 :     auto & updatedResult  = results[insertAtIndex];
     195           30 :     updatedResult         = result;
     196           30 :     updatedResult.address = addressWithAdjustedInterface;
     197              : 
     198           30 :     return true;
     199              : }
     200              : 
     201            0 : CHIP_ERROR Resolver::LookupNode(const NodeLookupRequest & request, Impl::NodeLookupHandle & handle)
     202              : {
     203              :     MATTER_LOG_NODE_LOOKUP(&request);
     204              : 
     205            0 :     VerifyOrReturnError(mSystemLayer != nullptr, CHIP_ERROR_INCORRECT_STATE);
     206              : 
     207            0 :     handle.ResetForLookup(mTimeSource.GetMonotonicTimestamp(), request);
     208            0 :     auto & peerId = request.GetPeerId();
     209            0 :     ReturnErrorOnFailure(Dnssd::Resolver::Instance().ResolveNodeId(peerId));
     210            0 :     mActiveLookups.PushBack(&handle);
     211            0 :     ReArmTimer();
     212            0 :     ChipLogProgress(Discovery, "Lookup started for " ChipLogFormatPeerId, ChipLogValuePeerId(peerId));
     213            0 :     return CHIP_NO_ERROR;
     214              : }
     215              : 
     216            0 : CHIP_ERROR Resolver::TryNextResult(Impl::NodeLookupHandle & handle)
     217              : {
     218            0 :     VerifyOrReturnError(!mActiveLookups.Contains(&handle), CHIP_ERROR_INCORRECT_STATE);
     219            0 :     VerifyOrReturnError(handle.HasLookupResult(), CHIP_ERROR_NOT_FOUND);
     220              : 
     221            0 :     auto listener = handle.GetListener();
     222            0 :     auto peerId   = handle.GetRequest().GetPeerId();
     223            0 :     auto result   = handle.TakeLookupResult();
     224              : 
     225              :     MATTER_LOG_NODE_DISCOVERED(Tracing::DiscoveryInfoType::kRetryDifferent, &peerId, &result);
     226              : 
     227            0 :     listener->OnNodeAddressResolved(peerId, result);
     228            0 :     return CHIP_NO_ERROR;
     229              : }
     230              : 
     231            0 : CHIP_ERROR Resolver::CancelLookup(Impl::NodeLookupHandle & handle, FailureCallback cancel_method)
     232              : {
     233            0 :     VerifyOrReturnError(handle.IsActive(), CHIP_ERROR_INVALID_ARGUMENT);
     234            0 :     mActiveLookups.Remove(&handle);
     235            0 :     Dnssd::Resolver::Instance().NodeIdResolutionNoLongerNeeded(handle.GetRequest().GetPeerId());
     236              : 
     237              :     // Adjust any timing updates.
     238            0 :     ReArmTimer();
     239              : 
     240              :     MATTER_LOG_NODE_DISCOVERY_FAILED(&handle.GetRequest().GetPeerId(), CHIP_ERROR_CANCELLED);
     241              : 
     242            0 :     if (cancel_method == FailureCallback::Call)
     243              :     {
     244            0 :         handle.GetListener()->OnNodeAddressResolutionFailed(handle.GetRequest().GetPeerId(), CHIP_ERROR_CANCELLED);
     245              :     }
     246              : 
     247              :     // TODO: There should be some form of cancel into Dnssd::Resolver::Instance()
     248              :     //       to stop any resolution mechanism if applicable.
     249              :     //
     250              :     // Current code just removes the internal list and any callbacks of resolution will
     251              :     // be ignored. This works from the perspective of the caller of this method,
     252              :     // but may be wasteful by letting dnssd still work in the background.
     253              : 
     254            0 :     return CHIP_NO_ERROR;
     255              : }
     256              : 
     257            1 : CHIP_ERROR Resolver::Init(System::Layer * systemLayer)
     258              : {
     259            1 :     mSystemLayer = systemLayer;
     260            1 :     Dnssd::Resolver::Instance().SetOperationalDelegate(this);
     261            1 :     return CHIP_NO_ERROR;
     262              : }
     263              : 
     264            1 : void Resolver::Shutdown()
     265              : {
     266              :     // mSystemLayer is set in ::Init, so if it's null that means the resolver
     267              :     // has not been initialized or has already been shut down.
     268            1 :     VerifyOrReturn(mSystemLayer != nullptr);
     269              : 
     270            1 :     while (mActiveLookups.begin() != mActiveLookups.end())
     271              :     {
     272            0 :         auto current = mActiveLookups.begin();
     273              : 
     274            0 :         const PeerId peerId     = current->GetRequest().GetPeerId();
     275            0 :         NodeListener * listener = current->GetListener();
     276              : 
     277            0 :         mActiveLookups.Erase(current);
     278              : 
     279              :         MATTER_LOG_NODE_DISCOVERY_FAILED(&peerId, CHIP_ERROR_SHUT_DOWN);
     280              : 
     281            0 :         Dnssd::Resolver::Instance().NodeIdResolutionNoLongerNeeded(peerId);
     282              :         // Failure callback only called after iterator was cleared:
     283              :         // This allows failure handlers to deallocate structures that may
     284              :         // contain the active lookup data as a member (intrusive lists members)
     285            0 :         listener->OnNodeAddressResolutionFailed(peerId, CHIP_ERROR_SHUT_DOWN);
     286              :     }
     287              : 
     288              :     // Re-arm of timer is expected to cancel any active timer as the
     289              :     // internal list of active lookups is empty at this point.
     290            1 :     ReArmTimer();
     291              : 
     292            1 :     mSystemLayer = nullptr;
     293            1 :     Dnssd::Resolver::Instance().SetOperationalDelegate(nullptr);
     294              : }
     295              : 
     296            0 : void Resolver::OnOperationalNodeResolved(const Dnssd::ResolvedNodeData & nodeData)
     297              : {
     298            0 :     auto it = mActiveLookups.begin();
     299            0 :     while (it != mActiveLookups.end())
     300              :     {
     301            0 :         auto current = it;
     302            0 :         it++;
     303            0 :         if (current->GetRequest().GetPeerId() != nodeData.operationalData.peerId)
     304              :         {
     305            0 :             continue;
     306              :         }
     307              : 
     308            0 :         ResolveResult result;
     309              : 
     310            0 :         result.address.SetPort(nodeData.resolutionData.port);
     311            0 :         result.address.SetInterface(nodeData.resolutionData.interfaceId);
     312            0 :         result.mrpRemoteConfig   = nodeData.resolutionData.GetRemoteMRPConfig();
     313            0 :         result.supportsTcpClient = nodeData.resolutionData.supportsTcpClient;
     314            0 :         result.supportsTcpServer = nodeData.resolutionData.supportsTcpServer;
     315              : 
     316            0 :         if (nodeData.resolutionData.isICDOperatingAsLIT.has_value())
     317              :         {
     318            0 :             result.isICDOperatingAsLIT = *(nodeData.resolutionData.isICDOperatingAsLIT);
     319              :         }
     320              : 
     321            0 :         for (size_t i = 0; i < nodeData.resolutionData.numIPs; i++)
     322              :         {
     323              : #if !INET_CONFIG_ENABLE_IPV4
     324              :             if (!nodeData.resolutionData.ipAddress[i].IsIPv6())
     325              :             {
     326              :                 ChipLogError(Discovery, "Skipping IPv4 address during operational resolve.");
     327              :                 continue;
     328              :             }
     329              : #endif
     330            0 :             result.address.SetIPAddress(nodeData.resolutionData.ipAddress[i]);
     331            0 :             current->LookupResult(result);
     332              :         }
     333              : 
     334            0 :         HandleAction(current);
     335              :     }
     336              : 
     337            0 :     ReArmTimer();
     338            0 : }
     339              : 
     340            0 : void Resolver::HandleAction(IntrusiveList<NodeLookupHandle>::Iterator & current)
     341              : {
     342            0 :     const NodeLookupAction action = current->NextAction(mTimeSource.GetMonotonicTimestamp());
     343              : 
     344            0 :     if (action.Type() == NodeLookupResult::kKeepSearching)
     345              :     {
     346              :         // No change in iterator
     347            0 :         return;
     348              :     }
     349              : 
     350              :     // final result, handle either success or failure
     351            0 :     const PeerId peerId     = current->GetRequest().GetPeerId();
     352            0 :     NodeListener * listener = current->GetListener();
     353            0 :     mActiveLookups.Erase(current);
     354              : 
     355            0 :     Dnssd::Resolver::Instance().NodeIdResolutionNoLongerNeeded(peerId);
     356              : 
     357              :     // ensure action is taken AFTER the current current lookup is marked complete
     358              :     // This allows failure handlers to deallocate structures that may
     359              :     // contain the active lookup data as a member (intrusive lists members)
     360            0 :     switch (action.Type())
     361              :     {
     362            0 :     case NodeLookupResult::kLookupError:
     363              :         MATTER_LOG_NODE_DISCOVERY_FAILED(&peerId, action.ErrorResult());
     364            0 :         listener->OnNodeAddressResolutionFailed(peerId, action.ErrorResult());
     365            0 :         break;
     366            0 :     case NodeLookupResult::kLookupSuccess:
     367              :         MATTER_LOG_NODE_DISCOVERED(Tracing::DiscoveryInfoType::kResolutionDone, &peerId, &action.ResolveResult());
     368            0 :         listener->OnNodeAddressResolved(peerId, action.ResolveResult());
     369            0 :         break;
     370            0 :     default:
     371            0 :         ChipLogError(Discovery, "Unexpected lookup state (not success or fail).");
     372            0 :         break;
     373              :     }
     374              : }
     375              : 
     376            0 : void Resolver::HandleTimer()
     377              : {
     378            0 :     auto it = mActiveLookups.begin();
     379            0 :     while (it != mActiveLookups.end())
     380              :     {
     381            0 :         auto current = it;
     382            0 :         it++;
     383              : 
     384            0 :         HandleAction(current);
     385              :     }
     386              : 
     387            0 :     ReArmTimer();
     388            0 : }
     389              : 
     390            0 : void Resolver::OnOperationalNodeResolutionFailed(const PeerId & peerId, CHIP_ERROR error)
     391              : {
     392            0 :     auto it = mActiveLookups.begin();
     393            0 :     while (it != mActiveLookups.end())
     394              :     {
     395            0 :         auto current = it;
     396            0 :         it++;
     397            0 :         if (current->GetRequest().GetPeerId() != peerId)
     398              :         {
     399            0 :             continue;
     400              :         }
     401              : 
     402            0 :         NodeListener * listener = current->GetListener();
     403            0 :         mActiveLookups.Erase(current);
     404              : 
     405            0 :         Dnssd::Resolver::Instance().NodeIdResolutionNoLongerNeeded(peerId);
     406              : 
     407              :         // Failure callback only called after iterator was cleared:
     408              :         // This allows failure handlers to deallocate structures that may
     409              :         // contain the active lookup data as a member (intrusive lists members)
     410            0 :         listener->OnNodeAddressResolutionFailed(peerId, error);
     411              :     }
     412            0 :     ReArmTimer();
     413            0 : }
     414              : 
     415            1 : void Resolver::ReArmTimer()
     416              : {
     417            1 :     mSystemLayer->CancelTimer(&OnResolveTimer, static_cast<void *>(this));
     418              : 
     419            1 :     System::Clock::Timestamp now = mTimeSource.GetMonotonicTimestamp();
     420              : 
     421            1 :     System::Clock::Timeout nextTimeout = kInvalidTimeout;
     422            1 :     for (auto & activeLookup : mActiveLookups)
     423              :     {
     424            0 :         System::Clock::Timeout timeout = activeLookup.NextEventTimeout(now);
     425              : 
     426            0 :         if (timeout < nextTimeout)
     427              :         {
     428            0 :             nextTimeout = timeout;
     429              :         }
     430              :     }
     431              : 
     432            1 :     if (nextTimeout == kInvalidTimeout)
     433              :     {
     434              :         // Generally this is only expected when no active lookups exist
     435            1 :         return;
     436              :     }
     437              : 
     438            0 :     CHIP_ERROR err = mSystemLayer->StartTimer(nextTimeout, &OnResolveTimer, static_cast<void *>(this));
     439            0 :     if (err != CHIP_NO_ERROR)
     440              :     {
     441            0 :         ChipLogError(Discovery, "Timer schedule error %s assumed permanent", err.AsString());
     442              : 
     443              :         // Clear out all active lookups: without timers there is no guarantee of success
     444            0 :         auto it = mActiveLookups.begin();
     445            0 :         while (it != mActiveLookups.end())
     446              :         {
     447            0 :             const PeerId peerId     = it->GetRequest().GetPeerId();
     448            0 :             NodeListener * listener = it->GetListener();
     449              : 
     450            0 :             mActiveLookups.Erase(it);
     451            0 :             it = mActiveLookups.begin();
     452              : 
     453            0 :             Dnssd::Resolver::Instance().NodeIdResolutionNoLongerNeeded(peerId);
     454              :             // Callback only called after active lookup is cleared
     455              :             // This allows failure handlers to deallocate structures that may
     456              :             // contain the active lookup data as a member (intrusive lists members)
     457            0 :             listener->OnNodeAddressResolutionFailed(peerId, err);
     458              :         }
     459              :     }
     460              : }
     461              : 
     462              : } // namespace Impl
     463              : 
     464            2 : Resolver & Resolver::Instance()
     465              : {
     466            2 :     static Impl::Resolver gResolver;
     467            2 :     return gResolver;
     468              : }
     469              : 
     470              : } // namespace AddressResolve
     471              : } // namespace chip
        

Generated by: LCOV version 2.0-1