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;