1 /*
2  *  Copyright (c) 2020, 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 implements Backbone Router management.
32  */
33 
34 #include "bbr_manager.hpp"
35 
36 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
37 
38 #include "common/as_core_type.hpp"
39 #include "common/code_utils.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/log.hpp"
42 #include "common/num_utils.hpp"
43 #include "common/numeric_limits.hpp"
44 #include "common/random.hpp"
45 #include "instance/instance.hpp"
46 #include "thread/mle_types.hpp"
47 #include "thread/thread_netif.hpp"
48 #include "thread/thread_tlvs.hpp"
49 #include "thread/uri_paths.hpp"
50 
51 namespace ot {
52 
53 namespace BackboneRouter {
54 
55 RegisterLogModule("BbrManager");
56 
Manager(Instance & aInstance)57 Manager::Manager(Instance &aInstance)
58     : InstanceLocator(aInstance)
59 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
60     , mNdProxyTable(aInstance)
61 #endif
62 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
63     , mMulticastListenersTable(aInstance)
64 #endif
65     , mTimer(aInstance)
66     , mBackboneTmfAgent(aInstance)
67 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
68 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
69     , mDuaResponseStatus(ThreadStatusTlv::kDuaSuccess)
70 #endif
71 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
72     , mMlrResponseStatus(ThreadStatusTlv::kMlrSuccess)
73 #endif
74 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
75     , mDuaResponseIsSpecified(false)
76 #endif
77 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
78     , mMlrResponseIsSpecified(false)
79 #endif
80 #endif
81 {
82 }
83 
HandleNotifierEvents(Events aEvents)84 void Manager::HandleNotifierEvents(Events aEvents)
85 {
86     Error error;
87 
88     if (aEvents.Contains(kEventThreadBackboneRouterStateChanged))
89     {
90         if (!Get<Local>().IsEnabled())
91         {
92 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
93             mMulticastListenersTable.Clear();
94 #endif
95             mTimer.Stop();
96 
97             error = mBackboneTmfAgent.Stop();
98 
99             if (error != kErrorNone)
100             {
101                 LogWarn("Stop Backbone TMF agent: %s", ErrorToString(error));
102             }
103             else
104             {
105                 LogInfo("Stop Backbone TMF agent: %s", ErrorToString(error));
106             }
107         }
108         else
109         {
110             if (!mTimer.IsRunning())
111             {
112                 mTimer.Start(kTimerInterval);
113             }
114 
115             error = mBackboneTmfAgent.Start();
116 
117             LogError("Start Backbone TMF agent", error);
118         }
119     }
120 }
121 
HandleTimer(void)122 void Manager::HandleTimer(void)
123 {
124 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
125     mMulticastListenersTable.Expire();
126 #endif
127 
128 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
129     mNdProxyTable.HandleTimer();
130 #endif
131 
132     mTimer.Start(kTimerInterval);
133 }
134 
135 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)136 template <> void Manager::HandleTmf<kUriMlr>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
137 {
138     VerifyOrExit(Get<Local>().IsEnabled());
139     HandleMulticastListenerRegistration(aMessage, aMessageInfo);
140 
141 exit:
142     return;
143 }
144 
HandleMulticastListenerRegistration(const Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)145 void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
146 {
147     Error                      error     = kErrorNone;
148     bool                       isPrimary = Get<Local>().IsPrimary();
149     ThreadStatusTlv::MlrStatus status    = ThreadStatusTlv::kMlrSuccess;
150     Config                     config;
151 
152     uint16_t     addressesOffset, addressesLength;
153     Ip6::Address address;
154     Ip6::Address addresses[Ip6AddressesTlv::kMaxAddresses];
155     uint8_t      failedAddressNum  = 0;
156     uint8_t      successAddressNum = 0;
157     TimeMilli    expireTime;
158     uint32_t     timeout;
159     uint16_t     commissionerSessionId;
160     bool         hasCommissionerSessionIdTlv = false;
161     bool         processTimeoutTlv           = false;
162 
163     VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorParse);
164 
165 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
166     // Required by Test Specification 5.10.22 DUA-TC-26, only for certification purpose
167     if (mMlrResponseIsSpecified)
168     {
169         mMlrResponseIsSpecified = false;
170         ExitNow(status = mMlrResponseStatus);
171     }
172 #endif
173 
174     VerifyOrExit(isPrimary, status = ThreadStatusTlv::kMlrBbrNotPrimary);
175 
176     // TODO: (MLR) send configured MLR response for Reference Device
177 
178     if (Tlv::Find<ThreadCommissionerSessionIdTlv>(aMessage, commissionerSessionId) == kErrorNone)
179     {
180         uint16_t localSessionId;
181 
182         VerifyOrExit((Get<NetworkData::Leader>().FindCommissioningSessionId(localSessionId) == kErrorNone) &&
183                          (localSessionId == commissionerSessionId),
184                      status = ThreadStatusTlv::kMlrGeneralFailure);
185 
186         hasCommissionerSessionIdTlv = true;
187     }
188 
189     processTimeoutTlv = hasCommissionerSessionIdTlv && (Tlv::Find<ThreadTimeoutTlv>(aMessage, timeout) == kErrorNone);
190 
191     VerifyOrExit(Tlv::FindTlvValueOffset(aMessage, Ip6AddressesTlv::kIp6Addresses, addressesOffset, addressesLength) ==
192                      kErrorNone,
193                  error = kErrorParse);
194     VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, status = ThreadStatusTlv::kMlrGeneralFailure);
195     VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= Ip6AddressesTlv::kMaxAddresses,
196                  status = ThreadStatusTlv::kMlrGeneralFailure);
197 
198     if (!processTimeoutTlv)
199     {
200         IgnoreError(Get<Leader>().GetConfig(config));
201 
202         timeout = config.mMlrTimeout;
203     }
204     else
205     {
206         VerifyOrExit(timeout < NumericLimits<uint32_t>::kMax, status = ThreadStatusTlv::kMlrNoPersistent);
207 
208         if (timeout != 0)
209         {
210             uint32_t origTimeout = timeout;
211 
212             timeout = Min(timeout, kMaxMlrTimeout);
213 
214             if (timeout != origTimeout)
215             {
216                 LogNote("MLR.req: MLR timeout is normalized from %lu to %lu", ToUlong(origTimeout), ToUlong(timeout));
217             }
218         }
219     }
220 
221     expireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(timeout);
222 
223     for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address))
224     {
225         IgnoreError(aMessage.Read(addressesOffset + offset, address));
226 
227         if (timeout == 0)
228         {
229             mMulticastListenersTable.Remove(address);
230 
231             // Put successfully de-registered addresses at the end of `addresses`.
232             addresses[Ip6AddressesTlv::kMaxAddresses - (++successAddressNum)] = address;
233         }
234         else
235         {
236             bool failed = true;
237 
238             switch (mMulticastListenersTable.Add(address, expireTime))
239             {
240             case kErrorNone:
241                 failed = false;
242                 break;
243             case kErrorInvalidArgs:
244                 if (status == ThreadStatusTlv::kMlrSuccess)
245                 {
246                     status = ThreadStatusTlv::kMlrInvalid;
247                 }
248                 break;
249             case kErrorNoBufs:
250                 if (status == ThreadStatusTlv::kMlrSuccess)
251                 {
252                     status = ThreadStatusTlv::kMlrNoResources;
253                 }
254                 break;
255             default:
256                 OT_ASSERT(false);
257             }
258 
259             if (failed)
260             {
261                 addresses[failedAddressNum++] = address;
262             }
263             else
264             {
265                 // Put successfully registered addresses at the end of `addresses`.
266                 addresses[Ip6AddressesTlv::kMaxAddresses - (++successAddressNum)] = address;
267             }
268         }
269     }
270 
271 exit:
272     if (error == kErrorNone)
273     {
274         SendMulticastListenerRegistrationResponse(aMessage, aMessageInfo, status, addresses, failedAddressNum);
275     }
276 
277     if (successAddressNum > 0)
278     {
279         SendBackboneMulticastListenerRegistration(&addresses[Ip6AddressesTlv::kMaxAddresses - successAddressNum],
280                                                   successAddressNum, timeout);
281     }
282 }
283 
SendMulticastListenerRegistrationResponse(const Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo,ThreadStatusTlv::MlrStatus aStatus,Ip6::Address * aFailedAddresses,uint8_t aFailedAddressNum)284 void Manager::SendMulticastListenerRegistrationResponse(const Coap::Message       &aMessage,
285                                                         const Ip6::MessageInfo    &aMessageInfo,
286                                                         ThreadStatusTlv::MlrStatus aStatus,
287                                                         Ip6::Address              *aFailedAddresses,
288                                                         uint8_t                    aFailedAddressNum)
289 {
290     Error          error = kErrorNone;
291     Coap::Message *message;
292 
293     message = Get<Tmf::Agent>().NewResponseMessage(aMessage);
294     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
295 
296     SuccessOrExit(Tlv::Append<ThreadStatusTlv>(*message, aStatus));
297 
298     if (aFailedAddressNum > 0)
299     {
300         Ip6AddressesTlv addressesTlv;
301 
302         addressesTlv.Init();
303         addressesTlv.SetLength(sizeof(Ip6::Address) * aFailedAddressNum);
304         SuccessOrExit(error = message->Append(addressesTlv));
305 
306         for (uint8_t i = 0; i < aFailedAddressNum; i++)
307         {
308             SuccessOrExit(error = message->Append(aFailedAddresses[i]));
309         }
310     }
311 
312     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
313 
314 exit:
315     FreeMessageOnError(message, error);
316     LogInfo("Sent MLR.rsp (status=%d): %s", aStatus, ErrorToString(error));
317 }
318 
SendBackboneMulticastListenerRegistration(const Ip6::Address * aAddresses,uint8_t aAddressNum,uint32_t aTimeout)319 void Manager::SendBackboneMulticastListenerRegistration(const Ip6::Address *aAddresses,
320                                                         uint8_t             aAddressNum,
321                                                         uint32_t            aTimeout)
322 {
323     Error             error   = kErrorNone;
324     Coap::Message    *message = nullptr;
325     Ip6::MessageInfo  messageInfo;
326     Ip6AddressesTlv   addressesTlv;
327     BackboneTmfAgent &backboneTmf = Get<BackboneRouter::BackboneTmfAgent>();
328 
329     OT_ASSERT(aAddressNum >= Ip6AddressesTlv::kMinAddresses && aAddressNum <= Ip6AddressesTlv::kMaxAddresses);
330 
331     message = backboneTmf.NewNonConfirmablePostMessage(kUriBackboneMlr);
332     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
333 
334     addressesTlv.Init();
335     addressesTlv.SetLength(sizeof(Ip6::Address) * aAddressNum);
336     SuccessOrExit(error = message->Append(addressesTlv));
337     SuccessOrExit(error = message->AppendBytes(aAddresses, sizeof(Ip6::Address) * aAddressNum));
338 
339     SuccessOrExit(error = Tlv::Append<ThreadTimeoutTlv>(*message, aTimeout));
340 
341     messageInfo.SetPeerAddr(Get<Local>().GetAllNetworkBackboneRoutersAddress());
342     messageInfo.SetPeerPort(BackboneRouter::kBackboneUdpPort); // TODO: Provide API for configuring Backbone COAP port.
343 
344     messageInfo.SetHopLimit(kDefaultHoplimit);
345     messageInfo.SetIsHostInterface(true);
346 
347     SuccessOrExit(error = backboneTmf.SendMessage(*message, messageInfo));
348 
349 exit:
350     FreeMessageOnError(message, error);
351     LogInfo("Sent BMLR.ntf: %s", ErrorToString(error));
352 }
353 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
354 
355 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
356 
357 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)358 void Manager::HandleTmf<kUriDuaRegistrationRequest>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
359 {
360     VerifyOrExit(Get<Local>().IsEnabled());
361     HandleDuaRegistration(aMessage, aMessageInfo);
362 
363 exit:
364     return;
365 }
366 
HandleDuaRegistration(const Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)367 void Manager::HandleDuaRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
368 {
369     Error                      error     = kErrorNone;
370     ThreadStatusTlv::DuaStatus status    = ThreadStatusTlv::kDuaSuccess;
371     bool                       isPrimary = Get<Local>().IsPrimary();
372     uint32_t                   lastTransactionTime;
373     bool                       hasLastTransactionTime;
374     Ip6::Address               target;
375     Ip6::InterfaceIdentifier   meshLocalIid;
376 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
377     Coap::Code duaRespCoapCode = Coap::kCodeEmpty;
378 #endif
379 
380     VerifyOrExit(aMessageInfo.GetPeerAddr().GetIid().IsRoutingLocator(), error = kErrorDrop);
381     VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorParse);
382 
383     SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, target));
384     SuccessOrExit(error = Tlv::Find<ThreadMeshLocalEidTlv>(aMessage, meshLocalIid));
385 
386 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
387     if (mDuaResponseIsSpecified && (mDuaResponseTargetMlIid.IsUnspecified() || mDuaResponseTargetMlIid == meshLocalIid))
388     {
389         mDuaResponseIsSpecified = false;
390         if (mDuaResponseStatus >= Coap::kCodeResponseMin)
391         {
392             duaRespCoapCode = static_cast<Coap::Code>(mDuaResponseStatus);
393         }
394         else
395         {
396             status = static_cast<ThreadStatusTlv::DuaStatus>(mDuaResponseStatus);
397         }
398         ExitNow();
399     }
400 #endif
401 
402     VerifyOrExit(isPrimary, status = ThreadStatusTlv::kDuaNotPrimary);
403     VerifyOrExit(Get<Leader>().HasDomainPrefix(), status = ThreadStatusTlv::kDuaGeneralFailure);
404     VerifyOrExit(Get<Leader>().IsDomainUnicast(target), status = ThreadStatusTlv::kDuaInvalid);
405 
406     hasLastTransactionTime = (Tlv::Find<ThreadLastTransactionTimeTlv>(aMessage, lastTransactionTime) == kErrorNone);
407 
408     switch (mNdProxyTable.Register(target.GetIid(), meshLocalIid, aMessageInfo.GetPeerAddr().GetIid().GetLocator(),
409                                    hasLastTransactionTime ? &lastTransactionTime : nullptr))
410     {
411     case kErrorNone:
412         // TODO: update its EID-to-RLOC Map Cache based on the pair {DUA, RLOC16-source} which is gleaned from the
413         // DUA.req packet according to Thread Spec. 5.23.3.6.2
414         break;
415     case kErrorDuplicated:
416         status = ThreadStatusTlv::kDuaDuplicate;
417         break;
418     case kErrorNoBufs:
419         status = ThreadStatusTlv::kDuaNoResources;
420         break;
421     default:
422         status = ThreadStatusTlv::kDuaGeneralFailure;
423         break;
424     }
425 
426 exit:
427     LogInfo("Received DUA.req on %s: %s", (isPrimary ? "PBBR" : "SBBR"), ErrorToString(error));
428 
429     if (error == kErrorNone)
430     {
431 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
432         if (duaRespCoapCode != Coap::kCodeEmpty)
433         {
434             IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo, duaRespCoapCode));
435         }
436         else
437 #endif
438         {
439             SendDuaRegistrationResponse(aMessage, aMessageInfo, target, status);
440         }
441     }
442 }
443 
SendDuaRegistrationResponse(const Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const Ip6::Address & aTarget,ThreadStatusTlv::DuaStatus aStatus)444 void Manager::SendDuaRegistrationResponse(const Coap::Message       &aMessage,
445                                           const Ip6::MessageInfo    &aMessageInfo,
446                                           const Ip6::Address        &aTarget,
447                                           ThreadStatusTlv::DuaStatus aStatus)
448 {
449     Error          error = kErrorNone;
450     Coap::Message *message;
451 
452     message = Get<Tmf::Agent>().NewResponseMessage(aMessage);
453     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
454 
455     SuccessOrExit(Tlv::Append<ThreadStatusTlv>(*message, aStatus));
456     SuccessOrExit(Tlv::Append<ThreadTargetTlv>(*message, aTarget));
457 
458     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
459 
460 exit:
461     FreeMessageOnError(message, error);
462     LogInfo("Sent DUA.rsp for DUA %s, status %d %s", aTarget.ToString().AsCString(), aStatus, ErrorToString(error));
463 }
464 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
465 
466 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
467 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
ConfigNextDuaRegistrationResponse(const Ip6::InterfaceIdentifier * aMlIid,uint8_t aStatus)468 void Manager::ConfigNextDuaRegistrationResponse(const Ip6::InterfaceIdentifier *aMlIid, uint8_t aStatus)
469 {
470     mDuaResponseIsSpecified = true;
471 
472     if (aMlIid)
473     {
474         mDuaResponseTargetMlIid = *aMlIid;
475     }
476     else
477     {
478         mDuaResponseTargetMlIid.Clear();
479     }
480 
481     mDuaResponseStatus = aStatus;
482 }
483 #endif
484 
485 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
ConfigNextMulticastListenerRegistrationResponse(ThreadStatusTlv::MlrStatus aStatus)486 void Manager::ConfigNextMulticastListenerRegistrationResponse(ThreadStatusTlv::MlrStatus aStatus)
487 {
488     mMlrResponseIsSpecified = true;
489     mMlrResponseStatus      = aStatus;
490 }
491 #endif
492 #endif
493 
494 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
GetNdProxyTable(void)495 NdProxyTable &Manager::GetNdProxyTable(void) { return mNdProxyTable; }
496 
ShouldForwardDuaToBackbone(const Ip6::Address & aAddress)497 bool Manager::ShouldForwardDuaToBackbone(const Ip6::Address &aAddress)
498 {
499     bool forwardToBackbone = false;
500 
501     VerifyOrExit(Get<Local>().IsPrimary());
502     VerifyOrExit(Get<Leader>().IsDomainUnicast(aAddress));
503 
504     // Do not forward to Backbone if the DUA is registered on PBBR
505     VerifyOrExit(!mNdProxyTable.IsRegistered(aAddress.GetIid()));
506     // Do not forward to Backbone if the DUA belongs to a MTD Child (which may have failed in DUA registration)
507     VerifyOrExit(Get<NeighborTable>().FindNeighbor(aAddress) == nullptr);
508     // Forward to Backbone only if the DUA is resolved to the PBBR's RLOC16
509     VerifyOrExit(Get<AddressResolver>().LookUp(aAddress) == Get<Mle::MleRouter>().GetRloc16());
510 
511     forwardToBackbone = true;
512 
513 exit:
514     return forwardToBackbone;
515 }
516 
SendBackboneQuery(const Ip6::Address & aDua,uint16_t aRloc16)517 Error Manager::SendBackboneQuery(const Ip6::Address &aDua, uint16_t aRloc16)
518 {
519     Error            error   = kErrorNone;
520     Coap::Message   *message = nullptr;
521     Ip6::MessageInfo messageInfo;
522 
523     VerifyOrExit(Get<Local>().IsPrimary(), error = kErrorInvalidState);
524 
525     message = mBackboneTmfAgent.NewPriorityNonConfirmablePostMessage(kUriBackboneQuery);
526     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
527 
528     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aDua));
529 
530     if (aRloc16 != Mac::kShortAddrInvalid)
531     {
532         SuccessOrExit(error = Tlv::Append<ThreadRloc16Tlv>(*message, aRloc16));
533     }
534 
535     messageInfo.SetPeerAddr(Get<Local>().GetAllDomainBackboneRoutersAddress());
536     messageInfo.SetPeerPort(BackboneRouter::kBackboneUdpPort);
537 
538     messageInfo.SetHopLimit(kDefaultHoplimit);
539     messageInfo.SetIsHostInterface(true);
540 
541     error = mBackboneTmfAgent.SendMessage(*message, messageInfo);
542 
543 exit:
544     LogInfo("SendBackboneQuery for %s (rloc16=%04x): %s", aDua.ToString().AsCString(), aRloc16, ErrorToString(error));
545     FreeMessageOnError(message, error);
546     return error;
547 }
548 
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)549 template <> void Manager::HandleTmf<kUriBackboneQuery>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
550 {
551     Error                  error = kErrorNone;
552     Ip6::Address           dua;
553     uint16_t               rloc16 = Mac::kShortAddrInvalid;
554     NdProxyTable::NdProxy *ndProxy;
555 
556     VerifyOrExit(aMessageInfo.IsHostInterface(), error = kErrorDrop);
557 
558     VerifyOrExit(Get<Local>().IsPrimary(), error = kErrorInvalidState);
559     VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorParse);
560 
561     SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, dua));
562 
563     error = Tlv::Find<ThreadRloc16Tlv>(aMessage, rloc16);
564     VerifyOrExit(error == kErrorNone || error == kErrorNotFound);
565 
566     LogInfo("Received BB.qry from %s for %s (rloc16=%04x)", aMessageInfo.GetPeerAddr().ToString().AsCString(),
567             dua.ToString().AsCString(), rloc16);
568 
569     ndProxy = mNdProxyTable.ResolveDua(dua);
570     VerifyOrExit(ndProxy != nullptr && !ndProxy->GetDadFlag(), error = kErrorNotFound);
571 
572     error = SendBackboneAnswer(aMessageInfo, dua, rloc16, *ndProxy);
573 
574 exit:
575     LogInfo("HandleBackboneQuery: %s", ErrorToString(error));
576 }
577 
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)578 template <> void Manager::HandleTmf<kUriBackboneAnswer>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
579 {
580     Error                    error = kErrorNone;
581     bool                     proactive;
582     Ip6::Address             dua;
583     Ip6::InterfaceIdentifier meshLocalIid;
584     uint16_t                 networkNameOffset, networkNameLength;
585     uint32_t                 timeSinceLastTransaction;
586     uint16_t                 srcRloc16 = Mac::kShortAddrInvalid;
587 
588     VerifyOrExit(aMessageInfo.IsHostInterface(), error = kErrorDrop);
589 
590     VerifyOrExit(Get<Local>().IsPrimary(), error = kErrorInvalidState);
591     VerifyOrExit(aMessage.IsPostRequest(), error = kErrorParse);
592 
593     proactive = !aMessage.IsConfirmable();
594 
595     SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, dua));
596     SuccessOrExit(error = Tlv::Find<ThreadMeshLocalEidTlv>(aMessage, meshLocalIid));
597     SuccessOrExit(error = Tlv::Find<ThreadLastTransactionTimeTlv>(aMessage, timeSinceLastTransaction));
598 
599     SuccessOrExit(error =
600                       Tlv::FindTlvValueOffset(aMessage, ThreadTlv::kNetworkName, networkNameOffset, networkNameLength));
601 
602     error = Tlv::Find<ThreadRloc16Tlv>(aMessage, srcRloc16);
603     VerifyOrExit(error == kErrorNone || error == kErrorNotFound);
604 
605     if (proactive)
606     {
607         HandleProactiveBackboneNotification(dua, meshLocalIid, timeSinceLastTransaction);
608     }
609     else if (srcRloc16 == Mac::kShortAddrInvalid)
610     {
611         HandleDadBackboneAnswer(dua, meshLocalIid);
612     }
613     else
614     {
615         HandleExtendedBackboneAnswer(dua, meshLocalIid, timeSinceLastTransaction, srcRloc16);
616     }
617 
618     SuccessOrExit(error = mBackboneTmfAgent.SendEmptyAck(aMessage, aMessageInfo));
619 
620 exit:
621     LogInfo("HandleBackboneAnswer: %s", ErrorToString(error));
622 }
623 
SendProactiveBackboneNotification(const Ip6::Address & aDua,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint32_t aTimeSinceLastTransaction)624 Error Manager::SendProactiveBackboneNotification(const Ip6::Address             &aDua,
625                                                  const Ip6::InterfaceIdentifier &aMeshLocalIid,
626                                                  uint32_t                        aTimeSinceLastTransaction)
627 {
628     return SendBackboneAnswer(Get<Local>().GetAllDomainBackboneRoutersAddress(), aDua, aMeshLocalIid,
629                               aTimeSinceLastTransaction, Mac::kShortAddrInvalid);
630 }
631 
SendBackboneAnswer(const Ip6::MessageInfo & aQueryMessageInfo,const Ip6::Address & aDua,uint16_t aSrcRloc16,const NdProxyTable::NdProxy & aNdProxy)632 Error Manager::SendBackboneAnswer(const Ip6::MessageInfo      &aQueryMessageInfo,
633                                   const Ip6::Address          &aDua,
634                                   uint16_t                     aSrcRloc16,
635                                   const NdProxyTable::NdProxy &aNdProxy)
636 {
637     return SendBackboneAnswer(aQueryMessageInfo.GetPeerAddr(), aDua, aNdProxy.GetMeshLocalIid(),
638                               aNdProxy.GetTimeSinceLastTransaction(), aSrcRloc16);
639 }
640 
SendBackboneAnswer(const Ip6::Address & aDstAddr,const Ip6::Address & aDua,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint32_t aTimeSinceLastTransaction,uint16_t aSrcRloc16)641 Error Manager::SendBackboneAnswer(const Ip6::Address             &aDstAddr,
642                                   const Ip6::Address             &aDua,
643                                   const Ip6::InterfaceIdentifier &aMeshLocalIid,
644                                   uint32_t                        aTimeSinceLastTransaction,
645                                   uint16_t                        aSrcRloc16)
646 {
647     Error            error   = kErrorNone;
648     Coap::Message   *message = nullptr;
649     Ip6::MessageInfo messageInfo;
650     bool             proactive = aDstAddr.IsMulticast();
651 
652     VerifyOrExit((message = mBackboneTmfAgent.NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
653 
654     SuccessOrExit(error = message->Init(proactive ? Coap::kTypeNonConfirmable : Coap::kTypeConfirmable, Coap::kCodePost,
655                                         kUriBackboneAnswer));
656     SuccessOrExit(error = message->SetPayloadMarker());
657 
658     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aDua));
659 
660     SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, aMeshLocalIid));
661 
662     SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, aTimeSinceLastTransaction));
663 
664     SuccessOrExit(error = Tlv::Append<ThreadNetworkNameTlv>(
665                       *message, Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsCString()));
666 
667     if (aSrcRloc16 != Mac::kShortAddrInvalid)
668     {
669         SuccessOrExit(Tlv::Append<ThreadRloc16Tlv>(*message, aSrcRloc16));
670     }
671 
672     messageInfo.SetPeerAddr(aDstAddr);
673     messageInfo.SetPeerPort(BackboneRouter::kBackboneUdpPort);
674 
675     messageInfo.SetHopLimit(kDefaultHoplimit);
676     messageInfo.SetIsHostInterface(true);
677 
678     error = mBackboneTmfAgent.SendMessage(*message, messageInfo);
679 
680 exit:
681     LogInfo("Send %s for %s (rloc16=%04x): %s", proactive ? "PRO_BB.ntf" : "BB.ans", aDua.ToString().AsCString(),
682             aSrcRloc16, ErrorToString(error));
683 
684     FreeMessageOnError(message, error);
685     return error;
686 }
687 
HandleDadBackboneAnswer(const Ip6::Address & aDua,const Ip6::InterfaceIdentifier & aMeshLocalIid)688 void Manager::HandleDadBackboneAnswer(const Ip6::Address &aDua, const Ip6::InterfaceIdentifier &aMeshLocalIid)
689 {
690     Error                  error     = kErrorNone;
691     NdProxyTable::NdProxy *ndProxy   = mNdProxyTable.ResolveDua(aDua);
692     bool                   duplicate = false;
693 
694     OT_UNUSED_VARIABLE(error);
695 
696     VerifyOrExit(ndProxy != nullptr, error = kErrorNotFound);
697 
698     duplicate = ndProxy->GetMeshLocalIid() != aMeshLocalIid;
699 
700     if (duplicate)
701     {
702         Ip6::Address dest;
703 
704         dest.SetToRoutingLocator(Get<Mle::MleRouter>().GetMeshLocalPrefix(), ndProxy->GetRloc16());
705         Get<AddressResolver>().SendAddressError(aDua, aMeshLocalIid, &dest);
706     }
707 
708     ot::BackboneRouter::NdProxyTable::NotifyDadComplete(*ndProxy, duplicate);
709 
710 exit:
711     LogInfo("HandleDadBackboneAnswer: %s, target=%s, mliid=%s, duplicate=%s", ErrorToString(error),
712             aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), duplicate ? "Y" : "N");
713 }
714 
HandleExtendedBackboneAnswer(const Ip6::Address & aDua,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint32_t aTimeSinceLastTransaction,uint16_t aSrcRloc16)715 void Manager::HandleExtendedBackboneAnswer(const Ip6::Address             &aDua,
716                                            const Ip6::InterfaceIdentifier &aMeshLocalIid,
717                                            uint32_t                        aTimeSinceLastTransaction,
718                                            uint16_t                        aSrcRloc16)
719 {
720     Ip6::Address dest;
721 
722     dest.SetToRoutingLocator(Get<Mle::MleRouter>().GetMeshLocalPrefix(), aSrcRloc16);
723     Get<AddressResolver>().SendAddressQueryResponse(aDua, aMeshLocalIid, &aTimeSinceLastTransaction, dest);
724 
725     LogInfo("HandleExtendedBackboneAnswer: target=%s, mliid=%s, LTT=%lus, rloc16=%04x", aDua.ToString().AsCString(),
726             aMeshLocalIid.ToString().AsCString(), ToUlong(aTimeSinceLastTransaction), aSrcRloc16);
727 }
728 
HandleProactiveBackboneNotification(const Ip6::Address & aDua,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint32_t aTimeSinceLastTransaction)729 void Manager::HandleProactiveBackboneNotification(const Ip6::Address             &aDua,
730                                                   const Ip6::InterfaceIdentifier &aMeshLocalIid,
731                                                   uint32_t                        aTimeSinceLastTransaction)
732 {
733     Error                  error   = kErrorNone;
734     NdProxyTable::NdProxy *ndProxy = mNdProxyTable.ResolveDua(aDua);
735 
736     OT_UNUSED_VARIABLE(error);
737 
738     VerifyOrExit(ndProxy != nullptr, error = kErrorNotFound);
739 
740     if (ndProxy->GetMeshLocalIid() == aMeshLocalIid)
741     {
742         uint32_t localTimeSinceLastTransaction = ndProxy->GetTimeSinceLastTransaction();
743 
744         if (aTimeSinceLastTransaction <= localTimeSinceLastTransaction)
745         {
746             BackboneRouter::NdProxyTable::Erase(*ndProxy);
747         }
748         else
749         {
750             IgnoreError(SendProactiveBackboneNotification(aDua, ndProxy->GetMeshLocalIid(),
751                                                           ndProxy->GetTimeSinceLastTransaction()));
752         }
753     }
754     else
755     {
756         // Duplicated address detected, send ADDR_ERR.ntf to ff03::2 in the Thread network
757         BackboneRouter::NdProxyTable::Erase(*ndProxy);
758         Get<AddressResolver>().SendAddressError(aDua, aMeshLocalIid, nullptr);
759     }
760 
761 exit:
762     LogInfo("HandleProactiveBackboneNotification: %s, target=%s, mliid=%s, LTT=%lus", ErrorToString(error),
763             aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), ToUlong(aTimeSinceLastTransaction));
764 }
765 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
766 
LogError(const char * aText,Error aError) const767 void Manager::LogError(const char *aText, Error aError) const
768 {
769     OT_UNUSED_VARIABLE(aText);
770 
771     if (aError == kErrorNone)
772     {
773         LogInfo("%s: %s", aText, ErrorToString(aError));
774     }
775     else
776     {
777         LogWarn("%s: %s", aText, ErrorToString(aError));
778     }
779 }
780 
781 } // namespace BackboneRouter
782 
783 } // namespace ot
784 
785 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
786