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