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 "coap/coap_message.hpp"
39 #include "common/instance.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/logging.hpp"
42 #include "meshcop/meshcop.hpp"
43 #include "meshcop/meshcop_tlvs.hpp"
44 #include "thread/thread_netif.hpp"
45 #include "thread/thread_tlvs.hpp"
46 #include "thread/uri_paths.hpp"
47 
48 namespace ot {
49 namespace MeshCoP {
50 
51 namespace {
52 constexpr uint16_t kBorderAgentUdpPort = OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT; ///< UDP port of border agent service.
53 }
54 
Init(Instance & aInstance,const Coap::Message & aMessage,bool aPetition,bool aSeparate)55 void BorderAgent::ForwardContext::Init(Instance &           aInstance,
56                                        const Coap::Message &aMessage,
57                                        bool                 aPetition,
58                                        bool                 aSeparate)
59 {
60     InstanceLocatorInit::Init(aInstance);
61     mMessageId   = aMessage.GetMessageId();
62     mPetition    = aPetition;
63     mSeparate    = aSeparate;
64     mType        = aMessage.GetType();
65     mTokenLength = aMessage.GetTokenLength();
66     memcpy(mToken, aMessage.GetToken(), mTokenLength);
67 }
68 
ToHeader(Coap::Message & aMessage,uint8_t aCode)69 Error BorderAgent::ForwardContext::ToHeader(Coap::Message &aMessage, uint8_t aCode)
70 {
71     if ((mType == Coap::kTypeNonConfirmable) || mSeparate)
72     {
73         aMessage.Init(Coap::kTypeNonConfirmable, static_cast<Coap::Code>(aCode));
74     }
75     else
76     {
77         aMessage.Init(Coap::kTypeAck, static_cast<Coap::Code>(aCode));
78     }
79 
80     if (!mSeparate)
81     {
82         aMessage.SetMessageId(mMessageId);
83     }
84 
85     return aMessage.SetToken(mToken, mTokenLength);
86 }
87 
CoapCodeFromError(Error aError)88 Coap::Message::Code BorderAgent::CoapCodeFromError(Error aError)
89 {
90     Coap::Message::Code code;
91 
92     switch (aError)
93     {
94     case kErrorNone:
95         code = Coap::kCodeChanged;
96         break;
97 
98     case kErrorParse:
99         code = Coap::kCodeBadRequest;
100         break;
101 
102     default:
103         code = Coap::kCodeInternalError;
104         break;
105     }
106 
107     return code;
108 }
109 
SendErrorMessage(ForwardContext & aForwardContext,Error aError)110 void BorderAgent::SendErrorMessage(ForwardContext &aForwardContext, Error aError)
111 {
112     Error             error   = kErrorNone;
113     Coap::CoapSecure &coaps   = Get<Coap::CoapSecure>();
114     Coap::Message *   message = nullptr;
115 
116     VerifyOrExit((message = NewMeshCoPMessage(coaps)) != nullptr, error = kErrorNoBufs);
117     SuccessOrExit(error = aForwardContext.ToHeader(*message, CoapCodeFromError(aError)));
118     SuccessOrExit(error = coaps.SendMessage(*message, coaps.GetMessageInfo()));
119 
120 exit:
121     FreeMessageOnError(message, error);
122     LogError("send error CoAP message", error);
123 }
124 
SendErrorMessage(const Coap::Message & aRequest,bool aSeparate,Error aError)125 void BorderAgent::SendErrorMessage(const Coap::Message &aRequest, bool aSeparate, Error aError)
126 {
127     Error             error   = kErrorNone;
128     Coap::CoapSecure &coaps   = Get<Coap::CoapSecure>();
129     Coap::Message *   message = nullptr;
130 
131     VerifyOrExit((message = NewMeshCoPMessage(coaps)) != nullptr, error = kErrorNoBufs);
132 
133     if (aRequest.IsNonConfirmable() || aSeparate)
134     {
135         message->Init(Coap::kTypeNonConfirmable, CoapCodeFromError(aError));
136     }
137     else
138     {
139         message->Init(Coap::kTypeAck, CoapCodeFromError(aError));
140     }
141 
142     if (!aSeparate)
143     {
144         message->SetMessageId(aRequest.GetMessageId());
145     }
146 
147     SuccessOrExit(error = message->SetTokenFromMessage(aRequest));
148 
149     SuccessOrExit(error = coaps.SendMessage(*message, coaps.GetMessageInfo()));
150 
151 exit:
152     FreeMessageOnError(message, error);
153     LogError("send error CoAP message", error);
154 }
155 
HandleCoapResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)156 void BorderAgent::HandleCoapResponse(void *               aContext,
157                                      otMessage *          aMessage,
158                                      const otMessageInfo *aMessageInfo,
159                                      Error                aResult)
160 {
161     OT_UNUSED_VARIABLE(aMessageInfo);
162 
163     ForwardContext &forwardContext = *static_cast<ForwardContext *>(aContext);
164 
165     forwardContext.Get<BorderAgent>().HandleCoapResponse(forwardContext, static_cast<const Coap::Message *>(aMessage),
166                                                          aResult);
167 }
168 
HandleCoapResponse(ForwardContext & aForwardContext,const Coap::Message * aResponse,Error aResult)169 void BorderAgent::HandleCoapResponse(ForwardContext &aForwardContext, const Coap::Message *aResponse, Error aResult)
170 {
171     Coap::Message *message = nullptr;
172     Error          error;
173 
174     SuccessOrExit(error = aResult);
175     VerifyOrExit((message = NewMeshCoPMessage(Get<Coap::CoapSecure>())) != nullptr, error = kErrorNoBufs);
176 
177     if (aForwardContext.IsPetition() && aResponse->GetCode() == Coap::kCodeChanged)
178     {
179         uint8_t state;
180 
181         SuccessOrExit(error = Tlv::Find<StateTlv>(*aResponse, state));
182 
183         if (state == StateTlv::kAccept)
184         {
185             uint16_t sessionId;
186 
187             SuccessOrExit(error = Tlv::Find<CommissionerSessionIdTlv>(*aResponse, sessionId));
188 
189             IgnoreError(Get<Mle::MleRouter>().GetCommissionerAloc(mCommissionerAloc.GetAddress(), sessionId));
190             Get<ThreadNetif>().AddUnicastAddress(mCommissionerAloc);
191             IgnoreError(Get<Ip6::Udp>().AddReceiver(mUdpReceiver));
192 
193             otLogInfoMeshCoP("commissioner accepted: session ID=%d, ALOC=%s", sessionId,
194                              mCommissionerAloc.GetAddress().ToString().AsCString());
195         }
196     }
197 
198     SuccessOrExit(error = aForwardContext.ToHeader(*message, aResponse->GetCode()));
199 
200     if (aResponse->GetLength() > aResponse->GetOffset())
201     {
202         SuccessOrExit(error = message->SetPayloadMarker());
203     }
204 
205     SuccessOrExit(error = ForwardToCommissioner(*message, *aResponse));
206 
207 exit:
208 
209     if (error != kErrorNone)
210     {
211         FreeMessage(message);
212 
213         otLogWarnMeshCoP("Commissioner request[%hu] failed: %s", aForwardContext.GetMessageId(), ErrorToString(error));
214 
215         SendErrorMessage(aForwardContext, error);
216     }
217 
218     Instance::HeapFree(&aForwardContext);
219 }
220 
221 template <Coap::Resource BorderAgent::*aResource>
HandleRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)222 void BorderAgent::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
223 {
224     IgnoreError(static_cast<BorderAgent *>(aContext)->ForwardToLeader(
225         *static_cast<Coap::Message *>(aMessage), *static_cast<const Ip6::MessageInfo *>(aMessageInfo),
226         (static_cast<BorderAgent *>(aContext)->*aResource).GetUriPath(), false, false));
227 }
228 
229 template <>
HandleRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)230 void BorderAgent::HandleRequest<&BorderAgent::mCommissionerPetition>(void *               aContext,
231                                                                      otMessage *          aMessage,
232                                                                      const otMessageInfo *aMessageInfo)
233 {
234     IgnoreError(static_cast<BorderAgent *>(aContext)->ForwardToLeader(
235         *static_cast<Coap::Message *>(aMessage), *static_cast<const Ip6::MessageInfo *>(aMessageInfo),
236         UriPath::kLeaderPetition, true, true));
237 }
238 
239 template <>
HandleRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)240 void BorderAgent::HandleRequest<&BorderAgent::mCommissionerKeepAlive>(void *               aContext,
241                                                                       otMessage *          aMessage,
242                                                                       const otMessageInfo *aMessageInfo)
243 {
244     static_cast<BorderAgent *>(aContext)->HandleKeepAlive(*static_cast<Coap::Message *>(aMessage),
245                                                           *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
246 }
247 
248 template <>
HandleRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)249 void BorderAgent::HandleRequest<&BorderAgent::mRelayTransmit>(void *               aContext,
250                                                               otMessage *          aMessage,
251                                                               const otMessageInfo *aMessageInfo)
252 {
253     OT_UNUSED_VARIABLE(aMessageInfo);
254     static_cast<BorderAgent *>(aContext)->HandleRelayTransmit(*static_cast<Coap::Message *>(aMessage));
255 }
256 
257 template <>
HandleRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)258 void BorderAgent::HandleRequest<&BorderAgent::mRelayReceive>(void *               aContext,
259                                                              otMessage *          aMessage,
260                                                              const otMessageInfo *aMessageInfo)
261 {
262     OT_UNUSED_VARIABLE(aMessageInfo);
263     static_cast<BorderAgent *>(aContext)->HandleRelayReceive(*static_cast<Coap::Message *>(aMessage));
264 }
265 
266 template <>
HandleRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)267 void BorderAgent::HandleRequest<&BorderAgent::mProxyTransmit>(void *               aContext,
268                                                               otMessage *          aMessage,
269                                                               const otMessageInfo *aMessageInfo)
270 {
271     OT_UNUSED_VARIABLE(aMessageInfo);
272     static_cast<BorderAgent *>(aContext)->HandleProxyTransmit(*static_cast<Coap::Message *>(aMessage));
273 }
274 
BorderAgent(Instance & aInstance)275 BorderAgent::BorderAgent(Instance &aInstance)
276     : InstanceLocator(aInstance)
277     , mCommissionerPetition(UriPath::kCommissionerPetition,
278                             BorderAgent::HandleRequest<&BorderAgent::mCommissionerPetition>,
279                             this)
280     , mCommissionerKeepAlive(UriPath::kCommissionerKeepAlive,
281                              BorderAgent::HandleRequest<&BorderAgent::mCommissionerKeepAlive>,
282                              this)
283     , mRelayTransmit(UriPath::kRelayTx, BorderAgent::HandleRequest<&BorderAgent::mRelayTransmit>, this)
284     , mRelayReceive(UriPath::kRelayRx, BorderAgent::HandleRequest<&BorderAgent::mRelayReceive>, this)
285     , mCommissionerGet(UriPath::kCommissionerGet, BorderAgent::HandleRequest<&BorderAgent::mCommissionerGet>, this)
286     , mCommissionerSet(UriPath::kCommissionerSet, BorderAgent::HandleRequest<&BorderAgent::mCommissionerSet>, this)
287     , mActiveGet(UriPath::kActiveGet, BorderAgent::HandleRequest<&BorderAgent::mActiveGet>, this)
288     , mActiveSet(UriPath::kActiveSet, BorderAgent::HandleRequest<&BorderAgent::mActiveSet>, this)
289     , mPendingGet(UriPath::kPendingGet, BorderAgent::HandleRequest<&BorderAgent::mPendingGet>, this)
290     , mPendingSet(UriPath::kPendingSet, BorderAgent::HandleRequest<&BorderAgent::mPendingSet>, this)
291     , mProxyTransmit(UriPath::kProxyTx, BorderAgent::HandleRequest<&BorderAgent::mProxyTransmit>, this)
292     , mUdpReceiver(BorderAgent::HandleUdpReceive, this)
293     , mTimer(aInstance, HandleTimeout)
294     , mState(kStateStopped)
295     , mUdpProxyPort(0)
296 {
297     mCommissionerAloc.InitAsThreadOriginRealmLocalScope();
298 }
299 
HandleNotifierEvents(Events aEvents)300 void BorderAgent::HandleNotifierEvents(Events aEvents)
301 {
302     VerifyOrExit(aEvents.ContainsAny(kEventThreadRoleChanged | kEventCommissionerStateChanged));
303 
304 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
305     VerifyOrExit(Get<MeshCoP::Commissioner>().IsDisabled());
306 #endif
307 
308     if (Get<Mle::MleRouter>().IsAttached())
309     {
310         Start();
311     }
312     else
313     {
314         Stop();
315     }
316 
317 exit:
318     return;
319 }
320 
HandleProxyTransmit(const Coap::Message & aMessage)321 void BorderAgent::HandleProxyTransmit(const Coap::Message &aMessage)
322 {
323     Message *           message = nullptr;
324     Ip6::MessageInfo    messageInfo;
325     uint16_t            offset;
326     Error               error;
327     UdpEncapsulationTlv tlv;
328 
329     SuccessOrExit(error = Tlv::FindTlvOffset(aMessage, Tlv::kUdpEncapsulation, offset));
330     SuccessOrExit(error = aMessage.Read(offset, tlv));
331 
332     VerifyOrExit((message = Get<Ip6::Udp>().NewMessage(0)) != nullptr, error = kErrorNoBufs);
333     SuccessOrExit(error = message->SetLength(tlv.GetUdpLength()));
334     aMessage.CopyTo(offset + sizeof(tlv), 0, tlv.GetUdpLength(), *message);
335 
336     VerifyOrExit(tlv.GetSourcePort() > 0 && tlv.GetDestinationPort() > 0, error = kErrorDrop);
337 
338     messageInfo.SetSockPort(tlv.GetSourcePort());
339     messageInfo.SetSockAddr(mCommissionerAloc.GetAddress());
340     messageInfo.SetPeerPort(tlv.GetDestinationPort());
341 
342     SuccessOrExit(error = Tlv::Find<Ip6AddressTlv>(aMessage, messageInfo.GetPeerAddr()));
343 
344     SuccessOrExit(error = Get<Ip6::Udp>().SendDatagram(*message, messageInfo, Ip6::kProtoUdp));
345     mUdpProxyPort = tlv.GetSourcePort();
346 
347     otLogInfoMeshCoP("Proxy transmit sent to %s", messageInfo.GetPeerAddr().ToString().AsCString());
348 
349 exit:
350     FreeMessageOnError(message, error);
351     LogError("send proxy stream", error);
352 }
353 
HandleUdpReceive(const Message & aMessage,const Ip6::MessageInfo & aMessageInfo)354 bool BorderAgent::HandleUdpReceive(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
355 {
356     Error          error;
357     Coap::Message *message = nullptr;
358 
359     VerifyOrExit(aMessageInfo.GetSockAddr() == mCommissionerAloc.GetAddress(),
360                  error = kErrorDestinationAddressFiltered);
361 
362     VerifyOrExit(aMessage.GetLength() > 0, error = kErrorNone);
363 
364     VerifyOrExit((message = NewMeshCoPMessage(Get<Coap::CoapSecure>())) != nullptr, error = kErrorNoBufs);
365 
366     message->InitAsNonConfirmablePost();
367     SuccessOrExit(error = message->AppendUriPathOptions(UriPath::kProxyRx));
368     SuccessOrExit(error = message->SetPayloadMarker());
369 
370     {
371         UdpEncapsulationTlv tlv;
372         uint16_t            offset;
373         uint16_t            udpLength = aMessage.GetLength() - aMessage.GetOffset();
374 
375         tlv.Init();
376         tlv.SetSourcePort(aMessageInfo.GetPeerPort());
377         tlv.SetDestinationPort(aMessageInfo.GetSockPort());
378         tlv.SetUdpLength(udpLength);
379         SuccessOrExit(error = message->Append(tlv));
380 
381         offset = message->GetLength();
382         SuccessOrExit(error = message->SetLength(offset + udpLength));
383         aMessage.CopyTo(aMessage.GetOffset(), offset, udpLength, *message);
384     }
385 
386     SuccessOrExit(error = Tlv::Append<Ip6AddressTlv>(*message, aMessageInfo.GetPeerAddr()));
387 
388     SuccessOrExit(error = Get<Coap::CoapSecure>().SendMessage(*message, Get<Coap::CoapSecure>().GetMessageInfo()));
389 
390     otLogInfoMeshCoP("Sent to commissioner on %s", UriPath::kProxyRx);
391 
392 exit:
393     FreeMessageOnError(message, error);
394     LogError("notify commissioner on ProxyRx (c/ur)", error);
395 
396     return error != kErrorDestinationAddressFiltered;
397 }
398 
HandleRelayReceive(const Coap::Message & aMessage)399 void BorderAgent::HandleRelayReceive(const Coap::Message &aMessage)
400 {
401     Coap::Message *message = nullptr;
402     Error          error;
403 
404     VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorDrop);
405     VerifyOrExit((message = NewMeshCoPMessage(Get<Coap::CoapSecure>())) != nullptr, error = kErrorNoBufs);
406 
407     message->InitAsNonConfirmablePost();
408     SuccessOrExit(error = message->AppendUriPathOptions(UriPath::kRelayRx));
409 
410     if (aMessage.GetLength() > aMessage.GetOffset())
411     {
412         SuccessOrExit(error = message->SetPayloadMarker());
413     }
414 
415     SuccessOrExit(error = ForwardToCommissioner(*message, aMessage));
416     otLogInfoMeshCoP("Sent to commissioner on %s", UriPath::kRelayRx);
417 
418 exit:
419     FreeMessageOnError(message, error);
420 }
421 
ForwardToCommissioner(Coap::Message & aForwardMessage,const Message & aMessage)422 Error BorderAgent::ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage)
423 {
424     Error    error  = kErrorNone;
425     uint16_t offset = 0;
426 
427     offset = aForwardMessage.GetLength();
428     SuccessOrExit(error = aForwardMessage.SetLength(offset + aMessage.GetLength() - aMessage.GetOffset()));
429     aMessage.CopyTo(aMessage.GetOffset(), offset, aMessage.GetLength() - aMessage.GetOffset(), aForwardMessage);
430 
431     SuccessOrExit(error =
432                       Get<Coap::CoapSecure>().SendMessage(aForwardMessage, Get<Coap::CoapSecure>().GetMessageInfo()));
433 
434     otLogInfoMeshCoP("Sent to commissioner");
435 
436 exit:
437     LogError("send to commissioner", error);
438     return error;
439 }
440 
HandleKeepAlive(const Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)441 void BorderAgent::HandleKeepAlive(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
442 {
443     Error error;
444 
445     error = ForwardToLeader(aMessage, aMessageInfo, UriPath::kLeaderKeepAlive, false, true);
446 
447     if (error == kErrorNone)
448     {
449         mTimer.Start(kKeepAliveTimeout);
450     }
451 }
452 
HandleRelayTransmit(const Coap::Message & aMessage)453 void BorderAgent::HandleRelayTransmit(const Coap::Message &aMessage)
454 {
455     Error            error = kErrorNone;
456     uint16_t         joinerRouterRloc;
457     Coap::Message *  message = nullptr;
458     Ip6::MessageInfo messageInfo;
459     uint16_t         offset = 0;
460 
461     VerifyOrExit(aMessage.IsNonConfirmablePostRequest());
462 
463     SuccessOrExit(error = Tlv::Find<JoinerRouterLocatorTlv>(aMessage, joinerRouterRloc));
464 
465     VerifyOrExit((message = NewMeshCoPMessage(Get<Tmf::Agent>())) != nullptr, error = kErrorNoBufs);
466 
467     SuccessOrExit(error = message->InitAsNonConfirmablePost(UriPath::kRelayTx));
468     SuccessOrExit(error = message->SetPayloadMarker());
469 
470     offset = message->GetLength();
471     SuccessOrExit(error = message->SetLength(offset + aMessage.GetLength() - aMessage.GetOffset()));
472     aMessage.CopyTo(aMessage.GetOffset(), offset, aMessage.GetLength() - aMessage.GetOffset(), *message);
473 
474     messageInfo.SetSockPort(Tmf::kUdpPort);
475     messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
476     messageInfo.SetPeerPort(Tmf::kUdpPort);
477     messageInfo.SetPeerAddr(Get<Mle::MleRouter>().GetMeshLocal16());
478     messageInfo.GetPeerAddr().GetIid().SetLocator(joinerRouterRloc);
479 
480     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
481 
482     otLogInfoMeshCoP("Sent to joiner router request on %s", UriPath::kRelayTx);
483 
484 exit:
485     FreeMessageOnError(message, error);
486     LogError("send to joiner router request RelayTx (c/tx)", error);
487 }
488 
ForwardToLeader(const Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const char * aPath,bool aPetition,bool aSeparate)489 Error BorderAgent::ForwardToLeader(const Coap::Message &   aMessage,
490                                    const Ip6::MessageInfo &aMessageInfo,
491                                    const char *            aPath,
492                                    bool                    aPetition,
493                                    bool                    aSeparate)
494 {
495     Error            error          = kErrorNone;
496     ForwardContext * forwardContext = nullptr;
497     Ip6::MessageInfo messageInfo;
498     Coap::Message *  message = nullptr;
499     uint16_t         offset  = 0;
500 
501     VerifyOrExit((message = NewMeshCoPMessage(Get<Tmf::Agent>())) != nullptr, error = kErrorNoBufs);
502 
503     if (aSeparate)
504     {
505         SuccessOrExit(error = Get<Coap::CoapSecure>().SendAck(aMessage, aMessageInfo));
506     }
507 
508     forwardContext = static_cast<ForwardContext *>(Instance::HeapCAlloc(1, sizeof(ForwardContext)));
509     VerifyOrExit(forwardContext != nullptr, error = kErrorNoBufs);
510 
511     forwardContext->Init(GetInstance(), aMessage, aPetition, aSeparate);
512 
513     SuccessOrExit(error = message->InitAsConfirmablePost(aPath));
514 
515     // Payload of c/cg may be empty
516     if (aMessage.GetLength() - aMessage.GetOffset() > 0)
517     {
518         SuccessOrExit(error = message->SetPayloadMarker());
519     }
520 
521     offset = message->GetLength();
522     SuccessOrExit(error = message->SetLength(offset + aMessage.GetLength() - aMessage.GetOffset()));
523     aMessage.CopyTo(aMessage.GetOffset(), offset, aMessage.GetLength() - aMessage.GetOffset(), *message);
524 
525     SuccessOrExit(error = Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
526     messageInfo.SetPeerPort(Tmf::kUdpPort);
527     messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
528     messageInfo.SetSockPort(Tmf::kUdpPort);
529 
530     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, HandleCoapResponse, forwardContext));
531 
532     // HandleCoapResponse is responsible to free this forward context.
533     forwardContext = nullptr;
534 
535     otLogInfoMeshCoP("Forwarded request to leader on %s", aPath);
536 
537 exit:
538     LogError("forward to leader", error);
539 
540     if (error != kErrorNone)
541     {
542         if (forwardContext != nullptr)
543         {
544             Instance::HeapFree(forwardContext);
545         }
546 
547         FreeMessage(message);
548         SendErrorMessage(aMessage, aSeparate, error);
549     }
550 
551     return error;
552 }
553 
HandleConnected(bool aConnected,void * aContext)554 void BorderAgent::HandleConnected(bool aConnected, void *aContext)
555 {
556     static_cast<BorderAgent *>(aContext)->HandleConnected(aConnected);
557 }
558 
HandleConnected(bool aConnected)559 void BorderAgent::HandleConnected(bool aConnected)
560 {
561     if (aConnected)
562     {
563         otLogInfoMeshCoP("Commissioner connected");
564         mState = kStateActive;
565         mTimer.Start(kKeepAliveTimeout);
566     }
567     else
568     {
569         otLogInfoMeshCoP("Commissioner disconnected");
570         IgnoreError(Get<Ip6::Udp>().RemoveReceiver(mUdpReceiver));
571         Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
572         mState        = kStateStarted;
573         mUdpProxyPort = 0;
574     }
575 }
576 
GetUdpPort(void) const577 uint16_t BorderAgent::GetUdpPort(void) const
578 {
579     return Get<Coap::CoapSecure>().GetUdpPort();
580 }
581 
Start(void)582 void BorderAgent::Start(void)
583 {
584     Error             error;
585     Coap::CoapSecure &coaps = Get<Coap::CoapSecure>();
586 
587     VerifyOrExit(mState == kStateStopped, error = kErrorAlready);
588 
589     SuccessOrExit(error = coaps.Start(kBorderAgentUdpPort));
590     SuccessOrExit(error = coaps.SetPsk(Get<KeyManager>().GetPskc().m8, OT_PSKC_MAX_SIZE));
591     coaps.SetConnectedCallback(HandleConnected, this);
592 
593     coaps.AddResource(mActiveGet);
594     coaps.AddResource(mActiveSet);
595     coaps.AddResource(mPendingGet);
596     coaps.AddResource(mPendingSet);
597     coaps.AddResource(mCommissionerPetition);
598     coaps.AddResource(mCommissionerKeepAlive);
599     coaps.AddResource(mCommissionerSet);
600     coaps.AddResource(mCommissionerGet);
601     coaps.AddResource(mProxyTransmit);
602     coaps.AddResource(mRelayTransmit);
603 
604     Get<Tmf::Agent>().AddResource(mRelayReceive);
605 
606     mState        = kStateStarted;
607     mUdpProxyPort = 0;
608 
609     otLogInfoMeshCoP("Border Agent start listening on port %d", kBorderAgentUdpPort);
610 
611 exit:
612     if (error != kErrorNone)
613     {
614         otLogWarnMeshCoP("failed to start Border Agent on port %d: %s", kBorderAgentUdpPort, ErrorToString(error));
615     }
616 }
617 
HandleTimeout(Timer & aTimer)618 void BorderAgent::HandleTimeout(Timer &aTimer)
619 {
620     aTimer.Get<BorderAgent>().HandleTimeout();
621 }
622 
HandleTimeout(void)623 void BorderAgent::HandleTimeout(void)
624 {
625     if (Get<Coap::CoapSecure>().IsConnected())
626     {
627         Get<Coap::CoapSecure>().Disconnect();
628         otLogWarnMeshCoP("Reset commissioner session");
629     }
630 }
631 
Stop(void)632 void BorderAgent::Stop(void)
633 {
634     Coap::CoapSecure &coaps = Get<Coap::CoapSecure>();
635 
636     VerifyOrExit(mState != kStateStopped);
637 
638     mTimer.Stop();
639 
640     coaps.RemoveResource(mCommissionerPetition);
641     coaps.RemoveResource(mCommissionerKeepAlive);
642     coaps.RemoveResource(mCommissionerSet);
643     coaps.RemoveResource(mCommissionerGet);
644     coaps.RemoveResource(mActiveGet);
645     coaps.RemoveResource(mActiveSet);
646     coaps.RemoveResource(mPendingGet);
647     coaps.RemoveResource(mPendingSet);
648     coaps.RemoveResource(mProxyTransmit);
649     coaps.RemoveResource(mRelayTransmit);
650 
651     Get<Tmf::Agent>().RemoveResource(mRelayReceive);
652 
653     coaps.Stop();
654 
655     mState        = kStateStopped;
656     mUdpProxyPort = 0;
657 
658     otLogInfoMeshCoP("Border Agent stopped");
659 
660 exit:
661     return;
662 }
663 
ApplyMeshLocalPrefix(void)664 void BorderAgent::ApplyMeshLocalPrefix(void)
665 {
666     VerifyOrExit(mState == kStateActive);
667 
668     if (Get<ThreadNetif>().HasUnicastAddress(mCommissionerAloc))
669     {
670         Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
671         mCommissionerAloc.GetAddress().SetPrefix(Get<Mle::MleRouter>().GetMeshLocalPrefix());
672         Get<ThreadNetif>().AddUnicastAddress(mCommissionerAloc);
673     }
674 
675 exit:
676     return;
677 }
678 
679 } // namespace MeshCoP
680 } // namespace ot
681 
682 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
683