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