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