xref: /aosp_15_r20/external/openthread/src/core/net/srp_advertising_proxy.hpp (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
1 /*
2  *  Copyright (c) 2023, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file includes definitions for Advertising Proxy.
32  */
33 
34 #ifndef SRP_ADVERTISING_PROXY_HPP_
35 #define SRP_ADVERTISING_PROXY_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
40 
41 #if !OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE && !OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
42 #error "OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE requires PLATFORM_DNSSD_ENABLE or MULTICAST_DNS_ENABLE"
43 #endif
44 
45 #if !OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
46 #error "OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE requires OPENTHREAD_CONFIG_SRP_SERVER_ENABLE"
47 #endif
48 
49 #if !OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
50 #error "OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE requires OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE"
51 #endif
52 
53 #include "common/clearable.hpp"
54 #include "common/heap_allocatable.hpp"
55 #include "common/locator.hpp"
56 #include "common/non_copyable.hpp"
57 #include "common/owning_list.hpp"
58 #include "common/tasklet.hpp"
59 #include "common/timer.hpp"
60 #include "net/dnssd.hpp"
61 #include "net/srp_server.hpp"
62 
63 namespace ot {
64 namespace Srp {
65 
66 /**
67  * Implements SRP Advertising Proxy.
68  *
69  */
70 class AdvertisingProxy : public InstanceLocator, private NonCopyable
71 {
72 public:
73     typedef Server::Host    Host;    ///< An SRP server host registration.
74     typedef Server::Service Service; ///< An SRP server service registration.
75 
76     /**
77      * Represents counters for Advertising Proxy.
78      *
79      */
80     struct Counters : public Clearable<Counters>
81     {
82         uint32_t mAdvTotal;           ///< Total number of advertisement requests, i.e., calls to `Advertise()`.
83         uint32_t mAdvReplaced;        ///< Number of advertisements that were replaced by a newer one.
84         uint32_t mAdvSkipped;         ///< Number of advertisement that were skipped (DNS-SD platform not yet ready).
85         uint32_t mAdvSuccessful;      ///< Number of successful adv (all requests registered successfully).
86         uint32_t mAdvRejected;        ///< Number of rejected adv (at least one request was rejected by DNS-SD plat).
87         uint32_t mAdvTimeout;         ///< Number of advertisements that timed out (no response from DNS-SD platform).
88         uint32_t mAdvHostRemovals;    ///< Number of host removal adv, i.e., calls to `AdvertiseRemovalOf(Host &)`
89         uint32_t mAdvServiceRemovals; ///< Number of service removal adv, i.e., calls to `AdvertiseRemovalOf(Service &)`
90         uint32_t mStateChanges;       ///< Number of state changes of Advertising Proxy.
91     };
92 
93     /**
94      * Initializes the `AdvertisingProxy` object.
95      *
96      * @param[in] aInstance  The OpenThread instance
97      *
98      */
99     explicit AdvertisingProxy(Instance &aInstance);
100 
101     /**
102      * Indicates whether or not the Advertising Proxy is running.
103      *
104      * @retval TRUE   The Advertising Proxy is running.
105      * @retval FALSE  The Advertising Proxy is not running (it is stopped).
106      *
107      */
IsRunning(void) const108     bool IsRunning(void) const { return mState == kStateRunning; }
109 
110     /**
111      * Requests advertisement of a newly received SRP Update message.
112      *
113      * Once advertisement is completed, `AdvertisingProxy` notifies server by invoking `Server::CommitSrpUpdate()`
114      * using the same `aHost` and `aMetadata` as input parameters along with an `Error` indicating the outcome of the
115      * advertisement.
116      *
117      * The `aHost` instance ownership is passed to `AdvertisingProxy` until it is passed back to the `Server` in the
118      * `CommitSrpUpdate()` call. The call to `CommitSrpUpdate()` may happen before this method returns, for example,
119      * if the proxy is not running and therefore the advertisement is skipped.
120      *
121      * @param[in] aHost     The `aHost` instance constructed from processing a newly received SRP Update message.
122      * @param[in] aMetadata The `MessageMetadata` associated with the received SRP Update message by server.
123      *
124      */
125     void Advertise(Host &aHost, const Server::MessageMetadata &aMetadata);
126 
127     /**
128      * Requests advertisement of removal of an already committed host and all its services, for example, due to its
129      * lease expiration.
130      *
131      * The removal does not use any callback to notify the SRP server since the server always immediately commits the
132      * removed entries.
133      *
134      * If there is an outstanding advertisement request (an earlier call to `Advertise()` that has not yet completed)
135      * that is registering the same host name as @p aHost that is being removed, the outstanding advertisement is
136      * rejected using the `kErrorAbort` error. This situation can happen if the client tries to refresh or update its
137      * registration close to its lease expiration time. By rejecting any outstanding advertisements, we ensure that an
138      * expired host is not re-added by mistake due to a delay in registration by the DNS-SD platform. The error is
139      * passed back to the client, triggering it to retry its registration.
140      *
141      * @param[in] aHost  The host which is being removed.
142      *
143      */
144     void AdvertiseRemovalOf(Host &aHost);
145 
146     /**
147      * Requests advertisement of removal of an already committed service, for example, due to its lease expiration.
148      *
149      * The removal does not use any callback to notify the SRP server since the server always immediately commits the
150      * removed services.
151      *
152      * If there is an outstanding advertisement request (an earlier call to `Advertise()` that has not yet completed)
153      * that is registering the same service as @p aService that is being removed, we skip the advertisement of service
154      * removal (do not unregister the service on infrastructure DNS-SD). This ensures that when the outstanding
155      * advertisement is completed, the service is re-added successfully (and it is still being advertised by proxy).
156      * This behavior is different from `AdvertiseRemovalOf(Host &)`, where the outstanding advertisement is rejected
157      * because service removals are individual, compared to when removing a host where the host and all its associated
158      * services are removed.
159      *
160      * @param[in] aHost  The host which is being removed.
161      *
162      */
163     void AdvertiseRemovalOf(Service &aService);
164 
165     /**
166      * Gets the set of counters.
167      *
168      * @returns The `AdvertisingProxy` counter.
169      *
170      */
GetCounters(void) const171     const Counters &GetCounters(void) const { return mCounters; }
172 
173     /**
174      * Resets the counters
175      *
176      */
ResetCounters(void)177     void ResetCounters(void) { mCounters.Clear(); }
178 
179     /**
180      * Gets the advertisement timeout (in msec).
181      *
182      * The default value of `OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT` is used when not explicitly set.
183      *
184      * @returns The advertisement timeout (in msec).
185      *
186      */
GetAdvTimeout(void) const187     uint32_t GetAdvTimeout(void) const { return mAdvTimeout; }
188 
189     /**
190      * Sets the advertisement timeout.
191      *
192      * Changing the timeout is intended for testing purposes only. This allows tests to use a long timeout to validate
193      * the behavior of `AdvertisingProxy` when new `Advertise()` requests replace entries in earlier requests.
194      *
195      * @param[in] aTimeout   The advertisement timeout (in msec).
196      *
197      */
SetAdvTimeout(uint32_t aTimeout)198     void SetAdvTimeout(uint32_t aTimeout) { mAdvTimeout = Max(aTimeout, kAdvTimeout); }
199 
200     /**
201      * Notifies `AdvertisingProxy` that SRP sever state changed.
202      *
203      */
HandleServerStateChange(void)204     void HandleServerStateChange(void) { UpdateState(); }
205 
206     /**
207      * Notifies `AdvertisingProxy` that DND-SD platform state changed.
208      *
209      */
HandleDnssdPlatformStateChange(void)210     void HandleDnssdPlatformStateChange(void) { UpdateState(); }
211 
212     /**
213      * Notifies `AdvertisingProxy` that `InfraIf` state changed.
214      *
215      */
HandleInfraIfStateChanged(void)216     void HandleInfraIfStateChanged(void) { UpdateState(); }
217 
218 private:
219     typedef Dnssd::RequestId RequestId;
220     typedef char             DnsName[Dns::Name::kMaxNameSize];
221 
222     static constexpr RequestId kInvalidRequestId = Server::kInvalidRequestId;
223 
224     static constexpr uint32_t kAdvTimeout = OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT; // in msec
225 
226     enum State : uint8_t
227     {
228         kStateStopped,
229         kStateRunning,
230     };
231 
232     struct AdvInfo : public Heap::Allocatable<AdvInfo>, public LinkedListEntry<AdvInfo>, public GetProvider<AdvInfo>
233     {
234         struct CompletionChecker
235         {
236             // Used in `Matches()` to check if advertisement is
237             // completed (successfully or failed).
238         };
239 
240         struct ExpirationChecker
241         {
ExpirationCheckerot::Srp::AdvertisingProxy::AdvInfo::ExpirationChecker242             explicit ExpirationChecker(TimeMilli aNow)
243                 : mNow(aNow)
244             {
245             }
246 
247             TimeMilli mNow;
248         };
249 
250         AdvInfo(Host &aHost, const Server::MessageMetadata &aMetadata, uint32_t aTimeout);
251         void      SignalServerToCommit(void);
252         bool      IsCompleted(void) const;
Matchesot::Srp::AdvertisingProxy::AdvInfo253         bool      Matches(const CompletionChecker &) const { return IsCompleted(); }
Matchesot::Srp::AdvertisingProxy::AdvInfo254         bool      Matches(const ExpirationChecker &aChecker) const { return (mExpireTime <= aChecker.mNow); }
GetInstanceot::Srp::AdvertisingProxy::AdvInfo255         Instance &GetInstance(void) const { return mHost.GetInstance(); }
256 
257         AdvInfo                *mNext;
258         AdvInfo                *mBlockingAdv;
259         Host                   &mHost;
260         TimeMilli               mExpireTime;
261         Server::MessageMetadata mMessageMetadata;
262         Ip6::MessageInfo        mMessageInfo;
263         Error                   mError;
264     };
265 
266     template <typename Entry> void UpdateAdvIdRangeOn(Entry &aEntry);
267     template <typename Entry> bool IsRegisteredOrRegistering(const Entry &aEntry) const;
268     template <typename Entry> bool IsKeyRegisteredOrRegistering(const Entry &aEntry) const;
269     template <typename Entry> void DecideToAdvertise(Entry &aEntry, bool aUnregisterEntry, bool aUnregisterKey);
270     template <typename Entry> void UpdateKeyRegistrationStatus(Entry &aEntry, const Entry &aExistingEntry);
271     template <typename Entry> bool CompareAndUpdate(Entry &aEntry, Entry &aExistingEntry);
272     template <typename Entry> bool EntriesMatch(const Entry &aFirstEntry, const Entry &aSecondEntry);
273 
274     void        Start(void);
275     void        Stop(void);
276     void        UpdateState(void);
277     RequestId   AllocateNextRequestId(void);
278     void        Advertise(Host &aHost);
279     void        UnregisterHostAndItsServicesAndKeys(Host &aHost);
280     bool        CompareAndUpdateHostAndServices(Host &aHost, Host &aExistingHost);
281     bool        CompareAndUpdateHost(Host &aHost, Host &aExistingHost);
282     bool        CompareAndUpdateService(Service &aService, Service &aExistingService);
283     void        RegisterHost(Host &aHost);
284     void        UnregisterHost(Host &aHost);
285     void        RegisterService(Service &aService);
286     void        UnregisterService(Service &aService);
287     void        RegisterKey(Host &aHost);
288     void        RegisterKey(Service &aService);
289     void        RegisterKey(const char      *aName,
290                             const char      *aServiceType,
291                             const Host::Key &aKey,
292                             RequestId        aRequestId,
293                             uint32_t         aTtl);
294     void        UnregisterKey(Service &aService);
295     void        UnregisterKey(Host &aHost);
296     void        UnregisterKey(const char *aName, const char *aServiceType);
297     void        CopyNameAndRemoveDomain(DnsName &aName, const char *aFullName);
298     static void HandleRegistered(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError);
299     void        HandleRegistered(RequestId aRequestId, Error aError);
300     bool        HandleRegisteredRequestIdOn(Host &aHost, RequestId aRequestId, Error aError);
301     void        HandleTimer(void);
302     void        HandleTasklet(void);
303     void        SignalAdvCompleted(AdvInfo &aAdvInfo);
304 
305     using AdvTimer   = TimerMilliIn<AdvertisingProxy, &AdvertisingProxy::HandleTimer>;
306     using AdvTasklet = TaskletIn<AdvertisingProxy, &AdvertisingProxy::HandleTasklet>;
307 
308     State               mState;
309     RequestId           mCurrentRequestId;
310     uint32_t            mAdvTimeout;
311     OwningList<AdvInfo> mAdvInfoList;
312     AdvTimer            mTimer;
313     AdvTasklet          mTasklet;
314     Counters            mCounters;
315 };
316 
317 } // namespace Srp
318 } // namespace ot
319 
320 #endif // OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
321 
322 #endif // SRP_ADVERTISING_PROXY_HPP_
323