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