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