blob: 6f84e27db87de3e6b7a0e864b4cde0bb56abaf52 [file] [log] [blame]
/*
* Copyright (c) 2018, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements the BorderAgent service.
*/
#include "border_agent.hpp"
#include "coap/coap_message.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "common/new.hpp"
#include "meshcop/meshcop.hpp"
#include "meshcop/meshcop_tlvs.hpp"
#include "thread/thread_netif.hpp"
#include "thread/thread_tlvs.hpp"
#include "thread/thread_uri_paths.hpp"
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
namespace ot {
namespace MeshCoP {
class ForwardContext
{
public:
/**
* This constructor initializes a forward context.
*
* @param[in] aBorderAgent A reference to the border agent.
* @param[in] aHeader A reference to the request header.
* @param[in] aPetition Whether this request is a petition.
* @param[in] aSeparate Whether this original request expects separate response.
*
*/
ForwardContext(BorderAgent &aBorderAgent, const Coap::Message &aMessage, bool aPetition, bool aSeparate)
: mBorderAgent(aBorderAgent)
, mMessageId(aMessage.GetMessageId())
, mPetition(aPetition)
, mSeparate(aSeparate)
, mTokenLength(aMessage.GetTokenLength())
, mType(aMessage.GetType() >> Coap::Message::kTypeOffset)
{
memcpy(mToken, aMessage.GetToken(), mTokenLength);
}
/**
* This method returns whether the request is a petition.
*
* @retval true This is a petition request.
* @retval false This is not a petition request.
*
*/
bool IsPetition(void) const { return mPetition; }
/**
* This method returns the border agent sending this request.
*
* @returns A reference to the border agent sending this request.
*
*/
BorderAgent &GetBorderAgent(void) { return mBorderAgent; }
/**
* This method returns the message id of the original request.
*
* @returns A message id of the original request.
*
*/
uint16_t GetMessageId(void) const { return mMessageId; }
/**
* This method generate the response header according to the saved metadata.
*
* @param[out] aHeader A reference to the response header.
* @param[in] aCode The response code to fill in the response header.
*
* @retval OT_ERROR_NONE Successfully generated the response header.
* @retval OT_ERROR_NO_BUFS Insufficient message buffers available to generate the response header.
*
*/
otError ToHeader(Coap::Message &aMessage, Coap::Message::Code aCode)
{
if (mType == (OT_COAP_TYPE_NON_CONFIRMABLE >> Coap::Message::kTypeOffset) || mSeparate)
{
aMessage.Init(OT_COAP_TYPE_NON_CONFIRMABLE, aCode);
}
else
{
aMessage.Init(OT_COAP_TYPE_ACKNOWLEDGMENT, aCode);
}
if (!mSeparate)
{
aMessage.SetMessageId(mMessageId);
}
return aMessage.SetToken(mToken, mTokenLength);
}
private:
enum
{
kMaxTokenLength = OT_COAP_MAX_TOKEN_LENGTH, ///< The max token size
};
BorderAgent &mBorderAgent;
uint16_t mMessageId; ///< The CoAP Message ID of the original request.
bool mPetition : 1; ///< Whether the forwarding request is leader petition.
bool mSeparate : 1; ///< Whether the original request expects separate response.
uint8_t mTokenLength : 4; ///< The CoAP Token Length of the original request.
uint8_t mType : 2; ///< The CoAP Type of the original request.
uint8_t mToken[kMaxTokenLength]; ///< The CoAP Token of the original request.
};
static Coap::Message::Code CoapCodeFromError(otError aError)
{
Coap::Message::Code code;
switch (aError)
{
case OT_ERROR_NONE:
code = OT_COAP_CODE_CHANGED;
break;
case OT_ERROR_PARSE:
code = OT_COAP_CODE_BAD_REQUEST;
break;
default:
code = OT_COAP_CODE_INTERNAL_ERROR;
break;
}
return code;
}
static void SendErrorMessage(Coap::CoapSecure &aCoapSecure, ForwardContext &aForwardContext, otError aError)
{
otError error = OT_ERROR_NONE;
Coap::Message *message = nullptr;
VerifyOrExit((message = NewMeshCoPMessage(aCoapSecure)) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = aForwardContext.ToHeader(*message, CoapCodeFromError(aError)));
SuccessOrExit(error = aCoapSecure.SendMessage(*message, aCoapSecure.GetMessageInfo()));
exit:
if (error != OT_ERROR_NONE)
{
if (message != nullptr)
{
message->Free();
}
otLogWarnMeshCoP("Failed to send error CoAP message: %s", otThreadErrorToString(error));
}
}
static void SendErrorMessage(Coap::CoapSecure & aCoapSecure,
const Coap::Message &aRequest,
bool aSeparate,
otError aError)
{
otError error = OT_ERROR_NONE;
Coap::Message *message = nullptr;
VerifyOrExit((message = NewMeshCoPMessage(aCoapSecure)) != nullptr, error = OT_ERROR_NO_BUFS);
if (aRequest.IsNonConfirmable() || aSeparate)
{
message->Init(OT_COAP_TYPE_NON_CONFIRMABLE, CoapCodeFromError(aError));
}
else
{
message->Init(OT_COAP_TYPE_ACKNOWLEDGMENT, CoapCodeFromError(aError));
}
if (!aSeparate)
{
message->SetMessageId(aRequest.GetMessageId());
}
SuccessOrExit(error = message->SetToken(aRequest.GetToken(), aRequest.GetTokenLength()));
SuccessOrExit(error = aCoapSecure.SendMessage(*message, aCoapSecure.GetMessageInfo()));
exit:
if (error != OT_ERROR_NONE)
{
if (message != nullptr)
{
message->Free();
}
otLogWarnMeshCoP("Failed to send error CoAP message: %s", otThreadErrorToString(error));
}
}
void BorderAgent::HandleCoapResponse(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo,
otError aResult)
{
OT_UNUSED_VARIABLE(aMessageInfo);
ForwardContext & forwardContext = *static_cast<ForwardContext *>(aContext);
BorderAgent & borderAgent = forwardContext.GetBorderAgent();
Instance & instance = borderAgent.GetInstance();
const Coap::Message *response = static_cast<const Coap::Message *>(aMessage);
Coap::Message * message = nullptr;
otError error;
SuccessOrExit(error = aResult);
VerifyOrExit((message = NewMeshCoPMessage(instance.Get<Coap::CoapSecure>())) != nullptr, error = OT_ERROR_NO_BUFS);
if (forwardContext.IsPetition() && response->GetCode() == OT_COAP_CODE_CHANGED)
{
uint8_t state;
SuccessOrExit(error = Tlv::FindUint8Tlv(*response, Tlv::kState, state));
if (state == StateTlv::kAccept)
{
uint16_t sessionId;
SuccessOrExit(error = Tlv::FindUint16Tlv(*response, Tlv::kCommissionerSessionId, sessionId));
IgnoreError(instance.Get<Mle::MleRouter>().GetCommissionerAloc(borderAgent.mCommissionerAloc.GetAddress(),
sessionId));
instance.Get<ThreadNetif>().AddUnicastAddress(borderAgent.mCommissionerAloc);
IgnoreError(instance.Get<Ip6::Udp>().AddReceiver(borderAgent.mUdpReceiver));
}
}
SuccessOrExit(error = forwardContext.ToHeader(*message, response->GetCode()));
if (response->GetLength() > response->GetOffset())
{
SuccessOrExit(error = message->SetPayloadMarker());
}
SuccessOrExit(error = borderAgent.ForwardToCommissioner(*message, *response));
exit:
if (error != OT_ERROR_NONE)
{
if (message != nullptr)
{
message->Free();
}
otLogWarnMeshCoP("Commissioner request[%hu] failed: %s", forwardContext.GetMessageId(),
otThreadErrorToString(error));
SendErrorMessage(instance.Get<Coap::CoapSecure>(), forwardContext, error);
}
instance.HeapFree(&forwardContext);
}
template <>
void BorderAgent::HandleRequest<&BorderAgent::mCommissionerPetition>(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo)
{
IgnoreError(static_cast<BorderAgent *>(aContext)->ForwardToLeader(
*static_cast<Coap::Message *>(aMessage), *static_cast<const Ip6::MessageInfo *>(aMessageInfo),
OT_URI_PATH_LEADER_PETITION, true, true));
}
template <>
void BorderAgent::HandleRequest<&BorderAgent::mCommissionerKeepAlive>(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo)
{
static_cast<BorderAgent *>(aContext)->HandleKeepAlive(*static_cast<Coap::Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
template <>
void BorderAgent::HandleRequest<&BorderAgent::mRelayTransmit>(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo)
{
OT_UNUSED_VARIABLE(aMessageInfo);
static_cast<BorderAgent *>(aContext)->HandleRelayTransmit(*static_cast<Coap::Message *>(aMessage));
}
template <>
void BorderAgent::HandleRequest<&BorderAgent::mRelayReceive>(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo)
{
OT_UNUSED_VARIABLE(aMessageInfo);
static_cast<BorderAgent *>(aContext)->HandleRelayReceive(*static_cast<Coap::Message *>(aMessage));
}
template <>
void BorderAgent::HandleRequest<&BorderAgent::mProxyTransmit>(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo)
{
OT_UNUSED_VARIABLE(aMessageInfo);
static_cast<BorderAgent *>(aContext)->HandleProxyTransmit(*static_cast<Coap::Message *>(aMessage));
}
BorderAgent::BorderAgent(Instance &aInstance)
: InstanceLocator(aInstance)
, Notifier::Receiver(aInstance, BorderAgent::HandleNotifierEvents)
, mCommissionerPetition(OT_URI_PATH_COMMISSIONER_PETITION,
BorderAgent::HandleRequest<&BorderAgent::mCommissionerPetition>,
this)
, mCommissionerKeepAlive(OT_URI_PATH_COMMISSIONER_KEEP_ALIVE,
BorderAgent::HandleRequest<&BorderAgent::mCommissionerKeepAlive>,
this)
, mRelayTransmit(OT_URI_PATH_RELAY_TX, BorderAgent::HandleRequest<&BorderAgent::mRelayTransmit>, this)
, mRelayReceive(OT_URI_PATH_RELAY_RX, BorderAgent::HandleRequest<&BorderAgent::mRelayReceive>, this)
, mCommissionerGet(OT_URI_PATH_COMMISSIONER_GET, BorderAgent::HandleRequest<&BorderAgent::mCommissionerGet>, this)
, mCommissionerSet(OT_URI_PATH_COMMISSIONER_SET, BorderAgent::HandleRequest<&BorderAgent::mCommissionerSet>, this)
, mActiveGet(OT_URI_PATH_ACTIVE_GET, BorderAgent::HandleRequest<&BorderAgent::mActiveGet>, this)
, mActiveSet(OT_URI_PATH_ACTIVE_SET, BorderAgent::HandleRequest<&BorderAgent::mActiveSet>, this)
, mPendingGet(OT_URI_PATH_PENDING_GET, BorderAgent::HandleRequest<&BorderAgent::mPendingGet>, this)
, mPendingSet(OT_URI_PATH_PENDING_SET, BorderAgent::HandleRequest<&BorderAgent::mPendingSet>, this)
, mProxyTransmit(OT_URI_PATH_PROXY_TX, BorderAgent::HandleRequest<&BorderAgent::mProxyTransmit>, this)
, mUdpReceiver(BorderAgent::HandleUdpReceive, this)
, mTimer(aInstance, HandleTimeout, this)
, mState(OT_BORDER_AGENT_STATE_STOPPED)
{
mCommissionerAloc.Clear();
mCommissionerAloc.mPrefixLength = 64;
mCommissionerAloc.mAddressOrigin = OT_ADDRESS_ORIGIN_THREAD;
mCommissionerAloc.mPreferred = true;
mCommissionerAloc.mValid = true;
mCommissionerAloc.mScopeOverride = Ip6::Address::kRealmLocalScope;
mCommissionerAloc.mScopeOverrideValid = true;
}
void BorderAgent::HandleNotifierEvents(Notifier::Receiver &aReceiver, Events aEvents)
{
static_cast<BorderAgent &>(aReceiver).HandleNotifierEvents(aEvents);
}
void BorderAgent::HandleNotifierEvents(Events aEvents)
{
VerifyOrExit(aEvents.ContainsAny(kEventThreadRoleChanged | kEventCommissionerStateChanged), OT_NOOP);
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
VerifyOrExit(Get<MeshCoP::Commissioner>().IsDisabled(), OT_NOOP);
#endif
if (Get<Mle::MleRouter>().IsAttached())
{
IgnoreError(Start());
}
else
{
IgnoreError(Stop());
}
exit:
return;
}
void BorderAgent::HandleProxyTransmit(const Coap::Message &aMessage)
{
Message * message = nullptr;
Ip6::MessageInfo messageInfo;
uint16_t offset;
otError error;
{
UdpEncapsulationTlv tlv;
SuccessOrExit(error = Tlv::FindTlvOffset(aMessage, Tlv::kUdpEncapsulation, offset));
VerifyOrExit(aMessage.Read(offset, sizeof(tlv), &tlv) == sizeof(tlv), error = OT_ERROR_PARSE);
VerifyOrExit((message = Get<Ip6::Udp>().NewMessage(0)) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = message->SetLength(tlv.GetUdpLength()));
aMessage.CopyTo(offset + sizeof(tlv), 0, tlv.GetUdpLength(), *message);
messageInfo.SetSockPort(tlv.GetSourcePort() != 0 ? tlv.GetSourcePort() : Get<Ip6::Udp>().GetEphemeralPort());
messageInfo.SetSockAddr(mCommissionerAloc.GetAddress());
messageInfo.SetPeerPort(tlv.GetDestinationPort());
}
SuccessOrExit(
error = Tlv::FindTlv(aMessage, Tlv::kIPv6Address, messageInfo.GetPeerAddr().mFields.m8, sizeof(Ip6::Address)));
SuccessOrExit(error = Get<Ip6::Udp>().SendDatagram(*message, messageInfo, Ip6::kProtoUdp));
otLogInfoMeshCoP("Proxy transmit sent");
exit:
if (error != OT_ERROR_NONE)
{
otLogWarnMeshCoP("Failed to send proxy stream: %s", otThreadErrorToString(error));
if (message != nullptr)
{
message->Free();
}
}
}
bool BorderAgent::HandleUdpReceive(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
otError error;
Coap::Message *message = nullptr;
VerifyOrExit(aMessageInfo.GetSockAddr() == mCommissionerAloc.GetAddress(),
error = OT_ERROR_DESTINATION_ADDRESS_FILTERED);
VerifyOrExit(aMessage.GetLength() > 0, error = OT_ERROR_NONE);
VerifyOrExit((message = NewMeshCoPMessage(Get<Coap::CoapSecure>())) != nullptr, error = OT_ERROR_NO_BUFS);
message->Init(OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST);
SuccessOrExit(error = message->AppendUriPathOptions(OT_URI_PATH_PROXY_RX));
SuccessOrExit(error = message->SetPayloadMarker());
{
UdpEncapsulationTlv tlv;
uint16_t offset;
uint16_t udpLength = aMessage.GetLength() - aMessage.GetOffset();
tlv.Init();
tlv.SetSourcePort(aMessageInfo.GetPeerPort());
tlv.SetDestinationPort(aMessageInfo.GetSockPort());
tlv.SetUdpLength(udpLength);
SuccessOrExit(error = message->Append(&tlv, sizeof(tlv)));
offset = message->GetLength();
SuccessOrExit(error = message->SetLength(offset + udpLength));
aMessage.CopyTo(aMessage.GetOffset(), offset, udpLength, *message);
}
SuccessOrExit(error =
Tlv::AppendTlv(*message, Tlv::kIPv6Address, &aMessageInfo.GetPeerAddr(), sizeof(Ip6::Address)));
SuccessOrExit(error = Get<Coap::CoapSecure>().SendMessage(*message, Get<Coap::CoapSecure>().GetMessageInfo()));
otLogInfoMeshCoP("Sent to commissioner on %s", OT_URI_PATH_PROXY_RX);
exit:
if (message != nullptr && error != OT_ERROR_NONE)
{
otLogWarnMeshCoP("Failed notify commissioner on %s", OT_URI_PATH_PROXY_RX);
message->Free();
}
return error != OT_ERROR_DESTINATION_ADDRESS_FILTERED;
}
void BorderAgent::HandleRelayReceive(const Coap::Message &aMessage)
{
Coap::Message *message = nullptr;
otError error;
VerifyOrExit(aMessage.IsNonConfirmable() && aMessage.GetCode() == OT_COAP_CODE_POST, error = OT_ERROR_DROP);
VerifyOrExit((message = NewMeshCoPMessage(Get<Coap::CoapSecure>())) != nullptr, error = OT_ERROR_NO_BUFS);
message->Init(OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST);
SuccessOrExit(error = message->AppendUriPathOptions(OT_URI_PATH_RELAY_RX));
if (aMessage.GetLength() > aMessage.GetOffset())
{
SuccessOrExit(error = message->SetPayloadMarker());
}
SuccessOrExit(error = ForwardToCommissioner(*message, aMessage));
otLogInfoMeshCoP("Sent to commissioner on %s", OT_URI_PATH_RELAY_RX);
exit:
if (error != OT_ERROR_NONE && message != nullptr)
{
message->Free();
}
}
otError BorderAgent::ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage)
{
otError error = OT_ERROR_NONE;
uint16_t offset = 0;
offset = aForwardMessage.GetLength();
SuccessOrExit(error = aForwardMessage.SetLength(offset + aMessage.GetLength() - aMessage.GetOffset()));
aMessage.CopyTo(aMessage.GetOffset(), offset, aMessage.GetLength() - aMessage.GetOffset(), aForwardMessage);
SuccessOrExit(error =
Get<Coap::CoapSecure>().SendMessage(aForwardMessage, Get<Coap::CoapSecure>().GetMessageInfo()));
otLogInfoMeshCoP("Sent to commissioner");
exit:
if (error != OT_ERROR_NONE)
{
otLogWarnMeshCoP("Failed to send to commissioner: %s", otThreadErrorToString(error));
}
return error;
}
void BorderAgent::HandleKeepAlive(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
otError error;
error = ForwardToLeader(aMessage, aMessageInfo, OT_URI_PATH_LEADER_KEEP_ALIVE, false, true);
if (error == OT_ERROR_NONE)
{
mTimer.Start(kKeepAliveTimeout);
}
}
void BorderAgent::HandleRelayTransmit(const Coap::Message &aMessage)
{
otError error = OT_ERROR_NONE;
uint16_t joinerRouterRloc;
Coap::Message * message = nullptr;
Ip6::MessageInfo messageInfo;
uint16_t offset = 0;
VerifyOrExit(aMessage.IsNonConfirmable() && aMessage.GetCode() == OT_COAP_CODE_POST, OT_NOOP);
SuccessOrExit(error = Tlv::FindUint16Tlv(aMessage, Tlv::kJoinerRouterLocator, joinerRouterRloc));
VerifyOrExit((message = NewMeshCoPMessage(Get<Coap::Coap>())) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = message->Init(OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST, OT_URI_PATH_RELAY_TX));
SuccessOrExit(error = message->SetPayloadMarker());
offset = message->GetLength();
SuccessOrExit(error = message->SetLength(offset + aMessage.GetLength() - aMessage.GetOffset()));
aMessage.CopyTo(aMessage.GetOffset(), offset, aMessage.GetLength() - aMessage.GetOffset(), *message);
messageInfo.SetSockPort(kCoapUdpPort);
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
messageInfo.SetPeerPort(kCoapUdpPort);
messageInfo.SetPeerAddr(Get<Mle::MleRouter>().GetMeshLocal16());
messageInfo.GetPeerAddr().GetIid().SetLocator(joinerRouterRloc);
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, messageInfo));
otLogInfoMeshCoP("Sent to joiner router request on %s", OT_URI_PATH_RELAY_TX);
exit:
if (error != OT_ERROR_NONE)
{
otLogWarnMeshCoP("Failed to sent to joiner router request " OT_URI_PATH_RELAY_TX " %s",
otThreadErrorToString(error));
if (message != nullptr)
{
message->Free();
}
}
}
otError BorderAgent::ForwardToLeader(const Coap::Message & aMessage,
const Ip6::MessageInfo &aMessageInfo,
const char * aPath,
bool aPetition,
bool aSeparate)
{
otError error = OT_ERROR_NONE;
ForwardContext * forwardContext = nullptr;
Ip6::MessageInfo messageInfo;
Coap::Message * message = nullptr;
uint16_t offset = 0;
VerifyOrExit((message = NewMeshCoPMessage(Get<Coap::Coap>())) != nullptr, error = OT_ERROR_NO_BUFS);
if (aSeparate)
{
SuccessOrExit(error = Get<Coap::CoapSecure>().SendAck(aMessage, aMessageInfo));
}
forwardContext = static_cast<ForwardContext *>(GetInstance().HeapCAlloc(1, sizeof(ForwardContext)));
VerifyOrExit(forwardContext != nullptr, error = OT_ERROR_NO_BUFS);
forwardContext = new (forwardContext) ForwardContext(*this, aMessage, aPetition, aSeparate);
SuccessOrExit(error = message->Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST, aPath));
// Payload of c/cg may be empty
if (aMessage.GetLength() - aMessage.GetOffset() > 0)
{
SuccessOrExit(error = message->SetPayloadMarker());
}
offset = message->GetLength();
SuccessOrExit(error = message->SetLength(offset + aMessage.GetLength() - aMessage.GetOffset()));
aMessage.CopyTo(aMessage.GetOffset(), offset, aMessage.GetLength() - aMessage.GetOffset(), *message);
SuccessOrExit(error = Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
messageInfo.SetPeerPort(kCoapUdpPort);
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
messageInfo.SetSockPort(kCoapUdpPort);
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, messageInfo, HandleCoapResponse, forwardContext));
// HandleCoapResponse is responsible to free this forward context.
forwardContext = nullptr;
otLogInfoMeshCoP("Forwarded request to leader on %s", aPath);
exit:
if (error != OT_ERROR_NONE)
{
if (forwardContext != nullptr)
{
GetInstance().HeapFree(forwardContext);
}
if (message != nullptr)
{
message->Free();
}
otLogWarnMeshCoP("Failed to forward to leader: %s", otThreadErrorToString(error));
SendErrorMessage(Get<Coap::CoapSecure>(), aMessage, aSeparate, error);
}
return error;
}
void BorderAgent::HandleConnected(bool aConnected)
{
if (aConnected)
{
otLogInfoMeshCoP("Commissioner connected");
SetState(OT_BORDER_AGENT_STATE_ACTIVE);
mTimer.Start(kKeepAliveTimeout);
}
else
{
otLogInfoMeshCoP("Commissioner disconnected");
Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
SetState(OT_BORDER_AGENT_STATE_STARTED);
}
}
otError BorderAgent::Start(void)
{
otError error;
Coap::CoapSecure &coaps = Get<Coap::CoapSecure>();
VerifyOrExit(mState == OT_BORDER_AGENT_STATE_STOPPED, error = OT_ERROR_ALREADY);
SuccessOrExit(error = coaps.Start(kBorderAgentUdpPort));
SuccessOrExit(error = coaps.SetPsk(Get<KeyManager>().GetPskc().m8, OT_PSKC_MAX_SIZE));
coaps.SetConnectedCallback(HandleConnected, this);
coaps.AddResource(mActiveGet);
coaps.AddResource(mActiveSet);
coaps.AddResource(mPendingGet);
coaps.AddResource(mPendingSet);
coaps.AddResource(mCommissionerPetition);
coaps.AddResource(mCommissionerKeepAlive);
coaps.AddResource(mCommissionerSet);
coaps.AddResource(mCommissionerGet);
coaps.AddResource(mProxyTransmit);
coaps.AddResource(mRelayTransmit);
Get<Coap::Coap>().AddResource(mRelayReceive);
SetState(OT_BORDER_AGENT_STATE_STARTED);
exit:
return error;
}
void BorderAgent::HandleTimeout(Timer &aTimer)
{
aTimer.GetOwner<BorderAgent>().HandleTimeout();
}
void BorderAgent::HandleTimeout(void)
{
if (Get<Coap::CoapSecure>().IsConnected())
{
Get<Coap::CoapSecure>().Disconnect();
otLogWarnMeshCoP("Reset commissioner session");
}
}
otError BorderAgent::Stop(void)
{
otError error = OT_ERROR_NONE;
Coap::CoapSecure &coaps = Get<Coap::CoapSecure>();
VerifyOrExit(mState != OT_BORDER_AGENT_STATE_STOPPED, error = OT_ERROR_ALREADY);
mTimer.Stop();
coaps.RemoveResource(mCommissionerPetition);
coaps.RemoveResource(mCommissionerKeepAlive);
coaps.RemoveResource(mCommissionerSet);
coaps.RemoveResource(mCommissionerGet);
coaps.RemoveResource(mActiveGet);
coaps.RemoveResource(mActiveSet);
coaps.RemoveResource(mPendingGet);
coaps.RemoveResource(mPendingSet);
coaps.RemoveResource(mProxyTransmit);
coaps.RemoveResource(mRelayTransmit);
Get<Coap::Coap>().RemoveResource(mRelayReceive);
coaps.Stop();
SetState(OT_BORDER_AGENT_STATE_STOPPED);
exit:
return error;
}
void BorderAgent::SetState(otBorderAgentState aState)
{
if (mState != aState)
{
mState = aState;
}
}
void BorderAgent::ApplyMeshLocalPrefix(void)
{
VerifyOrExit(mState == OT_BORDER_AGENT_STATE_ACTIVE, OT_NOOP);
if (Get<ThreadNetif>().HasUnicastAddress(mCommissionerAloc))
{
Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
mCommissionerAloc.GetAddress().SetPrefix(Get<Mle::MleRouter>().GetMeshLocalPrefix());
Get<ThreadNetif>().AddUnicastAddress(mCommissionerAloc);
}
exit:
return;
}
} // namespace MeshCoP
} // namespace ot
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE