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