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