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