Coverage Report

Created: 2025-05-12 06:47

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