diff --git a/include/openthread/coap.h b/include/openthread/coap.h index eb9505880..22ba3d247 100644 --- a/include/openthread/coap.h +++ b/include/openthread/coap.h @@ -361,6 +361,40 @@ typedef struct otCoapResource struct otCoapResource *mNext; ///< The next CoAP resource in the list } otCoapResource; +/** + * This structure represents the CoAP transmission parameters. + * + */ +typedef struct otCoapTxParameters +{ + /** + * Minimum spacing before first retransmission when ACK is not received, in milliseconds (RFC7252 default value is + * 2000ms). + * + */ + uint32_t mAckTimeout; + + /** + * 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). + * + */ + uint8_t mAckRandomFactorNumerator; + + /** + * 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). + * + */ + uint8_t mAckRandomFactorDenominator; + + /** + * Maximum number of retransmissions for CoAP Confirmable messages (RFC7252 default value is 4). + * + */ + uint8_t mMaxRetransmit; +} otCoapTxParameters; + /** * This function initializes the CoAP header. * @@ -693,6 +727,30 @@ otError otCoapOptionIteratorGetOptionValue(otCoapOptionIterator *aIterator, void */ otMessage *otCoapNewMessage(otInstance *aInstance, const otMessageSettings *aSettings); +/** + * This function sends a CoAP request with custom transmission parameters. + * + * 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. + * + * @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. + * @param[in] aTxParameters A pointer to transmission parameters for this request. Use NULL for defaults. + * + * @retval OT_ERROR_NONE Successfully sent CoAP message. + * @retval OT_ERROR_NO_BUFS Failed to allocate retransmission data. + * + */ +otError otCoapSendRequestWithParameters(otInstance * aInstance, + otMessage * aMessage, + const otMessageInfo * aMessageInfo, + otCoapResponseHandler aHandler, + void * aContext, + const otCoapTxParameters *aTxParameters); + /** * This function sends a CoAP request. * @@ -709,11 +767,14 @@ otMessage *otCoapNewMessage(otInstance *aInstance, const otMessageSettings *aSet * @retval OT_ERROR_NO_BUFS Failed to allocate retransmission data. * */ -otError otCoapSendRequest(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, - otCoapResponseHandler aHandler, - void * aContext); +static inline otError otCoapSendRequest(otInstance * aInstance, + otMessage * aMessage, + const otMessageInfo * aMessageInfo, + otCoapResponseHandler aHandler, + void * aContext) +{ + return otCoapSendRequestWithParameters(aInstance, aMessage, aMessageInfo, aHandler, aContext, NULL); +} /** * This function starts the CoAP server. @@ -767,6 +828,23 @@ void otCoapRemoveResource(otInstance *aInstance, otCoapResource *aResource); */ void otCoapSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandler, void *aContext); +/** + * This function sends a CoAP response from the server with custom transmission parameters. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aMessage A pointer to the CoAP response to send. + * @param[in] aMessageInfo A pointer to the message info associated with @p aMessage. + * @param[in] aTxParameters A pointer to transmission parameters for this response. Use NULL for defaults. + * + * @retval OT_ERROR_NONE Successfully enqueued the CoAP response message. + * @retval OT_ERROR_NO_BUFS Insufficient buffers available to send the CoAP response. + * + */ +otError otCoapSendResponseWithParameters(otInstance * aInstance, + otMessage * aMessage, + const otMessageInfo * aMessageInfo, + const otCoapTxParameters *aTxParameters); + /** * This function sends a CoAP response from the server. * @@ -778,7 +856,10 @@ void otCoapSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandle * @retval OT_ERROR_NO_BUFS Insufficient buffers available to send the CoAP response. * */ -otError otCoapSendResponse(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo); +static inline otError otCoapSendResponse(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo) +{ + return otCoapSendResponseWithParameters(aInstance, aMessage, aMessageInfo, NULL); +} /** * @} diff --git a/src/cli/README_COAP.md b/src/cli/README_COAP.md index a83adbdac..425b97abc 100644 --- a/src/cli/README_COAP.md +++ b/src/cli/README_COAP.md @@ -57,6 +57,7 @@ coap response sent * [help](#help) * [delete](#delete-address-uri-path-type-payload) * [get](#get-address-uri-path-type) +* [parameters](#parameters) * [post](#post-address-uri-path-type-payload) * [put](#put-address-uri-path-type-payload) * [resource](#resource-uri-path) @@ -72,6 +73,7 @@ coap response sent help delete get +parameters post put resource @@ -105,6 +107,43 @@ Done Done ``` +### parameters \ \["default"| \] + +Sets transmission parameters for the following interactions. + +* type: "request" for CoAP requests and "response" for CoAP responses. + +If no more parameters are given, the command prints the current configuration: + +```bash +> coap parameters request +Transmission parameters for request: +ACK_TIMEOUT=1000 ms, ACK_RANDOM_FACTOR=255/254, MAX_RETRANSMIT=2 +Done +``` + +If `"default"` is given, the command sets the default configuration for the transmission parameters. + +```bash +> coap parameters request default +Transmission parameters for request: +default +Done +``` + +Also, you can specify the transmission parameters in the command line: +* ack\_timeout (0~UINT32\_MAX): RFC7252 ACK\_TIMEOUT, in milliseconds. +* ack\_random\_factor\_numerator, ack\_random\_factor\_denominator (0~255): +RFC7252 ACK\_RANDOM\_FACTOR=ack\_random\_factor\_numerator/ack\_random\_factor\_denominator. +* max\_retransmit (0~255): RFC7252 MAX_RETRANSMIT. + +```bash +> coap parameters request 1000 255 254 2 +Transmission parameters for request: +ACK_TIMEOUT=1000 ms, ACK_RANDOM_FACTOR=255/254, MAX_RETRANSMIT=2 +Done +``` + ### post \ \ \[type\] \[payload\] * address: IPv6 address of the CoAP server. diff --git a/src/cli/cli_coap.cpp b/src/cli/cli_coap.cpp index cf9418a6b..545555f4e 100644 --- a/src/cli/cli_coap.cpp +++ b/src/cli/cli_coap.cpp @@ -45,13 +45,17 @@ namespace ot { namespace Cli { const struct Coap::Command Coap::sCommands[] = { - {"help", &Coap::ProcessHelp}, {"delete", &Coap::ProcessRequest}, {"get", &Coap::ProcessRequest}, - {"post", &Coap::ProcessRequest}, {"put", &Coap::ProcessRequest}, {"resource", &Coap::ProcessResource}, - {"start", &Coap::ProcessStart}, {"stop", &Coap::ProcessStop}, + {"help", &Coap::ProcessHelp}, {"delete", &Coap::ProcessRequest}, + {"get", &Coap::ProcessRequest}, {"parameters", &Coap::ProcessParameters}, + {"post", &Coap::ProcessRequest}, {"put", &Coap::ProcessRequest}, + {"resource", &Coap::ProcessResource}, {"start", &Coap::ProcessStart}, + {"stop", &Coap::ProcessStop}, }; Coap::Coap(Interpreter &aInterpreter) : mInterpreter(aInterpreter) + , mUseDefaultRequestTxParameters(true) + , mUseDefaultResponseTxParameters(true) { memset(&mResource, 0, sizeof(mResource)); } @@ -137,6 +141,80 @@ otError Coap::ProcessStop(int argc, char *argv[]) return otCoapStop(mInterpreter.mInstance); } +otError Coap::ProcessParameters(int argc, char *argv[]) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(argc > 0, error = OT_ERROR_INVALID_ARGS); + + bool * defaultTxParameters; + otCoapTxParameters *txParameters; + + if (strcmp(argv[1], "request") == 0) + { + txParameters = &mRequestTxParameters; + defaultTxParameters = &mUseDefaultRequestTxParameters; + } + else if (strcmp(argv[1], "response") == 0) + { + txParameters = &mResponseTxParameters; + defaultTxParameters = &mUseDefaultResponseTxParameters; + } + else + { + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + + if (argc > 2) + { + if (strcmp(argv[2], "default") == 0) + { + *defaultTxParameters = true; + } + else + { + unsigned long value; + + VerifyOrExit(argc >= 6, error = OT_ERROR_INVALID_ARGS); + + SuccessOrExit(error = mInterpreter.ParseUnsignedLong(argv[2], value)); + txParameters->mAckTimeout = static_cast(value); + + SuccessOrExit(error = mInterpreter.ParseUnsignedLong(argv[3], value)); + VerifyOrExit(value <= 255, error = OT_ERROR_INVALID_ARGS); + txParameters->mAckRandomFactorNumerator = static_cast(value); + + SuccessOrExit(error = mInterpreter.ParseUnsignedLong(argv[4], value)); + VerifyOrExit(value <= 255, error = OT_ERROR_INVALID_ARGS); + txParameters->mAckRandomFactorDenominator = static_cast(value); + + SuccessOrExit(error = mInterpreter.ParseUnsignedLong(argv[5], value)); + VerifyOrExit(value <= 255, error = OT_ERROR_INVALID_ARGS); + txParameters->mMaxRetransmit = static_cast(value); + + VerifyOrExit(txParameters->mAckRandomFactorNumerator > txParameters->mAckRandomFactorDenominator, + error = OT_ERROR_INVALID_ARGS); + + *defaultTxParameters = false; + } + } + + mInterpreter.mServer->OutputFormat("Transmission parameters for %s:\r\n", argv[1]); + if (*defaultTxParameters) + { + mInterpreter.mServer->OutputFormat("default\r\n"); + } + else + { + mInterpreter.mServer->OutputFormat("ACK_TIMEOUT=%u ms, ACK_RANDOM_FACTOR=%u/%u, MAX_RETRANSMIT=%u\r\n", + txParameters->mAckTimeout, txParameters->mAckRandomFactorNumerator, + txParameters->mAckRandomFactorDenominator, txParameters->mMaxRetransmit); + } + +exit: + return error; +} + otError Coap::ProcessRequest(int argc, char *argv[]) { otError error = OT_ERROR_NONE; @@ -233,11 +311,13 @@ otError Coap::ProcessRequest(int argc, char *argv[]) if ((coapType == OT_COAP_TYPE_CONFIRMABLE) || (coapCode == OT_COAP_CODE_GET)) { - error = otCoapSendRequest(mInterpreter.mInstance, message, &messageInfo, &Coap::HandleResponse, this); + error = otCoapSendRequestWithParameters(mInterpreter.mInstance, message, &messageInfo, &Coap::HandleResponse, + this, GetRequestTxParameters()); } else { - error = otCoapSendRequest(mInterpreter.mInstance, message, &messageInfo, NULL, NULL); + error = otCoapSendRequestWithParameters(mInterpreter.mInstance, message, &messageInfo, NULL, NULL, + GetResponseTxParameters()); } exit: @@ -339,7 +419,8 @@ void Coap::HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo) SuccessOrExit(error = otMessageAppend(responseMessage, &responseContent, sizeof(responseContent))); } - SuccessOrExit(error = otCoapSendResponse(mInterpreter.mInstance, responseMessage, aMessageInfo)); + SuccessOrExit(error = otCoapSendResponseWithParameters(mInterpreter.mInstance, responseMessage, aMessageInfo, + GetResponseTxParameters())); } exit: diff --git a/src/cli/cli_coap.hpp b/src/cli/cli_coap.hpp index cc534a3df..b3d84b046 100644 --- a/src/cli/cli_coap.hpp +++ b/src/cli/cli_coap.hpp @@ -85,6 +85,7 @@ private: void PrintPayload(otMessage *aMessage) const; otError ProcessHelp(int argc, char *argv[]); + otError ProcessParameters(int argc, char *argv[]); otError ProcessRequest(int argc, char *argv[]); otError ProcessResource(int argc, char *argv[]); otError ProcessStart(int argc, char *argv[]); @@ -96,9 +97,25 @@ private: static void HandleResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError); void HandleResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError); + const otCoapTxParameters *GetRequestTxParameters(void) const + { + return mUseDefaultRequestTxParameters ? NULL : &mRequestTxParameters; + } + + const otCoapTxParameters *GetResponseTxParameters(void) const + { + return mUseDefaultResponseTxParameters ? NULL : &mResponseTxParameters; + } + static const Command sCommands[]; Interpreter & mInterpreter; + bool mUseDefaultRequestTxParameters; + bool mUseDefaultResponseTxParameters; + + otCoapTxParameters mRequestTxParameters; + otCoapTxParameters mResponseTxParameters; + otCoapResource mResource; char mUriPath[kMaxUriLength]; }; diff --git a/src/core/api/coap_api.cpp b/src/core/api/coap_api.cpp index 7779db46f..85be09a79 100644 --- a/src/core/api/coap_api.cpp +++ b/src/core/api/coap_api.cpp @@ -195,17 +195,18 @@ otError otCoapOptionIteratorGetOptionValue(otCoapOptionIterator *aIterator, void return static_cast(aIterator)->GetOptionValue(aValue); } -otError otCoapSendRequest(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, - otCoapResponseHandler aHandler, - void * aContext) +otError otCoapSendRequestWithParameters(otInstance * aInstance, + otMessage * aMessage, + const otMessageInfo * aMessageInfo, + otCoapResponseHandler aHandler, + void * aContext, + const otCoapTxParameters *aTxParameters) { Instance &instance = *static_cast(aInstance); return instance.GetApplicationCoap().SendMessage(*static_cast(aMessage), - *static_cast(aMessageInfo), aHandler, - aContext); + *static_cast(aMessageInfo), + Coap::CoapTxParameters::From(aTxParameters), aHandler, aContext); } otError otCoapStart(otInstance *aInstance, uint16_t aPort) @@ -243,12 +244,16 @@ void otCoapSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandle instance.GetApplicationCoap().SetDefaultHandler(aHandler, aContext); } -otError otCoapSendResponse(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo) +otError otCoapSendResponseWithParameters(otInstance * aInstance, + otMessage * aMessage, + const otMessageInfo * aMessageInfo, + const otCoapTxParameters *aTxParameters) { Instance &instance = *static_cast(aInstance); return instance.GetApplicationCoap().SendMessage(*static_cast(aMessage), - *static_cast(aMessageInfo)); + *static_cast(aMessageInfo), + Coap::CoapTxParameters::From(aTxParameters), NULL, NULL); } #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp index 6bf5df23c..d4b143c56 100644 --- a/src/core/coap/coap.cpp +++ b/src/core/coap/coap.cpp @@ -109,18 +109,18 @@ exit: otError CoapBase::SendMessage(Message & aMessage, const Ip6::MessageInfo &aMessageInfo, + const CoapTxParameters &aTxParameters, otCoapResponseHandler aHandler, void * aContext) { - otError error; - CoapMetadata coapMetadata; - Message * storedCopy = NULL; - uint16_t copyLength = 0; + otError error; + Message *storedCopy = NULL; + uint16_t copyLength = 0; switch (aMessage.GetType()) { case OT_COAP_TYPE_ACKNOWLEDGMENT: - mResponsesQueue.EnqueueResponse(aMessage, aMessageInfo); + mResponsesQueue.EnqueueResponse(aMessage, aMessageInfo, aTxParameters); break; case OT_COAP_TYPE_RESET: assert(aMessage.GetCode() == OT_COAP_CODE_EMPTY); @@ -145,7 +145,8 @@ otError CoapBase::SendMessage(Message & aMessage, if (copyLength > 0) { - coapMetadata = CoapMetadata(aMessage.IsConfirmable(), aMessageInfo, aHandler, aContext); + CoapMetadata coapMetadata = + CoapMetadata(aMessage.IsConfirmable(), aMessageInfo, aHandler, aContext, aTxParameters); VerifyOrExit((storedCopy = CopyAndEnqueueMessage(aMessage, copyLength, coapMetadata)) != NULL, error = OT_ERROR_NO_BUFS); } @@ -247,7 +248,7 @@ void CoapBase::HandleRetransmissionTimer(void) if (now >= coapMetadata.mNextTimerShot) { - if (!coapMetadata.mConfirmable || (coapMetadata.mRetransmissionCount >= kMaxRetransmit)) + if (!coapMetadata.mConfirmable || (coapMetadata.mRetransmissionsRemaining == 0)) { // No expected response or acknowledgment. FinalizeCoapTransaction(*message, coapMetadata, NULL, NULL, OT_ERROR_RESPONSE_TIMEOUT); @@ -255,7 +256,7 @@ void CoapBase::HandleRetransmissionTimer(void) } // Increment retransmission counter and timer. - coapMetadata.mRetransmissionCount++; + coapMetadata.mRetransmissionsRemaining--; coapMetadata.mRetransmissionTimeout *= 2; coapMetadata.mNextTimerShot = now + coapMetadata.mRetransmissionTimeout; coapMetadata.UpdateIn(*message); @@ -623,18 +624,16 @@ exit: CoapMetadata::CoapMetadata(bool aConfirmable, const Ip6::MessageInfo &aMessageInfo, otCoapResponseHandler aHandler, - void * aContext) + void * aContext, + const CoapTxParameters &aTxParameters) { - mSourceAddress = aMessageInfo.GetSockAddr(); - mDestinationPort = aMessageInfo.GetPeerPort(); - mDestinationAddress = aMessageInfo.GetPeerAddr(); - mResponseHandler = aHandler; - mResponseContext = aContext; - mRetransmissionCount = 0; - mRetransmissionTimeout = Time::SecToMsec(kAckTimeout); - mRetransmissionTimeout += Random::NonCrypto::GetUint32InRange( - 0, Time::SecToMsec(kAckTimeout) * kAckRandomFactorNumerator / kAckRandomFactorDenominator - - Time::SecToMsec(kAckTimeout) + 1); + mSourceAddress = aMessageInfo.GetSockAddr(); + mDestinationPort = aMessageInfo.GetPeerPort(); + mDestinationAddress = aMessageInfo.GetPeerAddr(); + mResponseHandler = aHandler; + mResponseContext = aContext; + mRetransmissionsRemaining = aTxParameters.mMaxRetransmit; + mRetransmissionTimeout = aTxParameters.CalculateInitialRetransmissionTimeout(); if (aConfirmable) { @@ -644,7 +643,7 @@ CoapMetadata::CoapMetadata(bool aConfirmable, else { // Set overall response timeout. - mNextTimerShot = TimerMilli::GetNow() + Time::SecToMsec(kMaxTransmitWait); + mNextTimerShot = TimerMilli::GetNow() + aTxParameters.CalculateMaxTransmitWait(); } mAcknowledged = false; @@ -711,13 +710,17 @@ exit: return matchedResponse; } -void ResponsesQueue::EnqueueResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +void ResponsesQueue::EnqueueResponse(Message & aMessage, + const Ip6::MessageInfo &aMessageInfo, + const CoapTxParameters &aTxParameters) { otError error = OT_ERROR_NONE; Message * responseCopy = NULL; - EnqueuedResponseHeader enqueuedResponseHeader(aMessageInfo); uint16_t messageCount; uint16_t bufferCount; + uint32_t exchangeLifetime = aTxParameters.CalculateExchangeLifetime(); + TimeMilli dequeueTime = TimerMilli::GetNow() + exchangeLifetime; + EnqueuedResponseHeader enqueuedResponseHeader(dequeueTime, aMessageInfo); // return success if matched response already exists in the cache VerifyOrExit(FindMatchedResponse(aMessage, aMessageInfo) == NULL); @@ -736,7 +739,7 @@ void ResponsesQueue::EnqueueResponse(Message &aMessage, const Ip6::MessageInfo & if (!mTimer.IsRunning()) { - mTimer.Start(Time::SecToMsec(kExchangeLifetime)); + mTimer.Start(exchangeLifetime); } exit: @@ -809,6 +812,33 @@ uint32_t EnqueuedResponseHeader::GetRemainingTime(void) const return remainingTime; } +uint32_t CoapTxParameters::CalculateInitialRetransmissionTimeout(void) const +{ + return Random::NonCrypto::GetUint32InRange( + mAckTimeout, mAckTimeout * mAckRandomFactorNumerator / mAckRandomFactorDenominator + 1); +} + +uint32_t CoapTxParameters::CalculateExchangeLifetime(void) const +{ + uint32_t maxTransmitSpan = static_cast(mAckTimeout * ((1ULL << mMaxRetransmit) - 1) * + mAckRandomFactorNumerator / mAckRandomFactorDenominator); + uint32_t processingDelay = mAckTimeout; + return maxTransmitSpan + 2 * kDefaultMaxLatency + processingDelay; +} + +uint32_t CoapTxParameters::CalculateMaxTransmitWait(void) const +{ + return static_cast(mAckTimeout * ((2ULL << mMaxRetransmit) - 1) * mAckRandomFactorNumerator / + mAckRandomFactorDenominator); +} + +const otCoapTxParameters CoapTxParameters::kDefaultTxParameters = { + kDefaultAckTimeout, + kDefaultAckRandomFactorNumerator, + kDefaultAckRandomFactorDenominator, + kDefaultMaxRetransmit, +}; + Coap::Coap(Instance &aInstance) : CoapBase(aInstance, &Coap::Send) , mSocket(aInstance.Get()) diff --git a/src/core/coap/coap.hpp b/src/core/coap/coap.hpp index dc2f96a2e..5cd8ce9ba 100644 --- a/src/core/coap/coap.hpp +++ b/src/core/coap/coap.hpp @@ -59,30 +59,50 @@ namespace Coap { * */ -/** - * Protocol Constants (RFC 7252). - * - */ -enum +class CoapTxParameters : public otCoapTxParameters { - 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, +public: + /** + * Protocol Constants (RFC 7252). + * + */ + enum + { + kDefaultAckTimeout = OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT_MILLIS, + kDefaultAckRandomFactorNumerator = OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_NUMERATOR, + kDefaultAckRandomFactorDenominator = OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_DENOMINATOR, + kDefaultMaxRetransmit = OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT, + kDefaultNStart = 1, + kDefaultLeisure = 5000, // in milliseconds + kDefaultProbingRate = 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 + kDefaultMaxTransmitSpan = kDefaultAckTimeout * ((1ULL << kDefaultMaxRetransmit) - 1) * + kDefaultAckRandomFactorNumerator / kDefaultAckRandomFactorDenominator, + kDefaultMaxTransmitWait = kDefaultAckTimeout * ((2ULL << kDefaultMaxRetransmit) - 1) * + kDefaultAckRandomFactorNumerator / kDefaultAckRandomFactorDenominator, + kDefaultMaxLatency = 100000, // in milliseconds + kDefaultProcessingDelay = kDefaultAckTimeout, + kDefaultMaxRtt = 2 * kDefaultMaxLatency + kDefaultProcessingDelay, + kDefaultExchangeLifetime = kDefaultMaxTransmitSpan + 2 * kDefaultMaxLatency + kDefaultProcessingDelay, + kDefaultNonLifetime = kDefaultMaxTransmitSpan + kDefaultMaxLatency + }; + + uint32_t CalculateInitialRetransmissionTimeout(void) const; + uint32_t CalculateExchangeLifetime(void) const; + uint32_t CalculateMaxTransmitWait(void) const; + + static inline const CoapTxParameters &From(const otCoapTxParameters *aTxParameters) + { + return aTxParameters ? *static_cast(aTxParameters) : GetDefault(); + } + + static inline const CoapTxParameters &GetDefault() + { + return *static_cast(&kDefaultTxParameters); + } + +private: + static const otCoapTxParameters kDefaultTxParameters; }; /** @@ -104,23 +124,25 @@ public: , mResponseContext(NULL) , mNextTimerShot(0) , mRetransmissionTimeout(0) - , mRetransmissionCount(0) + , mRetransmissionsRemaining(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. + * @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. + * @param[in] aTxParameters Transmission parameters. * */ CoapMetadata(bool aConfirmable, const Ip6::MessageInfo &aMessageInfo, otCoapResponseHandler aHandler, - void * aContext); + void * aContext, + const CoapTxParameters &aTxParameters); /** * This method appends request data to the message. @@ -160,16 +182,16 @@ public: } private: - Ip6::Address mSourceAddress; ///< IPv6 address of the message source. - 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. - TimeMilli 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. + Ip6::Address mSourceAddress; ///< IPv6 address of the message source. + 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. + TimeMilli mNextTimerShot; ///< Time when the timer should shoot for this message. + uint32_t mRetransmissionTimeout; ///< Delay that is applied to next retransmission. + uint8_t mRetransmissionsRemaining; ///< Number of retransmissions remaining. + bool mAcknowledged : 1; ///< Information that request was acknowledged. + bool mConfirmable : 1; ///< Information that message is confirmable. }; /** @@ -241,8 +263,8 @@ public: * @param[in] aMessageInfo The message info containing source endpoint identification. * */ - explicit EnqueuedResponseHeader(const Ip6::MessageInfo &aMessageInfo) - : mDequeueTime(TimerMilli::GetNow() + Time::SecToMsec(kExchangeLifetime)) + explicit EnqueuedResponseHeader(TimeMilli aDequeueTime, const Ip6::MessageInfo &aMessageInfo) + : mDequeueTime(aDequeueTime) , mMessageInfo(aMessageInfo) { } @@ -316,9 +338,12 @@ public: * * @param[in] aMessage The CoAP response to add to the cache. * @param[in] aMessageInfo The message info corresponding to @p aMessage. + * @param[in] aTxParameters Transmission parameters. * */ - void EnqueueResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void EnqueueResponse(Message & aMessage, + const Ip6::MessageInfo &aMessageInfo, + const CoapTxParameters &aTxParameters); /** * This method removes the oldest response from the cache. @@ -469,7 +494,30 @@ public: Message *NewMessage(const otMessageSettings *aSettings = NULL); /** - * This method sends a CoAP message. + * This method sends a CoAP message with custom transmission parameters. + * + * 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] aTxParameters A reference to transmission parameters for this message. + * @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 OT_ERROR_NONE Successfully sent CoAP message. + * @retval OT_ERROR_NO_BUFS Failed to allocate retransmission data. + * + */ + otError SendMessage(Message & aMessage, + const Ip6::MessageInfo &aMessageInfo, + const CoapTxParameters &aTxParameters, + otCoapResponseHandler aHandler = NULL, + void * aContext = NULL); + + /** + * This method sends a CoAP message with default transmission parameters. * * 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. @@ -487,7 +535,10 @@ public: otError SendMessage(Message & aMessage, const Ip6::MessageInfo &aMessageInfo, otCoapResponseHandler aHandler = NULL, - void * aContext = NULL); + void * aContext = NULL) + { + return SendMessage(aMessage, aMessageInfo, CoapTxParameters::GetDefault(), aHandler, aContext); + } /** * This method sends a CoAP reset message. diff --git a/src/core/config/coap.h b/src/core/config/coap.h index d958c9d75..6daa7c7fe 100644 --- a/src/core/config/coap.h +++ b/src/core/config/coap.h @@ -36,13 +36,14 @@ #define CONFIG_COAP_H_ /** - * @def OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT + * @def OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT_MILLIS * - * Minimum spacing before first retransmission when ACK is not received (RFC7252 default value is 2). + * Minimum spacing before first retransmission when ACK is not received, in milliseconds (RFC7252 default value + * is 2000). * */ -#ifndef OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT -#define OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT 2 +#ifndef OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT_MILLIS +#define OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT_MILLIS 2000 #endif /** diff --git a/src/core/config/openthread-core-config-check.h b/src/core/config/openthread-core-config-check.h index c553356b0..28f5fb12e 100644 --- a/src/core/config/openthread-core-config-check.h +++ b/src/core/config/openthread-core-config-check.h @@ -463,4 +463,9 @@ #error "OPENTHREAD_CONFIG_INITIAL_LOG_LEVEL was replaced by OPENTHREAD_CONFIG_LOG_LEVEL_INIT." #endif +#ifdef OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT +#error \ + "OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT (in seconds) was replaced by OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT_MILLIS (in milliseconds)" +#endif + #endif // OPENTHREAD_CORE_CONFIG_CHECK_H_