From be73d98e0bd4e51eb34b0413566f7ce88db0cb2b Mon Sep 17 00:00:00 2001 From: Robert Lubos Date: Tue, 18 Oct 2016 18:26:06 +0200 Subject: [PATCH] CoAP: Implement CoAP client (#829) * CoAP Client implementation * CoAP update in MleRouter and AddressResolver * Add CoAP client to Windows project files --- etc/visual-studio/libopenthread.vcxproj | 2 + .../libopenthread.vcxproj.filters | 6 + etc/visual-studio/libopenthread_k.vcxproj | 2 + .../libopenthread_k.vcxproj.filters | 6 + include/Makefile.am | 1 + include/openthread-coap.h | 245 +++++++++ include/openthread-types.h | 5 + include/openthread.h | 1 + src/core/Makefile.am | 2 + src/core/coap/coap_client.cpp | 488 ++++++++++++++++++ src/core/coap/coap_client.hpp | 277 ++++++++++ src/core/coap/coap_header.cpp | 112 ++-- src/core/coap/coap_header.hpp | 175 +++++-- src/core/openthread-core-default-config.h | 82 ++- src/core/openthread.cpp | 49 ++ src/core/thread/address_resolver.cpp | 65 +-- src/core/thread/address_resolver.hpp | 4 +- src/core/thread/mle.hpp | 8 + src/core/thread/mle_router.cpp | 112 ++-- src/core/thread/mle_router.hpp | 11 +- src/core/thread/thread_netif.cpp | 3 + src/core/thread/thread_netif.hpp | 15 +- 22 files changed, 1452 insertions(+), 219 deletions(-) create mode 100644 include/openthread-coap.h create mode 100644 src/core/coap/coap_client.cpp create mode 100644 src/core/coap/coap_client.hpp diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj index 51e996e5c..77716830d 100644 --- a/etc/visual-studio/libopenthread.vcxproj +++ b/etc/visual-studio/libopenthread.vcxproj @@ -19,6 +19,7 @@ + @@ -78,6 +79,7 @@ + diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters index b3b11b03a..7862e96eb 100644 --- a/etc/visual-studio/libopenthread.vcxproj.filters +++ b/etc/visual-studio/libopenthread.vcxproj.filters @@ -57,6 +57,9 @@ + + Source Files\coap + Source Files\coap @@ -233,6 +236,9 @@ Header Files + + Header Files\coap + Header Files\coap diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj index 0bb07e985..357950394 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj +++ b/etc/visual-studio/libopenthread_k.vcxproj @@ -19,6 +19,7 @@ + @@ -76,6 +77,7 @@ + diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters index c632bf051..29ae84d4f 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj.filters +++ b/etc/visual-studio/libopenthread_k.vcxproj.filters @@ -57,6 +57,9 @@ + + Source Files\coap + Source Files\coap @@ -230,6 +233,9 @@ Header Files + + Header Files\coap + Header Files\coap diff --git a/include/Makefile.am b/include/Makefile.am index 4115d68f0..df0a7fdf5 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -59,6 +59,7 @@ include_HEADERS = \ openthread.h \ openthread-diag.h \ openthread-types.h \ + openthread-coap.h \ $(NULL) install-headers: install-includeHEADERS diff --git a/include/openthread-coap.h b/include/openthread-coap.h new file mode 100644 index 000000000..80c2043f4 --- /dev/null +++ b/include/openthread-coap.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2016, 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 + * @brief + * This file defines the top-level functions for the OpenThread CoAP implementation. + */ + +#ifndef OPENTHREAD_COAP_H_ +#define OPENTHREAD_COAP_H_ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup coap CoAP + * + * @brief + * This module includes functions that control CoAP communication. + * + * @{ + * + */ + +/** + * CoAP Type values. + * + */ +typedef enum otCoapType +{ + kCoapTypeConfirmable = 0x00, ///< Confirmable + kCoapTypeNonConfirmable = 0x10, ///< Non-confirmable + kCoapTypeAcknowledgment = 0x20, ///< Acknowledgment + kCoapTypeReset = 0x30, ///< Reset +} otCoapType; + +/** + * CoAP Code values. + * + */ +typedef enum otCoapCode +{ + kCoapRequestGet = 0x01, ///< Get + kCoapRequestPost = 0x02, ///< Post + kCoapRequestPut = 0x03, ///< Put + kCoapRequestDelete = 0x04, ///< Delete + kCoapResponseChanged = 0x44, ///< Changed + kCoapResponseContent = 0x45, ///< Content +} otCoapCode; + +/** + * CoAP Option Numbers + */ +typedef enum otCoapOptionType +{ + kCoapOptionUriPath = 11, ///< Uri-Path + kCoapOptionContentFormat = 12, ///< Content-Format +} otCoapOptionType; + +/** + * This structure represents a CoAP option. + * + */ +typedef struct otCoapOption +{ + uint16_t mNumber; ///< Option Number + uint16_t mLength; ///< Option Length + const uint8_t *mValue; ///< A pointer to the Option Value +} otCoapOption; + +#define OT_COAP_HEADER_MAX_LENGTH 128 ///< Max CoAP header length (bytes) + +/** + * This structure represents a CoAP header. + * + */ +typedef struct otCoapHeader +{ + union + { + struct + { + uint8_t mVersionTypeToken; + uint8_t mCode; + uint16_t mMessageId; + } mFields; + uint8_t mBytes[OT_COAP_HEADER_MAX_LENGTH]; + } mHeader; + uint8_t mHeaderLength; + uint16_t mOptionLast; + uint16_t mNextOptionOffset; + otCoapOption mOption; +} otCoapHeader; + +/** + * This function pointer is called when a CoAP response is received or on the request timeout. + * + * @param[in] aContext A pointer to application-specific context. + * @param[in[ aHeader A pointer to the received CoAP header. NULL if no response was received. + * @param[in] aMessage A pointer to the message buffer containing the response. NULL if no response was received. + * @param[in] aResult A result of the CoAP transaction. + * + * @retval kThreadError_None A response was received successfully. + * @retval kThreadError_Abort A CoAP transaction was reseted by peer. + * @retval kThreadError_ResponseTimeout No response or acknowledgment received during timeout period. + * + */ +typedef void (*otCoapResponseHandler)(void *aContext, otCoapHeader *aHeader, otMessage aMessage, + ThreadError aResult); + +/** + * This method initializes the CoAP header. + * + * @param[inout] aHeader A pointer to the CoAP header to initialize. + * @param[in] aType CoAP message type. + * @param[in] aCode CoAP message code. + * + */ +void otCoapHeaderInit(otCoapHeader *aHeader, otCoapType aType, otCoapCode aCode); + +/** + * This method sets the Token value and length in a header. + * + * @param[inout] aHeader A pointer to the CoAP header. + * @param[in] aToken A pointer to the Token value. + * @param[in] aTokenLength The Length of @p aToken. + * + */ +void otCoapHeaderSetToken(otCoapHeader *aHeader, const uint8_t *aToken, uint8_t aTokenLength); + +/** + * This method appends a CoAP option in a header. + * + * @param[inout] aHeader A pointer to the CoAP header. + * @param[in] aOption A pointer to the CoAP option. + * + * @retval kThreadError_None Successfully appended the option. + * @retval kThreadError_InvalidArgs The option type is not equal or greater than the last option type. + * @retval kThreadError_NoBufs The option length exceeds the buffer size. + * + */ +ThreadError otCoapHeaderAppendOption(otCoapHeader *aHeader, const otCoapOption *aOption); + +/** + * This method adds Payload Marker indicating beginning of the payload to the CoAP header. + * + * @param[inout] aHeader A pointer to the CoAP header. + * + * @retval kThreadError_None Payload Marker successfully added. + * @retval kThreadError_NoBufs Header Payload Marker exceeds the buffer size. + * + */ +void otCoapHeaderSetPayloadMarker(otCoapHeader *aHeader); + +/** + * This method returns a pointer to the current option. + * + * @param[in] aHeader A pointer to the CoAP header. + * + * @returns A pointer to the current option. If no option is present NULL pointer is returned. + * + */ +const otCoapOption *otCoapGetCurrentOption(const otCoapHeader *aHeader); + +/** + * This method returns a pointer to the next option. + * + * @param[in] aHeader A pointer to the CoAP header. + * + * @returns A pointer to the next option. If no more options are present NULL pointer is returned. + * + */ +const otCoapOption *otCoapGetNextOption(otCoapHeader *aHeader); + +/** + * This method creates a new message with a CoAP header. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aHeader A pointer to a CoAP header that is used to create the message. + * + * @returns A pointer to the message or NULL if failed to allocate message. + * + */ +otMessage otNewCoapMessage(otInstance *aInstance, const otCoapHeader *aHeader); + +/** + * This method sends a CoAP message. + * + * If a response for a request is expected, respective function and contex information should be provided. + * If no response is expected, these arguments should be NULL pointers. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aMessage A pointer to the message to send. + * @param[in] aMessageInfo A pointer to the message info associated with @p aMessage. + * @param[in] aHandler A function pointer that shall be called on response reception or timeout. + * @param[in] aContext A pointer to arbitrary context information. May be NULL if not used. + * + * @retval kThreadError_None Successfully sent CoAP message. + * @retval kThreadError_NoBufs Failed to allocate retransmission data. + * + */ +ThreadError otSendCoapMessage(otInstance *aInstance, otMessage aMessage, const otMessageInfo *aMessageInfo, + otCoapResponseHandler aHandler, void *aContext); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* OPENTHREAD_COAP_H_ */ diff --git a/include/openthread-types.h b/include/openthread-types.h index b94af44db..853039d8c 100644 --- a/include/openthread-types.h +++ b/include/openthread-types.h @@ -162,6 +162,11 @@ typedef enum ThreadError */ kThreadError_NotCapable = 29, + /** + * Coap response or acknowledgment not received. + */ + kThreadError_ResponseTimeout = 30, + kThreadError_Error = 255, } ThreadError; diff --git a/include/openthread.h b/include/openthread.h index 9020fcc1b..84069e020 100644 --- a/include/openthread.h +++ b/include/openthread.h @@ -59,6 +59,7 @@ extern "C" { * @defgroup messages Message Buffers * @defgroup ip6 IPv6 * @defgroup udp UDP + * @defgroup coap CoAP * * @} * diff --git a/src/core/Makefile.am b/src/core/Makefile.am index f4cf51d4c..d4f8de049 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -37,6 +37,7 @@ libopenthread_a_CPPFLAGS = \ libopenthread_a_SOURCES = \ openthread.cpp \ + coap/coap_client.cpp \ coap/coap_header.cpp \ coap/coap_server.cpp \ common/crc16.cpp \ @@ -114,6 +115,7 @@ noinst_HEADERS = \ openthread-core-config.h \ openthread-core-default-config.h \ openthread-instance.h \ + coap/coap_client.hpp \ coap/coap_header.hpp \ coap/coap_server.hpp \ common/code_utils.hpp \ diff --git a/src/core/coap/coap_client.cpp b/src/core/coap/coap_client.cpp new file mode 100644 index 000000000..01f152265 --- /dev/null +++ b/src/core/coap/coap_client.cpp @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2016, 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. + */ + +#include + +#include +#include +#include +#include +#include + +/** + * @file + * This file implements the CoAP client. + */ + +namespace Thread { +namespace Coap { + +Client::Client(Ip6::Netif &aNetif): + mSocket(aNetif.GetIp6().mUdp), + mRetransmissionTimer(aNetif.GetIp6().mTimerScheduler, &Client::HandleRetransmissionTimer, this) +{ + mMessageId = static_cast(otPlatRandomGet()); +} + +ThreadError Client::Start() +{ + return mSocket.Open(&Client::HandleUdpReceive, this); +} + +ThreadError Client::Stop() +{ + Message *message = mPendingRequests.GetHead(); + Message *messageToRemove; + RequestMetadata requestMetadata; + + // Remove all pending messages. + while (message != NULL) + { + messageToRemove = message; + message = message->GetNext(); + + requestMetadata.ReadFrom(*messageToRemove); + FinalizeCoapTransaction(*messageToRemove, requestMetadata, NULL, NULL, kThreadError_Abort); + } + + return mSocket.Close(); +} + +Message *Client::NewMessage(const Header &aHeader) +{ + Message *message = NULL; + + // Ensure that header has minimum required length. + VerifyOrExit(aHeader.GetLength() >= Header::kMinHeaderLength, ;); + + VerifyOrExit((message = mSocket.NewMessage(aHeader.GetLength())) != NULL, ;); + message->Prepend(aHeader.GetBytes(), aHeader.GetLength()); + message->SetOffset(0); + +exit: + return message; +} + +ThreadError Client::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, + otCoapResponseHandler aHandler, void *aContext) +{ + ThreadError error; + Header header; + RequestMetadata requestMetadata; + Message *storedCopy = NULL; + uint16_t copyLength = 0; + + SuccessOrExit(error = header.FromMessage(aMessage)); + + // Set Message Id if it was not already set. + if (header.GetMessageId() == 0) + { + header.SetMessageId(mMessageId++); + aMessage.Write(0, Header::kMinHeaderLength, header.GetBytes()); + } + + if (header.IsConfirmable()) + { + // Create a copy of entire message and enqueue it. + copyLength = aMessage.GetLength(); + } + else if (header.IsNonConfirmable() && header.IsRequest() && (aHandler != NULL)) + { + // As we do not retransmit non confirmable messages, create a copy of header only, for token information. + copyLength = header.GetLength(); + } + + if (copyLength > 0) + { + requestMetadata = RequestMetadata(header.IsConfirmable(), aMessageInfo, aHandler, aContext); + VerifyOrExit((storedCopy = CopyAndEnqueueMessage(aMessage, copyLength, requestMetadata)) != NULL, + error = kThreadError_NoBufs); + } + + SuccessOrExit(error = mSocket.SendTo(aMessage, aMessageInfo)); + +exit: + + if (error != kThreadError_None && storedCopy != NULL) + { + DequeueMessage(*storedCopy); + } + + return error; +} + +Message *Client::CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLength, + const RequestMetadata &aRequestMetadata) +{ + ThreadError error = kThreadError_None; + Message *messageCopy = NULL; + uint32_t alarmFireTime; + + // Create a message copy of requested size. + VerifyOrExit((messageCopy = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); + SuccessOrExit(error = messageCopy->SetLength(aCopyLength)); + aMessage.CopyTo(0, 0, aCopyLength, *messageCopy); + + // Append the copy with retransmission data. + SuccessOrExit(error = aRequestMetadata.AppendTo(*messageCopy)); + + // Setup the timer. + if (mRetransmissionTimer.IsRunning()) + { + // If timer is already running, check if it should be restarted with earlier fire time. + alarmFireTime = mRetransmissionTimer.Gett0() + mRetransmissionTimer.Getdt(); + + if (aRequestMetadata.IsEarlier(alarmFireTime)) + { + mRetransmissionTimer.Start(aRequestMetadata.mRetransmissionTimeout); + } + } + else + { + mRetransmissionTimer.Start(aRequestMetadata.mRetransmissionTimeout); + } + + // Enqueue the message. + mPendingRequests.Enqueue(*messageCopy); + +exit: + + if (error != kThreadError_None && messageCopy != NULL) + { + messageCopy->Free(); + messageCopy = NULL; + } + + return messageCopy; +} + +void Client::DequeueMessage(Message &aMessage) +{ + mPendingRequests.Dequeue(aMessage); + + if (mRetransmissionTimer.IsRunning() && (mPendingRequests.GetHead() == NULL)) + { + // No more requests pending, stop the timer. + mRetransmissionTimer.Stop(); + } + + // Free the message memory. + aMessage.Free(); + + // No need to worry that the earliest pending message was removed - + // the timer would just shoot earlier and then it'd be setup again. +} + +ThreadError Client::SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + ThreadError error; + Message *messageCopy = NULL; + + // Create a message copy for lower layers. + VerifyOrExit((messageCopy = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); + SuccessOrExit(error = messageCopy->SetLength(aMessage.GetLength() - sizeof(RequestMetadata))); + aMessage.CopyTo(0, 0, aMessage.GetLength() - sizeof(RequestMetadata), *messageCopy); + + // Send the copy. + SuccessOrExit(error = mSocket.SendTo(*messageCopy, aMessageInfo)); + +exit: + + if (error != kThreadError_None && messageCopy != NULL) + { + messageCopy->Free(); + } + + return error; +} + +void Client::SendEmptyMessage(const Ip6::Address &aAddress, uint16_t aPort, uint16_t aMessageId, Header::Type aType) +{ + Header header; + Ip6::MessageInfo messageInfo; + Message *message; + ThreadError error = kThreadError_None; + + header.Init(); + header.SetType(aType); + header.SetMessageId(aMessageId); + + VerifyOrExit((message = NewMessage(header)) != NULL, ;); + + memset(&messageInfo, 0, sizeof(messageInfo)); + messageInfo.GetPeerAddr() = aAddress; + messageInfo.mPeerPort = aPort; + + SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + +exit: + + if (error != kThreadError_None && message != NULL) + { + message->Free(); + } +} + +void Client::HandleRetransmissionTimer(void *aContext) +{ + static_cast(aContext)->HandleRetransmissionTimer(); +} + +void Client::HandleRetransmissionTimer(void) +{ + uint32_t now = otPlatAlarmGetNow(); + uint32_t nextDelta = 0xffffffff; + RequestMetadata requestMetadata; + Message *message = mPendingRequests.GetHead(); + Message *nextMessage = NULL; + Ip6::MessageInfo messageInfo; + + while (message != NULL) + { + nextMessage = message->GetNext(); + requestMetadata.ReadFrom(*message); + + if (requestMetadata.IsLater(now)) + { + // Calculate the next delay and choose the lowest. + if (requestMetadata.mNextTimerShot - now < nextDelta) + { + nextDelta = requestMetadata.mNextTimerShot - now; + } + } + else if ((requestMetadata.mConfirmable) && + (requestMetadata.mRetransmissionCount < kMaxRetransmit)) + { + // Increment retransmission counter and timer. + requestMetadata.mRetransmissionCount++; + requestMetadata.mRetransmissionTimeout *= 2; + requestMetadata.mNextTimerShot = now + requestMetadata.mRetransmissionTimeout; + requestMetadata.UpdateIn(*message); + + // Check if retransmission time is lower than current lowest. + if (requestMetadata.mRetransmissionTimeout < nextDelta) + { + nextDelta = requestMetadata.mRetransmissionTimeout; + } + + // Retransmit + if (!requestMetadata.mAcknowledged) + { + memset(&messageInfo, 0, sizeof(messageInfo)); + messageInfo.GetPeerAddr() = requestMetadata.mDestinationAddress; + messageInfo.mPeerPort = requestMetadata.mDestinationPort; + + SendCopy(*message, messageInfo); + } + } + else + { + // No expected response or acknowledgment. + FinalizeCoapTransaction(*message, requestMetadata, NULL, NULL, kThreadError_ResponseTimeout); + } + + message = nextMessage; + } + + if (nextDelta != 0xffffffff) + { + mRetransmissionTimer.Start(nextDelta); + } +} + +Message *Client::FindRelatedRequest(const Header &aResponseHeader, const Ip6::MessageInfo &aMessageInfo, + Header &aRequestHeader, RequestMetadata &aRequestMetadata) +{ + Message *message = mPendingRequests.GetHead(); + + while (message != NULL) + { + aRequestMetadata.ReadFrom(*message); + + if ((aRequestMetadata.mDestinationAddress == aMessageInfo.GetPeerAddr()) && + (aRequestMetadata.mDestinationPort == aMessageInfo.mPeerPort)) + { + assert(aRequestHeader.FromMessage(*message) == kThreadError_None); + + switch (aResponseHeader.GetType()) + { + case Header::kTypeReset: + case Header::kTypeAcknowledgment: + if (aResponseHeader.GetMessageId() == aRequestHeader.GetMessageId()) + { + ExitNow(); + } + + break; + + case Header::kTypeConfirmable: + case Header::kTypeNonConfirmable: + if (aResponseHeader.IsTokenEqual(aRequestHeader)) + { + ExitNow(); + } + + break; + } + } + + message = message->GetNext(); + } + +exit: + return message; +} + +void Client::FinalizeCoapTransaction(Message &aRequest, const RequestMetadata &aRequestMetadata, + Header *aResponseHeader, Message *aResponse, ThreadError aResult) +{ + DequeueMessage(aRequest); + + if (aRequestMetadata.mResponseHandler != NULL) + { + aRequestMetadata.mResponseHandler(aRequestMetadata.mResponseContext, aResponseHeader, + aResponse, aResult); + } +} + +void Client::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo) +{ + static_cast(aContext)->HandleUdpReceive(*static_cast(aMessage), + *static_cast(aMessageInfo)); +} + +void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + Header responseHeader; + Header requestHeader; + RequestMetadata requestMetadata; + Message *message = NULL; + ThreadError error; + + SuccessOrExit(error = responseHeader.FromMessage(aMessage)); + aMessage.MoveOffset(responseHeader.GetLength()); + + message = FindRelatedRequest(responseHeader, aMessageInfo, requestHeader, requestMetadata); + + if (message == NULL) + { + ExitNow(); + } + + switch (responseHeader.GetType()) + { + case Header::kTypeReset: + if (responseHeader.IsEmpty()) + { + FinalizeCoapTransaction(*message, requestMetadata, NULL, NULL, kThreadError_Abort); + } + + // Silently ignore non-empty reset messages (RFC 7252, p. 4.2). + break; + + case Header::kTypeAcknowledgment: + if (responseHeader.IsEmpty()) + { + // Empty acknowledgment. + if (requestMetadata.mConfirmable) + { + requestMetadata.mAcknowledged = true; + requestMetadata.UpdateIn(*message); + } + + // Remove the message if response is not expected, otherwise await response. + if (requestMetadata.mResponseHandler == NULL) + { + DequeueMessage(*message); + } + } + else if (responseHeader.IsResponse() && responseHeader.IsTokenEqual(requestHeader)) + { + // Piggybacked response. + FinalizeCoapTransaction(*message, requestMetadata, &responseHeader, &aMessage, kThreadError_None); + } + + // Silently ignore acknowledgments carrying requests (RFC 7252, p. 4.2) + // or with no token match (RFC 7252, p. 5.3.2) + break; + + case Header::kTypeConfirmable: + case Header::kTypeNonConfirmable: + if (responseHeader.IsConfirmable()) + { + // Send empty ACK if it is a CON message. + SendEmptyAck(aMessageInfo.GetPeerAddr(), aMessageInfo.mPeerPort, responseHeader.GetMessageId()); + } + + FinalizeCoapTransaction(*message, requestMetadata, &responseHeader, &aMessage, kThreadError_None); + + break; + } + +exit: + + if (error == kThreadError_None && message == NULL) + { + if (responseHeader.IsConfirmable() || responseHeader.IsNonConfirmable()) + { + // Successfully parsed a header but no matching request was found - reject the message by sending reset. + SendReset(aMessageInfo.GetPeerAddr(), aMessageInfo.mPeerPort, responseHeader.GetMessageId()); + } + } +} + +RequestMetadata::RequestMetadata(bool aConfirmable, const Ip6::MessageInfo &aMessageInfo, + otCoapResponseHandler aHandler, void *aContext) +{ + mDestinationPort = aMessageInfo.mPeerPort; + mDestinationAddress = aMessageInfo.GetPeerAddr(); + mResponseHandler = aHandler; + mResponseContext = aContext; + mRetransmissionCount = 0; + mRetransmissionTimeout = Timer::SecToMsec(kAckTimeout); + mRetransmissionTimeout += otPlatRandomGet() % + (Timer::SecToMsec(kAckTimeout) * kAckRandomFactorNumerator / kAckRandomFactorDenominator - + Timer::SecToMsec(kAckTimeout) + 1); + + if (aConfirmable) + { + // Set next retransmission timeout. + mNextTimerShot = Timer::GetNow() + mRetransmissionTimeout; + } + else + { + // Set overall response timeout. + mNextTimerShot = Timer::GetNow() + kMaxTransmitWait; + } + + mAcknowledged = false; + mConfirmable = aConfirmable; +} + +} // namespace Coap +} // namespace Thread diff --git a/src/core/coap/coap_client.hpp b/src/core/coap/coap_client.hpp new file mode 100644 index 000000000..6bf60c768 --- /dev/null +++ b/src/core/coap/coap_client.hpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2016, 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. + */ + +#ifndef COAP_CLIENT_HPP_ +#define COAP_CLIENT_HPP_ + +#include +#include +#include +#include +#include +#include +#include + +/** + * @file + * This file includes definitions for the CoAP client. + */ + +namespace Thread { +namespace Coap { + +class Client; + +/** + * Protocol Constants (RFC 7252). + * + */ +enum +{ + kAckTimeout = OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT, + kAckRandomFactorNumerator = OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_NUMERATOR, + kAckRandomFactorDenominator = OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_DENOMINATOR, + kMaxRetransmit = OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT, + kNStart = 1, + kDefaultLeisure = 5, + kProbingRate = 1, + + // Note that 2 << (kMaxRetransmit - 1) is equal to kMaxRetransmit power of 2 + kMaxTransmitSpan = kAckTimeout * ((2 << (kMaxRetransmit - 1)) - 1) * + kAckRandomFactorNumerator / kAckRandomFactorDenominator, + kMaxTransmitWait = kAckTimeout * ((2 << kMaxRetransmit) - 1) * + kAckRandomFactorNumerator / kAckRandomFactorDenominator, + kMaxLatency = 100, + kProcessingDelay = kAckTimeout, + kMaxRtt = 2 * kMaxLatency + kProcessingDelay, + kExchangeLifetime = kMaxTransmitSpan + 2 * (kMaxLatency) + kProcessingDelay, + kNonLifetime = kMaxTransmitSpan + kMaxLatency +}; + +/** + * This class implements metadata required for CoAP retransmission. + * + */ +OT_TOOL_PACKED_BEGIN +class RequestMetadata +{ + friend class Client; + +public: + + /** + * Default constructor for the object. + * + */ + RequestMetadata(void): + mDestinationPort(0), + mResponseHandler(NULL), + mResponseContext(NULL), + mNextTimerShot(0), + mRetransmissionTimeout(0), + mRetransmissionCount(0), + mAcknowledged(false), + mConfirmable(false) {}; + + /** + * This constructor initializes the object with specific values. + * + * @param[in] aConfirmable Information if the request is confirmable or not. + * @param[in] aMessageInfo Addressing information. + * @param[in] aHandler Pointer to a handler function for the response. + * @param[in] aContext Context for the handler function. + * + */ + RequestMetadata(bool aConfirmable, const Ip6::MessageInfo &aMessageInfo, + otCoapResponseHandler aHandler, void *aContext); + + /** + * This method appends request data to the message. + * + * @param[in] aMessage A reference to the message. + * + * @retval kThreadError_None Successfully appended the bytes. + * @retval kThreadError_NoBufs Insufficient available buffers to grow the message. + * + */ + ThreadError AppendTo(Message &aMessage) const { + return aMessage.Append(this, sizeof(*this)); + }; + + /** + * This method reads request data from the message. + * + * @param[in] aMessage A reference to the message. + * + * @returns The number of bytes read. + * + */ + uint16_t ReadFrom(const Message &aMessage) { + return aMessage.Read(aMessage.GetLength() - sizeof(*this), sizeof(*this), this); + }; + + /** + * This method updates request data in the message. + * + * @param[in] aMessage A reference to the message. + * + * @returns The number of bytes updated. + * + */ + int UpdateIn(Message &aMessage) const { + return aMessage.Write(aMessage.GetLength() - sizeof(*this), sizeof(*this), this); + } + + /** + * This method checks if the message shall be sent before the given time. + * + * @param[in] aTime A time to compare. + * + * @retval TRUE If the message shall be sent before the given time. + * @retval FALSE Otherwise. + */ + bool IsEarlier(uint32_t aTime) const { return (static_cast(aTime - mNextTimerShot) > 0); }; + + /** + * This method checks if the message shall be sent after the given time. + * + * @param[in] aTime A time to compare. + * + * @retval TRUE If the message shall be sent after the given time. + * @retval FALSE Otherwise. + */ + bool IsLater(uint32_t aTime) const { return (static_cast(aTime - mNextTimerShot) < 0); }; + +private: + + Ip6::Address mDestinationAddress; ///< IPv6 address of the message destination. + uint16_t mDestinationPort; ///< UDP port of the message destination. + otCoapResponseHandler mResponseHandler; ///< A function pointer that is called on response reception. + void *mResponseContext; ///< A pointer to arbitrary context information. + uint32_t mNextTimerShot; ///< Time when the timer should shoot for this message. + uint32_t mRetransmissionTimeout; ///< Delay that is applied to next retransmission. + uint8_t mRetransmissionCount; ///< Number of retransmissions. + bool mAcknowledged: 1; ///< Information that request was acknowledged. + bool mConfirmable: 1; ///< Information that message is confirmable. +} OT_TOOL_PACKED_END; + +/** + * This class implements CoAP client. + * + */ +class Client +{ +public: + + /** + * This constructor initializes the object. + * + * @param[in] aNetif A reference to the network interface that CoAP client should be assigned to. + * + */ + Client(Ip6::Netif &aNetif); + + /** + * This method starts the CoAP client. + * + * @retval kThreadError_None Successfully started the CoAP client. + * + */ + ThreadError Start(void); + + /** + * This method stops the CoAP client. + * + * @retval kThreadError_None Successfully stopped the CoAP client. + * + */ + ThreadError Stop(void); + + /** + * This method creates a new message with a CoAP header. + * + * @param[in] aHeader A reference to a CoAP header that is used to create the message. + * + * @returns A pointer to the message or NULL if failed to allocate message. + * + */ + Message *NewMessage(const Header &aHeader); + + /** + * This method sends a CoAP message. + * + * If a response for a request is expected, respective function and context information should be provided. + * If no response is expected, these arguments should be NULL pointers. + * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message. + * + * @param[in] aMessage A reference to the message to send. + * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. + * @param[in] aHandler A function pointer that shall be called on response reception or time-out. + * @param[in] aContext A pointer to arbitrary context information. + * + * @retval kThreadError_None Successfully sent CoAP message. + * @retval kThreadError_NoBufs Failed to allocate retransmission data. + * + */ + ThreadError SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, + otCoapResponseHandler aHandler = NULL, void *aContext = NULL); + +private: + Message *CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLength, + const RequestMetadata &aRequestMetadata); + void DequeueMessage(Message &aMessage); + Message *FindRelatedRequest(const Header &aResponseHeader, const Ip6::MessageInfo &aMessageInfo, + Header &aRequestHeader, RequestMetadata &aRequestMetadata); + void FinalizeCoapTransaction(Message &aRequest, const RequestMetadata &aRequestMetadata, + Header *aResponseHeader, Message *aResponse, ThreadError aResult); + + ThreadError SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void SendEmptyMessage(const Ip6::Address &aAddress, uint16_t aPort, uint16_t aMessageId, Header::Type aType); + void SendReset(const Ip6::Address &aAddress, uint16_t aPort, uint16_t aMessageId) { + SendEmptyMessage(aAddress, aPort, aMessageId, Header::kTypeReset); + }; + void SendEmptyAck(const Ip6::Address &aAddress, uint16_t aPort, uint16_t aMessageId) { + SendEmptyMessage(aAddress, aPort, aMessageId, Header::kTypeAcknowledgment); + }; + + static void HandleRetransmissionTimer(void *aContext); + void HandleRetransmissionTimer(void); + + static void HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo); + void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + Ip6::UdpSocket mSocket; + MessageQueue mPendingRequests; + uint16_t mMessageId; + Timer mRetransmissionTimer; +}; + +} // namespace Coap +} // namespace Thread + +#endif // COAP_CLIENT_HPP_ diff --git a/src/core/coap/coap_header.cpp b/src/core/coap/coap_header.cpp index c2d89b377..813f6cb69 100644 --- a/src/core/coap/coap_header.cpp +++ b/src/core/coap/coap_header.cpp @@ -32,8 +32,10 @@ */ #include +#include #include #include +#include namespace Thread { namespace Coap { @@ -44,7 +46,7 @@ void Header::Init(void) mOptionLast = 0; mNextOptionOffset = 0; memset(&mOption, 0, sizeof(mOption)); - memset(mHeader, 0, sizeof(mHeader)); + memset(&mHeader, 0, sizeof(mHeader)); SetVersion(kVersion1); } @@ -59,7 +61,7 @@ ThreadError Header::FromMessage(const Message &aMessage) uint16_t optionLength; VerifyOrExit(length >= kTokenOffset, error = kThreadError_Parse); - aMessage.Read(offset, kTokenOffset, mHeader); + aMessage.Read(offset, kTokenOffset, mHeader.mBytes); mHeaderLength = kTokenOffset; offset += kTokenOffset; length -= kTokenOffset; @@ -68,23 +70,23 @@ ThreadError Header::FromMessage(const Message &aMessage) tokenLength = GetTokenLength(); VerifyOrExit(tokenLength <= kMaxTokenLength && tokenLength < length, error = kThreadError_Parse); - aMessage.Read(offset, tokenLength, mHeader + mHeaderLength); + aMessage.Read(offset, tokenLength, mHeader.mBytes + mHeaderLength); mHeaderLength += tokenLength; offset += tokenLength; length -= tokenLength; while (length > 0) { - aMessage.Read(offset, 5, mHeader + mHeaderLength); + aMessage.Read(offset, kMaxOptionHeaderSize, mHeader.mBytes + mHeaderLength); - if (mHeader[mHeaderLength] == 0xff) + if (mHeader.mBytes[mHeaderLength] == 0xff) { mHeaderLength += sizeof(uint8_t); ExitNow(error = kThreadError_None); } - optionDelta = mHeader[mHeaderLength] >> 4; - optionLength = mHeader[mHeaderLength] & 0xf; + optionDelta = mHeader.mBytes[mHeaderLength] >> 4; + optionLength = mHeader.mBytes[mHeaderLength] & 0xf; mHeaderLength += sizeof(uint8_t); offset += sizeof(uint8_t); length -= sizeof(uint8_t); @@ -95,7 +97,7 @@ ThreadError Header::FromMessage(const Message &aMessage) } else if (optionDelta == kOption1ByteExtension) { - optionDelta = kOption1ByteExtensionOffset + mHeader[mHeaderLength]; + optionDelta = kOption1ByteExtensionOffset + mHeader.mBytes[mHeaderLength]; mHeaderLength += sizeof(uint8_t); offset += sizeof(uint8_t); length -= sizeof(uint8_t); @@ -103,7 +105,7 @@ ThreadError Header::FromMessage(const Message &aMessage) else if (optionDelta == kOption2ByteExtension) { optionDelta = kOption2ByteExtensionOffset + - static_cast((mHeader[mHeaderLength] << 8) | mHeader[mHeaderLength + 1]); + static_cast((mHeader.mBytes[mHeaderLength] << 8) | mHeader.mBytes[mHeaderLength + 1]); mHeaderLength += sizeof(uint16_t); offset += sizeof(uint16_t); length -= sizeof(uint16_t); @@ -119,7 +121,7 @@ ThreadError Header::FromMessage(const Message &aMessage) } else if (optionLength == kOption1ByteExtension) { - optionLength = kOption1ByteExtensionOffset + mHeader[mHeaderLength]; + optionLength = kOption1ByteExtensionOffset + mHeader.mBytes[mHeaderLength]; mHeaderLength += sizeof(uint8_t); offset += sizeof(uint8_t); length -= sizeof(uint8_t); @@ -127,7 +129,7 @@ ThreadError Header::FromMessage(const Message &aMessage) else if (optionLength == kOption2ByteExtension) { optionLength = kOption2ByteExtensionOffset + - static_cast((mHeader[mHeaderLength] << 8) | mHeader[mHeaderLength + 1]); + static_cast((mHeader.mBytes[mHeaderLength] << 8) | mHeader.mBytes[mHeaderLength + 1]); mHeaderLength += sizeof(uint16_t); offset += sizeof(uint16_t); length -= sizeof(uint16_t); @@ -141,13 +143,13 @@ ThreadError Header::FromMessage(const Message &aMessage) { mOption.mNumber = optionDelta; mOption.mLength = optionLength; - mOption.mValue = mHeader + mHeaderLength; + mOption.mValue = mHeader.mBytes + mHeaderLength; mNextOptionOffset = mHeaderLength + optionLength; firstOption = false; } VerifyOrExit(optionLength <= length, error = kThreadError_Parse); - aMessage.Read(offset, optionLength, mHeader + mHeaderLength); + aMessage.Read(offset, optionLength, mHeader.mBytes + mHeaderLength); mHeaderLength += static_cast(optionLength); offset += optionLength; length -= optionLength; @@ -159,11 +161,24 @@ exit: ThreadError Header::AppendOption(const Option &aOption) { - uint8_t *buf = mHeader + mHeaderLength; + ThreadError error = kThreadError_None; + uint8_t *buf = mHeader.mBytes + mHeaderLength; uint8_t *cur = buf + 1; uint16_t optionDelta = aOption.mNumber - mOptionLast; uint16_t optionLength; + // Assure that no option is inserted out of order. + VerifyOrExit(aOption.mNumber >= mOptionLast, error = kThreadError_InvalidArgs); + + // Calculate the total option size and check the buffers. + optionLength = 1 + aOption.mLength; + optionLength += optionDelta < kOption1ByteExtensionOffset ? 0 : + (optionDelta < kOption2ByteExtensionOffset ? 1 : 2); + optionLength += aOption.mLength < kOption1ByteExtensionOffset ? 0 : + (aOption.mLength < kOption2ByteExtensionOffset ? 1 : 2); + VerifyOrExit(mHeaderLength + optionLength < kMaxHeaderLength, error = kThreadError_NoBufs); + + // Insert option delta. if (optionDelta < kOption1ByteExtensionOffset) { *buf = (optionDelta << Option::kOptionDeltaOffset) & Option::kOptionDeltaMask; @@ -181,6 +196,7 @@ ThreadError Header::AppendOption(const Option &aOption) *cur++ = optionDelta & 0xff; } + // Insert option length. if (aOption.mLength < kOption1ByteExtensionOffset) { *buf |= aOption.mLength; @@ -198,17 +214,20 @@ ThreadError Header::AppendOption(const Option &aOption) *cur++ = optionLength & 0xff; } + // Insert option value. memcpy(cur, aOption.mValue, aOption.mLength); cur += aOption.mLength; mHeaderLength += static_cast(cur - buf); mOptionLast = aOption.mNumber; - return kThreadError_None; +exit: + return error; } ThreadError Header::AppendUriPathOptions(const char *aUriPath) { + ThreadError error = kThreadError_None; const char *cur = aUriPath; const char *end; Header::Option coapOption; @@ -219,15 +238,16 @@ ThreadError Header::AppendUriPathOptions(const char *aUriPath) { coapOption.mLength = static_cast(end - cur); coapOption.mValue = reinterpret_cast(cur); - AppendOption(coapOption); + SuccessOrExit(error = AppendOption(coapOption)); cur = end + 1; } coapOption.mLength = static_cast(strlen(cur)); coapOption.mValue = reinterpret_cast(cur); - AppendOption(coapOption); + SuccessOrExit(error = AppendOption(coapOption)); - return kThreadError_None; +exit: + return error; } ThreadError Header::AppendContentFormatOption(MediaType aType) @@ -238,14 +258,13 @@ ThreadError Header::AppendContentFormatOption(MediaType aType) coapOption.mNumber = Option::kOptionContentFormat; coapOption.mLength = 1; coapOption.mValue = &type; - AppendOption(coapOption); - return kThreadError_None; + return AppendOption(coapOption); } const Header::Option *Header::GetCurrentOption(void) const { - return &mOption; + return static_cast(&mOption); } const Header::Option *Header::GetNextOption(void) @@ -256,8 +275,8 @@ const Header::Option *Header::GetNextOption(void) VerifyOrExit(mNextOptionOffset < mHeaderLength, ;); - optionDelta = mHeader[mNextOptionOffset] >> 4; - optionLength = mHeader[mNextOptionOffset] & 0xf; + optionDelta = mHeader.mBytes[mNextOptionOffset] >> 4; + optionLength = mHeader.mBytes[mNextOptionOffset] & 0xf; mNextOptionOffset += sizeof(uint8_t); if (optionDelta < kOption1ByteExtension) @@ -266,13 +285,13 @@ const Header::Option *Header::GetNextOption(void) } else if (optionDelta == kOption1ByteExtension) { - optionDelta = kOption1ByteExtensionOffset + mHeader[mNextOptionOffset]; + optionDelta = kOption1ByteExtensionOffset + mHeader.mBytes[mNextOptionOffset]; mNextOptionOffset += sizeof(uint8_t); } else if (optionDelta == kOption2ByteExtension) { optionDelta = kOption2ByteExtensionOffset + - static_cast((mHeader[mNextOptionOffset] << 8) | mHeader[mNextOptionOffset + 1]); + static_cast((mHeader.mBytes[mNextOptionOffset] << 8) | mHeader.mBytes[mNextOptionOffset + 1]); mNextOptionOffset += sizeof(uint16_t); } else @@ -286,13 +305,13 @@ const Header::Option *Header::GetNextOption(void) } else if (optionLength == kOption1ByteExtension) { - optionLength = kOption1ByteExtensionOffset + mHeader[mNextOptionOffset]; + optionLength = kOption1ByteExtensionOffset + mHeader.mBytes[mNextOptionOffset]; mNextOptionOffset += sizeof(uint8_t); } else if (optionLength == kOption2ByteExtension) { optionLength = kOption2ByteExtensionOffset + - static_cast((mHeader[mNextOptionOffset] << 8) | mHeader[mNextOptionOffset + 1]); + static_cast((mHeader.mBytes[mNextOptionOffset] << 8) | mHeader.mBytes[mNextOptionOffset + 1]); mNextOptionOffset += sizeof(uint16_t); } else @@ -302,13 +321,48 @@ const Header::Option *Header::GetNextOption(void) mOption.mNumber += optionDelta; mOption.mLength = optionLength; - mOption.mValue = mHeader + mNextOptionOffset; + mOption.mValue = mHeader.mBytes + mNextOptionOffset; mNextOptionOffset += optionLength; - rval = &mOption; + rval = static_cast(&mOption); exit: return rval; } +ThreadError Header::Finalize(void) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(mHeaderLength < kMaxHeaderLength, error = kThreadError_NoBufs); + mHeader.mBytes[mHeaderLength++] = 0xff; + +exit: + return error; +} + +void Header::SetToken(uint8_t aTokenLength) +{ + assert(aTokenLength <= kMaxTokenLength); + + uint8_t token[kMaxTokenLength] = { 0 }; + + for (uint8_t i = 0; i < aTokenLength; i++) + { + token[i] = static_cast(otPlatRandomGet()); + } + + SetToken(token, aTokenLength); +} + +void Header::SetDefaultResponseHeader(const Header &aRequestHeader) +{ + Init(); + SetType(kTypeAcknowledgment); + SetCode(kCodeChanged); + SetMessageId(aRequestHeader.GetMessageId()); + SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength()); + Finalize(); +} + } // namespace Coap } // namespace Thread diff --git a/src/core/coap/coap_header.hpp b/src/core/coap/coap_header.hpp index 9eb0fee09..997a499ea 100644 --- a/src/core/coap/coap_header.hpp +++ b/src/core/coap/coap_header.hpp @@ -36,6 +36,8 @@ #include +#include +#include #include #include @@ -65,12 +67,15 @@ namespace Coap { * This class implements CoAP header generation and parsing. * */ -class Header +class Header : public otCoapHeader { public: enum { - kVersion1 = 1, ///< Version 1 + kVersion1 = 1, ///< Version 1 + kMinHeaderLength = 4, ///< Minimum header length + kMaxHeaderLength = OT_COAP_HEADER_MAX_LENGTH, ///< Maximum header length + kDefaultTokenLength = 2 ///< Default token length }; /** @@ -96,7 +101,7 @@ public: * @returns The Version value. * */ - uint8_t GetVersion(void) const { return (mHeader[0] & kVersionMask) >> kVersionOffset; } + uint8_t GetVersion(void) const { return (mHeader.mFields.mVersionTypeToken & kVersionMask) >> kVersionOffset; } /** * This method sets the Version value. @@ -104,7 +109,10 @@ public: * @param[in] aVersion The Version value. * */ - void SetVersion(uint8_t aVersion) { mHeader[0] &= ~kVersionMask; mHeader[0] |= aVersion << kVersionOffset; } + void SetVersion(uint8_t aVersion) { + mHeader.mFields.mVersionTypeToken &= ~kVersionMask; + mHeader.mFields.mVersionTypeToken |= aVersion << kVersionOffset; + } /** * CoAP Type values. @@ -124,7 +132,7 @@ public: * @returns The Type value. * */ - Type GetType(void) const { return static_cast(mHeader[0] & kTypeMask); } + Type GetType(void) const { return static_cast(mHeader.mFields.mVersionTypeToken & kTypeMask); } /** * This method sets the Type value. @@ -132,7 +140,11 @@ public: * @param[in] aType The Type value. * */ - void SetType(Type aType) { mHeader[0] &= ~kTypeMask; mHeader[0] |= aType; } + void SetType(Type aType) { + mHeader.mFields.mVersionTypeToken &= ~kTypeMask; + mHeader.mFields.mVersionTypeToken |= aType; + } + /** * CoAP Code values. @@ -154,7 +166,7 @@ public: * @returns The Code value. * */ - Code GetCode(void) const { return static_cast(mCode); } + Code GetCode(void) const { return static_cast(mHeader.mFields.mCode); } /** * This method sets the Code value. @@ -162,7 +174,7 @@ public: * @param[in] aCode The Code value. * */ - void SetCode(Code aCode) { mCode = static_cast(aCode); } + void SetCode(Code aCode) { mHeader.mFields.mCode = static_cast(aCode); } /** * This method returns the Message ID value. @@ -170,7 +182,7 @@ public: * @returns The Message ID value. * */ - uint16_t GetMessageId(void) const { return HostSwap16(mMessageId); } + uint16_t GetMessageId(void) const { return HostSwap16(mHeader.mFields.mMessageId); } /** * This method sets the Message ID value. @@ -178,7 +190,7 @@ public: * @param[in] aMessageId The Message ID value. * */ - void SetMessageId(uint16_t aMessageId) { mMessageId = HostSwap16(aMessageId); } + void SetMessageId(uint16_t aMessageId) { mHeader.mFields.mMessageId = HostSwap16(aMessageId); } /** * This method returns the Token length. @@ -186,7 +198,7 @@ public: * @returns The Token length. * */ - uint8_t GetTokenLength(void) const { return (mHeader[0] & kTokenLengthMask) >> kTokenLengthOffset; } + uint8_t GetTokenLength(void) const { return (mHeader.mFields.mVersionTypeToken & kTokenLengthMask) >> kTokenLengthOffset; } /** * This method returns a pointer to the Token value. @@ -194,7 +206,7 @@ public: * @returns A pointer to the Token value. * */ - const uint8_t *GetToken(void) const { return mHeader + kTokenOffset; } + const uint8_t *GetToken(void) const { return mHeader.mBytes + kTokenOffset; } /** * This method sets the Token value and length. @@ -204,16 +216,39 @@ public: * */ void SetToken(const uint8_t *aToken, uint8_t aTokenLength) { - mHeader[0] = (mHeader[0] & ~kTokenLengthMask) | ((aTokenLength << kTokenLengthOffset) & kTokenLengthMask); - memcpy(mHeader + kTokenOffset, aToken, aTokenLength); + mHeader.mFields.mVersionTypeToken = (mHeader.mFields.mVersionTypeToken & ~kTokenLengthMask) | + ((aTokenLength << kTokenLengthOffset) & kTokenLengthMask); + memcpy(mHeader.mBytes + kTokenOffset, aToken, aTokenLength); mHeaderLength += aTokenLength; } + /** + * This method sets the Token length and randomizes its value. + * + * @param[in] aTokenLength The Length of a Token to set. + * + */ + void SetToken(uint8_t aTokenLength); + + /** + * This method checks if Tokens in two CoAP headers are equal. + * + * @param[in] aHeader A header to compare. + * + * @retval TRUE If two Tokens are equal. + * @retval FALSE If Tokens differ in length or value. + * + */ + bool IsTokenEqual(const Header &aHeader) const { + return ((this->GetTokenLength() == aHeader.GetTokenLength()) && + (memcmp(this->GetToken(), aHeader.GetToken(), this->GetTokenLength()) == 0)); + } + /** * This structure represents a CoAP option. * */ - struct Option + struct Option : public otCoapOption { /** * Protocol Constants @@ -233,10 +268,6 @@ public: kOptionUriPath = 11, ///< Uri-Path kOptionContentFormat = 12, ///< Content-Format }; - - uint16_t mNumber; ///< Option Number - uint16_t mLength; ///< Option Length - const uint8_t *mValue; ///< A pointer to the Option Value }; /** @@ -246,6 +277,7 @@ public: * * @retval kThreadError_None Successfully appended the option. * @retval kThreadError_InvalidArgs The option type is not equal or greater than the last option type. + * @retval kThreadError_NoBufs The option length exceeds the buffer size. * */ ThreadError AppendOption(const Option &aOption); @@ -257,6 +289,7 @@ public: * * @retval kThreadError_None Successfully appended the option. * @retval kThreadError_InvalidArgs The option type is not equal or greater than the last option type. + * @retval kThreadError_NoBufs The option length exceeds the buffer size. * */ ThreadError AppendUriPathOptions(const char *aUriPath); @@ -277,6 +310,7 @@ public: * * @retval kThreadError_None Successfully appended the option. * @retval kThreadError_InvalidArgs The option type is not equal or greater than the last option type. + * @retval kThreadError_NoBufs The option length exceeds the buffer size. * */ ThreadError AppendContentFormatOption(MediaType aType); @@ -300,8 +334,11 @@ public: /** * This method terminates the CoAP header. * + * @retval kThreadError_None Header successfully terminated. + * @retval kThreadError_NoBufs Header Payload Marker exceeds the buffer size. + * */ - void Finalize(void) { mHeader[mHeaderLength++] = 0xff; } + ThreadError Finalize(void); /** * This method returns a pointer to the first byte of the header. @@ -309,7 +346,7 @@ public: * @returns A pointer to the first byte of the header. * */ - const uint8_t *GetBytes(void) const { return mHeader; } + const uint8_t *GetBytes(void) const { return mHeader.mBytes; } /** * This method returns the header length in bytes. @@ -319,6 +356,77 @@ public: */ uint8_t GetLength(void) const { return mHeaderLength; } + /** + * This method sets a default response header based on request header. + * + * @param[in] aRequestHeader Request header to base on. + * + */ + void SetDefaultResponseHeader(const Header &aRequestHeader); + + /** + * This method checks if a header is an empty message header. + * + * @retval TRUE Header is an empty message header. + * @retval FALSE Header is not an empty message header. + * + */ + bool IsEmpty(void) const { return (GetCode() == 0); }; + + /** + * This method checks if a header is a request header. + * + * @retval TRUE Header is a request header. + * @retval FALSE Header is not a request header. + * + */ + bool IsRequest(void) const { return (GetCode() >= kCodeGet && GetCode() <= kCodeDelete); }; + + /** + * This method checks if a header is a response header. + * + * @retval TRUE Header is a response header. + * @retval FALSE Header is not a response header. + * + */ + bool IsResponse(void) const { return (GetCode() >= kCodeChanged); }; + + /** + * This method checks if a header is a CON message header. + * + * @retval TRUE Header is a CON message header. + * @retval FALSE Header is not is a CON message header. + * + */ + bool IsConfirmable(void) const { return (GetType() == kTypeConfirmable); }; + + /** + * This method checks if a header is a NON message header. + * + * @retval TRUE Header is a NON message header. + * @retval FALSE Header is not is a NON message header. + * + */ + bool IsNonConfirmable(void) const { return (GetType() == kTypeNonConfirmable); }; + + /** + * This method checks if a header is a ACK message header. + * + * @retval TRUE Header is a ACK message header. + * @retval FALSE Header is not is a ACK message header. + * + */ + bool IsAck(void) const { return (GetType() == kTypeAcknowledgment); }; + + /** + * This method checks if a header is a RST message header. + * + * @retval TRUE Header is a RST message header. + * @retval FALSE Header is not is a RST message header. + * + */ + bool IsReset(void) const { return (GetType() == kTypeReset); }; + private: /** * Protocol Constants (RFC 7252). @@ -329,38 +437,21 @@ private: kVersionMask = 0xc0, ///< Version mask as specified (RFC 7252). kVersionOffset = 6, ///< Version offset as specified (RFC 7252). + kTypeMask = 0x30, ///< Type mask as specified (RFC 7252). + kTokenLengthMask = 0x0f, ///< Token Length mask as specified (RFC 7252). kTokenLengthOffset = 0, ///< Token Length offset as specified (RFC 7252). kTokenOffset = 4, ///< Token offset as specified (RFC 7252). kMaxTokenLength = 8, ///< Max token length as specified (RFC 7252). + kMaxOptionHeaderSize = 5, ///< Maximum size of an Option header + kOption1ByteExtension = 13, ///< Indicates a 1 byte extension (RFC 7252). kOption2ByteExtension = 14, ///< Indicates a 1 byte extension (RFC 7252). kOption1ByteExtensionOffset = 13, ///< Delta/Length offset as specified (RFC 7252). kOption2ByteExtensionOffset = 269, ///< Delta/Length offset as specified (RFC 7252). }; - - enum - { - kTypeMask = 0x30, - kMinHeaderLength = 4, - kMaxHeaderLength = 128, - }; - union - { - struct - { - uint8_t mVersionTypeToken; - uint8_t mCode; - uint16_t mMessageId; - }; - uint8_t mHeader[kMaxHeaderLength]; - }; - uint8_t mHeaderLength; - uint16_t mOptionLast; - uint16_t mNextOptionOffset; - Option mOption; }; /** diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index 89663bcc3..bc0c31378 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -42,7 +42,7 @@ * */ #ifndef OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS -#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 40 +#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 40 #endif // OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS /** @@ -52,7 +52,7 @@ * */ #ifndef OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE -#define OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE 128 +#define OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE 128 #endif // OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE /** @@ -62,7 +62,7 @@ * */ #ifndef OPENTHREAD_CONFIG_DEFAULT_CHANNEL -#define OPENTHREAD_CONFIG_DEFAULT_CHANNEL 11 +#define OPENTHREAD_CONFIG_DEFAULT_CHANNEL 11 #endif // OPENTHREAD_CONFIG_DEFAULT_CHANNEL /** @@ -72,7 +72,7 @@ * */ #ifndef OPENTHREAD_CONFIG_DEFAULT_MAX_TRANSMIT_POWER -#define OPENTHREAD_CONFIG_DEFAULT_MAX_TRANSMIT_POWER 0 +#define OPENTHREAD_CONFIG_DEFAULT_MAX_TRANSMIT_POWER 0 #endif // OPENTHREAD_CONFIG_DEFAULT_MAX_TRANSMIT_POWER /** @@ -82,7 +82,7 @@ * */ #ifndef OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD -#define OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD 100 +#define OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD 100 #endif // OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD /** @@ -92,7 +92,7 @@ * */ #ifndef OPENTHREAD_CONFIG_ADDRESS_CACHE_ENTRIES -#define OPENTHREAD_CONFIG_ADDRESS_CACHE_ENTRIES 10 +#define OPENTHREAD_CONFIG_ADDRESS_CACHE_ENTRIES 10 #endif // OPENTHREAD_CONFIG_ADDRESS_CACHE_ENTRIES /** @@ -102,7 +102,7 @@ * */ #ifndef OPENTHREAD_CONFIG_MAX_CHILDREN -#define OPENTHREAD_CONFIG_MAX_CHILDREN 10 +#define OPENTHREAD_CONFIG_MAX_CHILDREN 10 #endif // OPENTHREAD_CONFIG_MAX_CHILDREN /** @@ -112,7 +112,7 @@ * */ #ifndef OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD -#define OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD 4 +#define OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD 4 #endif // OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD /** @@ -122,7 +122,7 @@ * */ #ifndef OPENTHREAD_CONFIG_MAX_EXT_IP_ADDRS -#define OPENTHREAD_CONFIG_MAX_EXT_IP_ADDRS 4 +#define OPENTHREAD_CONFIG_MAX_EXT_IP_ADDRS 4 #endif // OPENTHREAD_CONFIG_MAX_EXT_IP_ADDRS /** @@ -132,7 +132,7 @@ * */ #ifndef OPENTHREAD_CONFIG_6LOWPAN_REASSEMBLY_TIMEOUT -#define OPENTHREAD_CONFIG_6LOWPAN_REASSEMBLY_TIMEOUT 5 +#define OPENTHREAD_CONFIG_6LOWPAN_REASSEMBLY_TIMEOUT 5 #endif // OPENTHREAD_CONFIG_6LOWPAN_REASSEMBLY_TIMEOUT /** @@ -142,7 +142,7 @@ * */ #ifndef OPENTHREAD_CONFIG_MPL_CACHE_ENTRIES -#define OPENTHREAD_CONFIG_MPL_CACHE_ENTRIES 32 +#define OPENTHREAD_CONFIG_MPL_CACHE_ENTRIES 32 #endif // OPENTHREAD_CONFIG_MPL_CACHE_ENTRIES /** @@ -152,7 +152,7 @@ * */ #ifndef OPENTHREAD_CONFIG_MPL_CACHE_ENTRY_LIFETIME -#define OPENTHREAD_CONFIG_MPL_CACHE_ENTRY_LIFETIME 5 +#define OPENTHREAD_CONFIG_MPL_CACHE_ENTRY_LIFETIME 5 #endif // OPENTHREAD_CONFIG_MPL_CACHE_ENTRY_LIFETIME /** @@ -162,7 +162,7 @@ * */ #ifndef OPENTHREAD_CONFIG_JOINER_UDP_PORT -#define OPENTHREAD_CONFIG_JOINER_UDP_PORT 1000 +#define OPENTHREAD_CONFIG_JOINER_UDP_PORT 1000 #endif // OPENTHREAD_CONFIG_JOINER_UDP_PORT /** @@ -172,7 +172,7 @@ * */ #ifndef OPENTHREAD_CONFIG_MAX_ENERGY_RESULTS -#define OPENTHREAD_CONFIG_MAX_ENERGY_RESULTS 64 +#define OPENTHREAD_CONFIG_MAX_ENERGY_RESULTS 64 #endif // OPENTHREAD_CONFIG_MAX_ENERGY_RESULTS /** @@ -182,7 +182,7 @@ * */ #ifndef OPENTHREAD_CONFIG_MAX_JOINER_ENTRIES -#define OPENTHREAD_CONFIG_MAX_JOINER_ENTRIES 2 +#define OPENTHREAD_CONFIG_MAX_JOINER_ENTRIES 2 #endif // OPENTHREAD_CONFIG_MAX_JOINER_ENTRIES /** @@ -192,9 +192,50 @@ * */ #ifndef OPENTHREAD_CONFIG_MAX_STATECHANGE_HANDLERS -#define OPENTHREAD_CONFIG_MAX_STATECHANGE_HANDLERS 1 +#define OPENTHREAD_CONFIG_MAX_STATECHANGE_HANDLERS 1 #endif // OPENTHREAD_CONFIG_MAX_STATECHANGE_HANDLERS +/** + * @def OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT + * + * Minimum spacing before first retransmission when ACK is not received (RFC7252 default value is 2). + * + */ +#ifndef OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT +#define OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT 2 +#endif // OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT + +/** + * @def OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_NUMERATOR + * + * Numerator of ACK_RANDOM_FACTOR used to calculate maximum spacing before first retransmission when + * ACK is not received (RFC7252 default value of ACK_RANDOM_FACTOR is 1.5, must not be decreased below 1). + * + */ +#ifndef OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_NUMERATOR +#define OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_NUMERATOR 3 +#endif // OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_NUMERATOR + +/** + * @def OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_DENOMINATOR + * + * Denominator of ACK_RANDOM_FACTOR used to calculate maximum spacing before first retransmission when + * ACK is not received (RFC7252 default value of ACK_RANDOM_FACTOR is 1.5, must not be decreased below 1). + * + */ +#ifndef OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_DENOMINATOR +#define OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_DENOMINATOR 2 +#endif // OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_DENOMINATOR + +/** + * @def OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT + * + * Maximum number of retransmissions for CoAP Confirmable messages (RFC7252 default value is 4). + * + */ +#ifndef OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT +#define OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT 4 +#endif // OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT /** * @def OPENTHREAD_CONFIG_LOG_LEVEL @@ -203,7 +244,7 @@ * */ #ifndef OPENTHREAD_CONFIG_LOG_LEVEL -#define OPENTHREAD_CONFIG_LOG_LEVEL OPENTHREAD_LOG_LEVEL_CRIT +#define OPENTHREAD_CONFIG_LOG_LEVEL OPENTHREAD_LOG_LEVEL_CRIT #endif // OPENTHREAD_CONFIG_LOG_LEVEL /** @@ -270,7 +311,6 @@ */ #define OPENTHREAD_CONFIG_LOG_MEM - /** * @def OPENTHREAD_CONFIG_LOG_NETDIAG * @@ -286,7 +326,7 @@ * */ #ifndef OPENTHREAD_CONFIG_SETTINGS_BASE_ADDRESS -#define OPENTHREAD_CONFIG_SETTINGS_BASE_ADDRESS 0x39000 +#define OPENTHREAD_CONFIG_SETTINGS_BASE_ADDRESS 0x39000 #endif // OPENTHREAD_CONFIG_SETTINGS_BASE_ADDRESS /** @@ -296,7 +336,7 @@ * */ #ifndef OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE -#define OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE 0x800 +#define OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE 0x800 #endif // OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE /** @@ -306,7 +346,7 @@ * */ #ifndef OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM -#define OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM 2 +#define OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM 2 #endif // OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM #endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_ diff --git a/src/core/openthread.cpp b/src/core/openthread.cpp index 4ec6a90e3..9d3cf369f 100644 --- a/src/core/openthread.cpp +++ b/src/core/openthread.cpp @@ -57,6 +57,8 @@ #include #include #include +#include +#include #ifndef OPENTHREAD_MULTIPLE_INSTANCE static otDEFINE_ALIGNED_VAR(sInstanceRaw, sizeof(otInstance), uint64_t); @@ -1518,6 +1520,53 @@ ThreadError otJoinerStop(otInstance *aInstance) } #endif // OPENTHREAD_ENABLE_JOINER +void otCoapHeaderInit(otCoapHeader *aHeader, otCoapType aType, otCoapCode aCode) +{ + Coap::Header *header = static_cast(aHeader); + header->Init(); + header->SetType(static_cast(aType)); + header->SetCode(static_cast(aCode)); +} + +void otCoapHeaderSetToken(otCoapHeader *aHeader, const uint8_t *aToken, uint8_t aTokenLength) +{ + static_cast(aHeader)->SetToken(aToken, aTokenLength); +} + +ThreadError otCoapHeaderAppendOption(otCoapHeader *aHeader, const otCoapOption *aOption) +{ + return static_cast(aHeader)->AppendOption(*static_cast(aOption)); +} + +void otCoapHeaderSetPayloadMarker(otCoapHeader *aHeader) +{ + static_cast(aHeader)->Finalize(); +} + +const otCoapOption *otCoapGetCurrentOption(const otCoapHeader *aHeader) +{ + return static_cast(static_cast(aHeader)->GetCurrentOption()); +} + +const otCoapOption *otCoapGetNextOption(otCoapHeader *aHeader) +{ + return static_cast(static_cast(aHeader)->GetNextOption()); +} + +otMessage otNewCoapMessage(otInstance *aInstance, const otCoapHeader *aHeader) +{ + return aInstance->mThreadNetif.GetCoapClient().NewMessage(*(static_cast(aHeader))); +} + +ThreadError otSendCoapMessage(otInstance *aInstance, otMessage aMessage, const otMessageInfo *aMessageInfo, + otCoapResponseHandler aHandler, void *aContext) +{ + return aInstance->mThreadNetif.GetCoapClient().SendMessage( + *static_cast(aMessage), + *static_cast(aMessageInfo), + aHandler, aContext); +} + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index 8b8df0150..aa47fc669 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -56,10 +56,10 @@ AddressResolver::AddressResolver(ThreadNetif &aThreadNetif) : mAddressQuery(OPENTHREAD_URI_ADDRESS_QUERY, &AddressResolver::HandleAddressQuery, this), mAddressNotification(OPENTHREAD_URI_ADDRESS_NOTIFY, &AddressResolver::HandleAddressNotification, this), mIcmpHandler(&AddressResolver::HandleDstUnreach, this), - mSocket(aThreadNetif.GetIp6().mUdp), mTimer(aThreadNetif.GetIp6().mTimerScheduler, &AddressResolver::HandleTimer, this), mMeshForwarder(aThreadNetif.GetMeshForwarder()), mCoapServer(aThreadNetif.GetCoapServer()), + mCoapClient(aThreadNetif.GetCoapClient()), mMle(aThreadNetif.GetMle()), mNetif(aThreadNetif) { @@ -68,7 +68,6 @@ AddressResolver::AddressResolver(ThreadNetif &aThreadNetif) : mCoapServer.AddResource(mAddressError); mCoapServer.AddResource(mAddressQuery); mCoapServer.AddResource(mAddressNotification); - mCoapMessageId = static_cast(otPlatRandomGet()); mNetif.GetIp6().mIcmp.RegisterCallbacks(mIcmpHandler); } @@ -168,26 +167,18 @@ exit: ThreadError AddressResolver::SendAddressQuery(const Ip6::Address &aEid) { ThreadError error; - Ip6::SockAddr sockaddr; Message *message; Coap::Header header; ThreadTargetTlv targetTlv; Ip6::MessageInfo messageInfo; - sockaddr.mPort = kCoapUdpPort; - mSocket.Open(&AddressResolver::HandleUdpReceive, this); - mSocket.Bind(sockaddr); - - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); - header.Init(); header.SetType(Coap::Header::kTypeNonConfirmable); header.SetCode(Coap::Header::kCodePost); - header.SetMessageId(++mCoapMessageId); - header.SetToken(NULL, 0); header.AppendUriPathOptions(OPENTHREAD_URI_ADDRESS_QUERY); header.Finalize(); - SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); + + VerifyOrExit((message = mCoapClient.NewMessage(header)) != NULL, error = kThreadError_NoBufs); targetTlv.Init(); targetTlv.SetTarget(aEid); @@ -200,7 +191,7 @@ ThreadError AddressResolver::SendAddressQuery(const Ip6::Address &aEid) messageInfo.mPeerPort = kCoapUdpPort; messageInfo.mInterfaceId = mNetif.GetInterfaceId(); - SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + SuccessOrExit(error = mCoapClient.SendMessage(*message, messageInfo, NULL, NULL)); otLogInfoArp("Sent address query\n"); @@ -219,13 +210,6 @@ exit: return error; } -void AddressResolver::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo) -{ - (void)aContext; - (void)aMessage; - (void)aMessageInfo; -} - void AddressResolver::HandleAddressNotification(void *aContext, Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { @@ -318,12 +302,7 @@ void AddressResolver::SendAddressNotificationResponse(const Coap::Header &aReque VerifyOrExit((message = mCoapServer.NewMessage(0)) != NULL, error = kThreadError_NoBufs); - responseHeader.Init(); - responseHeader.SetType(Coap::Header::kTypeAcknowledgment); - responseHeader.SetCode(Coap::Header::kCodeChanged); - responseHeader.SetMessageId(aRequestHeader.GetMessageId()); - responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength()); - responseHeader.Finalize(); + responseHeader.SetDefaultResponseHeader(aRequestHeader); SuccessOrExit(error = message->Append(responseHeader.GetBytes(), responseHeader.GetLength())); memcpy(&responseInfo, &aRequestInfo, sizeof(responseInfo)); @@ -347,22 +326,15 @@ ThreadError AddressResolver::SendAddressError(const ThreadTargetTlv &aTarget, co Message *message; Coap::Header header; Ip6::MessageInfo messageInfo; - Ip6::SockAddr sockaddr; - - sockaddr.mPort = kCoapUdpPort; - mSocket.Open(&AddressResolver::HandleUdpReceive, this); - mSocket.Bind(sockaddr); - - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); header.Init(); - header.SetType(Coap::Header::kTypeConfirmable); + header.SetType(aDestination == NULL ? Coap::Header::kTypeNonConfirmable : Coap::Header::kTypeConfirmable); header.SetCode(Coap::Header::kCodePost); - header.SetMessageId(++mCoapMessageId); - header.SetToken(NULL, 0); header.AppendUriPathOptions(OPENTHREAD_URI_ADDRESS_ERROR); header.Finalize(); - SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); + + VerifyOrExit((message = mCoapClient.NewMessage(header)) != NULL, error = kThreadError_NoBufs); + SuccessOrExit(error = message->Append(&aTarget, sizeof(aTarget))); SuccessOrExit(error = message->Append(&aEid, sizeof(aEid))); @@ -381,7 +353,7 @@ ThreadError AddressResolver::SendAddressError(const ThreadTargetTlv &aTarget, co messageInfo.mPeerPort = kCoapUdpPort; messageInfo.mInterfaceId = mNetif.GetInterfaceId(); - SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + SuccessOrExit(error = mCoapClient.SendMessage(*message, messageInfo, NULL, NULL)); otLogInfoArp("Sent address error\n"); @@ -405,12 +377,8 @@ void AddressResolver::SendAddressErrorResponse(const Coap::Header &aRequestHeade VerifyOrExit((message = mCoapServer.NewMessage(0)) != NULL, error = kThreadError_NoBufs); - responseHeader.Init(); - responseHeader.SetType(Coap::Header::kTypeAcknowledgment); - responseHeader.SetCode(Coap::Header::kCodeChanged); - responseHeader.SetMessageId(aRequestHeader.GetMessageId()); - responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength()); - responseHeader.Finalize(); + responseHeader.SetDefaultResponseHeader(aRequestHeader); + SuccessOrExit(error = message->Append(responseHeader.GetBytes(), responseHeader.GetLength())); memcpy(&responseInfo, &aRequestInfo, sizeof(responseInfo)); @@ -581,16 +549,13 @@ void AddressResolver::SendAddressQueryResponse(const ThreadTargetTlv &aTargetTlv ThreadRloc16Tlv rloc16Tlv; Ip6::MessageInfo messageInfo; - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); - header.Init(); header.SetType(Coap::Header::kTypeConfirmable); header.SetCode(Coap::Header::kCodePost); - header.SetMessageId(++mCoapMessageId); - header.SetToken(NULL, 0); header.AppendUriPathOptions(OPENTHREAD_URI_ADDRESS_NOTIFY); header.Finalize(); - SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); + + VerifyOrExit((message = mCoapClient.NewMessage(header)) != NULL, error = kThreadError_NoBufs); SuccessOrExit(error = message->Append(&aTargetTlv, sizeof(aTargetTlv))); SuccessOrExit(error = message->Append(&aMlIidTlv, sizeof(aMlIidTlv))); @@ -609,7 +574,7 @@ void AddressResolver::SendAddressQueryResponse(const ThreadTargetTlv &aTargetTlv messageInfo.GetSockAddr() = *mMle.GetMeshLocal16(); messageInfo.mPeerPort = kCoapUdpPort; - SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + SuccessOrExit(error = mCoapClient.SendMessage(*message, messageInfo, NULL, NULL)); otLogInfoArp("Sent address notification\n"); diff --git a/src/core/thread/address_resolver.hpp b/src/core/thread/address_resolver.hpp index 4c50ca2f0..55032e0ca 100644 --- a/src/core/thread/address_resolver.hpp +++ b/src/core/thread/address_resolver.hpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -181,13 +182,12 @@ private: Coap::Resource mAddressQuery; Coap::Resource mAddressNotification; Cache mCache[kCacheEntries]; - uint16_t mCoapMessageId; Ip6::IcmpHandler mIcmpHandler; - Ip6::UdpSocket mSocket; Timer mTimer; MeshForwarder &mMeshForwarder; Coap::Server &mCoapServer; + Coap::Client &mCoapClient; Mle::MleRouter &mMle; Ip6::Netif &mNetif; }; diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 8139673c5..f8fd8c589 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -1105,6 +1105,14 @@ protected: */ ThreadError SetStateChild(uint16_t aRloc16); + /** + * This method returns a new MLE message. + * + * @returns A pointer to the message or NULL if no buffers are available. + * + */ + Message *NewMessage(void) { return mSocket.NewMessage(0); }; + /** * This method sets the Leader's Partition ID, Weighting, and Router ID values. * diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index f8fb441bf..48ed878bc 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -55,10 +55,10 @@ MleRouter::MleRouter(ThreadNetif &aThreadNetif): mAdvertiseTimer(aThreadNetif.GetIp6().mTimerScheduler, &MleRouter::HandleAdvertiseTimer, NULL, this), mStateUpdateTimer(aThreadNetif.GetIp6().mTimerScheduler, &MleRouter::HandleStateUpdateTimer, this), mDelayedResponseTimer(aThreadNetif.GetIp6().mTimerScheduler, &MleRouter::HandleDelayedResponseTimer, this), - mSocket(aThreadNetif.GetIp6().mUdp), mAddressSolicit(OPENTHREAD_URI_ADDRESS_SOLICIT, &MleRouter::HandleAddressSolicit, this), mAddressRelease(OPENTHREAD_URI_ADDRESS_RELEASE, &MleRouter::HandleAddressRelease, this), - mCoapServer(aThreadNetif.GetCoapServer()) + mCoapServer(aThreadNetif.GetCoapServer()), + mCoapClient(aThreadNetif.GetCoapClient()) { mChallengeTimeout = 0; mNextChildId = kMaxChildId; @@ -76,8 +76,6 @@ MleRouter::MleRouter(ThreadNetif &aThreadNetif): mPreviousRouterId = kInvalidRouterId; mRouterIdSequenceLastUpdated = 0; mRouterRoleEnabled = true; - - mCoapMessageId = static_cast(otPlatRandomGet()); } bool MleRouter::IsRouterRoleEnabled(void) const @@ -210,7 +208,6 @@ ThreadError MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus) mRouters[i].mNextHop = kInvalidRouterId; } - mSocket.Open(&MleRouter::HandleUdpReceive, this); mAdvertiseTimer.Stop(); mAddressResolver.Clear(); @@ -251,7 +248,6 @@ ThreadError MleRouter::BecomeLeader(void) mRouters[i].mNextHop = kInvalidRouterId; } - mSocket.Open(&MleRouter::HandleUdpReceive, this); mAdvertiseTimer.Stop(); mStateUpdateTimer.Start(kStateUpdatePeriod); mAddressResolver.Clear(); @@ -465,7 +461,7 @@ ThreadError MleRouter::SendAdvertisement(void) Ip6::Address destination; Message *message; - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); + VerifyOrExit((message = NewMessage()) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandAdvertisement)); SuccessOrExit(error = AppendSourceAddress(*message)); @@ -514,7 +510,7 @@ ThreadError MleRouter::SendLinkRequest(Neighbor *aNeighbor) memset(&destination, 0, sizeof(destination)); - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); + VerifyOrExit((message = NewMessage()) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandLinkRequest)); SuccessOrExit(error = AppendVersion(*message)); @@ -690,7 +686,7 @@ ThreadError MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, Neig command = (aNeighbor == NULL || aNeighbor->mState == Neighbor::kStateValid) ? Header::kCommandLinkAccept : Header::kCommandLinkAcceptAndRequest; - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); + VerifyOrExit((message = NewMessage()) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, command)); SuccessOrExit(error = AppendVersion(*message)); @@ -982,7 +978,7 @@ ThreadError MleRouter::SendLinkReject(const Ip6::Address &aDestination) ThreadError error = kThreadError_None; Message *message; - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); + VerifyOrExit((message = NewMessage()) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandLinkReject)); SuccessOrExit(error = AppendStatus(*message, StatusTlv::kError)); @@ -1884,7 +1880,7 @@ ThreadError MleRouter::SendParentResponse(Child *aChild, const ChallengeTlv &cha Message *message; uint16_t delay; - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); + VerifyOrExit((message = NewMessage()) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandParentResponse)); SuccessOrExit(error = AppendSourceAddress(*message)); @@ -2306,7 +2302,7 @@ ThreadError MleRouter::SendChildIdResponse(Child *aChild) Ip6::Address destination; Message *message; - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); + VerifyOrExit((message = NewMessage()) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandChildIdResponse)); SuccessOrExit(error = AppendSourceAddress(*message)); @@ -2391,7 +2387,7 @@ ThreadError MleRouter::SendChildUpdateResponse(Child *aChild, const Ip6::Message ThreadError error = kThreadError_None; Message *message; - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); + VerifyOrExit((message = NewMessage()) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandChildUpdateResponse)); @@ -2454,7 +2450,7 @@ ThreadError MleRouter::SendDataResponse(const Ip6::Address &aDestination, const Neighbor *neighbor; bool stableOnly; - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); + VerifyOrExit((message = NewMessage()) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandDataResponse)); SuccessOrExit(error = AppendSourceAddress(*message)); @@ -3118,21 +3114,14 @@ ThreadError MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus) Ip6::MessageInfo messageInfo; Message *message; - for (size_t i = 0; i < sizeof(mCoapToken); i++) - { - mCoapToken[i] = static_cast(otPlatRandomGet()); - } - header.Init(); header.SetType(Coap::Header::kTypeConfirmable); header.SetCode(Coap::Header::kCodePost); - header.SetMessageId(++mCoapMessageId); - header.SetToken(mCoapToken, sizeof(mCoapToken)); + header.SetToken(Coap::Header::kDefaultTokenLength); header.AppendUriPathOptions(OPENTHREAD_URI_ADDRESS_SOLICIT); header.Finalize(); - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); - SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); + VerifyOrExit((message = mCoapClient.NewMessage(header)) != NULL, error = kThreadError_NoBufs); macAddr64Tlv.Init(); macAddr64Tlv.SetMacAddr(*mMac.GetExtAddress()); @@ -3152,11 +3141,19 @@ ThreadError MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus) memset(&messageInfo, 0, sizeof(messageInfo)); SuccessOrExit(error = GetLeaderAddress(messageInfo.GetPeerAddr())); messageInfo.mPeerPort = kCoapUdpPort; - SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + + SuccessOrExit(error = mCoapClient.SendMessage(*message, messageInfo, + &MleRouter::HandleAddressSolicitResponse, this)); otLogInfoMle("Sent address solicit to %04x\n", HostSwap16(messageInfo.GetPeerAddr().mFields.m16[7])); exit: + + if (error != kThreadError_None && message != NULL) + { + message->Free(); + } + return error; } @@ -3169,21 +3166,14 @@ ThreadError MleRouter::SendAddressRelease(void) Ip6::MessageInfo messageInfo; Message *message; - for (size_t i = 0; i < sizeof(mCoapToken); i++) - { - mCoapToken[i] = static_cast(otPlatRandomGet()); - } - header.Init(); header.SetType(Coap::Header::kTypeConfirmable); header.SetCode(Coap::Header::kCodePost); - header.SetMessageId(++mCoapMessageId); - header.SetToken(mCoapToken, sizeof(mCoapToken)); + header.SetToken(Coap::Header::kDefaultTokenLength); header.AppendUriPathOptions(OPENTHREAD_URI_ADDRESS_RELEASE); header.Finalize(); - VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); - SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); + VerifyOrExit((message = mCoapClient.NewMessage(header)) != NULL, error = kThreadError_NoBufs); rlocTlv.Init(); rlocTlv.SetRloc16(GetRloc16(mRouterId)); @@ -3196,54 +3186,51 @@ ThreadError MleRouter::SendAddressRelease(void) memset(&messageInfo, 0, sizeof(messageInfo)); SuccessOrExit(error = GetLeaderAddress(messageInfo.GetPeerAddr())); messageInfo.mPeerPort = kCoapUdpPort; - SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + SuccessOrExit(error = mCoapClient.SendMessage(*message, messageInfo, NULL, NULL)); otLogInfoMle("Sent address release\n"); exit: + + if (error != kThreadError_None && message != NULL) + { + message->Free(); + } + return error; } -void MleRouter::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo) +void MleRouter::HandleAddressSolicitResponse(void *aContext, otCoapHeader *aHeader, otMessage aMessage, + ThreadError result) { - static_cast(aContext)->HandleUdpReceive(*static_cast(aMessage), - *static_cast(aMessageInfo)); - (void)aMessageInfo; + static_cast(aContext)->HandleAddressSolicitResponse(static_cast(aHeader), + static_cast(aMessage), result); } -void MleRouter::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +void MleRouter::HandleAddressSolicitResponse(Coap::Header *aHeader, Message *aMessage, ThreadError result) { - (void)aMessageInfo; - HandleAddressSolicitResponse(aMessage); -} + (void) result; -void MleRouter::HandleAddressSolicitResponse(Message &aMessage) -{ - Coap::Header header; ThreadStatusTlv statusTlv; ThreadRloc16Tlv rlocTlv; ThreadRouterMaskTlv routerMaskTlv; uint8_t routerId; bool old; - SuccessOrExit(header.FromMessage(aMessage)); - VerifyOrExit(header.GetType() == Coap::Header::kTypeAcknowledgment && - header.GetCode() == Coap::Header::kCodeChanged && - header.GetMessageId() == mCoapMessageId && - header.GetTokenLength() == sizeof(mCoapToken) && - memcmp(mCoapToken, header.GetToken(), sizeof(mCoapToken)) == 0, ;); - aMessage.MoveOffset(header.GetLength()); + VerifyOrExit(result == kThreadError_None && aHeader != NULL && aMessage != NULL, ;); + + VerifyOrExit(aHeader->GetCode() == Coap::Header::kCodeChanged, ;); otLogInfoMle("Received address reply\n"); - SuccessOrExit(ThreadTlv::GetTlv(aMessage, ThreadTlv::kStatus, sizeof(statusTlv), statusTlv)); + SuccessOrExit(ThreadTlv::GetTlv(*aMessage, ThreadTlv::kStatus, sizeof(statusTlv), statusTlv)); VerifyOrExit(statusTlv.IsValid() && statusTlv.GetStatus() == statusTlv.kSuccess, ;); - SuccessOrExit(ThreadTlv::GetTlv(aMessage, ThreadTlv::kRloc16, sizeof(rlocTlv), rlocTlv)); + SuccessOrExit(ThreadTlv::GetTlv(*aMessage, ThreadTlv::kRloc16, sizeof(rlocTlv), rlocTlv)); VerifyOrExit(rlocTlv.IsValid(), ;); VerifyOrExit(IsRouterIdValid(routerId = GetRouterId(rlocTlv.GetRloc16())), ;); - SuccessOrExit(ThreadTlv::GetTlv(aMessage, ThreadTlv::kRouterMask, sizeof(routerMaskTlv), routerMaskTlv)); + SuccessOrExit(ThreadTlv::GetTlv(*aMessage, ThreadTlv::kRouterMask, sizeof(routerMaskTlv), routerMaskTlv)); VerifyOrExit(routerMaskTlv.IsValid(), ;); // assign short address @@ -3419,12 +3406,7 @@ void MleRouter::SendAddressSolicitResponse(const Coap::Header &aRequestHeader, u Message *message; VerifyOrExit((message = mCoapServer.NewMessage(0)) != NULL, error = kThreadError_NoBufs); - responseHeader.Init(); - responseHeader.SetType(Coap::Header::kTypeAcknowledgment); - responseHeader.SetCode(Coap::Header::kCodeChanged); - responseHeader.SetMessageId(aRequestHeader.GetMessageId()); - responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength()); - responseHeader.Finalize(); + responseHeader.SetDefaultResponseHeader(aRequestHeader); SuccessOrExit(error = message->Append(responseHeader.GetBytes(), responseHeader.GetLength())); statusTlv.Init(); @@ -3509,12 +3491,8 @@ void MleRouter::SendAddressReleaseResponse(const Coap::Header &aRequestHeader, c Message *message; VerifyOrExit((message = mCoapServer.NewMessage(0)) != NULL, error = kThreadError_NoBufs); - responseHeader.Init(); - responseHeader.SetType(Coap::Header::kTypeAcknowledgment); - responseHeader.SetCode(Coap::Header::kCodeChanged); - responseHeader.SetMessageId(aRequestHeader.GetMessageId()); - responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength()); - responseHeader.Finalize(); + responseHeader.SetDefaultResponseHeader(aRequestHeader); + SuccessOrExit(error = message->Append(responseHeader.GetBytes(), responseHeader.GetLength())); SuccessOrExit(error = mCoapServer.SendMessage(*message, aMessageInfo)); diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index b52d7c665..ece21e6eb 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -686,9 +687,9 @@ private: ThreadError UpdateChildAddresses(const AddressRegistrationTlv &aTlv, Child &aChild); void UpdateRoutes(const RouteTlv &aTlv, uint8_t aRouterId); - static void HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo); - void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - void HandleAddressSolicitResponse(Message &aMessage); + static void HandleAddressSolicitResponse(void *aContext, otCoapHeader *aHeader, otMessage aMessage, + ThreadError result); + void HandleAddressSolicitResponse(Coap::Header *aHeader, Message *aMessage, ThreadError result); static void HandleAddressRelease(void *aContext, Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void HandleAddressRelease(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); @@ -726,7 +727,6 @@ private: Timer mStateUpdateTimer; Timer mDelayedResponseTimer; - Ip6::UdpSocket mSocket; Coap::Resource mAddressSolicit; Coap::Resource mAddressRelease; @@ -750,8 +750,7 @@ private: uint8_t mPreviousRouterId; Coap::Server &mCoapServer; - uint8_t mCoapToken[2]; - uint16_t mCoapMessageId; + Coap::Client &mCoapClient; }; } // namespace Mle diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index 3e5fa7930..bc9e7b99b 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -58,6 +58,7 @@ static const char name[] = "thread"; ThreadNetif::ThreadNetif(Ip6::Ip6 &aIp6): Netif(aIp6), mCoapServer(aIp6.mUdp, kCoapUdpPort), + mCoapClient(*this), mAddressResolver(*this), mActiveDataset(*this), mPendingDataset(*this), @@ -103,12 +104,14 @@ ThreadError ThreadNetif::Up(void) mIsUp = true; } + mCoapClient.Start(); return kThreadError_None; } ThreadError ThreadNetif::Down(void) { mCoapServer.Stop(); + mCoapClient.Stop(); mMleRouter.Disable(); mMeshForwarder.Stop(); mIp6.RemoveNetif(*this); diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index 439a2ecaf..5c7d168ba 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -42,6 +42,8 @@ #include +#include +#include #include #include #include @@ -170,13 +172,21 @@ public: NetworkDiagnostic::NetworkDiagnostic &GetNetworkDiagnostic(void) { return mNetworkDiagnostic; } /** - * This method returns a pointer to the coap server object. + * This method returns a reference to the CoAP server object. * - * @returns A pointer to the coap server object. + * @returns A reference to the CoAP server object. * */ Coap::Server &GetCoapServer(void) { return mCoapServer; } + /** + * This method returns a reference to the CoAP client object. + * + * @returns A reference to the CoAP client object. + * + */ + Coap::Client &GetCoapClient(void) { return mCoapClient; } + /** * This method returns a reference to the IPv6 filter object. * @@ -273,6 +283,7 @@ public: private: Coap::Server mCoapServer; + Coap::Client mCoapClient; AddressResolver mAddressResolver; MeshCoP::ActiveDataset mActiveDataset; MeshCoP::PendingDataset mPendingDataset;