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 IgnoreError(Get<Mle::MleRouter>().GetCommissionerAloc(mCommissionerAloc.GetAddress(), sessionId));
204 Get<ThreadNetif>().AddUnicastAddress(mCommissionerAloc);
205 IgnoreError(Get<Ip6::Udp>().AddReceiver(mUdpReceiver));
206
207 LogInfo("Commissioner accepted - SessionId:%u ALOC:%s", sessionId,
208 mCommissionerAloc.GetAddress().ToString().AsCString());
209 }
210 }
211
212 SuccessOrExit(error = aForwardContext.ToHeader(*message, aResponse->GetCode()));
213
214 if (aResponse->GetLength() > aResponse->GetOffset())
215 {
216 SuccessOrExit(error = message->SetPayloadMarker());
217 }
218
219 SuccessOrExit(error = ForwardToCommissioner(*message, *aResponse));
220
221 exit:
222
223 if (error != kErrorNone)
224 {
225 FreeMessage(message);
226
227 LogWarn("Commissioner request[%u] failed: %s", aForwardContext.GetMessageId(), ErrorToString(error));
228
229 SendErrorMessage(aForwardContext, error);
230 }
231 }
232
BorderAgent(Instance & aInstance)233 BorderAgent::BorderAgent(Instance &aInstance)
234 : InstanceLocator(aInstance)
235 , mState(kStateStopped)
236 , mUdpProxyPort(0)
237 , mUdpReceiver(BorderAgent::HandleUdpReceive, this)
238 , mTimer(aInstance)
239 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
240 , mIdInitialized(false)
241 #endif
242 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
243 , mUsingEphemeralKey(false)
244 , mOldUdpPort(0)
245 , mEphemeralKeyTimer(aInstance)
246 , mEphemeralKeyTask(aInstance)
247 #endif
248 {
249 mCommissionerAloc.InitAsThreadOriginMeshLocal();
250 }
251
252 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
GetId(Id & aId)253 Error BorderAgent::GetId(Id &aId)
254 {
255 Error error = kErrorNone;
256 Settings::BorderAgentId id;
257
258 VerifyOrExit(!mIdInitialized, error = kErrorNone);
259
260 if (Get<Settings>().Read(id) != kErrorNone)
261 {
262 Random::NonCrypto::Fill(id.GetId());
263 SuccessOrExit(error = Get<Settings>().Save(id));
264 }
265
266 mId = id.GetId();
267 mIdInitialized = true;
268
269 exit:
270 if (error == kErrorNone)
271 {
272 aId = mId;
273 }
274 return error;
275 }
276
SetId(const Id & aId)277 Error BorderAgent::SetId(const Id &aId)
278 {
279 Error error = kErrorNone;
280 Settings::BorderAgentId id;
281
282 id.SetId(aId);
283 SuccessOrExit(error = Get<Settings>().Save(id));
284 mId = aId;
285 mIdInitialized = true;
286
287 exit:
288 return error;
289 }
290 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
291
HandleNotifierEvents(Events aEvents)292 void BorderAgent::HandleNotifierEvents(Events aEvents)
293 {
294 VerifyOrExit(aEvents.ContainsAny(kEventThreadRoleChanged | kEventCommissionerStateChanged));
295
296 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
297 VerifyOrExit(Get<Commissioner>().IsDisabled());
298 #endif
299
300 if (Get<Mle::MleRouter>().IsAttached())
301 {
302 Start();
303 }
304 else
305 {
306 Stop();
307 }
308
309 exit:
310 return;
311 }
312
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)313 template <> void BorderAgent::HandleTmf<kUriProxyTx>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
314 {
315 OT_UNUSED_VARIABLE(aMessageInfo);
316
317 Error error = kErrorNone;
318 Message *message = nullptr;
319 Ip6::MessageInfo messageInfo;
320 uint16_t offset;
321 uint16_t length;
322 UdpEncapsulationTlvHeader udpEncapHeader;
323
324 VerifyOrExit(mState != kStateStopped);
325
326 SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Tlv::kUdpEncapsulation, offset, length));
327
328 SuccessOrExit(error = aMessage.Read(offset, udpEncapHeader));
329 offset += sizeof(UdpEncapsulationTlvHeader);
330 length -= sizeof(UdpEncapsulationTlvHeader);
331
332 VerifyOrExit(udpEncapHeader.GetSourcePort() > 0 && udpEncapHeader.GetDestinationPort() > 0, error = kErrorDrop);
333
334 VerifyOrExit((message = Get<Ip6::Udp>().NewMessage()) != nullptr, error = kErrorNoBufs);
335 SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offset, length));
336
337 messageInfo.SetSockPort(udpEncapHeader.GetSourcePort());
338 messageInfo.SetSockAddr(mCommissionerAloc.GetAddress());
339 messageInfo.SetPeerPort(udpEncapHeader.GetDestinationPort());
340
341 SuccessOrExit(error = Tlv::Find<Ip6AddressTlv>(aMessage, messageInfo.GetPeerAddr()));
342
343 SuccessOrExit(error = Get<Ip6::Udp>().SendDatagram(*message, messageInfo));
344 mUdpProxyPort = udpEncapHeader.GetSourcePort();
345
346 LogInfo("Proxy transmit sent to %s", messageInfo.GetPeerAddr().ToString().AsCString());
347
348 exit:
349 FreeMessageOnError(message, error);
350 LogWarnOnError(error, "send proxy stream");
351 }
352
HandleUdpReceive(void * aContext,const otMessage * aMessage,const otMessageInfo * aMessageInfo)353 bool BorderAgent::HandleUdpReceive(void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo)
354 {
355 return static_cast<BorderAgent *>(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo));
356 }
357
HandleUdpReceive(const Message & aMessage,const Ip6::MessageInfo & aMessageInfo)358 bool BorderAgent::HandleUdpReceive(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
359 {
360 Error error;
361 Coap::Message *message = nullptr;
362
363 if (aMessageInfo.GetSockAddr() != mCommissionerAloc.GetAddress())
364 {
365 LogDebg("Filtered out message for commissioner: dest %s != %s (ALOC)",
366 aMessageInfo.GetSockAddr().ToString().AsCString(),
367 mCommissionerAloc.GetAddress().ToString().AsCString());
368 ExitNow(error = kErrorDestinationAddressFiltered);
369 }
370
371 VerifyOrExit(aMessage.GetLength() > 0, error = kErrorNone);
372
373 message = Get<Tmf::SecureAgent>().NewPriorityNonConfirmablePostMessage(kUriProxyRx);
374 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
375
376 {
377 ExtendedTlv extTlv;
378 UdpEncapsulationTlvHeader udpEncapHeader;
379 uint16_t udpLength = aMessage.GetLength() - aMessage.GetOffset();
380
381 extTlv.SetType(Tlv::kUdpEncapsulation);
382 extTlv.SetLength(sizeof(UdpEncapsulationTlvHeader) + udpLength);
383 SuccessOrExit(error = message->Append(extTlv));
384
385 udpEncapHeader.SetSourcePort(aMessageInfo.GetPeerPort());
386 udpEncapHeader.SetDestinationPort(aMessageInfo.GetSockPort());
387 SuccessOrExit(error = message->Append(udpEncapHeader));
388 SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(), udpLength));
389 }
390
391 SuccessOrExit(error = Tlv::Append<Ip6AddressTlv>(*message, aMessageInfo.GetPeerAddr()));
392
393 SuccessOrExit(error = SendMessage(*message));
394
395 LogInfo("Sent to commissioner on ProxyRx (c/ur)");
396
397 exit:
398 FreeMessageOnError(message, error);
399 if (error != kErrorDestinationAddressFiltered)
400 {
401 LogWarnOnError(error, "notify commissioner on ProxyRx (c/ur)");
402 }
403
404 return error != kErrorDestinationAddressFiltered;
405 }
406
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)407 template <> void BorderAgent::HandleTmf<kUriRelayRx>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
408 {
409 OT_UNUSED_VARIABLE(aMessageInfo);
410
411 Coap::Message *message = nullptr;
412 Error error = kErrorNone;
413
414 VerifyOrExit(mState != kStateStopped);
415
416 VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorDrop);
417
418 message = Get<Tmf::SecureAgent>().NewPriorityNonConfirmablePostMessage(kUriRelayRx);
419 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
420
421 SuccessOrExit(error = ForwardToCommissioner(*message, aMessage));
422 LogInfo("Sent to commissioner on RelayRx (c/rx)");
423
424 exit:
425 FreeMessageOnError(message, error);
426 }
427
ForwardToCommissioner(Coap::Message & aForwardMessage,const Message & aMessage)428 Error BorderAgent::ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage)
429 {
430 Error error;
431
432 SuccessOrExit(error = aForwardMessage.AppendBytesFromMessage(aMessage, aMessage.GetOffset(),
433 aMessage.GetLength() - aMessage.GetOffset()));
434 SuccessOrExit(error = SendMessage(aForwardMessage));
435
436 LogInfo("Sent to commissioner");
437
438 exit:
439 LogWarnOnError(error, "send to commissioner");
440 return error;
441 }
442
443 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)444 void BorderAgent::HandleTmf<kUriCommissionerPetition>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
445 {
446 IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriLeaderPetition));
447 }
448
449 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)450 void BorderAgent::HandleTmf<kUriCommissionerGet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
451 {
452 IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriCommissionerGet));
453 }
454
455 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)456 void BorderAgent::HandleTmf<kUriCommissionerSet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
457 {
458 IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriCommissionerSet));
459 }
460
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)461 template <> void BorderAgent::HandleTmf<kUriActiveGet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
462 {
463 IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriActiveGet));
464 }
465
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)466 template <> void BorderAgent::HandleTmf<kUriActiveSet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
467 {
468 IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriActiveSet));
469 }
470
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)471 template <> void BorderAgent::HandleTmf<kUriPendingGet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
472 {
473 IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriPendingGet));
474 }
475
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)476 template <> void BorderAgent::HandleTmf<kUriPendingSet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
477 {
478 IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriPendingSet));
479 }
480
481 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)482 void BorderAgent::HandleTmf<kUriCommissionerKeepAlive>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
483 {
484 VerifyOrExit(mState != kStateStopped);
485
486 SuccessOrExit(ForwardToLeader(aMessage, aMessageInfo, kUriLeaderKeepAlive));
487 mTimer.Start(kKeepAliveTimeout);
488
489 exit:
490 return;
491 }
492
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)493 template <> void BorderAgent::HandleTmf<kUriRelayTx>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
494 {
495 OT_UNUSED_VARIABLE(aMessageInfo);
496
497 Error error = kErrorNone;
498 uint16_t joinerRouterRloc;
499 Coap::Message *message = nullptr;
500 Tmf::MessageInfo messageInfo(GetInstance());
501
502 VerifyOrExit(mState != kStateStopped);
503
504 VerifyOrExit(aMessage.IsNonConfirmablePostRequest());
505
506 SuccessOrExit(error = Tlv::Find<JoinerRouterLocatorTlv>(aMessage, joinerRouterRloc));
507
508 message = Get<Tmf::Agent>().NewPriorityNonConfirmablePostMessage(kUriRelayTx);
509 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
510
511 SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(),
512 aMessage.GetLength() - aMessage.GetOffset()));
513
514 messageInfo.SetSockAddrToRlocPeerAddrTo(joinerRouterRloc);
515 messageInfo.SetSockPortToTmf();
516
517 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
518
519 LogInfo("Sent to joiner router request on RelayTx (c/tx)");
520
521 exit:
522 FreeMessageOnError(message, error);
523 LogWarnOnError(error, "send to joiner router request RelayTx (c/tx)");
524 }
525
ForwardToLeader(const Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo,Uri aUri)526 Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri)
527 {
528 Error error = kErrorNone;
529 OwnedPtr<ForwardContext> forwardContext;
530 Tmf::MessageInfo messageInfo(GetInstance());
531 Coap::Message *message = nullptr;
532 bool petition = false;
533 bool separate = false;
534
535 VerifyOrExit(mState != kStateStopped);
536
537 switch (aUri)
538 {
539 case kUriLeaderPetition:
540 petition = true;
541 separate = true;
542 break;
543 case kUriLeaderKeepAlive:
544 separate = true;
545 break;
546 default:
547 break;
548 }
549
550 if (separate)
551 {
552 SuccessOrExit(error = Get<Tmf::SecureAgent>().SendAck(aMessage, aMessageInfo));
553 }
554
555 forwardContext.Reset(ForwardContext::AllocateAndInit(GetInstance(), aMessage, petition, separate));
556 VerifyOrExit(!forwardContext.IsNull(), error = kErrorNoBufs);
557
558 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(aUri);
559 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
560
561 SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(),
562 aMessage.GetLength() - aMessage.GetOffset()));
563
564 SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
565 messageInfo.SetSockPortToTmf();
566
567 SuccessOrExit(error =
568 Get<Tmf::Agent>().SendMessage(*message, messageInfo, HandleCoapResponse, forwardContext.Get()));
569
570 // Release the ownership of `forwardContext` since `SendMessage()`
571 // will own it. We take back ownership from `HandleCoapResponse()`
572 // callback.
573
574 forwardContext.Release();
575
576 LogInfo("Forwarded request to leader on %s", PathForUri(aUri));
577
578 exit:
579 LogWarnOnError(error, "forward to leader");
580
581 if (error != kErrorNone)
582 {
583 FreeMessage(message);
584 SendErrorMessage(aMessage, separate, error);
585 }
586
587 return error;
588 }
589
HandleConnected(bool aConnected,void * aContext)590 void BorderAgent::HandleConnected(bool aConnected, void *aContext)
591 {
592 static_cast<BorderAgent *>(aContext)->HandleConnected(aConnected);
593 }
594
HandleConnected(bool aConnected)595 void BorderAgent::HandleConnected(bool aConnected)
596 {
597 if (aConnected)
598 {
599 LogInfo("Commissioner connected");
600 mState = kStateActive;
601 mTimer.Start(kKeepAliveTimeout);
602 }
603 else
604 {
605 LogInfo("Commissioner disconnected");
606 IgnoreError(Get<Ip6::Udp>().RemoveReceiver(mUdpReceiver));
607 Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
608
609 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
610 if (mUsingEphemeralKey)
611 {
612 RestartAfterRemovingEphemeralKey();
613 }
614 else
615 #endif
616 {
617 mState = kStateStarted;
618 mUdpProxyPort = 0;
619 }
620 }
621 }
622
GetUdpPort(void) const623 uint16_t BorderAgent::GetUdpPort(void) const { return Get<Tmf::SecureAgent>().GetUdpPort(); }
624
Start(uint16_t aUdpPort)625 Error BorderAgent::Start(uint16_t aUdpPort)
626 {
627 Error error;
628 Pskc pskc;
629
630 Get<KeyManager>().GetPskc(pskc);
631 error = Start(aUdpPort, pskc.m8, Pskc::kSize);
632 pskc.Clear();
633
634 return error;
635 }
636
Start(uint16_t aUdpPort,const uint8_t * aPsk,uint8_t aPskLength)637 Error BorderAgent::Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLength)
638 {
639 Error error = kErrorNone;
640
641 VerifyOrExit(mState == kStateStopped);
642
643 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
644 if (mUsingEphemeralKey)
645 {
646 SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(aUdpPort, kMaxEphemeralKeyConnectionAttempts,
647 HandleSecureAgentStopped, this));
648 }
649 else
650 #endif
651 {
652 SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(aUdpPort));
653 }
654
655 SuccessOrExit(error = Get<Tmf::SecureAgent>().SetPsk(aPsk, aPskLength));
656
657 Get<Tmf::SecureAgent>().SetConnectedCallback(HandleConnected, this);
658
659 mState = kStateStarted;
660 mUdpProxyPort = 0;
661
662 LogInfo("Border Agent start listening on port %u", GetUdpPort());
663
664 exit:
665 LogWarnOnError(error, "start agent");
666 return error;
667 }
668
HandleTimeout(void)669 void BorderAgent::HandleTimeout(void)
670 {
671 if (Get<Tmf::SecureAgent>().IsConnected())
672 {
673 Get<Tmf::SecureAgent>().Disconnect();
674 LogWarn("Reset commissioner session");
675 }
676 }
677
Stop(void)678 void BorderAgent::Stop(void)
679 {
680 VerifyOrExit(mState != kStateStopped);
681
682 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
683 if (mUsingEphemeralKey)
684 {
685 mUsingEphemeralKey = false;
686 mEphemeralKeyTimer.Stop();
687 mEphemeralKeyTask.Post();
688 }
689 #endif
690
691 mTimer.Stop();
692 Get<Tmf::SecureAgent>().Stop();
693
694 mState = kStateStopped;
695 mUdpProxyPort = 0;
696 LogInfo("Border Agent stopped");
697
698 exit:
699 return;
700 }
701
702 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
703
SetEphemeralKey(const char * aKeyString,uint32_t aTimeout,uint16_t aUdpPort)704 Error BorderAgent::SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, uint16_t aUdpPort)
705 {
706 Error error = kErrorNone;
707 uint16_t length = StringLength(aKeyString, kMaxEphemeralKeyLength + 1);
708
709 VerifyOrExit(mState == kStateStarted, error = kErrorInvalidState);
710 VerifyOrExit((length >= kMinEphemeralKeyLength) && (length <= kMaxEphemeralKeyLength), error = kErrorInvalidArgs);
711
712 if (!mUsingEphemeralKey)
713 {
714 mOldUdpPort = GetUdpPort();
715 }
716
717 Stop();
718
719 // We set the `mUsingEphemeralKey` before `Start()` since
720 // callbacks (like `HandleConnected()`) may be invoked from
721 // `Start()` itself.
722
723 mUsingEphemeralKey = true;
724
725 error = Start(aUdpPort, reinterpret_cast<const uint8_t *>(aKeyString), static_cast<uint8_t>(length));
726
727 if (error != kErrorNone)
728 {
729 mUsingEphemeralKey = false;
730 IgnoreError(Start(mOldUdpPort));
731 ExitNow();
732 }
733
734 mEphemeralKeyTask.Post();
735
736 if (aTimeout == 0)
737 {
738 aTimeout = kDefaultEphemeralKeyTimeout;
739 }
740
741 aTimeout = Min(aTimeout, kMaxEphemeralKeyTimeout);
742
743 mEphemeralKeyTimer.Start(aTimeout);
744
745 LogInfo("Allow ephemeral key for %lu msec on port %u", ToUlong(aTimeout), GetUdpPort());
746
747 exit:
748 return error;
749 }
750
ClearEphemeralKey(void)751 void BorderAgent::ClearEphemeralKey(void)
752 {
753 VerifyOrExit(mUsingEphemeralKey);
754
755 LogInfo("Clearing ephemeral key");
756 mEphemeralKeyTimer.Stop();
757
758 switch (mState)
759 {
760 case kStateStarted:
761 RestartAfterRemovingEphemeralKey();
762 break;
763
764 case kStateStopped:
765 case kStateActive:
766 // If there is an active commissioner connection, we wait till
767 // it gets disconnected before removing ephemeral key and
768 // restarting the agent.
769 break;
770 }
771
772 exit:
773 return;
774 }
775
HandleEphemeralKeyTimeout(void)776 void BorderAgent::HandleEphemeralKeyTimeout(void)
777 {
778 LogInfo("Ephemeral key timed out");
779 ClearEphemeralKey();
780 }
781
InvokeEphemeralKeyCallback(void)782 void BorderAgent::InvokeEphemeralKeyCallback(void) { mEphemeralKeyCallback.InvokeIfSet(); }
783
RestartAfterRemovingEphemeralKey(void)784 void BorderAgent::RestartAfterRemovingEphemeralKey(void)
785 {
786 LogInfo("Removing ephemeral key and restarting agent");
787
788 Stop();
789 IgnoreError(Start(mOldUdpPort));
790 }
791
HandleSecureAgentStopped(void * aContext)792 void BorderAgent::HandleSecureAgentStopped(void *aContext)
793 {
794 reinterpret_cast<BorderAgent *>(aContext)->HandleSecureAgentStopped();
795 }
796
HandleSecureAgentStopped(void)797 void BorderAgent::HandleSecureAgentStopped(void)
798 {
799 LogInfo("Reached max allowed connection attempts with ephemeral key");
800 RestartAfterRemovingEphemeralKey();
801 }
802
803 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
804
805 } // namespace MeshCoP
806 } // namespace ot
807
808 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
809