From 8efb3c50e5deb86ead67adc25554371b85d304b5 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Tue, 7 Aug 2018 03:50:41 +0800 Subject: [PATCH] [meshcop] implement commissioning UDP proxy (#2926) --- include/openthread/types.h | 2 + include/openthread/udp.h | 20 +++ src/core/meshcop/border_agent.cpp | 210 ++++++++++++++++++++++--- src/core/meshcop/border_agent.hpp | 15 +- src/core/meshcop/joiner_router.cpp | 17 +- src/core/meshcop/joiner_router.hpp | 2 - src/core/meshcop/meshcop.cpp | 16 ++ src/core/meshcop/meshcop.hpp | 12 ++ src/core/meshcop/meshcop_tlvs.hpp | 146 +++++++++++++++++ src/core/net/udp6.cpp | 46 ++++++ src/core/net/udp6.hpp | 58 ++++++- src/core/thread/mesh_forwarder_ftd.cpp | 5 + src/core/thread/mle.cpp | 9 +- src/core/thread/mle.hpp | 34 +++- src/core/thread/thread_uri_paths.hpp | 16 ++ 15 files changed, 560 insertions(+), 48 deletions(-) diff --git a/include/openthread/types.h b/include/openthread/types.h index 915503615..71fe7ed43 100644 --- a/include/openthread/types.h +++ b/include/openthread/types.h @@ -641,6 +641,8 @@ typedef enum otMeshcopTlvType { OT_MESHCOP_TLV_VENDOR_SW_VERSION_TLV = 35, ///< meshcop Vendor SW Version TLV OT_MESHCOP_TLV_VENDOR_DATA_TLV = 36, ///< meshcop Vendor Data TLV OT_MESHCOP_TLV_VENDOR_STACK_VERSION_TLV = 37, ///< meshcop Vendor Stack Version TLV + OT_MESHCOP_TLV_UDP_ENCAPSULATION_TLV = 48, ///< meshcop UDP encapsulation TLV + OT_MESHCOP_TLV_IPV6_ADDRESS_TLV = 49, ///< meshcop IPv6 address TLV OT_MESHCOP_TLV_PENDINGTIMESTAMP = 51, ///< meshcop Pending Timestamp TLV OT_MESHCOP_TLV_DELAYTIMER = 52, ///< meshcop Delay Timer TLV OT_MESHCOP_TLV_CHANNELMASK = 53, ///< meshcop Channel Mask TLV diff --git a/include/openthread/udp.h b/include/openthread/udp.h index e98f1ccd6..ba2a9c552 100644 --- a/include/openthread/udp.h +++ b/include/openthread/udp.h @@ -53,6 +53,26 @@ extern "C" { * */ +/** + * This callback allows OpenThread to provide specific handlers for certain UDP messages. + * + * @retval true The message is handled by this receiver and should not be further processed. + * @retval false The message is not handled by this receiver. + * + */ +typedef bool (*otUdpHandler)(void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo); + +/** + * This structure represents a UDP receiver. + * + */ +typedef struct otUdpReceiver +{ + struct otUdpReceiver *mNext; ///< A pointer to the next UDP receiver (internal use only). + otUdpHandler mHandler; ///< A function pointer to the receiver callback. + void * mContext; ///< A pointer to application-specific context. +} otUdpReceiver; + /** * This callback allows OpenThread to inform the application of a received UDP message. * diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index 7a8512c19..f483106da 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -61,21 +61,46 @@ public: * * @param[in] aBorderAgent A reference to the border agent. * @param[in] aHeader A reference to the request header. - * @param[in] aSeparate Whether this request should be responded separately. + * @param[in] aPetition Whether this request is a petition. + * @param[in] aSeparate Whether this original request expects separate response. * */ - ForwardContext(BorderAgent &aBorderAgent, const Coap::Header &aHeader, bool aSeparate) + ForwardContext(BorderAgent &aBorderAgent, const Coap::Header &aHeader, bool aPetition, bool aSeparate) : mBorderAgent(aBorderAgent) , mMessageId(aHeader.GetMessageId()) + , mPetition(aPetition) , mSeparate(aSeparate) - , mType(aHeader.GetType() >> kTypeOffset) , mTokenLength(aHeader.GetTokenLength()) + , mType(aHeader.GetType() >> kTypeOffset) { memcpy(mToken, aHeader.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. * @@ -105,11 +130,12 @@ private: }; BorderAgent &mBorderAgent; - uint16_t mMessageId; ///< The CoAP Message ID - bool mSeparate : 1; - uint8_t mType : 2; ///< Type - uint8_t mTokenLength : 4; ///< The CoAP Version, Type, and Token Length - uint8_t mToken[OT_COAP_MAX_TOKEN_LENGTH]; + 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[OT_COAP_MAX_TOKEN_LENGTH]; ///< The CoAP Token of the original request. }; static Coap::Header::Code CoapCodeFromError(otError aError) @@ -166,29 +192,52 @@ void BorderAgent::HandleCoapResponse(void * aContext, ForwardContext &forwardContext = *static_cast(aContext); BorderAgent & borderAgent = forwardContext.GetBorderAgent(); ThreadNetif & netif = borderAgent.GetNetif(); + const Message * message = static_cast(aMessage); Coap::Header header; + otError error; OT_UNUSED_VARIABLE(aMessageInfo); - otLogInfoMeshCoP(GetInstance(), "Got CoAP response[%s]", otThreadErrorToString(aResult)); - if (aResult != OT_ERROR_NONE) + SuccessOrExit(error = aResult); + + if (forwardContext.IsPetition()) { - forwardContext.ToHeader(header, CoapCodeFromError(aResult)); - ExitNow(borderAgent.SendErrorMessage(header)); - } + StateTlv stateTlv; - assert(aMessage != NULL); + SuccessOrExit(error = Tlv::GetTlv(*message, Tlv::kState, sizeof(stateTlv), stateTlv)); + + if (stateTlv.GetState() == StateTlv::kAccept) + { + CommissionerSessionIdTlv sessionIdTlv; + + SuccessOrExit(error = + Tlv::GetTlv(*message, Tlv::kCommissionerSessionId, sizeof(sessionIdTlv), sessionIdTlv)); + + netif.GetMle().GetCommissionerAloc(borderAgent.mCommissionerAloc.GetAddress(), + sessionIdTlv.GetCommissionerSessionId()); + netif.AddUnicastAddress(borderAgent.mCommissionerAloc); + netif.GetIp6().GetUdp().AddReceiver(borderAgent.mProxyReceiver); + } + } forwardContext.ToHeader(header, static_cast(aHeader)->GetCode()); - if (static_cast(aMessage)->GetLength() - static_cast(aMessage)->GetOffset() > 0) + if (message->GetLength() - message->GetOffset() > 0) { header.SetPayloadMarker(); } - borderAgent.ForwardToCommissioner(header, *static_cast(aMessage)); + SuccessOrExit(error = borderAgent.ForwardToCommissioner(header, *message)); exit: + if (error != OT_ERROR_NONE) + { + otLogWarnMeshCoP(GetInstance(), "Commissioner request[%hu] failed: %s", forwardContext.GetMessageId(), + otThreadErrorToString(aResult)); + forwardContext.ToHeader(header, CoapCodeFromError(error)); + borderAgent.SendErrorMessage(header); + } + netif.GetInstance().GetHeap().Free(&forwardContext); } @@ -200,7 +249,7 @@ void BorderAgent::HandleRequest<&BorderAgent::mCommissionerPetition>(void * { static_cast(aContext)->ForwardToLeader( *static_cast(aHeader), *static_cast(aMessage), - *static_cast(aMessageInfo), OT_URI_PATH_LEADER_PETITION, true); + *static_cast(aMessageInfo), OT_URI_PATH_LEADER_PETITION, true, true); } template <> @@ -236,6 +285,17 @@ void BorderAgent::HandleRequest<&BorderAgent::mRelayReceive>(void * *static_cast(aMessage)); } +template <> +void BorderAgent::HandleRequest<&BorderAgent::mProxyTransmit>(void * aContext, + otCoapHeader * aHeader, + otMessage * aMessage, + const otMessageInfo *aMessageInfo) +{ + OT_UNUSED_VARIABLE(aMessageInfo); + static_cast(aContext)->HandleProxyTransmit(*static_cast(aHeader), + *static_cast(aMessage)); +} + BorderAgent::BorderAgent(Instance &aInstance) : InstanceLocator(aInstance) , mCommissionerPetition(OT_URI_PATH_COMMISSIONER_PETITION, @@ -252,11 +312,116 @@ BorderAgent::BorderAgent(Instance &aInstance) , 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) + , mProxyReceiver(BorderAgent::HandleProxyReceive, this) , mTimer(aInstance, HandleTimeout, this) , mIsStarted(false) { } +void BorderAgent::HandleProxyTransmit(const Coap::Header &aHeader, const Message &aMessage) +{ + Message * message = NULL; + Ip6::MessageInfo messageInfo; + uint16_t offset; + otError error; + + OT_UNUSED_VARIABLE(aHeader); + + { + UdpEncapsulationTlv tlv; + + SuccessOrExit(error = Tlv::GetOffset(aMessage, Tlv::kUdpEncapsulation, offset)); + aMessage.Read(offset, sizeof(tlv), &tlv); + + VerifyOrExit((message = GetInstance().GetIp6().GetUdp().NewMessage(0)) != NULL, 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() + : GetInstance().GetIp6().GetUdp().GetEphemeralPort()); + messageInfo.SetSockAddr(mCommissionerAloc.GetAddress()); + messageInfo.SetPeerPort(tlv.GetDestinationPort()); + } + + { + IPv6AddressTlv tlv; + + SuccessOrExit(error = Tlv::Get(aMessage, Tlv::kIPv6Address, sizeof(tlv), tlv)); + messageInfo.SetPeerAddr(tlv.GetAddress()); + } + + SuccessOrExit(error = GetInstance().GetIp6().GetUdp().SendDatagram(*message, messageInfo, Ip6::kProtoUdp)); + otLogInfoMeshCoP(GetInstance(), "Proxy transmit sent"); + +exit: + if (error != OT_ERROR_NONE) + { + otLogWarnMeshCoP(GetInstance(), "Failed to send proxy stream: %s", otThreadErrorToString(error)); + + if (message != NULL) + { + message->Free(); + } + } +} + +bool BorderAgent::HandleProxyReceive(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + Coap::Header header; + otError error; + Message * message = NULL; + ThreadNetif &netif = GetNetif(); + + VerifyOrExit(aMessageInfo.GetSockAddr() == mCommissionerAloc.GetAddress(), + error = OT_ERROR_DESTINATION_ADDRESS_FILTERED); + + VerifyOrExit(aMessage.GetLength() > 0, error = OT_ERROR_NONE); + + header.Init(OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST); + header.AppendUriPathOptions(OT_URI_PATH_PROXY_RX); + header.SetPayloadMarker(); + + VerifyOrExit((message = NewMeshCoPMessage(netif.GetCoapSecure(), header)) != NULL, error = OT_ERROR_NO_BUFS); + + { + 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); + } + + { + IPv6AddressTlv tlv; + + tlv.Init(); + tlv.SetAddress(aMessageInfo.GetPeerAddr()); + SuccessOrExit(error = message->Append(&tlv, sizeof(tlv))); + } + + SuccessOrExit(error = netif.GetCoapSecure().SendMessage(*message, netif.GetCoapSecure().GetPeerMessageInfo())); + + otLogInfoMeshCoP(GetInstance(), "Sent to commissioner on %s", OT_URI_PATH_PROXY_RX); + +exit: + if (message != NULL && error != OT_ERROR_NONE) + { + otLogWarnMeshCoP(GetInstance(), "Failed notify commissioner on %s", OT_URI_PATH_PROXY_RX); + message->Free(); + } + + return error != OT_ERROR_DESTINATION_ADDRESS_FILTERED; +} + void BorderAgent::HandleRelayReceive(const Coap::Header &aHeader, const Message &aMessage) { Coap::Header header; @@ -314,7 +479,7 @@ void BorderAgent::HandleKeepAlive(const Coap::Header & aHeader, { otError error; - error = ForwardToLeader(aHeader, aMessage, aMessageInfo, OT_URI_PATH_LEADER_KEEP_ALIVE, true); + error = ForwardToLeader(aHeader, aMessage, aMessageInfo, OT_URI_PATH_LEADER_KEEP_ALIVE, false, true); if (error == OT_ERROR_NONE) { @@ -375,6 +540,7 @@ otError BorderAgent::ForwardToLeader(const Coap::Header & aHeader, const Message & aMessage, const Ip6::MessageInfo &aMessageInfo, const char * aPath, + bool aPetition, bool aSeparate) { ThreadNetif & netif = GetNetif(); @@ -393,7 +559,7 @@ otError BorderAgent::ForwardToLeader(const Coap::Header & aHeader, forwardContext = static_cast(GetInstance().GetHeap().CAlloc(1, sizeof(ForwardContext))); VerifyOrExit(forwardContext != NULL, error = OT_ERROR_NO_BUFS); - forwardContext = new (forwardContext) ForwardContext(*this, aHeader, aSeparate); + forwardContext = new (forwardContext) ForwardContext(*this, aHeader, aPetition, aSeparate); header.Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST); header.SetToken(Coap::Header::kDefaultTokenLength); @@ -465,7 +631,11 @@ void BorderAgent::HandleConnected(bool aConnected) } else { + ThreadNetif &netif = GetNetif(); + otLogInfoMeshCoP(GetInstance(), "Commissioner disconnected"); + netif.GetIp6().GetUdp().RemoveReceiver(mProxyReceiver); + netif.RemoveUnicastAddress(mCommissionerAloc); mTimer.Start(kRestartDelay); } } @@ -503,6 +673,7 @@ otError BorderAgent::Start(void) coaps.AddResource(mCommissionerKeepAlive); coaps.AddResource(mCommissionerSet); coaps.AddResource(mCommissionerGet); + coaps.AddResource(mProxyTransmit); coaps.AddResource(mRelayTransmit); coap.AddResource(mRelayReceive); @@ -561,6 +732,7 @@ otError BorderAgent::Stop(void) coaps.RemoveResource(mActiveSet); coaps.RemoveResource(mPendingGet); coaps.RemoveResource(mPendingSet); + coaps.RemoveResource(mProxyTransmit); coaps.RemoveResource(mRelayTransmit); coap.RemoveResource(mRelayReceive); diff --git a/src/core/meshcop/border_agent.hpp b/src/core/meshcop/border_agent.hpp index de1c36a74..1a73af333 100644 --- a/src/core/meshcop/border_agent.hpp +++ b/src/core/meshcop/border_agent.hpp @@ -38,6 +38,7 @@ #include "coap/coap.hpp" #include "common/locator.hpp" +#include "net/udp6.hpp" namespace ot { @@ -89,7 +90,7 @@ private: static_cast(aContext)->ForwardToLeader( *static_cast(aHeader), *static_cast(aMessage), *static_cast(aMessageInfo), - (static_cast(aContext)->*aResource).GetUriPath(), false); + (static_cast(aContext)->*aResource).GetUriPath(), false, false); } static void HandleTimeout(Timer &aTimer); @@ -106,11 +107,19 @@ private: const Message & aMessage, const Ip6::MessageInfo &aMessageInfo, const char * aPath, + bool aPetition, bool aSeparate); otError ForwardToCommissioner(const Coap::Header &aHeader, const Message &aMessage); void HandleKeepAlive(const Coap::Header &aHeader, const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void HandleRelayTransmit(const Coap::Header &aHeader, const Message &aMessage); void HandleRelayReceive(const Coap::Header &aHeader, const Message &aMessage); + void HandleProxyTransmit(const Coap::Header &aHeader, const Message &aMessage); + static bool HandleProxyReceive(void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo) + { + return static_cast(aContext)->HandleProxyReceive( + *static_cast(aMessage), *static_cast(aMessageInfo)); + } + bool HandleProxyReceive(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); enum { @@ -131,6 +140,10 @@ private: Coap::Resource mActiveSet; Coap::Resource mPendingGet; Coap::Resource mPendingSet; + Coap::Resource mProxyTransmit; + + Ip6::UdpReceiver mProxyReceiver; ///< The UDP receiver to handle proxy packets to Commissioner + Ip6::NetifUnicastAddress mCommissionerAloc; TimerMilli mTimer; bool mIsStarted; diff --git a/src/core/meshcop/joiner_router.cpp b/src/core/meshcop/joiner_router.cpp index 25a3e67ce..59a7bee27 100644 --- a/src/core/meshcop/joiner_router.cpp +++ b/src/core/meshcop/joiner_router.cpp @@ -105,21 +105,6 @@ exit: return; } -otError JoinerRouter::GetBorderAgentRloc(uint16_t &aRloc) -{ - otError error = OT_ERROR_NONE; - BorderAgentLocatorTlv *borderAgentLocator; - - borderAgentLocator = static_cast( - GetNetif().GetNetworkDataLeader().GetCommissioningDataSubTlv(Tlv::kBorderAgentLocator)); - VerifyOrExit(borderAgentLocator != NULL, error = OT_ERROR_NOT_FOUND); - - aRloc = borderAgentLocator->GetBorderAgentLocator(); - -exit: - return error; -} - uint16_t JoinerRouter::GetJoinerUdpPort(void) { uint16_t rval = OPENTHREAD_CONFIG_JOINER_UDP_PORT; @@ -167,7 +152,7 @@ void JoinerRouter::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &a otLogInfoMeshCoP(GetInstance(), "JoinerRouter::HandleUdpReceive"); - SuccessOrExit(error = GetBorderAgentRloc(borderAgentRloc)); + SuccessOrExit(error = GetBorderAgentRloc(GetNetif(), borderAgentRloc)); header.Init(OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST); header.SetToken(Coap::Header::kDefaultTokenLength); diff --git a/src/core/meshcop/joiner_router.hpp b/src/core/meshcop/joiner_router.hpp index 1cd629629..f3b5d8ab7 100644 --- a/src/core/meshcop/joiner_router.hpp +++ b/src/core/meshcop/joiner_router.hpp @@ -117,8 +117,6 @@ private: void SendDelayedJoinerEntrust(void); otError SendJoinerEntrust(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - otError GetBorderAgentRloc(uint16_t &aRloc); - Ip6::UdpSocket mSocket; Coap::Resource mRelayTransmit; diff --git a/src/core/meshcop/meshcop.cpp b/src/core/meshcop/meshcop.cpp index f37d9a137..2d1dc1c76 100644 --- a/src/core/meshcop/meshcop.cpp +++ b/src/core/meshcop/meshcop.cpp @@ -33,6 +33,7 @@ #include "crypto/sha256.hpp" #include "mac/mac_frame.hpp" +#include "thread/thread_netif.hpp" namespace ot { namespace MeshCoP { @@ -50,5 +51,20 @@ void ComputeJoinerId(const Mac::ExtAddress &aEui64, Mac::ExtAddress &aJoinerId) aJoinerId.SetLocal(true); } +otError GetBorderAgentRloc(ThreadNetif &aNetif, uint16_t &aRloc) +{ + otError error = OT_ERROR_NONE; + BorderAgentLocatorTlv *borderAgentLocator; + + borderAgentLocator = static_cast( + aNetif.GetNetworkDataLeader().GetCommissioningDataSubTlv(Tlv::kBorderAgentLocator)); + VerifyOrExit(borderAgentLocator != NULL, error = OT_ERROR_NOT_FOUND); + + aRloc = borderAgentLocator->GetBorderAgentLocator(); + +exit: + return error; +} + } // namespace MeshCoP } // namespace ot diff --git a/src/core/meshcop/meshcop.hpp b/src/core/meshcop/meshcop.hpp index d7bcf4fe5..4853d0e5a 100644 --- a/src/core/meshcop/meshcop.hpp +++ b/src/core/meshcop/meshcop.hpp @@ -67,6 +67,18 @@ inline Message *NewMeshCoPMessage(Coap::CoapBase &aCoap, const Coap::Header &aHe */ void ComputeJoinerId(const Mac::ExtAddress &aEui64, Mac::ExtAddress &aJoinerId); +/** + * This function gets the border agent RLOC. + * + * @param[in] aNetif A reference to the thread interface. + * @param[out] aRloc Border agent RLOC. + * + * @retval OT_ERROR_NONE Successfully got the Border Agent Rloc. + * @retval OT_ERROR_NOT_FOUND Border agent is not available. + * + */ +otError GetBorderAgentRloc(ThreadNetif &aNetIf, uint16_t &aRloc); + } // namespace MeshCoP } // namespace ot diff --git a/src/core/meshcop/meshcop_tlvs.hpp b/src/core/meshcop/meshcop_tlvs.hpp index 9259eb0d4..af364c2b5 100644 --- a/src/core/meshcop/meshcop_tlvs.hpp +++ b/src/core/meshcop/meshcop_tlvs.hpp @@ -46,6 +46,7 @@ #include "common/message.hpp" #include "common/tlvs.hpp" #include "meshcop/timestamp.hpp" +#include "net/ip6_address.hpp" using ot::Encoding::BigEndian::HostSwap16; using ot::Encoding::BigEndian::HostSwap32; @@ -95,6 +96,8 @@ public: kVendorSwVersion = OT_MESHCOP_TLV_VENDOR_SW_VERSION_TLV, ///< meshcop Vendor SW Version TLV kVendorData = OT_MESHCOP_TLV_VENDOR_DATA_TLV, ///< meshcop Vendor Data TLV kVendorStackVersion = OT_MESHCOP_TLV_VENDOR_STACK_VERSION_TLV, ///< meshcop Vendor Stack Version TLV + kUdpEncapsulation = OT_MESHCOP_TLV_UDP_ENCAPSULATION_TLV, ///< meshcop UDP encapsulation TLV + kIPv6Address = OT_MESHCOP_TLV_IPV6_ADDRESS_TLV, ///< meshcop IPv6 address TLV kPendingTimestamp = OT_MESHCOP_TLV_PENDINGTIMESTAMP, ///< Pending Timestamp TLV kDelayTimer = OT_MESHCOP_TLV_DELAYTIMER, ///< Delay Timer TLV kChannelMask = OT_MESHCOP_TLV_CHANNELMASK, ///< Channel Mask TLV @@ -178,6 +181,31 @@ public: } OT_TOOL_PACKED_END; +/** + * This class implements extended MeshCoP TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ExtendedTlv : public ot::ExtendedTlv +{ +public: + /** + * This method returns the Type value. + * + * @returns The Type value. + * + */ + MeshCoP::Tlv::Type GetType(void) const { return static_cast(ot::ExtendedTlv::GetType()); } + + /** + * This method sets the Type value. + * + * @param[in] aType The Type value. + * + */ + void SetType(MeshCoP::Tlv::Type aType) { ot::ExtendedTlv::SetType(static_cast(aType)); } +} OT_TOOL_PACKED_END; + /** * This class implements Channel TLV generation and parsing. * @@ -2175,6 +2203,124 @@ private: uint8_t mMinorMajor; } OT_TOOL_PACKED_END; +/** + * This class implements IPv6 Address TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class IPv6AddressTlv : public Tlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) + { + SetType(kIPv6Address); + SetLength(sizeof(mAddress)); + } + + /** + * This method returns the IPv6 Address. + * + * @returns A reference to the IPv6 Address. + * + */ + const Ip6::Address &GetAddress(void) const { return mAddress; } + + /** + * This method sets the IPv6 Address. + * + * @param[in] aAddress A reference to the IPv6 Address. + * + */ + void SetAddress(const Ip6::Address &aAddress) { mAddress = aAddress; } + +private: + Ip6::Address mAddress; +} OT_TOOL_PACKED_END; + +/** + * This class implements UDP Encapsulation TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class UdpEncapsulationTlv : public ExtendedTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) + { + SetType(MeshCoP::Tlv::kUdpEncapsulation); + SetLength(sizeof(*this) - sizeof(ExtendedTlv)); + } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(ExtendedTlv); } + + /** + * This method returns the source port. + * + * @returns The source port. + * + */ + uint16_t GetSourcePort(void) const { return HostSwap16(mSourcePort); } + + /** + * This method updates the source port. + * + * @param[in] aSourcePort The source port. + * + */ + void SetSourcePort(uint16_t aSourcePort) { mSourcePort = HostSwap16(aSourcePort); } + + /** + * This method returns the destination port. + * + * @returns The destination port. + * + */ + uint16_t GetDestinationPort(void) const { return HostSwap16(mDestinationPort); } + + /** + * This method updates the destination port. + * + * @param[in] aDestinationPort The destination port. + * + */ + void SetDestinationPort(uint16_t aDestinationPort) { mDestinationPort = HostSwap16(aDestinationPort); } + + /** + * This method returns the calculated UDP length. + * + * @returns The calculated UDP length. + * + */ + uint16_t GetUdpLength(void) const { return GetLength() - sizeof(mSourcePort) - sizeof(mDestinationPort); } + + /** + * This method updates the UDP length. + * + * @param[in] aLength The length of UDP payload in bytes. + * + */ + void SetUdpLength(uint16_t aLength) { SetLength(sizeof(mSourcePort) + sizeof(mDestinationPort) + aLength); } + +private: + uint16_t mSourcePort; + uint16_t mDestinationPort; +} OT_TOOL_PACKED_END; + /** * This class implements Discovery Request TLV generation and parsing. * diff --git a/src/core/net/udp6.cpp b/src/core/net/udp6.cpp index 1d233772b..9bebf8eb4 100644 --- a/src/core/net/udp6.cpp +++ b/src/core/net/udp6.cpp @@ -135,10 +135,51 @@ exit: Udp::Udp(Instance &aInstance) : InstanceLocator(aInstance) , mEphemeralPort(kDynamicPortMin) + , mReceivers(NULL) , mSockets(NULL) { } +otError Udp::AddReceiver(UdpReceiver &aReceiver) +{ + for (UdpReceiver *cur = mReceivers; cur; cur = cur->GetNext()) + { + if (cur == &aReceiver) + { + ExitNow(); + } + } + + aReceiver.SetNext(mReceivers); + mReceivers = &aReceiver; + +exit: + return OT_ERROR_NONE; +} + +otError Udp::RemoveReceiver(UdpReceiver &aReceiver) +{ + if (mReceivers == &aReceiver) + { + mReceivers = mReceivers->GetNext(); + } + else + { + for (UdpReceiver *handler = mReceivers; handler; handler = handler->GetNext()) + { + if (handler->GetNext() == &aReceiver) + { + handler->SetNext(aReceiver.GetNext()); + break; + } + } + } + + aReceiver.SetNext(NULL); + + return OT_ERROR_NONE; +} + otError Udp::AddSocket(UdpSocket &aSocket) { for (UdpSocket *cur = mSockets; cur; cur = cur->GetNext()) @@ -259,6 +300,11 @@ otError Udp::HandleMessage(Message &aMessage, MessageInfo &aMessageInfo) aMessageInfo.mPeerPort = udpHeader.GetSourcePort(); aMessageInfo.mSockPort = udpHeader.GetDestinationPort(); + for (UdpReceiver *receiver = mReceivers; receiver; receiver = receiver->GetNext()) + { + VerifyOrExit(!receiver->HandleMessage(aMessage, aMessageInfo)); + } + HandlePayload(aMessage, aMessageInfo); exit: diff --git a/src/core/net/udp6.hpp b/src/core/net/udp6.hpp index 3e789791b..1b4cc3ced 100644 --- a/src/core/net/udp6.hpp +++ b/src/core/net/udp6.hpp @@ -56,6 +56,39 @@ class Udp; * */ +/** + * This class implements a UDP receiver. + * + */ +class UdpReceiver : public otUdpReceiver +{ + friend class Udp; + +public: + /** + * This constructor initializes the object. + * + * @param[in] aUdpHandler A pointer to the function to handle UDP message. + * @param[in] aContext A pointer to arbitrary context information. + * + */ + UdpReceiver(otUdpHandler aHandler, void *aContext) + { + mNext = NULL; + mHandler = aHandler; + mContext = aContext; + } + +private: + UdpReceiver *GetNext(void) { return static_cast(mNext); } + void SetNext(UdpReceiver *aReceiver) { mNext = static_cast(aReceiver); } + + bool HandleMessage(Message &aMessage, const MessageInfo &aMessageInfo) + { + return mHandler(mContext, &aMessage, &aMessageInfo); + } +}; + /** * This class implements a UDP/IPv6 socket. * @@ -178,6 +211,26 @@ public: */ explicit Udp(Instance &aInstance); + /** + * This method adds a UDP receiver. + * + * @param[in] aReceiver A reference to the UDP receiver. + * + * @retval OT_ERROR_NONE Successfully added the UDP receiver. + * + */ + otError AddReceiver(UdpReceiver &aReceiver); + + /** + * This method removes a UDP receiver. + * + * @param[in] aReceiver A reference to the UDP receiver. + * + * @retval OT_ERROR_NONE Successfully removed the UDP receiver. + * + */ + otError RemoveReceiver(UdpReceiver &aReceiver); + /** * This method adds a UDP socket. * @@ -283,8 +336,9 @@ private: kDynamicPortMin = 49152, ///< Service Name and Transport Protocol Port Number Registry kDynamicPortMax = 65535, ///< Service Name and Transport Protocol Port Number Registry }; - uint16_t mEphemeralPort; - UdpSocket *mSockets; + uint16_t mEphemeralPort; + UdpReceiver *mReceivers; + UdpSocket * mSockets; #if OPENTHREAD_ENABLE_UDP_PROXY void * mProxySenderContext; otUdpProxySender mProxySender; diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 734148b1c..079a9d8c9 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -39,6 +39,7 @@ #include "common/logging.hpp" #include "common/owner-locator.hpp" +#include "meshcop/meshcop.hpp" #include "net/ip6.hpp" #include "net/tcp.hpp" #include "net/udp6.hpp" @@ -754,6 +755,10 @@ otError MeshForwarder::UpdateIp6RouteFtd(Ip6::Header &ip6Header) { mMeshDest = netif.GetMle().GetRloc16(netif.GetMle().GetLeaderId()); } + else if ((aloc16 >= Mle::kAloc16CommissionerStart) && (aloc16 <= Mle::kAloc16CommissionerEnd)) + { + SuccessOrExit(error = MeshCoP::GetBorderAgentRloc(netif, mMeshDest)); + } #if OPENTHREAD_ENABLE_DHCP6_SERVER || OPENTHREAD_ENABLE_DHCP6_CLIENT else if (aloc16 <= Mle::kAloc16DhcpAgentEnd) diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index b5da6fa17..43c62e5b3 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -981,17 +981,14 @@ exit: return error; } -otError Mle::GetLeaderAloc(Ip6::Address &aAddress) const +otError Mle::GetAlocAddress(Ip6::Address &aAddress, uint16_t aAloc16) const { otError error = OT_ERROR_NONE; VerifyOrExit(GetRloc16() != Mac::kShortAddrInvalid, error = OT_ERROR_DETACHED); - memcpy(&aAddress, &mMeshLocal16.GetAddress(), 8); - aAddress.mFields.m16[4] = HostSwap16(0x0000); - aAddress.mFields.m16[5] = HostSwap16(0x00ff); - aAddress.mFields.m16[6] = HostSwap16(0xfe00); - aAddress.mFields.m16[7] = HostSwap16(kAloc16Leader); + memcpy(&aAddress, &mMeshLocal16.GetAddress(), 14); + aAddress.mFields.m16[7] = HostSwap16(aAloc16); exit: return error; diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 75bf425d6..d8fa2d06d 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -110,6 +110,7 @@ enum AlocAllocation kAloc16ServiceEnd = 0xfc2f, kAloc16CommissionerStart = 0xfc30, kAloc16CommissionerEnd = 0xfc37, + kAloc16CommissionerMask = 0x0007, kAloc16NeighborDiscoveryAgentStart = 0xfc40, kAloc16NeighborDiscoveryAgentEnd = 0xfc4e, }; @@ -855,7 +856,22 @@ public: * @retval OT_ERROR_DETACHED The Thread interface is not currently attached to a Thread Partition. * */ - otError GetLeaderAloc(Ip6::Address &aAddress) const; + otError GetLeaderAloc(Ip6::Address &aAddress) const { return GetAlocAddress(aAddress, kAloc16Leader); } + + /** + * This method computes the Commissioner's ALOC. + * + * @param[out] aAddress A reference to the Commissioner's ALOC. + * @param[in] aSessionId Commissioner session id. + * + * @retval OT_ERROR_NONE Successfully retrieved the Commissioner's ALOC. + * @retval OT_ERROR_DETACHED The Thread interface is not currently attached to a Thread Partition. + * + */ + otError GetCommissionerAloc(Ip6::Address &aAddress, uint16_t aSessionId) const + { + return GetAlocAddress(aAddress, GetCommissionerAloc16FromId(aSessionId)); + } #if OPENTHREAD_ENABLE_SERVICE /** @@ -950,7 +966,7 @@ public: /** * This method returns the Service Aloc corresponding to a Service ID. * - * @param[in] aAloc16 The Servicer ID value. + * @param[in] aServiceId The Service ID value. * * @returns The Service ALOC16 corresponding to given ID. * @@ -960,6 +976,19 @@ public: return static_cast(aServiceId + kAloc16ServiceStart); } + /** + * This method returns the Commissioner Aloc corresponding to a Commissioner Session ID. + * + * @param[in] aSessionId The Commissioner Session ID value. + * + * @returns The Commissioner ALOC16 corresponding to given ID. + * + */ + static uint16_t GetCommissionerAloc16FromId(uint16_t aSessionId) + { + return static_cast((aSessionId & kAloc16CommissionerMask) + kAloc16CommissionerStart); + } + /** * This method returns the RLOC16 of a given Router ID. * @@ -1648,6 +1677,7 @@ private: bool IsBetterParent(uint16_t aRloc16, uint8_t aLinkQuality, uint8_t aLinkMargin, ConnectivityTlv &aConnectivityTlv); void ResetParentCandidate(void); + otError GetAlocAddress(Ip6::Address &aAddress, uint16_t aAloc16) const; #if OPENTHREAD_ENABLE_SERVICE /** * This method scans for network data from the leader and updates ip addresses assigned to this diff --git a/src/core/thread/thread_uri_paths.hpp b/src/core/thread/thread_uri_paths.hpp index c4f83e3b4..ef7190596 100644 --- a/src/core/thread/thread_uri_paths.hpp +++ b/src/core/thread/thread_uri_paths.hpp @@ -148,6 +148,22 @@ namespace ot { */ #define OT_URI_PATH_ANNOUNCE_BEGIN "c/ab" +/** + * @def OT_URI_PATH_PROXY_RX + * + * The URI Path for Proxy RX. + * + */ +#define OT_URI_PATH_PROXY_RX "c/ur" + +/** + * @def OT_URI_PATH_PROXY_TX + * + * The URI Path for Proxy TX. + * + */ +#define OT_URI_PATH_PROXY_TX "c/ut" + /** * @def OT_URI_PATH_RELAY_RX *