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