LCOV - code coverage report
Current view: top level - lib/address_resolve - AddressResolve_DefaultImpl.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 43 180 23.9 %
Date: 2024-02-15 08:20:41 Functions: 5 16 31.2 %

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

Generated by: LCOV version 1.14