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 }
674 else
675 #endif
676 {
677 mCounters.mPskcSecureSessionSuccesses++;
678 }
679 }
680 else
681 {
682 LogInfo("SecureSession disconnected");
683 IgnoreError(Get<Ip6::Udp>().RemoveReceiver(mUdpReceiver));
684 Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
685
686 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
687 if (mUsingEphemeralKey)
688 {
689 RestartAfterRemovingEphemeralKey();
690 if (aEvent == SecureTransport::kDisconnectedError)
691 {
692 mCounters.mEpskcSecureSessionFailures++;
693 }
694 if (aEvent == SecureTransport::kDisconnectedPeerClosed)
695 {
696 mCounters.mEpskcDeactivationDisconnects++;
697 }
698 }
699 else
700 #endif
701 {
702 mState = kStateStarted;
703 mUdpProxyPort = 0;
704 if (aEvent == SecureTransport::kDisconnectedError)
705 {
706 mCounters.mPskcSecureSessionFailures++;
707 }
708 }
709 }
710 }
711
GetUdpPort(void) const712 uint16_t BorderAgent::GetUdpPort(void) const { return Get<Tmf::SecureAgent>().GetUdpPort(); }
713
Start(uint16_t aUdpPort)714 Error BorderAgent::Start(uint16_t aUdpPort)
715 {
716 Error error;
717 Pskc pskc;
718
719 Get<KeyManager>().GetPskc(pskc);
720 error = Start(aUdpPort, pskc.m8, Pskc::kSize);
721 pskc.Clear();
722
723 return error;
724 }
725
Start(uint16_t aUdpPort,const uint8_t * aPsk,uint8_t aPskLength)726 Error BorderAgent::Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLength)
727 {
728 Error error = kErrorNone;
729
730 VerifyOrExit(mState == kStateStopped);
731
732 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
733 if (mUsingEphemeralKey)
734 {
735 SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(aUdpPort, kMaxEphemeralKeyConnectionAttempts,
736 HandleSecureAgentStopped, this));
737 }
738 else
739 #endif
740 {
741 SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(aUdpPort));
742 }
743
744 SuccessOrExit(error = Get<Tmf::SecureAgent>().SetPsk(aPsk, aPskLength));
745
746 Get<Tmf::SecureAgent>().SetConnectEventCallback(HandleConnected, this);
747
748 mState = kStateStarted;
749 mUdpProxyPort = 0;
750
751 LogInfo("Border Agent start listening on port %u", GetUdpPort());
752
753 exit:
754 LogWarnOnError(error, "start agent");
755 return error;
756 }
757
HandleTimeout(void)758 void BorderAgent::HandleTimeout(void)
759 {
760 if (Get<Tmf::SecureAgent>().IsConnected())
761 {
762 Get<Tmf::SecureAgent>().Disconnect();
763 LogWarn("Reset commissioner session");
764 }
765 }
766
Stop(void)767 void BorderAgent::Stop(void)
768 {
769 VerifyOrExit(mState != kStateStopped);
770
771 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
772 if (mUsingEphemeralKey)
773 {
774 mUsingEphemeralKey = false;
775 mEphemeralKeyTimer.Stop();
776 mEphemeralKeyTask.Post();
777 }
778 #endif
779
780 mTimer.Stop();
781 Get<Tmf::SecureAgent>().Stop();
782
783 mState = kStateStopped;
784 mUdpProxyPort = 0;
785 LogInfo("Border Agent stopped");
786
787 exit:
788 return;
789 }
790
791 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
792
SetEphemeralKey(const char * aKeyString,uint32_t aTimeout,uint16_t aUdpPort)793 Error BorderAgent::SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, uint16_t aUdpPort)
794 {
795 Error error = kErrorNone;
796 uint16_t length = StringLength(aKeyString, kMaxEphemeralKeyLength + 1);
797
798 VerifyOrExit(mState == kStateStarted, error = kErrorInvalidState);
799 VerifyOrExit((length >= kMinEphemeralKeyLength) && (length <= kMaxEphemeralKeyLength), error = kErrorInvalidArgs);
800
801 if (!mUsingEphemeralKey)
802 {
803 mOldUdpPort = GetUdpPort();
804 }
805
806 Stop();
807
808 // We set the `mUsingEphemeralKey` before `Start()` since
809 // callbacks (like `HandleConnected()`) may be invoked from
810 // `Start()` itself.
811
812 mUsingEphemeralKey = true;
813
814 error = Start(aUdpPort, reinterpret_cast<const uint8_t *>(aKeyString), static_cast<uint8_t>(length));
815
816 if (error != kErrorNone)
817 {
818 mUsingEphemeralKey = false;
819 IgnoreError(Start(mOldUdpPort));
820 mCounters.mEpskcStartSecureSessionErrors++;
821 ExitNow();
822 }
823
824 mEphemeralKeyTask.Post();
825
826 if (aTimeout == 0)
827 {
828 aTimeout = kDefaultEphemeralKeyTimeout;
829 }
830
831 aTimeout = Min(aTimeout, kMaxEphemeralKeyTimeout);
832
833 mEphemeralKeyTimer.Start(aTimeout);
834 mCounters.mEpskcActivations++;
835
836 LogInfo("Allow ephemeral key for %lu msec on port %u", ToUlong(aTimeout), GetUdpPort());
837
838 exit:
839 switch (error)
840 {
841 case kErrorInvalidState:
842 mCounters.mEpskcInvalidBaStateErrors++;
843 break;
844 case kErrorInvalidArgs:
845 mCounters.mEpskcInvalidArgsErrors++;
846 break;
847 default:
848 break;
849 }
850
851 return error;
852 }
853
ClearEphemeralKey(void)854 void BorderAgent::ClearEphemeralKey(void)
855 {
856 VerifyOrExit(mUsingEphemeralKey);
857
858 LogInfo("Clearing ephemeral key");
859
860 if (mEphemeralKeyTimer.IsRunning())
861 {
862 mCounters.mEpskcDeactivationClears++;
863 }
864 else
865 {
866 mCounters.mEpskcDeactivationTimeouts++;
867 }
868
869 mEphemeralKeyTimer.Stop();
870
871 switch (mState)
872 {
873 case kStateStarted:
874 RestartAfterRemovingEphemeralKey();
875 break;
876
877 case kStateStopped:
878 case kStateConnected:
879 case kStateAccepted:
880 // If there is an active commissioner connection, we wait till
881 // it gets disconnected before removing ephemeral key and
882 // restarting the agent.
883 break;
884 }
885
886 exit:
887 return;
888 }
889
HandleEphemeralKeyTimeout(void)890 void BorderAgent::HandleEphemeralKeyTimeout(void)
891 {
892 LogInfo("Ephemeral key timed out");
893 ClearEphemeralKey();
894 }
895
InvokeEphemeralKeyCallback(void)896 void BorderAgent::InvokeEphemeralKeyCallback(void) { mEphemeralKeyCallback.InvokeIfSet(); }
897
RestartAfterRemovingEphemeralKey(void)898 void BorderAgent::RestartAfterRemovingEphemeralKey(void)
899 {
900 LogInfo("Removing ephemeral key and restarting agent");
901
902 Stop();
903 IgnoreError(Start(mOldUdpPort));
904 }
905
HandleSecureAgentStopped(void * aContext)906 void BorderAgent::HandleSecureAgentStopped(void *aContext)
907 {
908 reinterpret_cast<BorderAgent *>(aContext)->HandleSecureAgentStopped();
909 }
910
HandleSecureAgentStopped(void)911 void BorderAgent::HandleSecureAgentStopped(void)
912 {
913 LogInfo("Reached max allowed connection attempts with ephemeral key");
914 RestartAfterRemovingEphemeralKey();
915 mCounters.mEpskcDeactivationMaxAttempts++;
916 }
917
918 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
919
920 } // namespace MeshCoP
921 } // namespace ot
922
923 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
924