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