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