1 /*
2 * Copyright (c) 2018, 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 the BorderAgent service.
32 */
33
34 #include "border_agent.hpp"
35
36 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
37
38 #include "coap/coap_message.hpp"
39 #include "common/as_core_type.hpp"
40 #include "common/heap.hpp"
41 #include "common/locator_getters.hpp"
42 #include "common/log.hpp"
43 #include "common/owned_ptr.hpp"
44 #include "common/settings.hpp"
45 #include "instance/instance.hpp"
46 #include "meshcop/meshcop.hpp"
47 #include "meshcop/meshcop_tlvs.hpp"
48 #include "thread/thread_netif.hpp"
49 #include "thread/thread_tlvs.hpp"
50 #include "thread/uri_paths.hpp"
51
52 namespace ot {
53 namespace MeshCoP {
54
55 RegisterLogModule("BorderAgent");
56
57 //----------------------------------------------------------------------------------------------------------------------
58 // `BorderAgent::ForwardContext`
59
Init(Instance & aInstance,const Coap::Message & aMessage,bool aPetition,bool aSeparate)60 Error BorderAgent::ForwardContext::Init(Instance &aInstance,
61 const Coap::Message &aMessage,
62 bool aPetition,
63 bool aSeparate)
64 {
65 InstanceLocatorInit::Init(aInstance);
66 mMessageId = aMessage.GetMessageId();
67 mPetition = aPetition;
68 mSeparate = aSeparate;
69 mType = aMessage.GetType();
70 mTokenLength = aMessage.GetTokenLength();
71 memcpy(mToken, aMessage.GetToken(), mTokenLength);
72
73 return kErrorNone;
74 }
75
ToHeader(Coap::Message & aMessage,uint8_t aCode) const76 Error BorderAgent::ForwardContext::ToHeader(Coap::Message &aMessage, uint8_t aCode) const
77 {
78 if ((mType == Coap::kTypeNonConfirmable) || mSeparate)
79 {
80 aMessage.Init(Coap::kTypeNonConfirmable, static_cast<Coap::Code>(aCode));
81 }
82 else
83 {
84 aMessage.Init(Coap::kTypeAck, static_cast<Coap::Code>(aCode));
85 }
86
87 if (!mSeparate)
88 {
89 aMessage.SetMessageId(mMessageId);
90 }
91
92 return aMessage.SetToken(mToken, mTokenLength);
93 }
94
95 //----------------------------------------------------------------------------------------------------------------------
96 // `BorderAgent`
97
CoapCodeFromError(Error aError)98 Coap::Message::Code BorderAgent::CoapCodeFromError(Error aError)
99 {
100 Coap::Message::Code code;
101
102 switch (aError)
103 {
104 case kErrorNone:
105 code = Coap::kCodeChanged;
106 break;
107
108 case kErrorParse:
109 code = Coap::kCodeBadRequest;
110 break;
111
112 default:
113 code = Coap::kCodeInternalError;
114 break;
115 }
116
117 return code;
118 }
119
SendErrorMessage(const ForwardContext & aForwardContext,Error aError)120 void BorderAgent::SendErrorMessage(const ForwardContext &aForwardContext, Error aError)
121 {
122 Error error = kErrorNone;
123 Coap::Message *message = nullptr;
124
125 VerifyOrExit((message = Get<Tmf::SecureAgent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
126 SuccessOrExit(error = aForwardContext.ToHeader(*message, CoapCodeFromError(aError)));
127 SuccessOrExit(error = SendMessage(*message));
128
129 exit:
130 FreeMessageOnError(message, error);
131 LogWarnOnError(error, "send error CoAP message");
132 }
133
SendErrorMessage(const Coap::Message & aRequest,bool aSeparate,Error aError)134 void BorderAgent::SendErrorMessage(const Coap::Message &aRequest, bool aSeparate, Error aError)
135 {
136 Error error = kErrorNone;
137 Coap::Message *message = nullptr;
138
139 VerifyOrExit((message = Get<Tmf::SecureAgent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
140
141 if (aRequest.IsNonConfirmable() || aSeparate)
142 {
143 message->Init(Coap::kTypeNonConfirmable, CoapCodeFromError(aError));
144 }
145 else
146 {
147 message->Init(Coap::kTypeAck, CoapCodeFromError(aError));
148 }
149
150 if (!aSeparate)
151 {
152 message->SetMessageId(aRequest.GetMessageId());
153 }
154
155 SuccessOrExit(error = message->SetTokenFromMessage(aRequest));
156
157 SuccessOrExit(error = SendMessage(*message));
158
159 exit:
160 FreeMessageOnError(message, error);
161 LogWarnOnError(error, "send error CoAP message");
162 }
163
SendMessage(Coap::Message & aMessage)164 Error BorderAgent::SendMessage(Coap::Message &aMessage)
165 {
166 return Get<Tmf::SecureAgent>().SendMessage(aMessage, Get<Tmf::SecureAgent>().GetMessageInfo());
167 }
168
HandleCoapResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)169 void BorderAgent::HandleCoapResponse(void *aContext,
170 otMessage *aMessage,
171 const otMessageInfo *aMessageInfo,
172 Error aResult)
173 {
174 OT_UNUSED_VARIABLE(aMessageInfo);
175
176 OwnedPtr<ForwardContext> forwardContext(static_cast<ForwardContext *>(aContext));
177
178 forwardContext->Get<BorderAgent>().HandleCoapResponse(*forwardContext.Get(), AsCoapMessagePtr(aMessage), aResult);
179 }
180
HandleCoapResponse(const ForwardContext & aForwardContext,const Coap::Message * aResponse,Error aResult)181 void BorderAgent::HandleCoapResponse(const ForwardContext &aForwardContext,
182 const Coap::Message *aResponse,
183 Error aResult)
184 {
185 Coap::Message *message = nullptr;
186 Error error;
187
188 SuccessOrExit(error = aResult);
189 VerifyOrExit((message = Get<Tmf::SecureAgent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
190
191 if (aForwardContext.IsPetition() && aResponse->GetCode() == Coap::kCodeChanged)
192 {
193 uint8_t state;
194
195 SuccessOrExit(error = Tlv::Find<StateTlv>(*aResponse, state));
196
197 if (state == StateTlv::kAccept)
198 {
199 uint16_t sessionId;
200
201 SuccessOrExit(error = Tlv::Find<CommissionerSessionIdTlv>(*aResponse, sessionId));
202
203 Get<Mle::Mle>().GetCommissionerAloc(sessionId, mCommissionerAloc.GetAddress());
204 Get<ThreadNetif>().AddUnicastAddress(mCommissionerAloc);
205 IgnoreError(Get<Ip6::Udp>().AddReceiver(mUdpReceiver));
206 mState = kStateAccepted;
207
208 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
209 if (mUsingEphemeralKey)
210 {
211 mCounters.mEpskcCommissionerPetitions++;
212 }
213 else
214 #endif
215 {
216 mCounters.mPskcCommissionerPetitions++;
217 }
218
219 LogInfo("Commissioner accepted - SessionId:%u ALOC:%s", sessionId,
220 mCommissionerAloc.GetAddress().ToString().AsCString());
221 }
222 else
223 {
224 LogInfo("Commissioner rejected");
225 }
226 }
227
228 SuccessOrExit(error = aForwardContext.ToHeader(*message, aResponse->GetCode()));
229
230 if (aResponse->GetLength() > aResponse->GetOffset())
231 {
232 SuccessOrExit(error = message->SetPayloadMarker());
233 }
234
235 SuccessOrExit(error = ForwardToCommissioner(*message, *aResponse));
236
237 exit:
238
239 if (error != kErrorNone)
240 {
241 FreeMessage(message);
242
243 LogWarn("Commissioner request[%u] failed: %s", aForwardContext.GetMessageId(), ErrorToString(error));
244
245 SendErrorMessage(aForwardContext, error);
246 }
247 }
248
BorderAgent(Instance & aInstance)249 BorderAgent::BorderAgent(Instance &aInstance)
250 : InstanceLocator(aInstance)
251 , mState(kStateStopped)
252 , mUdpProxyPort(0)
253 , mUdpReceiver(BorderAgent::HandleUdpReceive, this)
254 , mTimer(aInstance)
255 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
256 , mIdInitialized(false)
257 #endif
258 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
259 , mUsingEphemeralKey(false)
260 , mOldUdpPort(0)
261 , mEphemeralKeyTimer(aInstance)
262 , mEphemeralKeyTask(aInstance)
263 #endif
264 {
265 mCommissionerAloc.InitAsThreadOriginMeshLocal();
266 ClearAllBytes(mCounters);
267 }
268
269 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
GetId(Id & aId)270 Error BorderAgent::GetId(Id &aId)
271 {
272 Error error = kErrorNone;
273 Settings::BorderAgentId id;
274
275 VerifyOrExit(!mIdInitialized, error = kErrorNone);
276
277 if (Get<Settings>().Read(id) != kErrorNone)
278 {
279 Random::NonCrypto::Fill(id.GetId());
280 SuccessOrExit(error = Get<Settings>().Save(id));
281 }
282
283 mId = id.GetId();
284 mIdInitialized = true;
285
286 exit:
287 if (error == kErrorNone)
288 {
289 aId = mId;
290 }
291 return error;
292 }
293
SetId(const Id & aId)294 Error BorderAgent::SetId(const Id &aId)
295 {
296 Error error = kErrorNone;
297 Settings::BorderAgentId id;
298
299 id.SetId(aId);
300 SuccessOrExit(error = Get<Settings>().Save(id));
301 mId = aId;
302 mIdInitialized = true;
303
304 exit:
305 return error;
306 }
307 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
308
HandleNotifierEvents(Events aEvents)309 void BorderAgent::HandleNotifierEvents(Events aEvents)
310 {
311 if ((aEvents.ContainsAny(kEventThreadRoleChanged | kEventCommissionerStateChanged)))
312 {
313 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
314 VerifyOrExit(Get<Commissioner>().IsDisabled());
315 #endif
316
317 if (Get<Mle::MleRouter>().IsAttached())
318 {
319 Start();
320 }
321 else
322 {
323 Stop();
324 }
325 }
326
327 if (aEvents.ContainsAny(kEventPskcChanged))
328 {
329 VerifyOrExit(mState != kStateStopped);
330
331 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
332 // No-op if Ephemeralkey mode is activated, new pskc will be applied
333 // when Ephemeralkey mode is deactivated.
334 VerifyOrExit(!mUsingEphemeralKey);
335 #endif
336
337 {
338 Pskc pskc;
339 Get<KeyManager>().GetPskc(pskc);
340
341 // If there is secure session already established, it won't be impacted,
342 // new pskc will be applied for next connection.
343 SuccessOrExit(Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
344 pskc.Clear();
345 }
346 }
347
348 exit:
349 return;
350 }
351
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)352 template <> void BorderAgent::HandleTmf<kUriProxyTx>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
353 {
354 OT_UNUSED_VARIABLE(aMessageInfo);
355
356 Error error = kErrorNone;
357 Message *message = nullptr;
358 Ip6::MessageInfo messageInfo;
359 OffsetRange offsetRange;
360 UdpEncapsulationTlvHeader udpEncapHeader;
361
362 VerifyOrExit(mState != kStateStopped);
363
364 SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Tlv::kUdpEncapsulation, offsetRange));
365
366 SuccessOrExit(error = aMessage.Read(offsetRange, udpEncapHeader));
367 offsetRange.AdvanceOffset(sizeof(UdpEncapsulationTlvHeader));
368
369 VerifyOrExit(udpEncapHeader.GetSourcePort() > 0 && udpEncapHeader.GetDestinationPort() > 0, error = kErrorDrop);
370
371 VerifyOrExit((message = Get<Ip6::Udp>().NewMessage()) != nullptr, error = kErrorNoBufs);
372 SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange));
373
374 messageInfo.SetSockPort(udpEncapHeader.GetSourcePort());
375 messageInfo.SetSockAddr(mCommissionerAloc.GetAddress());
376 messageInfo.SetPeerPort(udpEncapHeader.GetDestinationPort());
377
378 SuccessOrExit(error = Tlv::Find<Ip6AddressTlv>(aMessage, messageInfo.GetPeerAddr()));
379
380 SuccessOrExit(error = Get<Ip6::Udp>().SendDatagram(*message, messageInfo));
381 mUdpProxyPort = udpEncapHeader.GetSourcePort();
382
383 LogInfo("Proxy transmit sent to %s", messageInfo.GetPeerAddr().ToString().AsCString());
384
385 exit:
386 FreeMessageOnError(message, error);
387 LogWarnOnError(error, "send proxy stream");
388 }
389
HandleUdpReceive(void * aContext,const otMessage * aMessage,const otMessageInfo * aMessageInfo)390 bool BorderAgent::HandleUdpReceive(void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo)
391 {
392 return static_cast<BorderAgent *>(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo));
393 }
394
HandleUdpReceive(const Message & aMessage,const Ip6::MessageInfo & aMessageInfo)395 bool BorderAgent::HandleUdpReceive(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
396 {
397 Error error;
398 Coap::Message *message = nullptr;
399
400 if (aMessageInfo.GetSockAddr() != mCommissionerAloc.GetAddress())
401 {
402 LogDebg("Filtered out message for commissioner: dest %s != %s (ALOC)",
403 aMessageInfo.GetSockAddr().ToString().AsCString(),
404 mCommissionerAloc.GetAddress().ToString().AsCString());
405 ExitNow(error = kErrorDestinationAddressFiltered);
406 }
407
408 VerifyOrExit(aMessage.GetLength() > 0, error = kErrorNone);
409
410 message = Get<Tmf::SecureAgent>().NewPriorityNonConfirmablePostMessage(kUriProxyRx);
411 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
412
413 {
414 ExtendedTlv extTlv;
415 UdpEncapsulationTlvHeader udpEncapHeader;
416 OffsetRange offsetRange;
417
418 offsetRange.InitFromMessageOffsetToEnd(aMessage);
419
420 extTlv.SetType(Tlv::kUdpEncapsulation);
421 extTlv.SetLength(sizeof(UdpEncapsulationTlvHeader) + offsetRange.GetLength());
422 SuccessOrExit(error = message->Append(extTlv));
423
424 udpEncapHeader.SetSourcePort(aMessageInfo.GetPeerPort());
425 udpEncapHeader.SetDestinationPort(aMessageInfo.GetSockPort());
426 SuccessOrExit(error = message->Append(udpEncapHeader));
427 SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange));
428 }
429
430 SuccessOrExit(error = Tlv::Append<Ip6AddressTlv>(*message, aMessageInfo.GetPeerAddr()));
431
432 SuccessOrExit(error = SendMessage(*message));
433
434 LogInfo("Sent to commissioner on ProxyRx (c/ur)");
435
436 exit:
437 FreeMessageOnError(message, error);
438 if (error != kErrorDestinationAddressFiltered)
439 {
440 LogWarnOnError(error, "notify commissioner on ProxyRx (c/ur)");
441 }
442
443 return error != kErrorDestinationAddressFiltered;
444 }
445
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)446 template <> void BorderAgent::HandleTmf<kUriRelayRx>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
447 {
448 OT_UNUSED_VARIABLE(aMessageInfo);
449
450 Coap::Message *message = nullptr;
451 Error error = kErrorNone;
452
453 VerifyOrExit(mState != kStateStopped);
454
455 VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorDrop);
456
457 message = Get<Tmf::SecureAgent>().NewPriorityNonConfirmablePostMessage(kUriRelayRx);
458 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
459
460 SuccessOrExit(error = ForwardToCommissioner(*message, aMessage));
461 LogInfo("Sent to commissioner on RelayRx (c/rx)");
462
463 exit:
464 FreeMessageOnError(message, error);
465 }
466
ForwardToCommissioner(Coap::Message & aForwardMessage,const Message & aMessage)467 Error BorderAgent::ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage)
468 {
469 Error error;
470 OffsetRange offsetRange;
471
472 offsetRange.InitFromMessageOffsetToEnd(aMessage);
473 SuccessOrExit(error = aForwardMessage.AppendBytesFromMessage(aMessage, offsetRange));
474
475 SuccessOrExit(error = SendMessage(aForwardMessage));
476
477 LogInfo("Sent to commissioner");
478
479 exit:
480 LogWarnOnError(error, "send to commissioner");
481 return error;
482 }
483
484 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)485 void BorderAgent::HandleTmf<kUriCommissionerPetition>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
486 {
487 IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriLeaderPetition));
488 }
489
490 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)491 void BorderAgent::HandleTmf<kUriCommissionerGet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
492 {
493 HandleTmfDatasetGet(aMessage, aMessageInfo, kUriCommissionerGet);
494 }
495
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)496 template <> void BorderAgent::HandleTmf<kUriActiveGet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
497 {
498 HandleTmfDatasetGet(aMessage, aMessageInfo, kUriActiveGet);
499 mCounters.mMgmtActiveGets++;
500 }
501
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)502 template <> void BorderAgent::HandleTmf<kUriPendingGet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
503 {
504 HandleTmfDatasetGet(aMessage, aMessageInfo, kUriPendingGet);
505 mCounters.mMgmtPendingGets++;
506 }
507
508 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)509 void BorderAgent::HandleTmf<kUriCommissionerKeepAlive>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
510 {
511 VerifyOrExit(mState != kStateStopped);
512
513 SuccessOrExit(ForwardToLeader(aMessage, aMessageInfo, kUriLeaderKeepAlive));
514 mTimer.Start(kKeepAliveTimeout);
515
516 exit:
517 return;
518 }
519
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)520 template <> void BorderAgent::HandleTmf<kUriRelayTx>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
521 {
522 OT_UNUSED_VARIABLE(aMessageInfo);
523
524 Error error = kErrorNone;
525 uint16_t joinerRouterRloc;
526 Coap::Message *message = nullptr;
527 Tmf::MessageInfo messageInfo(GetInstance());
528 OffsetRange offsetRange;
529
530 VerifyOrExit(mState != kStateStopped);
531
532 VerifyOrExit(aMessage.IsNonConfirmablePostRequest());
533
534 SuccessOrExit(error = Tlv::Find<JoinerRouterLocatorTlv>(aMessage, joinerRouterRloc));
535
536 message = Get<Tmf::Agent>().NewPriorityNonConfirmablePostMessage(kUriRelayTx);
537 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
538
539 offsetRange.InitFromMessageOffsetToEnd(aMessage);
540 SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange));
541
542 messageInfo.SetSockAddrToRlocPeerAddrTo(joinerRouterRloc);
543 messageInfo.SetSockPortToTmf();
544
545 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
546
547 LogInfo("Sent to joiner router request on RelayTx (c/tx)");
548
549 exit:
550 FreeMessageOnError(message, error);
551 LogWarnOnError(error, "send to joiner router request RelayTx (c/tx)");
552 }
553
ForwardToLeader(const Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo,Uri aUri)554 Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri)
555 {
556 Error error = kErrorNone;
557 OwnedPtr<ForwardContext> forwardContext;
558 Tmf::MessageInfo messageInfo(GetInstance());
559 Coap::Message *message = nullptr;
560 bool petition = false;
561 bool separate = false;
562 OffsetRange offsetRange;
563
564 VerifyOrExit(mState != kStateStopped);
565
566 switch (aUri)
567 {
568 case kUriLeaderPetition:
569 petition = true;
570 separate = true;
571 break;
572 case kUriLeaderKeepAlive:
573 separate = true;
574 break;
575 default:
576 break;
577 }
578
579 if (separate)
580 {
581 SuccessOrExit(error = Get<Tmf::SecureAgent>().SendAck(aMessage, aMessageInfo));
582 }
583
584 forwardContext.Reset(ForwardContext::AllocateAndInit(GetInstance(), aMessage, petition, separate));
585 VerifyOrExit(!forwardContext.IsNull(), error = kErrorNoBufs);
586
587 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(aUri);
588 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
589
590 offsetRange.InitFromMessageOffsetToEnd(aMessage);
591 SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange));
592
593 messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc();
594 messageInfo.SetSockPortToTmf();
595
596 SuccessOrExit(error =
597 Get<Tmf::Agent>().SendMessage(*message, messageInfo, HandleCoapResponse, forwardContext.Get()));
598
599 // Release the ownership of `forwardContext` since `SendMessage()`
600 // will own it. We take back ownership from `HandleCoapResponse()`
601 // callback.
602
603 forwardContext.Release();
604
605 LogInfo("Forwarded request to leader on %s", PathForUri(aUri));
606
607 exit:
608 LogWarnOnError(error, "forward to leader");
609
610 if (error != kErrorNone)
611 {
612 FreeMessage(message);
613 SendErrorMessage(aMessage, separate, error);
614 }
615
616 return error;
617 }
618
HandleTmfDatasetGet(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo,Uri aUri)619 void BorderAgent::HandleTmfDatasetGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri)
620 {
621 Error error = kErrorNone;
622 Coap::Message *response = nullptr;
623
624 // When processing `MGMT_GET` request directly on Border Agent,
625 // the Security Policy flags (O-bit) should be ignore to allow
626 // the commissioner candidate to get the full Operational Dataset.
627
628 switch (aUri)
629 {
630 case kUriActiveGet:
631 response = Get<ActiveDatasetManager>().ProcessGetRequest(aMessage, DatasetManager::kIgnoreSecurityPolicyFlags);
632 break;
633
634 case kUriPendingGet:
635 response = Get<PendingDatasetManager>().ProcessGetRequest(aMessage, DatasetManager::kIgnoreSecurityPolicyFlags);
636 break;
637
638 case kUriCommissionerGet:
639 response = Get<NetworkData::Leader>().ProcessCommissionerGetRequest(aMessage);
640 break;
641
642 default:
643 break;
644 }
645
646 VerifyOrExit(response != nullptr, error = kErrorParse);
647
648 SuccessOrExit(error = Get<Tmf::SecureAgent>().SendMessage(*response, aMessageInfo));
649
650 LogInfo("Sent %s response to non-active commissioner", PathForUri(aUri));
651
652 exit:
653 LogWarnOnError(error, "send Active/Pending/CommissionerGet response");
654 FreeMessageOnError(response, error);
655 }
656
HandleConnected(SecureTransport::ConnectEvent aEvent,void * aContext)657 void BorderAgent::HandleConnected(SecureTransport::ConnectEvent aEvent, void *aContext)
658 {
659 static_cast<BorderAgent *>(aContext)->HandleConnected(aEvent);
660 }
661
HandleConnected(SecureTransport::ConnectEvent aEvent)662 void BorderAgent::HandleConnected(SecureTransport::ConnectEvent aEvent)
663 {
664 if (aEvent == SecureTransport::kConnected)
665 {
666 LogInfo("SecureSession connected");
667 mState = kStateConnected;
668 mTimer.Start(kKeepAliveTimeout);
669 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
670 if (mUsingEphemeralKey)
671 {
672 mCounters.mEpskcSecureSessionSuccesses++;
673 mEphemeralKeyTask.Post();
674 }
675 else
676 #endif
677 {
678 mCounters.mPskcSecureSessionSuccesses++;
679 }
680 }
681 else
682 {
683 LogInfo("SecureSession disconnected");
684 IgnoreError(Get<Ip6::Udp>().RemoveReceiver(mUdpReceiver));
685 Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
686
687 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
688 if (mUsingEphemeralKey)
689 {
690 RestartAfterRemovingEphemeralKey();
691
692 if (aEvent == SecureTransport::kDisconnectedError)
693 {
694 mCounters.mEpskcSecureSessionFailures++;
695 }
696 else if (aEvent == SecureTransport::kDisconnectedPeerClosed)
697 {
698 mCounters.mEpskcDeactivationDisconnects++;
699 }
700 }
701 else
702 #endif
703 {
704 mState = kStateStarted;
705 mUdpProxyPort = 0;
706
707 if (aEvent == SecureTransport::kDisconnectedError)
708 {
709 mCounters.mPskcSecureSessionFailures++;
710 }
711 }
712 }
713 }
714
GetUdpPort(void) const715 uint16_t BorderAgent::GetUdpPort(void) const { return Get<Tmf::SecureAgent>().GetUdpPort(); }
716
Start(uint16_t aUdpPort)717 Error BorderAgent::Start(uint16_t aUdpPort)
718 {
719 Error error;
720 Pskc pskc;
721
722 Get<KeyManager>().GetPskc(pskc);
723 error = Start(aUdpPort, pskc.m8, Pskc::kSize);
724 pskc.Clear();
725
726 return error;
727 }
728
Start(uint16_t aUdpPort,const uint8_t * aPsk,uint8_t aPskLength)729 Error BorderAgent::Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLength)
730 {
731 Error error = kErrorNone;
732
733 VerifyOrExit(mState == kStateStopped);
734
735 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
736 if (mUsingEphemeralKey)
737 {
738 SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(aUdpPort, kMaxEphemeralKeyConnectionAttempts,
739 HandleSecureAgentStopped, this));
740 }
741 else
742 #endif
743 {
744 SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(aUdpPort));
745 }
746
747 SuccessOrExit(error = Get<Tmf::SecureAgent>().SetPsk(aPsk, aPskLength));
748
749 Get<Tmf::SecureAgent>().SetConnectEventCallback(HandleConnected, this);
750
751 mState = kStateStarted;
752 mUdpProxyPort = 0;
753
754 LogInfo("Border Agent start listening on port %u", GetUdpPort());
755
756 exit:
757 LogWarnOnError(error, "start agent");
758 return error;
759 }
760
HandleTimeout(void)761 void BorderAgent::HandleTimeout(void)
762 {
763 if (Get<Tmf::SecureAgent>().IsConnected())
764 {
765 Get<Tmf::SecureAgent>().Disconnect();
766 LogWarn("Reset commissioner session");
767 }
768 }
769
Stop(void)770 void BorderAgent::Stop(void)
771 {
772 VerifyOrExit(mState != kStateStopped);
773
774 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
775 if (mUsingEphemeralKey)
776 {
777 mUsingEphemeralKey = false;
778 mEphemeralKeyTimer.Stop();
779 mEphemeralKeyTask.Post();
780 }
781 #endif
782
783 mTimer.Stop();
784 Get<Tmf::SecureAgent>().Stop();
785
786 mState = kStateStopped;
787 mUdpProxyPort = 0;
788 LogInfo("Border Agent stopped");
789
790 exit:
791 return;
792 }
793
Disconnect(void)794 void BorderAgent::Disconnect(void)
795 {
796 VerifyOrExit(mState == kStateConnected || mState == kStateAccepted);
797
798 Get<Tmf::SecureAgent>().Disconnect();
799
800 exit:
801 return;
802 }
803
804 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
805
SetEphemeralKey(const char * aKeyString,uint32_t aTimeout,uint16_t aUdpPort)806 Error BorderAgent::SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, uint16_t aUdpPort)
807 {
808 Error error = kErrorNone;
809 uint16_t length = StringLength(aKeyString, kMaxEphemeralKeyLength + 1);
810
811 VerifyOrExit(mState == kStateStarted, error = kErrorInvalidState);
812 VerifyOrExit((length >= kMinEphemeralKeyLength) && (length <= kMaxEphemeralKeyLength), error = kErrorInvalidArgs);
813
814 if (!mUsingEphemeralKey)
815 {
816 mOldUdpPort = GetUdpPort();
817 }
818
819 Stop();
820
821 // We set the `mUsingEphemeralKey` before `Start()` since
822 // callbacks (like `HandleConnected()`) may be invoked from
823 // `Start()` itself.
824
825 mUsingEphemeralKey = true;
826
827 error = Start(aUdpPort, reinterpret_cast<const uint8_t *>(aKeyString), static_cast<uint8_t>(length));
828
829 if (error != kErrorNone)
830 {
831 mUsingEphemeralKey = false;
832 IgnoreError(Start(mOldUdpPort));
833 mCounters.mEpskcStartSecureSessionErrors++;
834 ExitNow();
835 }
836
837 mEphemeralKeyTask.Post();
838
839 if (aTimeout == 0)
840 {
841 aTimeout = kDefaultEphemeralKeyTimeout;
842 }
843
844 aTimeout = Min(aTimeout, kMaxEphemeralKeyTimeout);
845
846 mEphemeralKeyTimer.Start(aTimeout);
847 mCounters.mEpskcActivations++;
848
849 LogInfo("Allow ephemeral key for %lu msec on port %u", ToUlong(aTimeout), GetUdpPort());
850
851 exit:
852 switch (error)
853 {
854 case kErrorInvalidState:
855 mCounters.mEpskcInvalidBaStateErrors++;
856 break;
857 case kErrorInvalidArgs:
858 mCounters.mEpskcInvalidArgsErrors++;
859 break;
860 default:
861 break;
862 }
863
864 return error;
865 }
866
ClearEphemeralKey(void)867 void BorderAgent::ClearEphemeralKey(void)
868 {
869 VerifyOrExit(mUsingEphemeralKey);
870
871 LogInfo("Clearing ephemeral key");
872
873 mCounters.mEpskcDeactivationClears++;
874
875 switch (mState)
876 {
877 case kStateStarted:
878 RestartAfterRemovingEphemeralKey();
879 break;
880
881 case kStateStopped:
882 case kStateConnected:
883 case kStateAccepted:
884 // If a commissioner connection is currently active, we'll
885 // wait for it to disconnect or for the ephemeral key timeout
886 // or `kKeepAliveTimeout` to expire before removing the key
887 // and restarting the agent.
888 break;
889 }
890
891 exit:
892 return;
893 }
894
HandleEphemeralKeyTimeout(void)895 void BorderAgent::HandleEphemeralKeyTimeout(void)
896 {
897 LogInfo("Ephemeral key timed out");
898 mCounters.mEpskcDeactivationTimeouts++;
899 RestartAfterRemovingEphemeralKey();
900 }
901
InvokeEphemeralKeyCallback(void)902 void BorderAgent::InvokeEphemeralKeyCallback(void) { mEphemeralKeyCallback.InvokeIfSet(); }
903
RestartAfterRemovingEphemeralKey(void)904 void BorderAgent::RestartAfterRemovingEphemeralKey(void)
905 {
906 LogInfo("Removing ephemeral key and restarting agent");
907
908 Stop();
909 IgnoreError(Start(mOldUdpPort));
910 }
911
HandleSecureAgentStopped(void * aContext)912 void BorderAgent::HandleSecureAgentStopped(void *aContext)
913 {
914 reinterpret_cast<BorderAgent *>(aContext)->HandleSecureAgentStopped();
915 }
916
HandleSecureAgentStopped(void)917 void BorderAgent::HandleSecureAgentStopped(void)
918 {
919 LogInfo("Reached max allowed connection attempts with ephemeral key");
920 mCounters.mEpskcDeactivationMaxAttempts++;
921 RestartAfterRemovingEphemeralKey();
922 }
923
924 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
925
926 } // namespace MeshCoP
927 } // namespace ot
928
929 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
930