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