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/num_utils.hpp"
44 #include "common/random.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 const MeshCoP::CommissionerSessionIdTlv *commissionerSessionIdTlv = As<MeshCoP::CommissionerSessionIdTlv>(
180 Get<NetworkData::Leader>().GetCommissioningDataSubTlv(MeshCoP::Tlv::kCommissionerSessionId));
181
182 VerifyOrExit(commissionerSessionIdTlv != nullptr &&
183 commissionerSessionIdTlv->GetCommissionerSessionId() == commissionerSessionId,
184 status = ThreadStatusTlv::kMlrGeneralFailure);
185
186 hasCommissionerSessionIdTlv = true;
187 }
188
189 processTimeoutTlv = hasCommissionerSessionIdTlv && (Tlv::Find<ThreadTimeoutTlv>(aMessage, timeout) == kErrorNone);
190
191 VerifyOrExit(Tlv::FindTlvValueOffset(aMessage, Ip6AddressesTlv::kIp6Addresses, addressesOffset, addressesLength) ==
192 kErrorNone,
193 error = kErrorParse);
194 VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, status = ThreadStatusTlv::kMlrGeneralFailure);
195 VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= Ip6AddressesTlv::kMaxAddresses,
196 status = ThreadStatusTlv::kMlrGeneralFailure);
197
198 if (!processTimeoutTlv)
199 {
200 IgnoreError(Get<Leader>().GetConfig(config));
201
202 timeout = config.mMlrTimeout;
203 }
204 else
205 {
206 VerifyOrExit(timeout < UINT32_MAX, status = ThreadStatusTlv::kMlrNoPersistent);
207
208 if (timeout != 0)
209 {
210 uint32_t origTimeout = timeout;
211
212 timeout = Min(timeout, Mle::kMlrTimeoutMax);
213
214 if (timeout != origTimeout)
215 {
216 LogNote("MLR.req: MLR timeout is normalized from %lu to %lu", ToUlong(origTimeout), ToUlong(timeout));
217 }
218 }
219 }
220
221 expireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(timeout);
222
223 for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address))
224 {
225 IgnoreError(aMessage.Read(addressesOffset + offset, 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(Mle::kDefaultBackboneHoplimit);
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 != Mac::kShortAddrInvalid)
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(Mle::kDefaultBackboneHoplimit);
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 = Mac::kShortAddrInvalid;
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 uint16_t networkNameOffset, networkNameLength;
585 uint32_t timeSinceLastTransaction;
586 uint16_t srcRloc16 = Mac::kShortAddrInvalid;
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
599 SuccessOrExit(error =
600 Tlv::FindTlvValueOffset(aMessage, ThreadTlv::kNetworkName, networkNameOffset, networkNameLength));
601
602 error = Tlv::Find<ThreadRloc16Tlv>(aMessage, srcRloc16);
603 VerifyOrExit(error == kErrorNone || error == kErrorNotFound);
604
605 if (proactive)
606 {
607 HandleProactiveBackboneNotification(dua, meshLocalIid, timeSinceLastTransaction);
608 }
609 else if (srcRloc16 == Mac::kShortAddrInvalid)
610 {
611 HandleDadBackboneAnswer(dua, meshLocalIid);
612 }
613 else
614 {
615 HandleExtendedBackboneAnswer(dua, meshLocalIid, timeSinceLastTransaction, srcRloc16);
616 }
617
618 SuccessOrExit(error = mBackboneTmfAgent.SendEmptyAck(aMessage, aMessageInfo));
619
620 exit:
621 LogInfo("HandleBackboneAnswer: %s", ErrorToString(error));
622 }
623
SendProactiveBackboneNotification(const Ip6::Address & aDua,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint32_t aTimeSinceLastTransaction)624 Error Manager::SendProactiveBackboneNotification(const Ip6::Address &aDua,
625 const Ip6::InterfaceIdentifier &aMeshLocalIid,
626 uint32_t aTimeSinceLastTransaction)
627 {
628 return SendBackboneAnswer(Get<Local>().GetAllDomainBackboneRoutersAddress(), aDua, aMeshLocalIid,
629 aTimeSinceLastTransaction, Mac::kShortAddrInvalid);
630 }
631
SendBackboneAnswer(const Ip6::MessageInfo & aQueryMessageInfo,const Ip6::Address & aDua,uint16_t aSrcRloc16,const NdProxyTable::NdProxy & aNdProxy)632 Error Manager::SendBackboneAnswer(const Ip6::MessageInfo &aQueryMessageInfo,
633 const Ip6::Address &aDua,
634 uint16_t aSrcRloc16,
635 const NdProxyTable::NdProxy &aNdProxy)
636 {
637 return SendBackboneAnswer(aQueryMessageInfo.GetPeerAddr(), aDua, aNdProxy.GetMeshLocalIid(),
638 aNdProxy.GetTimeSinceLastTransaction(), aSrcRloc16);
639 }
640
SendBackboneAnswer(const Ip6::Address & aDstAddr,const Ip6::Address & aDua,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint32_t aTimeSinceLastTransaction,uint16_t aSrcRloc16)641 Error Manager::SendBackboneAnswer(const Ip6::Address &aDstAddr,
642 const Ip6::Address &aDua,
643 const Ip6::InterfaceIdentifier &aMeshLocalIid,
644 uint32_t aTimeSinceLastTransaction,
645 uint16_t aSrcRloc16)
646 {
647 Error error = kErrorNone;
648 Coap::Message *message = nullptr;
649 Ip6::MessageInfo messageInfo;
650 bool proactive = aDstAddr.IsMulticast();
651
652 VerifyOrExit((message = mBackboneTmfAgent.NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
653
654 SuccessOrExit(error = message->Init(proactive ? Coap::kTypeNonConfirmable : Coap::kTypeConfirmable, Coap::kCodePost,
655 kUriBackboneAnswer));
656 SuccessOrExit(error = message->SetPayloadMarker());
657
658 SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aDua));
659
660 SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, aMeshLocalIid));
661
662 SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, aTimeSinceLastTransaction));
663
664 SuccessOrExit(error = Tlv::Append<ThreadNetworkNameTlv>(
665 *message, Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsCString()));
666
667 if (aSrcRloc16 != Mac::kShortAddrInvalid)
668 {
669 SuccessOrExit(Tlv::Append<ThreadRloc16Tlv>(*message, aSrcRloc16));
670 }
671
672 messageInfo.SetPeerAddr(aDstAddr);
673 messageInfo.SetPeerPort(BackboneRouter::kBackboneUdpPort);
674
675 messageInfo.SetHopLimit(Mle::kDefaultBackboneHoplimit);
676 messageInfo.SetIsHostInterface(true);
677
678 error = mBackboneTmfAgent.SendMessage(*message, messageInfo);
679
680 exit:
681 LogInfo("Send %s for %s (rloc16=%04x): %s", proactive ? "PRO_BB.ntf" : "BB.ans", aDua.ToString().AsCString(),
682 aSrcRloc16, ErrorToString(error));
683
684 FreeMessageOnError(message, error);
685 return error;
686 }
687
HandleDadBackboneAnswer(const Ip6::Address & aDua,const Ip6::InterfaceIdentifier & aMeshLocalIid)688 void Manager::HandleDadBackboneAnswer(const Ip6::Address &aDua, const Ip6::InterfaceIdentifier &aMeshLocalIid)
689 {
690 Error error = kErrorNone;
691 NdProxyTable::NdProxy *ndProxy = mNdProxyTable.ResolveDua(aDua);
692 bool duplicate = false;
693
694 OT_UNUSED_VARIABLE(error);
695
696 VerifyOrExit(ndProxy != nullptr, error = kErrorNotFound);
697
698 duplicate = ndProxy->GetMeshLocalIid() != aMeshLocalIid;
699
700 if (duplicate)
701 {
702 Ip6::Address dest;
703
704 dest.SetToRoutingLocator(Get<Mle::MleRouter>().GetMeshLocalPrefix(), ndProxy->GetRloc16());
705 Get<AddressResolver>().SendAddressError(aDua, aMeshLocalIid, &dest);
706 }
707
708 ot::BackboneRouter::NdProxyTable::NotifyDadComplete(*ndProxy, duplicate);
709
710 exit:
711 LogInfo("HandleDadBackboneAnswer: %s, target=%s, mliid=%s, duplicate=%s", ErrorToString(error),
712 aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), duplicate ? "Y" : "N");
713 }
714
HandleExtendedBackboneAnswer(const Ip6::Address & aDua,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint32_t aTimeSinceLastTransaction,uint16_t aSrcRloc16)715 void Manager::HandleExtendedBackboneAnswer(const Ip6::Address &aDua,
716 const Ip6::InterfaceIdentifier &aMeshLocalIid,
717 uint32_t aTimeSinceLastTransaction,
718 uint16_t aSrcRloc16)
719 {
720 Ip6::Address dest;
721
722 dest.SetToRoutingLocator(Get<Mle::MleRouter>().GetMeshLocalPrefix(), aSrcRloc16);
723 Get<AddressResolver>().SendAddressQueryResponse(aDua, aMeshLocalIid, &aTimeSinceLastTransaction, dest);
724
725 LogInfo("HandleExtendedBackboneAnswer: target=%s, mliid=%s, LTT=%lus, rloc16=%04x", aDua.ToString().AsCString(),
726 aMeshLocalIid.ToString().AsCString(), ToUlong(aTimeSinceLastTransaction), aSrcRloc16);
727 }
728
HandleProactiveBackboneNotification(const Ip6::Address & aDua,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint32_t aTimeSinceLastTransaction)729 void Manager::HandleProactiveBackboneNotification(const Ip6::Address &aDua,
730 const Ip6::InterfaceIdentifier &aMeshLocalIid,
731 uint32_t aTimeSinceLastTransaction)
732 {
733 Error error = kErrorNone;
734 NdProxyTable::NdProxy *ndProxy = mNdProxyTable.ResolveDua(aDua);
735
736 OT_UNUSED_VARIABLE(error);
737
738 VerifyOrExit(ndProxy != nullptr, error = kErrorNotFound);
739
740 if (ndProxy->GetMeshLocalIid() == aMeshLocalIid)
741 {
742 uint32_t localTimeSinceLastTransaction = ndProxy->GetTimeSinceLastTransaction();
743
744 if (aTimeSinceLastTransaction <= localTimeSinceLastTransaction)
745 {
746 BackboneRouter::NdProxyTable::Erase(*ndProxy);
747 }
748 else
749 {
750 IgnoreError(SendProactiveBackboneNotification(aDua, ndProxy->GetMeshLocalIid(),
751 ndProxy->GetTimeSinceLastTransaction()));
752 }
753 }
754 else
755 {
756 // Duplicated address detected, send ADDR_ERR.ntf to ff03::2 in the Thread network
757 BackboneRouter::NdProxyTable::Erase(*ndProxy);
758 Get<AddressResolver>().SendAddressError(aDua, aMeshLocalIid, nullptr);
759 }
760
761 exit:
762 LogInfo("HandleProactiveBackboneNotification: %s, target=%s, mliid=%s, LTT=%lus", ErrorToString(error),
763 aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), ToUlong(aTimeSinceLastTransaction));
764 }
765 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
766
LogError(const char * aText,Error aError) const767 void Manager::LogError(const char *aText, Error aError) const
768 {
769 OT_UNUSED_VARIABLE(aText);
770
771 if (aError == kErrorNone)
772 {
773 LogInfo("%s: %s", aText, ErrorToString(aError));
774 }
775 else
776 {
777 LogWarn("%s: %s", aText, ErrorToString(aError));
778 }
779 }
780
781 } // namespace BackboneRouter
782
783 } // namespace ot
784
785 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
786