• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     , mIsRunning(false)
51     , mDtlsTransport(aInstance, kNoLinkSecurity)
52 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
53     , mIdInitialized(false)
54 #endif
55     , mNotifyMeshCoPServiceChangedTask(aInstance)
56 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
57     , mEphemeralKeyManager(aInstance)
58 #endif
59 {
60     ClearAllBytes(mCounters);
61 }
62 
63 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
GetId(Id & aId)64 Error BorderAgent::GetId(Id &aId)
65 {
66     Error error = kErrorNone;
67 
68     if (mIdInitialized)
69     {
70         aId = mId;
71         ExitNow();
72     }
73 
74     if (Get<Settings>().Read<Settings::BorderAgentId>(mId) != kErrorNone)
75     {
76         Random::NonCrypto::Fill(mId);
77         SuccessOrExit(error = Get<Settings>().Save<Settings::BorderAgentId>(mId));
78     }
79 
80     mIdInitialized = true;
81     aId            = mId;
82 
83 exit:
84     return error;
85 }
86 
SetId(const Id & aId)87 Error BorderAgent::SetId(const Id &aId)
88 {
89     Error error = kErrorNone;
90 
91     SuccessOrExit(error = Get<Settings>().Save<Settings::BorderAgentId>(aId));
92     mId            = aId;
93     mIdInitialized = true;
94 
95 exit:
96     return error;
97 }
98 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
99 
Start(void)100 void BorderAgent::Start(void)
101 {
102     Error error = kErrorNone;
103     Pskc  pskc;
104 
105     VerifyOrExit(!mIsRunning);
106 
107     mDtlsTransport.SetAcceptCallback(BorderAgent::HandleAcceptSession, this);
108     mDtlsTransport.SetRemoveSessionCallback(BorderAgent::HandleRemoveSession, this);
109 
110     SuccessOrExit(error = mDtlsTransport.Open());
111     SuccessOrExit(error = mDtlsTransport.Bind(kUdpPort));
112 
113     Get<KeyManager>().GetPskc(pskc);
114     SuccessOrExit(error = mDtlsTransport.SetPsk(pskc.m8, Pskc::kSize));
115     pskc.Clear();
116 
117     mIsRunning = true;
118     PostNotifyMeshCoPServiceChangedTask();
119 
120     LogInfo("Border Agent start listening on port %u", GetUdpPort());
121 
122 exit:
123     if (!mIsRunning)
124     {
125         mDtlsTransport.Close();
126     }
127 
128     LogWarnOnError(error, "start agent");
129 }
130 
Stop(void)131 void BorderAgent::Stop(void)
132 {
133     VerifyOrExit(mIsRunning);
134 
135     mDtlsTransport.Close();
136     mIsRunning = false;
137     PostNotifyMeshCoPServiceChangedTask();
138 
139     LogInfo("Border Agent stopped");
140 
141 exit:
142     return;
143 }
144 
GetUdpPort(void) const145 uint16_t BorderAgent::GetUdpPort(void) const { return mDtlsTransport.GetUdpPort(); }
146 
SetMeshCoPServiceChangedCallback(MeshCoPServiceChangedCallback aCallback,void * aContext)147 void BorderAgent::SetMeshCoPServiceChangedCallback(MeshCoPServiceChangedCallback aCallback, void *aContext)
148 {
149     mMeshCoPServiceChangedCallback.Set(aCallback, aContext);
150 
151     mNotifyMeshCoPServiceChangedTask.Post();
152 }
153 
GetMeshCoPServiceTxtData(MeshCoPServiceTxtData & aTxtData) const154 Error BorderAgent::GetMeshCoPServiceTxtData(MeshCoPServiceTxtData &aTxtData) const
155 {
156     MeshCoPTxtEncoder meshCoPTxtEncoder(GetInstance(), aTxtData);
157 
158     return meshCoPTxtEncoder.EncodeTxtData();
159 }
160 
HandleNotifierEvents(Events aEvents)161 void BorderAgent::HandleNotifierEvents(Events aEvents)
162 {
163     if (aEvents.Contains(kEventThreadRoleChanged))
164     {
165         if (Get<Mle::MleRouter>().IsAttached())
166         {
167             Start();
168         }
169         else
170         {
171             Stop();
172         }
173     }
174 
175     if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadExtPanIdChanged | kEventThreadNetworkNameChanged |
176                             kEventThreadBackboneRouterStateChanged | kEventActiveDatasetChanged))
177     {
178         PostNotifyMeshCoPServiceChangedTask();
179     }
180 
181     if (aEvents.ContainsAny(kEventPskcChanged))
182     {
183         Pskc pskc;
184 
185         VerifyOrExit(mIsRunning);
186 
187         Get<KeyManager>().GetPskc(pskc);
188 
189         // If there is secure session already established, it won't be impacted,
190         // new pskc will be applied for next connection.
191         SuccessOrExit(mDtlsTransport.SetPsk(pskc.m8, Pskc::kSize));
192         pskc.Clear();
193     }
194 
195 exit:
196     return;
197 }
198 
HandleAcceptSession(void * aContext,const Ip6::MessageInfo & aMessageInfo)199 SecureSession *BorderAgent::HandleAcceptSession(void *aContext, const Ip6::MessageInfo &aMessageInfo)
200 {
201     OT_UNUSED_VARIABLE(aMessageInfo);
202 
203     return static_cast<BorderAgent *>(aContext)->HandleAcceptSession();
204 }
205 
HandleAcceptSession(void)206 BorderAgent::CoapDtlsSession *BorderAgent::HandleAcceptSession(void)
207 {
208     return CoapDtlsSession::Allocate(GetInstance(), mDtlsTransport);
209 }
210 
HandleRemoveSession(void * aContext,SecureSession & aSession)211 void BorderAgent::HandleRemoveSession(void *aContext, SecureSession &aSession)
212 {
213     static_cast<BorderAgent *>(aContext)->HandleRemoveSession(aSession);
214 }
215 
HandleRemoveSession(SecureSession & aSession)216 void BorderAgent::HandleRemoveSession(SecureSession &aSession)
217 {
218     CoapDtlsSession &coapSession = static_cast<CoapDtlsSession &>(aSession);
219 
220     coapSession.Cleanup();
221     coapSession.Free();
222 }
223 
HandleSessionConnected(CoapDtlsSession & aSession)224 void BorderAgent::HandleSessionConnected(CoapDtlsSession &aSession)
225 {
226     OT_UNUSED_VARIABLE(aSession);
227 
228 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
229     if (mEphemeralKeyManager.OwnsSession(aSession))
230     {
231         mEphemeralKeyManager.HandleSessionConnected();
232     }
233     else
234 #endif
235     {
236         mCounters.mPskcSecureSessionSuccesses++;
237     }
238 }
239 
HandleSessionDisconnected(CoapDtlsSession & aSession,CoapDtlsSession::ConnectEvent aEvent)240 void BorderAgent::HandleSessionDisconnected(CoapDtlsSession &aSession, CoapDtlsSession::ConnectEvent aEvent)
241 {
242     OT_UNUSED_VARIABLE(aSession);
243 
244 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
245     if (mEphemeralKeyManager.OwnsSession(aSession))
246     {
247         mEphemeralKeyManager.HandleSessionDisconnected(aEvent);
248     }
249     else
250 #endif
251     {
252         if (aEvent == CoapDtlsSession::kDisconnectedError)
253         {
254             mCounters.mPskcSecureSessionFailures++;
255         }
256     }
257 }
258 
HandleCommissionerPetitionAccepted(CoapDtlsSession & aSession)259 void BorderAgent::HandleCommissionerPetitionAccepted(CoapDtlsSession &aSession)
260 {
261     OT_UNUSED_VARIABLE(aSession);
262 
263 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
264     if (mEphemeralKeyManager.OwnsSession(aSession))
265     {
266         mEphemeralKeyManager.HandleCommissionerPetitionAccepted();
267     }
268     else
269 #endif
270     {
271         mCounters.mPskcCommissionerPetitions++;
272     }
273 }
274 
FindActiveCommissionerSession(void)275 BorderAgent::CoapDtlsSession *BorderAgent::FindActiveCommissionerSession(void)
276 {
277     CoapDtlsSession *commissionerSession = nullptr;
278 
279     for (SecureSession &session : mDtlsTransport.GetSessions())
280     {
281         CoapDtlsSession &coapSession = static_cast<CoapDtlsSession &>(session);
282 
283         if (coapSession.IsActiveCommissioner())
284         {
285             commissionerSession = &coapSession;
286             break;
287         }
288     }
289 
290 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
291     if ((mEphemeralKeyManager.mCoapDtlsSession != nullptr) &&
292         mEphemeralKeyManager.mCoapDtlsSession->IsActiveCommissioner())
293     {
294         commissionerSession = mEphemeralKeyManager.mCoapDtlsSession;
295     }
296 #endif
297 
298     return commissionerSession;
299 }
300 
CoapCodeFromError(Error aError)301 Coap::Message::Code BorderAgent::CoapCodeFromError(Error aError)
302 {
303     Coap::Message::Code code;
304 
305     switch (aError)
306     {
307     case kErrorNone:
308         code = Coap::kCodeChanged;
309         break;
310 
311     case kErrorParse:
312         code = Coap::kCodeBadRequest;
313         break;
314 
315     default:
316         code = Coap::kCodeInternalError;
317         break;
318     }
319 
320     return code;
321 }
322 
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)323 template <> void BorderAgent::HandleTmf<kUriRelayRx>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
324 {
325     // This is from TMF agent.
326 
327     OT_UNUSED_VARIABLE(aMessageInfo);
328 
329     Coap::Message   *message = nullptr;
330     Error            error   = kErrorNone;
331     CoapDtlsSession *session;
332 
333     VerifyOrExit(mIsRunning);
334 
335     VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorDrop);
336 
337     session = FindActiveCommissionerSession();
338     VerifyOrExit(session != nullptr);
339 
340     message = session->NewPriorityNonConfirmablePostMessage(kUriRelayRx);
341     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
342 
343     SuccessOrExit(error = session->ForwardToCommissioner(*message, aMessage));
344     LogInfo("Sent to commissioner on RelayRx (c/rx)");
345 
346 exit:
347     FreeMessageOnError(message, error);
348 }
349 
NotifyMeshCoPServiceChanged(void)350 void BorderAgent::NotifyMeshCoPServiceChanged(void) { mMeshCoPServiceChangedCallback.InvokeIfSet(); }
351 
PostNotifyMeshCoPServiceChangedTask(void)352 void BorderAgent::PostNotifyMeshCoPServiceChangedTask(void)
353 {
354     if (mMeshCoPServiceChangedCallback.IsSet())
355     {
356         mNotifyMeshCoPServiceChangedTask.Post();
357     }
358 }
359 
360 //----------------------------------------------------------------------------------------------------------------------
361 // BorderAgent::MeshCoPTxtEncoder
362 
AppendTxtEntry(const char * aKey,const void * aValue,uint16_t aValueLength)363 Error BorderAgent::MeshCoPTxtEncoder::AppendTxtEntry(const char *aKey, const void *aValue, uint16_t aValueLength)
364 {
365     Dns::TxtEntry txtEntry;
366 
367     txtEntry.Init(aKey, reinterpret_cast<const uint8_t *>(aValue), aValueLength);
368     return txtEntry.AppendTo(mAppender);
369 }
370 
AppendTxtEntry(const char * aKey,const NameData & aObject)371 template <> Error BorderAgent::MeshCoPTxtEncoder::AppendTxtEntry<NameData>(const char *aKey, const NameData &aObject)
372 {
373     return AppendTxtEntry(aKey, aObject.GetBuffer(), aObject.GetLength());
374 }
375 
EncodeTxtData(void)376 Error BorderAgent::MeshCoPTxtEncoder::EncodeTxtData(void)
377 {
378     Error error = kErrorNone;
379 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
380     Id id;
381 #endif
382     StateBitmap state;
383 
384 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
385     if (Get<BorderAgent>().GetId(id) == kErrorNone)
386     {
387         SuccessOrExit(error = AppendTxtEntry("id", id));
388     }
389 #endif
390     SuccessOrExit(error = AppendTxtEntry("nn", Get<NetworkNameManager>().GetNetworkName().GetAsData()));
391     SuccessOrExit(error = AppendTxtEntry("xp", Get<ExtendedPanIdManager>().GetExtPanId()));
392     SuccessOrExit(error = AppendTxtEntry("tv", NameData(kThreadVersionString, strlen(kThreadVersionString))));
393     SuccessOrExit(error = AppendTxtEntry("xa", Get<Mac::Mac>().GetExtAddress()));
394 
395     state = GetStateBitmap();
396     SuccessOrExit(error = AppendTxtEntry("sb", BigEndian::HostSwap32(state.ToUint32())));
397 
398     if (state.mThreadIfStatus == kThreadIfStatusActive)
399     {
400         SuccessOrExit(error = AppendTxtEntry(
401                           "pt", BigEndian::HostSwap32(Get<Mle::MleRouter>().GetLeaderData().GetPartitionId())));
402         if (Get<MeshCoP::ActiveDatasetManager>().GetTimestamp().IsValid())
403         {
404             SuccessOrExit(error = AppendTxtEntry("at", Get<MeshCoP::ActiveDatasetManager>().GetTimestamp()));
405         }
406     }
407 
408 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
409     SuccessOrExit(error = AppendBbrTxtEntry(state));
410 #endif
411 #if OTBR_ENABLE_BORDER_ROUTING
412     SuccessOrExit(error = AppendOmrTxtEntry());
413 #endif
414     mTxtData.mLength = mAppender.GetAppendedLength();
415 
416 exit:
417     return error;
418 }
419 
420 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
AppendBbrTxtEntry(StateBitmap aState)421 Error BorderAgent::MeshCoPTxtEncoder::AppendBbrTxtEntry(StateBitmap aState)
422 {
423     Error             error      = kErrorNone;
424     const DomainName &domainName = Get<MeshCoP::NetworkNameManager>().GetDomainName();
425 
426     if (aState.mBbrIsActive)
427     {
428         BackboneRouter::Config bbrConfig;
429 
430         Get<BackboneRouter::Local>().GetConfig(bbrConfig);
431         SuccessOrExit(error = AppendTxtEntry("sq", bbrConfig.mSequenceNumber));
432         SuccessOrExit(error = AppendTxtEntry("bb", BigEndian::HostSwap16(BackboneRouter::kBackboneUdpPort)));
433     }
434 
435     error = AppendTxtEntry(
436         "dn", NameData(domainName.GetAsCString(), StringLength(domainName.GetAsCString(), sizeof(domainName))));
437 
438 exit:
439     return error;
440 }
441 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
442 
443 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
AppendOmrTxtEntry(void)444 Error BorderAgent::MeshCoPTxtEncoder::AppendOmrTxtEntry(void)
445 {
446     Error                                         error = kErrorNone;
447     Ip6::Prefix                                   prefix;
448     BorderRouter::RoutingManager::RoutePreference preference;
449 
450     if ((error = Get<BorderRouter::RoutingManager>().GetFavoredOmrPrefix(prefix, preference)) == kErrorNone)
451     {
452         uint8_t omrData[Ip6::NetworkPrefix::kSize + 1];
453         omrData[0] = prefix.GetLength();
454         memcpy(omrData + 1, prefix.GetBytes(), prefix.GetBytesSize());
455 
456         SuccessOrExit(error = AppendTxtEntry("omr", omrData));
457     }
458 
459 exit:
460     return error;
461 }
462 #endif
463 
GetStateBitmap(void)464 BorderAgent::MeshCoPTxtEncoder::StateBitmap BorderAgent::MeshCoPTxtEncoder::GetStateBitmap(void)
465 {
466     StateBitmap state;
467 
468     state.mConnectionMode = kConnectionModePskc;
469     state.mAvailability   = kAvailabilityHigh;
470 
471     switch (Get<Mle::MleRouter>().GetRole())
472     {
473     case Mle::DeviceRole::kRoleDisabled:
474         state.mThreadIfStatus = kThreadIfStatusNotInitialized;
475         state.mThreadRole     = kThreadRoleDisabledOrDetached;
476         break;
477     case Mle::DeviceRole::kRoleDetached:
478         state.mThreadIfStatus = kThreadIfStatusInitialized;
479         state.mThreadRole     = kThreadRoleDisabledOrDetached;
480         break;
481     case Mle::DeviceRole::kRoleChild:
482         state.mThreadIfStatus = kThreadIfStatusActive;
483         state.mThreadRole     = kThreadRoleChild;
484         break;
485     case Mle::DeviceRole::kRoleRouter:
486         state.mThreadIfStatus = kThreadIfStatusActive;
487         state.mThreadRole     = kThreadRoleRouter;
488         break;
489     case Mle::DeviceRole::kRoleLeader:
490         state.mThreadIfStatus = kThreadIfStatusActive;
491         state.mThreadRole     = kThreadRoleLeader;
492         break;
493     }
494 
495 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
496     state.mBbrIsActive = state.mThreadIfStatus == kThreadIfStatusActive &&
497                          Get<BackboneRouter::Local>().GetState() != BackboneRouter::Local::State::kStateDisabled;
498     state.mBbrIsPrimary = state.mThreadIfStatus == kThreadIfStatusActive &&
499                           Get<BackboneRouter::Local>().GetState() == BackboneRouter::Local::State::kStatePrimary;
500 #endif
501 
502 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
503     state.mEpskcSupported =
504         Get<BorderAgent::EphemeralKeyManager>().GetState() != EphemeralKeyManager::State::kStateDisabled;
505 #endif
506 
507     return state;
508 }
509 
510 //----------------------------------------------------------------------------------------------------------------------
511 // BorderAgent::SessionIterator
512 
Init(Instance & aInstance)513 void BorderAgent::SessionIterator::Init(Instance &aInstance)
514 {
515     SetSession(static_cast<CoapDtlsSession *>(aInstance.Get<BorderAgent>().mDtlsTransport.GetSessions().GetHead()));
516     SetInitTime(aInstance.Get<Uptime>().GetUptime());
517 }
518 
GetNextSessionInfo(SessionInfo & aSessionInfo)519 Error BorderAgent::SessionIterator::GetNextSessionInfo(SessionInfo &aSessionInfo)
520 {
521     Error            error   = kErrorNone;
522     CoapDtlsSession *session = GetSession();
523 
524     VerifyOrExit(session != nullptr, error = kErrorNotFound);
525 
526     SetSession(static_cast<CoapDtlsSession *>(session->GetNext()));
527 
528     aSessionInfo.mPeerSockAddr.mAddress = session->GetMessageInfo().GetPeerAddr();
529     aSessionInfo.mPeerSockAddr.mPort    = session->GetMessageInfo().GetPeerPort();
530     aSessionInfo.mIsConnected           = session->IsConnected();
531     aSessionInfo.mIsCommissioner        = session->IsActiveCommissioner();
532     aSessionInfo.mLifetime              = GetInitTime() - session->GetAllocationTime();
533 
534 exit:
535     return error;
536 }
537 
538 //----------------------------------------------------------------------------------------------------------------------
539 // BorderAgent::EphemeralKeyManager
540 
541 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
542 
EphemeralKeyManager(Instance & aInstance)543 BorderAgent::EphemeralKeyManager::EphemeralKeyManager(Instance &aInstance)
544     : InstanceLocator(aInstance)
545 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_FEATURE_ENABLED_BY_DEFAULT
546     , mState(kStateStopped)
547 #else
548     , mState(kStateDisabled)
549 #endif
550     , mDtlsTransport(aInstance, kNoLinkSecurity)
551     , mCoapDtlsSession(nullptr)
552     , mTimer(aInstance)
553     , mCallbackTask(aInstance)
554 {
555 }
556 
SetEnabled(bool aEnabled)557 void BorderAgent::EphemeralKeyManager::SetEnabled(bool aEnabled)
558 {
559     if (aEnabled)
560     {
561         VerifyOrExit(mState == kStateDisabled);
562         SetState(kStateStopped);
563         Get<BorderAgent>().PostNotifyMeshCoPServiceChangedTask();
564     }
565     else
566     {
567         VerifyOrExit(mState != kStateDisabled);
568         Stop();
569         SetState(kStateDisabled);
570         Get<BorderAgent>().PostNotifyMeshCoPServiceChangedTask();
571     }
572 
573 exit:
574     return;
575 }
576 
Start(const char * aKeyString,uint32_t aTimeout,uint16_t aUdpPort)577 Error BorderAgent::EphemeralKeyManager::Start(const char *aKeyString, uint32_t aTimeout, uint16_t aUdpPort)
578 {
579     Error    error = kErrorNone;
580     uint16_t length;
581 
582     VerifyOrExit(mState == kStateStopped, error = kErrorInvalidState);
583 
584     length = StringLength(aKeyString, kMaxKeyLength + 1);
585     VerifyOrExit((length >= kMinKeyLength) && (length <= kMaxKeyLength), error = kErrorInvalidArgs);
586 
587     IgnoreError(mDtlsTransport.SetMaxConnectionAttempts(kMaxConnectionAttempts, HandleTransportClosed, this));
588 
589     mDtlsTransport.SetAcceptCallback(EphemeralKeyManager::HandleAcceptSession, this);
590     mDtlsTransport.SetRemoveSessionCallback(EphemeralKeyManager::HandleRemoveSession, this);
591 
592     SuccessOrExit(error = mDtlsTransport.Open());
593     SuccessOrExit(error = mDtlsTransport.Bind(aUdpPort));
594 
595     SuccessOrExit(
596         error = mDtlsTransport.SetPsk(reinterpret_cast<const uint8_t *>(aKeyString), static_cast<uint8_t>(length)));
597 
598     aTimeout = Min((aTimeout == 0) ? kDefaultTimeout : aTimeout, kMaxTimeout);
599     mTimer.Start(aTimeout);
600 
601     LogInfo("Allow ephemeral key for %lu msec on port %u", ToUlong(aTimeout), GetUdpPort());
602 
603     SetState(kStateStarted);
604 
605 exit:
606     switch (error)
607     {
608     case kErrorNone:
609         Get<BorderAgent>().mCounters.mEpskcActivations++;
610         break;
611     case kErrorInvalidState:
612         Get<BorderAgent>().mCounters.mEpskcInvalidBaStateErrors++;
613         break;
614     case kErrorInvalidArgs:
615         Get<BorderAgent>().mCounters.mEpskcInvalidArgsErrors++;
616         break;
617     default:
618         Get<BorderAgent>().mCounters.mEpskcStartSecureSessionErrors++;
619         break;
620     }
621 
622     return error;
623 }
624 
Stop(void)625 void BorderAgent::EphemeralKeyManager::Stop(void) { Stop(kReasonLocalDisconnect); }
626 
Stop(StopReason aReason)627 void BorderAgent::EphemeralKeyManager::Stop(StopReason aReason)
628 {
629     switch (mState)
630     {
631     case kStateStarted:
632     case kStateConnected:
633     case kStateAccepted:
634         break;
635     case kStateDisabled:
636     case kStateStopped:
637         ExitNow();
638     }
639 
640     LogInfo("Stopping ephemeral key use - reason: %s", StopReasonToString(aReason));
641     SetState(kStateStopped);
642 
643     mTimer.Stop();
644     mDtlsTransport.Close();
645 
646     switch (aReason)
647     {
648     case kReasonLocalDisconnect:
649         Get<BorderAgent>().mCounters.mEpskcDeactivationClears++;
650         break;
651     case kReasonPeerDisconnect:
652         Get<BorderAgent>().mCounters.mEpskcDeactivationDisconnects++;
653         break;
654     case kReasonSessionError:
655         Get<BorderAgent>().mCounters.mEpskcStartSecureSessionErrors++;
656         break;
657     case kReasonMaxFailedAttempts:
658         Get<BorderAgent>().mCounters.mEpskcDeactivationMaxAttempts++;
659         break;
660     case kReasonTimeout:
661         Get<BorderAgent>().mCounters.mEpskcDeactivationTimeouts++;
662         break;
663     case kReasonUnknown:
664         break;
665     }
666 
667 exit:
668     return;
669 }
670 
SetState(State aState)671 void BorderAgent::EphemeralKeyManager::SetState(State aState)
672 {
673     VerifyOrExit(mState != aState);
674     LogInfo("Ephemeral key - state: %s -> %s", StateToString(mState), StateToString(aState));
675     mState = aState;
676     mCallbackTask.Post();
677 
678 exit:
679     return;
680 }
681 
HandleAcceptSession(void * aContext,const Ip6::MessageInfo & aMessageInfo)682 SecureSession *BorderAgent::EphemeralKeyManager::HandleAcceptSession(void                   *aContext,
683                                                                      const Ip6::MessageInfo &aMessageInfo)
684 {
685     OT_UNUSED_VARIABLE(aMessageInfo);
686 
687     return static_cast<EphemeralKeyManager *>(aContext)->HandleAcceptSession();
688 }
689 
HandleAcceptSession(void)690 BorderAgent::CoapDtlsSession *BorderAgent::EphemeralKeyManager::HandleAcceptSession(void)
691 {
692     CoapDtlsSession *session = nullptr;
693 
694     VerifyOrExit(mCoapDtlsSession == nullptr);
695 
696     session = CoapDtlsSession::Allocate(GetInstance(), mDtlsTransport);
697     VerifyOrExit(session != nullptr);
698 
699     mCoapDtlsSession = session;
700 
701 exit:
702     return session;
703 }
704 
HandleRemoveSession(void * aContext,SecureSession & aSession)705 void BorderAgent::EphemeralKeyManager::HandleRemoveSession(void *aContext, SecureSession &aSession)
706 {
707     static_cast<EphemeralKeyManager *>(aContext)->HandleRemoveSession(aSession);
708 }
709 
HandleRemoveSession(SecureSession & aSession)710 void BorderAgent::EphemeralKeyManager::HandleRemoveSession(SecureSession &aSession)
711 {
712     CoapDtlsSession &coapSession = static_cast<CoapDtlsSession &>(aSession);
713 
714     coapSession.Cleanup();
715     coapSession.Free();
716     mCoapDtlsSession = nullptr;
717 }
718 
HandleSessionConnected(void)719 void BorderAgent::EphemeralKeyManager::HandleSessionConnected(void)
720 {
721     SetState(kStateConnected);
722     Get<BorderAgent>().mCounters.mEpskcSecureSessionSuccesses++;
723 }
724 
HandleSessionDisconnected(SecureSession::ConnectEvent aEvent)725 void BorderAgent::EphemeralKeyManager::HandleSessionDisconnected(SecureSession::ConnectEvent aEvent)
726 {
727     StopReason reason = kReasonUnknown;
728 
729     // The ephemeral key can be used once
730 
731     VerifyOrExit((mState == kStateConnected) || (mState == kStateAccepted));
732 
733     switch (aEvent)
734     {
735     case SecureSession::kDisconnectedError:
736         reason = kReasonSessionError;
737         break;
738     case SecureSession::kDisconnectedPeerClosed:
739         reason = kReasonPeerDisconnect;
740         break;
741     case SecureSession::kDisconnectedMaxAttempts:
742         reason = kReasonMaxFailedAttempts;
743         break;
744     default:
745         break;
746     }
747 
748     Stop(reason);
749 
750 exit:
751     return;
752 }
753 
HandleCommissionerPetitionAccepted(void)754 void BorderAgent::EphemeralKeyManager::HandleCommissionerPetitionAccepted(void)
755 {
756     SetState(kStateAccepted);
757     Get<BorderAgent>().mCounters.mEpskcCommissionerPetitions++;
758 }
759 
HandleTimer(void)760 void BorderAgent::EphemeralKeyManager::HandleTimer(void) { Stop(kReasonTimeout); }
761 
HandleTask(void)762 void BorderAgent::EphemeralKeyManager::HandleTask(void) { mCallback.InvokeIfSet(); }
763 
HandleTransportClosed(void * aContext)764 void BorderAgent::EphemeralKeyManager::HandleTransportClosed(void *aContext)
765 {
766     reinterpret_cast<EphemeralKeyManager *>(aContext)->HandleTransportClosed();
767 }
768 
HandleTransportClosed(void)769 void BorderAgent::EphemeralKeyManager::HandleTransportClosed(void)
770 {
771     Stop(kReasonMaxFailedAttempts);
772     ;
773 }
774 
StateToString(State aState)775 const char *BorderAgent::EphemeralKeyManager::StateToString(State aState)
776 {
777     static const char *const kStateStrings[] = {
778         "Disabled",  // (0) kStateDisabled
779         "Stopped",   // (1) kStateStopped
780         "Started",   // (2) kStateStarted
781         "Connected", // (3) kStateConnected
782         "Accepted",  // (4) kStateAccepted
783     };
784 
785     struct EnumCheck
786     {
787         InitEnumValidatorCounter();
788         ValidateNextEnum(kStateDisabled);
789         ValidateNextEnum(kStateStopped);
790         ValidateNextEnum(kStateStarted);
791         ValidateNextEnum(kStateConnected);
792         ValidateNextEnum(kStateAccepted);
793     };
794 
795     return kStateStrings[aState];
796 }
797 
798 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
799 
StopReasonToString(StopReason aReason)800 const char *BorderAgent::EphemeralKeyManager::StopReasonToString(StopReason aReason)
801 {
802     static const char *const kReasonStrings[] = {
803         "LocalDisconnect",   // (0) kReasonLocalDisconnect
804         "PeerDisconnect",    // (1) kReasonPeerDisconnect
805         "SessionError",      // (2) kReasonSessionError
806         "MaxFailedAttempts", // (3) kReasonMaxFailedAttempts
807         "Timeout",           // (4) kReasonTimeout
808         "Unknown",           // (5) kReasonUnknown
809     };
810 
811     struct EnumCheck
812     {
813         InitEnumValidatorCounter();
814         ValidateNextEnum(kReasonLocalDisconnect);
815         ValidateNextEnum(kReasonPeerDisconnect);
816         ValidateNextEnum(kReasonSessionError);
817         ValidateNextEnum(kReasonMaxFailedAttempts);
818         ValidateNextEnum(kReasonTimeout);
819         ValidateNextEnum(kReasonUnknown);
820     };
821 
822     return kReasonStrings[aReason];
823 }
824 
825 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
826 
827 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
828 
829 //----------------------------------------------------------------------------------------------------------------------
830 // `BorderAgent::CoapDtlsSession
831 
CoapDtlsSession(Instance & aInstance,Dtls::Transport & aDtlsTransport)832 BorderAgent::CoapDtlsSession::CoapDtlsSession(Instance &aInstance, Dtls::Transport &aDtlsTransport)
833     : Coap::SecureSession(aInstance, aDtlsTransport)
834     , mIsActiveCommissioner(false)
835     , mTimer(aInstance, HandleTimer, this)
836     , mUdpReceiver(HandleUdpReceive, this)
837     , mAllocationTime(aInstance.Get<Uptime>().GetUptime())
838 {
839     mCommissionerAloc.InitAsThreadOriginMeshLocal();
840 
841     SetResourceHandler(&HandleResource);
842     SetConnectCallback(&HandleConnected, this);
843 }
844 
Cleanup(void)845 void BorderAgent::CoapDtlsSession::Cleanup(void)
846 {
847     while (!mForwardContexts.IsEmpty())
848     {
849         ForwardContext *forwardContext = mForwardContexts.Pop();
850 
851         IgnoreError(Get<Tmf::Agent>().AbortTransaction(HandleCoapResponse, forwardContext));
852     }
853 
854     mTimer.Stop();
855     IgnoreError(Get<Ip6::Udp>().RemoveReceiver(mUdpReceiver));
856     Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
857 
858     Coap::SecureSession::Cleanup();
859 }
860 
HandleResource(CoapBase & aCoapBase,const char * aUriPath,Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)861 bool BorderAgent::CoapDtlsSession::HandleResource(CoapBase               &aCoapBase,
862                                                   const char             *aUriPath,
863                                                   Coap::Message          &aMessage,
864                                                   const Ip6::MessageInfo &aMessageInfo)
865 {
866     return static_cast<CoapDtlsSession &>(aCoapBase).HandleResource(aUriPath, aMessage, aMessageInfo);
867 }
868 
HandleResource(const char * aUriPath,Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)869 bool BorderAgent::CoapDtlsSession::HandleResource(const char             *aUriPath,
870                                                   Coap::Message          &aMessage,
871                                                   const Ip6::MessageInfo &aMessageInfo)
872 {
873     bool didHandle = true;
874     Uri  uri       = UriFromPath(aUriPath);
875 
876     switch (uri)
877     {
878     case kUriCommissionerPetition:
879         IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriLeaderPetition));
880         break;
881     case kUriCommissionerKeepAlive:
882         HandleTmfCommissionerKeepAlive(aMessage, aMessageInfo);
883         break;
884     case kUriRelayTx:
885         HandleTmfRelayTx(aMessage);
886         break;
887     case kUriCommissionerGet:
888     case kUriActiveGet:
889     case kUriPendingGet:
890         HandleTmfDatasetGet(aMessage, uri);
891         break;
892     case kUriProxyTx:
893         HandleTmfProxyTx(aMessage);
894         break;
895     default:
896         didHandle = false;
897         break;
898     }
899 
900     return didHandle;
901 }
902 
HandleConnected(ConnectEvent aEvent,void * aContext)903 void BorderAgent::CoapDtlsSession::HandleConnected(ConnectEvent aEvent, void *aContext)
904 {
905     static_cast<CoapDtlsSession *>(aContext)->HandleConnected(aEvent);
906 }
907 
HandleConnected(ConnectEvent aEvent)908 void BorderAgent::CoapDtlsSession::HandleConnected(ConnectEvent aEvent)
909 {
910     if (aEvent == kConnected)
911     {
912         LogInfo("SecureSession connected");
913         mTimer.Start(kKeepAliveTimeout);
914         Get<BorderAgent>().HandleSessionConnected(*this);
915     }
916     else
917     {
918         LogInfo("SecureSession disconnected");
919         IgnoreError(Get<Ip6::Udp>().RemoveReceiver(mUdpReceiver));
920         Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
921 
922         Get<BorderAgent>().HandleSessionDisconnected(*this, aEvent);
923     }
924 }
925 
HandleTmfCommissionerKeepAlive(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)926 void BorderAgent::CoapDtlsSession::HandleTmfCommissionerKeepAlive(Coap::Message          &aMessage,
927                                                                   const Ip6::MessageInfo &aMessageInfo)
928 {
929     VerifyOrExit(mIsActiveCommissioner);
930     SuccessOrExit(ForwardToLeader(aMessage, aMessageInfo, kUriLeaderKeepAlive));
931     mTimer.Start(kKeepAliveTimeout);
932 
933 exit:
934     return;
935 }
936 
ForwardToLeader(const Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo,Uri aUri)937 Error BorderAgent::CoapDtlsSession::ForwardToLeader(const Coap::Message    &aMessage,
938                                                     const Ip6::MessageInfo &aMessageInfo,
939                                                     Uri                     aUri)
940 {
941     Error                    error = kErrorNone;
942     OwnedPtr<ForwardContext> forwardContext;
943     Tmf::MessageInfo         messageInfo(GetInstance());
944     Coap::Message           *message  = nullptr;
945     bool                     petition = false;
946     bool                     separate = false;
947     OffsetRange              offsetRange;
948 
949     switch (aUri)
950     {
951     case kUriLeaderPetition:
952         petition = true;
953         separate = true;
954         break;
955     case kUriLeaderKeepAlive:
956         separate = true;
957         break;
958     default:
959         break;
960     }
961 
962     if (separate)
963     {
964         SuccessOrExit(error = SendAck(aMessage, aMessageInfo));
965     }
966 
967     forwardContext.Reset(ForwardContext::Allocate(*this, aMessage, petition, separate));
968     VerifyOrExit(!forwardContext.IsNull(), error = kErrorNoBufs);
969 
970     message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(aUri);
971     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
972 
973     offsetRange.InitFromMessageOffsetToEnd(aMessage);
974     SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange));
975 
976     messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc();
977     messageInfo.SetSockPortToTmf();
978 
979     SuccessOrExit(error =
980                       Get<Tmf::Agent>().SendMessage(*message, messageInfo, HandleCoapResponse, forwardContext.Get()));
981 
982     // Release the ownership of `forwardContext` since `SendMessage()`
983     // will own it. We take back ownership from `HandleCoapResponse()`
984     // callback.
985 
986     mForwardContexts.Push(*forwardContext.Release());
987 
988     LogInfo("Forwarded request to leader on %s", PathForUri(aUri));
989 
990 exit:
991     LogWarnOnError(error, "forward to leader");
992 
993     if (error != kErrorNone)
994     {
995         FreeMessage(message);
996         SendErrorMessage(aMessage, separate, error);
997     }
998 
999     return error;
1000 }
1001 
HandleCoapResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aResult)1002 void BorderAgent::CoapDtlsSession::HandleCoapResponse(void                *aContext,
1003                                                       otMessage           *aMessage,
1004                                                       const otMessageInfo *aMessageInfo,
1005                                                       otError              aResult)
1006 {
1007     OT_UNUSED_VARIABLE(aMessageInfo);
1008 
1009     OwnedPtr<ForwardContext> forwardContext(static_cast<ForwardContext *>(aContext));
1010 
1011     forwardContext->mSession.HandleCoapResponse(*forwardContext.Get(), AsCoapMessagePtr(aMessage), aResult);
1012 }
1013 
HandleCoapResponse(const ForwardContext & aForwardContext,const Coap::Message * aResponse,Error aResult)1014 void BorderAgent::CoapDtlsSession::HandleCoapResponse(const ForwardContext &aForwardContext,
1015                                                       const Coap::Message  *aResponse,
1016                                                       Error                 aResult)
1017 {
1018     Coap::Message *message = nullptr;
1019     Error          error;
1020 
1021     IgnoreError(mForwardContexts.Remove(aForwardContext));
1022 
1023     SuccessOrExit(error = aResult);
1024     VerifyOrExit((message = NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
1025 
1026     if (aForwardContext.mPetition && aResponse->GetCode() == Coap::kCodeChanged)
1027     {
1028         uint8_t state;
1029 
1030         SuccessOrExit(error = Tlv::Find<StateTlv>(*aResponse, state));
1031 
1032         if (state == StateTlv::kAccept)
1033         {
1034             uint16_t sessionId;
1035 
1036             SuccessOrExit(error = Tlv::Find<CommissionerSessionIdTlv>(*aResponse, sessionId));
1037 
1038             Get<Mle::Mle>().GetCommissionerAloc(sessionId, mCommissionerAloc.GetAddress());
1039             Get<ThreadNetif>().AddUnicastAddress(mCommissionerAloc);
1040             IgnoreError(Get<Ip6::Udp>().AddReceiver(mUdpReceiver));
1041             mIsActiveCommissioner = true;
1042             Get<BorderAgent>().HandleCommissionerPetitionAccepted(*this);
1043 
1044             LogInfo("Commissioner accepted - SessionId:%u ALOC:%s", sessionId,
1045                     mCommissionerAloc.GetAddress().ToString().AsCString());
1046         }
1047         else
1048         {
1049             LogInfo("Commissioner rejected");
1050         }
1051     }
1052 
1053     SuccessOrExit(error = aForwardContext.ToHeader(*message, aResponse->GetCode()));
1054 
1055     if (aResponse->GetLength() > aResponse->GetOffset())
1056     {
1057         SuccessOrExit(error = message->SetPayloadMarker());
1058     }
1059 
1060     SuccessOrExit(error = ForwardToCommissioner(*message, *aResponse));
1061 
1062 exit:
1063 
1064     if (error != kErrorNone)
1065     {
1066         FreeMessage(message);
1067 
1068         LogWarn("Commissioner request[%u] failed: %s", aForwardContext.mMessageId, ErrorToString(error));
1069 
1070         SendErrorMessage(aForwardContext, error);
1071     }
1072 }
1073 
HandleUdpReceive(void * aContext,const otMessage * aMessage,const otMessageInfo * aMessageInfo)1074 bool BorderAgent::CoapDtlsSession::HandleUdpReceive(void                *aContext,
1075                                                     const otMessage     *aMessage,
1076                                                     const otMessageInfo *aMessageInfo)
1077 {
1078     return static_cast<CoapDtlsSession *>(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo));
1079 }
1080 
HandleUdpReceive(const Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1081 bool BorderAgent::CoapDtlsSession::HandleUdpReceive(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1082 {
1083     Error                     error     = kErrorNone;
1084     Coap::Message            *message   = nullptr;
1085     bool                      didHandle = false;
1086     ExtendedTlv               extTlv;
1087     UdpEncapsulationTlvHeader udpEncapHeader;
1088     OffsetRange               offsetRange;
1089 
1090     VerifyOrExit(aMessageInfo.GetSockAddr() == mCommissionerAloc.GetAddress());
1091 
1092     didHandle = true;
1093 
1094     VerifyOrExit(aMessage.GetLength() > 0);
1095 
1096     message = NewPriorityNonConfirmablePostMessage(kUriProxyRx);
1097     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
1098 
1099     offsetRange.InitFromMessageOffsetToEnd(aMessage);
1100 
1101     extTlv.SetType(Tlv::kUdpEncapsulation);
1102     extTlv.SetLength(sizeof(UdpEncapsulationTlvHeader) + offsetRange.GetLength());
1103 
1104     udpEncapHeader.SetSourcePort(aMessageInfo.GetPeerPort());
1105     udpEncapHeader.SetDestinationPort(aMessageInfo.GetSockPort());
1106 
1107     SuccessOrExit(error = message->Append(extTlv));
1108     SuccessOrExit(error = message->Append(udpEncapHeader));
1109     SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange));
1110 
1111     SuccessOrExit(error = Tlv::Append<Ip6AddressTlv>(*message, aMessageInfo.GetPeerAddr()));
1112 
1113     SuccessOrExit(error = SendMessage(*message));
1114 
1115     LogInfo("Sent ProxyRx (c/ur) to commissioner");
1116 
1117 exit:
1118     FreeMessageOnError(message, error);
1119     LogWarnOnError(error, "send ProxyRx (c/ur)");
1120 
1121     return didHandle;
1122 }
1123 
ForwardToCommissioner(Coap::Message & aForwardMessage,const Message & aMessage)1124 Error BorderAgent::CoapDtlsSession::ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage)
1125 {
1126     Error       error = kErrorNone;
1127     OffsetRange offsetRange;
1128 
1129     offsetRange.InitFromMessageOffsetToEnd(aMessage);
1130     SuccessOrExit(error = aForwardMessage.AppendBytesFromMessage(aMessage, offsetRange));
1131 
1132     SuccessOrExit(error = SendMessage(aForwardMessage));
1133 
1134     LogInfo("Sent to commissioner");
1135 
1136 exit:
1137     LogWarnOnError(error, "send to commissioner");
1138     return error;
1139 }
1140 
SendErrorMessage(const ForwardContext & aForwardContext,Error aError)1141 void BorderAgent::CoapDtlsSession::SendErrorMessage(const ForwardContext &aForwardContext, Error aError)
1142 {
1143     Error          error   = kErrorNone;
1144     Coap::Message *message = nullptr;
1145 
1146     VerifyOrExit((message = NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
1147     SuccessOrExit(error = aForwardContext.ToHeader(*message, CoapCodeFromError(aError)));
1148     SuccessOrExit(error = SendMessage(*message));
1149 
1150 exit:
1151     FreeMessageOnError(message, error);
1152     LogWarnOnError(error, "send error CoAP message");
1153 }
1154 
SendErrorMessage(const Coap::Message & aRequest,bool aSeparate,Error aError)1155 void BorderAgent::CoapDtlsSession::SendErrorMessage(const Coap::Message &aRequest, bool aSeparate, Error aError)
1156 {
1157     Error          error   = kErrorNone;
1158     Coap::Message *message = nullptr;
1159 
1160     VerifyOrExit((message = NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
1161 
1162     if (aRequest.IsNonConfirmable() || aSeparate)
1163     {
1164         message->Init(Coap::kTypeNonConfirmable, CoapCodeFromError(aError));
1165     }
1166     else
1167     {
1168         message->Init(Coap::kTypeAck, CoapCodeFromError(aError));
1169     }
1170 
1171     if (!aSeparate)
1172     {
1173         message->SetMessageId(aRequest.GetMessageId());
1174     }
1175 
1176     SuccessOrExit(error = message->SetTokenFromMessage(aRequest));
1177 
1178     SuccessOrExit(error = SendMessage(*message));
1179 
1180 exit:
1181     FreeMessageOnError(message, error);
1182     LogWarnOnError(error, "send error CoAP message");
1183 }
1184 
HandleTmfProxyTx(Coap::Message & aMessage)1185 void BorderAgent::CoapDtlsSession::HandleTmfProxyTx(Coap::Message &aMessage)
1186 {
1187     Error                     error   = kErrorNone;
1188     Message                  *message = nullptr;
1189     Ip6::MessageInfo          messageInfo;
1190     OffsetRange               offsetRange;
1191     UdpEncapsulationTlvHeader udpEncapHeader;
1192 
1193     SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Tlv::kUdpEncapsulation, offsetRange));
1194 
1195     SuccessOrExit(error = aMessage.Read(offsetRange, udpEncapHeader));
1196     offsetRange.AdvanceOffset(sizeof(UdpEncapsulationTlvHeader));
1197 
1198     VerifyOrExit(udpEncapHeader.GetSourcePort() > 0 && udpEncapHeader.GetDestinationPort() > 0, error = kErrorDrop);
1199 
1200     VerifyOrExit((message = Get<Ip6::Udp>().NewMessage()) != nullptr, error = kErrorNoBufs);
1201     SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange));
1202 
1203     messageInfo.SetSockPort(udpEncapHeader.GetSourcePort());
1204     messageInfo.SetSockAddr(mCommissionerAloc.GetAddress());
1205     messageInfo.SetPeerPort(udpEncapHeader.GetDestinationPort());
1206 
1207     SuccessOrExit(error = Tlv::Find<Ip6AddressTlv>(aMessage, messageInfo.GetPeerAddr()));
1208 
1209     SuccessOrExit(error = Get<Ip6::Udp>().SendDatagram(*message, messageInfo));
1210 
1211     LogInfo("Proxy transmit sent to %s", messageInfo.GetPeerAddr().ToString().AsCString());
1212 
1213 exit:
1214     FreeMessageOnError(message, error);
1215     LogWarnOnError(error, "send proxy stream");
1216 }
1217 
HandleTmfRelayTx(Coap::Message & aMessage)1218 void BorderAgent::CoapDtlsSession::HandleTmfRelayTx(Coap::Message &aMessage)
1219 {
1220     Error            error = kErrorNone;
1221     uint16_t         joinerRouterRloc;
1222     Coap::Message   *message = nullptr;
1223     Tmf::MessageInfo messageInfo(GetInstance());
1224     OffsetRange      offsetRange;
1225 
1226     VerifyOrExit(aMessage.IsNonConfirmablePostRequest());
1227 
1228     SuccessOrExit(error = Tlv::Find<JoinerRouterLocatorTlv>(aMessage, joinerRouterRloc));
1229 
1230     message = Get<Tmf::Agent>().NewPriorityNonConfirmablePostMessage(kUriRelayTx);
1231     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
1232 
1233     offsetRange.InitFromMessageOffsetToEnd(aMessage);
1234     SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offsetRange));
1235 
1236     messageInfo.SetSockAddrToRlocPeerAddrTo(joinerRouterRloc);
1237     messageInfo.SetSockPortToTmf();
1238 
1239     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
1240 
1241     LogInfo("Sent to joiner router request on RelayTx (c/tx)");
1242 
1243 exit:
1244     FreeMessageOnError(message, error);
1245     LogWarnOnError(error, "send to joiner router request RelayTx (c/tx)");
1246 }
1247 
HandleTmfDatasetGet(Coap::Message & aMessage,Uri aUri)1248 void BorderAgent::CoapDtlsSession::HandleTmfDatasetGet(Coap::Message &aMessage, Uri aUri)
1249 {
1250     Error          error    = kErrorNone;
1251     Coap::Message *response = nullptr;
1252 
1253     // When processing `MGMT_GET` request directly on Border Agent,
1254     // the Security Policy flags (O-bit) should be ignored to allow
1255     // the commissioner candidate to get the full Operational Dataset.
1256 
1257     switch (aUri)
1258     {
1259     case kUriActiveGet:
1260         response = Get<ActiveDatasetManager>().ProcessGetRequest(aMessage, DatasetManager::kIgnoreSecurityPolicyFlags);
1261         Get<BorderAgent>().mCounters.mMgmtActiveGets++;
1262         break;
1263 
1264     case kUriPendingGet:
1265         response = Get<PendingDatasetManager>().ProcessGetRequest(aMessage, DatasetManager::kIgnoreSecurityPolicyFlags);
1266         Get<BorderAgent>().mCounters.mMgmtPendingGets++;
1267         break;
1268 
1269     case kUriCommissionerGet:
1270         response = Get<NetworkData::Leader>().ProcessCommissionerGetRequest(aMessage);
1271         break;
1272 
1273     default:
1274         break;
1275     }
1276 
1277     VerifyOrExit(response != nullptr, error = kErrorParse);
1278 
1279     SuccessOrExit(error = SendMessage(*response));
1280 
1281     LogInfo("Sent %s response to non-active commissioner", PathForUri(aUri));
1282 
1283 exit:
1284     LogWarnOnError(error, "send Active/Pending/CommissionerGet response");
1285     FreeMessageOnError(response, error);
1286 }
1287 
HandleTimer(Timer & aTimer)1288 void BorderAgent::CoapDtlsSession::HandleTimer(Timer &aTimer)
1289 {
1290     static_cast<CoapDtlsSession *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleTimer();
1291 }
1292 
HandleTimer(void)1293 void BorderAgent::CoapDtlsSession::HandleTimer(void)
1294 {
1295     if (IsConnected())
1296     {
1297         LogInfo("Session timed out - disconnecting");
1298         Disconnect();
1299     }
1300 }
1301 
1302 //----------------------------------------------------------------------------------------------------------------------
1303 // `BorderAgent::CoapDtlsSession::ForwardContext`
1304 
ForwardContext(CoapDtlsSession & aSession,const Coap::Message & aMessage,bool aPetition,bool aSeparate)1305 BorderAgent::CoapDtlsSession::ForwardContext::ForwardContext(CoapDtlsSession     &aSession,
1306                                                              const Coap::Message &aMessage,
1307                                                              bool                 aPetition,
1308                                                              bool                 aSeparate)
1309     : mSession(aSession)
1310     , mMessageId(aMessage.GetMessageId())
1311     , mPetition(aPetition)
1312     , mSeparate(aSeparate)
1313     , mTokenLength(aMessage.GetTokenLength())
1314     , mType(aMessage.GetType())
1315 {
1316     memcpy(mToken, aMessage.GetToken(), mTokenLength);
1317 }
1318 
ToHeader(Coap::Message & aMessage,uint8_t aCode) const1319 Error BorderAgent::CoapDtlsSession::ForwardContext::ToHeader(Coap::Message &aMessage, uint8_t aCode) const
1320 {
1321     if ((mType == Coap::kTypeNonConfirmable) || mSeparate)
1322     {
1323         aMessage.Init(Coap::kTypeNonConfirmable, static_cast<Coap::Code>(aCode));
1324     }
1325     else
1326     {
1327         aMessage.Init(Coap::kTypeAck, static_cast<Coap::Code>(aCode));
1328     }
1329 
1330     if (!mSeparate)
1331     {
1332         aMessage.SetMessageId(mMessageId);
1333     }
1334 
1335     return aMessage.SetToken(mToken, mTokenLength);
1336 }
1337 
1338 } // namespace MeshCoP
1339 } // namespace ot
1340 
1341 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
1342