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