| /* |
| * |
| * Copyright (c) 2014-2017 Nest Labs, Inc. |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file implements an automated Test suite for testing |
| * functionalities of WRMP. |
| * |
| */ |
| |
| #define __STDC_FORMAT_MACROS |
| |
| #include <inttypes.h> |
| |
| #include "ToolCommon.h" |
| #include <Weave/Core/WeaveSecurityMgr.h> |
| #include <Weave/Profiles/security/WeaveSecurity.h> |
| #include <Weave/Core/WeaveCore.h> |
| #include <Weave/Profiles/WeaveProfiles.h> |
| #include <Weave/Profiles/common/CommonProfile.h> |
| #include <InetLayer/InetLayer.h> |
| #include <SystemLayer/SystemTimer.h> |
| #include <Weave/Profiles/service-directory/ServiceDirectory.h> |
| #include "TestWRMP.h" |
| |
| #if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| |
| #define TOOL_NAME "TestWRMP" |
| |
| #ifndef WEAVE_CONFIG_SECURITY_TEST_MODE |
| #define WEAVE_CONFIG_SECURITY_TEST_MODE 1 |
| #endif |
| |
| #define TEST_INITIAL_RETRANS_TIMEOUT (5000) |
| #define TEST_ACTIVE_RETRANS_TIMEOUT (2000) |
| #define VerifyOrFail(TST, MSG) \ |
| do { \ |
| if (!(TST)) \ |
| { \ |
| fprintf(stderr, "%s FAILED: ", __FUNCTION__); \ |
| fputs(MSG, stderr); \ |
| exit(EXIT_FAILURE); \ |
| } \ |
| } while (0) |
| |
| #define SuccessOrFail(ERR, MSG) \ |
| do { \ |
| if ((ERR) != WEAVE_NO_ERROR) \ |
| { \ |
| fprintf(stderr, "%s FAILED: ", __FUNCTION__); \ |
| fputs(MSG, stderr); \ |
| fputs(ErrorStr(ERR), stderr); \ |
| fputs("\n", stderr); \ |
| exit(EXIT_FAILURE); \ |
| } \ |
| } while (0) |
| |
| extern uint32_t appContext; |
| |
| static bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg); |
| static bool HandleNonOptionArgs(const char *progName, int argc, char *argv[]); |
| static void HandleEchoRequestReceived(uint64_t nodeId, IPAddress nodeAddr, PacketBuffer *payload); |
| static void HandleEchoResponseReceived(uint64_t nodeId, IPAddress nodeAddr, PacketBuffer *payload); |
| static void HandleAckRcvd(ExchangeContext *ec, void *ctxt); |
| static void HandleDDRcvd(ExchangeContext *ec, uint32_t pauseTime); |
| static void HandleThrottleRcvd(ExchangeContext *ec, uint32_t pauseTime); |
| static void ThrottleTimeout(System::Layer* aSystemLayer, void* aAppState, System::Error aError); |
| static WEAVE_ERROR SendCustomMessage(ExchangeContext *ec, uint32_t ProfileId, uint8_t msgType, uint16_t sendFlags, PacketBuffer *payload, uint32_t *lAppContext = &appContext); |
| static void ParseDestAddress(); |
| |
| using nl::StatusReportStr; |
| using namespace nl::Weave::Profiles::Security; |
| nl::Weave::WeaveExchangeManager *globalExchMgr = 0; |
| uint32_t appContext = 0xcafebabe; |
| uint32_t appContext2 = 0xbaddcafe; |
| uint32_t ThrottlePeriodicMsgCount = 0; |
| uint32_t PeriodicMsgCount = 0; |
| uint32_t DDTestCount = 0; |
| uint32_t ThrottlePauseTime = 2000; |
| uint64_t NodeId = 0xdeadbeefcafebabe; |
| uint64_t FirstDDTestTime = 0; |
| uint64_t SecondDDTestTime = 0; |
| bool isAckRcvd = false; |
| uint8_t ackCount = 0; |
| bool throttleRcvd = false; |
| bool DDRcvd = false; |
| bool FlowThrottled = false; |
| bool ThrottleTimeoutFired = false; |
| bool Listening = false; |
| int32_t MaxEchoCount = 1; |
| int32_t RetransInterval = 0; |
| int32_t MaxAckReceiptInterval = 3000000; |
| int32_t EchoInterval = 1000000; |
| int32_t EchoLength = -1; |
| bool UseTCP = true; |
| bool UsePASE = false; |
| bool UseCASE = false; |
| bool UseGroupKeyEnc = false; |
| bool Debug = false; |
| uint64_t DestNodeId; |
| const char *DestAddr = NULL; |
| uint32_t TestNum = 0; |
| IPAddress DestIPAddr; // only used for UDP |
| uint16_t DestPort; // only used for UDP |
| InterfaceId DestIntf = INET_NULL_INTERFACEID; // only used for UDP |
| uint64_t LastEchoTime = 0; |
| bool WaitingForEchoResp = false; |
| uint64_t EchoCount = 0; |
| uint64_t EchoRespCount = 0; |
| uint64_t CloseECMsgCount = 0; |
| uint8_t EncryptionType = kWeaveEncryptionType_None; |
| uint16_t KeyId = WeaveKeyId::kNone; |
| WRMPTestClient WRMPClient; |
| WRMPTestServer WRMPServer; |
| bool AllowDuplicateMsgs = false; |
| |
| enum |
| { |
| kToolOpt_Listen = 1000, |
| kToolOpt_Count, |
| kToolOpt_AllowDups, |
| }; |
| |
| static OptionDef gToolOptionDefs[] = |
| { |
| { "listen", kNoArgument, kToolOpt_Listen }, |
| { "dest-addr", kArgumentRequired, 'D' }, |
| { "count", kArgumentRequired, kToolOpt_Count }, |
| { "allow-dups", kNoArgument, kToolOpt_AllowDups }, |
| { "test", kArgumentRequired, 'T' }, |
| { "wait", kArgumentRequired, 'W' }, |
| { "retrans", kArgumentRequired, 'R' }, |
| #if WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| { "group-enc", kNoArgument, 'G' }, |
| #endif // WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| { NULL } |
| }; |
| |
| static const char *gToolOptionHelp = |
| " -D, --dest-addr <host>[:<port>][%<interface>]\n" |
| " Send Echo Requests to a specific address rather than one\n" |
| " derived from the destination node id. <host> can be a hostname,\n" |
| " an IPv4 address or an IPv6 address. If <port> is specified, Echo\n" |
| " requests will be sent to the specified port. If <interface> is\n" |
| " specified, Echo Requests will be sent over the specified local\n" |
| " interface.\n" |
| "\n" |
| " NOTE: When specifying a port with an IPv6 address, the IPv6 address\n" |
| " must be enclosed in brackets, e.g. [fd00:0:1:1::1]:11095.\n" |
| "\n" |
| " -T, --test <num>\n" |
| " Execute the corresponding test with the specified number. \n" |
| " TestWRMPTimeoutSolitaryAckReceipt---------------------[1] \n" |
| " TestWRMPTimeoutSolitaryAckReceiptNoInitator-----------[2] \n" |
| " TestWRMPFlushedSolitaryAckReceipt --------------------[3] \n" |
| " TestWRMPPiggybackedAckReceipt-------------------------[4] \n" |
| " TestWRMPRetransmitMessage-----------------------------[5] \n" |
| " TestWRMPTwoStageRetransmitTimeout---------------------[6] \n" |
| " TestWRMPSendThrottleFlowMessage-----------------------[7] \n" |
| " TestWRMPSendDelayedDeliveryMessage--------------------[8] \n" |
| " TestWRMPThrottleFlowBehavior--------------------------[9] \n" |
| " TestWRMPDelayedDeliveryBehavior-----------------------[10] \n" |
| " TestWRMPSendVer2AfterVer1-----------------------------[11] \n" |
| " TestWRMPDuplicateMsgAcking----------------------------[12]\n" |
| " TestWRMPDuplicateMsgLostAck---------------------------[13]\n" |
| " TestWRMPDuplicateMsgAckOnClosedExResponder------------[14]\n" |
| " TestWRMPDuplicateMsgAckOnClosedExInitiator------------[15]\n" |
| " TestWRMPDuplicateMsgDetection-------------------------[16]\n" |
| "\n" |
| " -W, --wait <TestWaitTime>\n" |
| "\n" |
| " -R, --retrans <MaxRetransInterval>\n" |
| "\n" |
| " --count <num>\n" |
| " Send the specified number of Echo Requests and exit.\n" |
| "\n" |
| " --allow-dups\n" |
| " Allow reception of duplicate messages.\n" |
| "\n" |
| " --listen\n" |
| " Listen and respond to Echo Requests sent from another node.\n" |
| "\n" |
| #if WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| " -G, --group-enc\n" |
| " Use a group key to encrypt messages.\n" |
| " When group key encryption option is chosen the key id should be also specified.\n" |
| " Below are two examples how group key id can be specified:\n" |
| " --group-enc-key-id 0x00005536\n" |
| " --group-enc-key-type r --group-enc-root-key c --group-enc-epoch-key-num 2 --group-enc-app-key-num 54\n" |
| " Note that both examples describe the same rotating group key derived from client\n" |
| " root key, epoch key number 4 and app group master key number 54 (0x36).\n" |
| "\n" |
| #endif // WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| ; |
| |
| static OptionSet gToolOptions = |
| { |
| HandleOption, |
| gToolOptionDefs, |
| "GENERAL OPTIONS", |
| gToolOptionHelp |
| }; |
| |
| static HelpOptions gHelpOptions( |
| TOOL_NAME, |
| "Usage: " TOOL_NAME " [<options...>] <dest-node-id>[@<dest-host>[:<dest-port>][%%<interface>]]\n" |
| " " TOOL_NAME " [<options...>] --listen\n", |
| WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT |
| ); |
| |
| static OptionSet *gToolOptionSets[] = |
| { |
| &gToolOptions, |
| &gNetworkOptions, |
| &gWeaveNodeOptions, |
| &gGroupKeyEncOptions, |
| &gFaultInjectionOptions, |
| &gHelpOptions, |
| NULL |
| }; |
| |
| void PrepareNewBuf(PacketBuffer **buf) |
| { |
| *buf = PacketBuffer::New(); |
| if (*buf == NULL) |
| { |
| printf("Unable to allocate PacketBuffer\n"); |
| LastEchoTime = Now(); |
| return; |
| } |
| |
| char *p = (char *) (*buf)->Start(); |
| int32_t len = sprintf(p, "WRMP Echo Message %" PRIu64 "\n", EchoCount); |
| |
| if (EchoLength > (*buf)->MaxDataLength()) |
| EchoLength = (*buf)->MaxDataLength(); |
| |
| if (EchoLength != -1) |
| { |
| if (len > EchoLength) |
| len = EchoLength; |
| else |
| while (len < EchoLength) |
| { |
| int32_t copyLen = EchoLength - len; |
| if (copyLen > len) |
| copyLen = len; |
| memcpy(p + len, p, copyLen); |
| len += copyLen; |
| } |
| } |
| |
| (*buf)->SetDataLength((uint16_t) len); |
| |
| } |
| |
| static bool IsRetransOutsideWindow(uint64_t transmitTime, uint32_t retransTimeout) |
| { |
| int32_t AckReceiptBufferTime = 600 * System::kTimerFactor_micro_per_milli; // 600 msec |
| |
| return (Now() < (transmitTime + retransTimeout * System::kTimerFactor_micro_per_milli - AckReceiptBufferTime) || |
| Now() > (transmitTime + retransTimeout * System::kTimerFactor_micro_per_milli + AckReceiptBufferTime)); |
| } |
| |
| //Send Echo Request |
| //Wait for Ack piggybacked on Echo Response |
| testStatus_t TestWRMPPiggybackedAckReceipt(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| PrepareNewBuf(&payloadBuf); |
| //Prepare Echo Request |
| isAckRcvd = false; |
| Done = false; |
| LastEchoTime = Now(); |
| |
| //Set the retrans timeout |
| if (RetransInterval) |
| { |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| } |
| |
| err = WRMPClient.SendEchoRequest(payloadBuf); |
| if (err == WEAVE_NO_ERROR) |
| { |
| WaitingForEchoResp = true; |
| EchoCount++; |
| } |
| else |
| { |
| printf("WRMPTestClient.SendEchoRequest() failed: %s\n", ErrorStr(err)); |
| } |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) { |
| if (Now() < LastEchoTime + MaxAckReceiptInterval + RetransInterval) |
| { |
| if (isAckRcvd && !WaitingForEchoResp) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| continue; |
| } |
| } |
| |
| if (isAckRcvd && !WaitingForEchoResp) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| printf("No response received\n"); |
| WaitingForEchoResp = false; |
| Done = true; |
| return TEST_FAIL; |
| } |
| } |
| } |
| return TEST_FAIL; |
| } |
| |
| //Send message that does not solicit reply |
| //Allow recipient to Ack timeout and send ack back |
| testStatus_t TestWRMPTimeoutSolitaryAckReceipt(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| uint16_t sendFlags = ExchangeContext::kSendFlag_RequestAck; |
| PrepareNewBuf(&payloadBuf); |
| //Prepare Echo Request |
| isAckRcvd = false; |
| Done = false; |
| LastEchoTime = Now(); |
| |
| //Set the retrans timeout |
| if (RetransInterval) |
| { |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| } |
| |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_No_Response, sendFlags, payloadBuf); |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| Done = true; |
| return TEST_FAIL; |
| } |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) { |
| if (Now() < LastEchoTime + MaxAckReceiptInterval + RetransInterval) |
| { |
| if (isAckRcvd) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| continue; |
| } |
| } |
| |
| if (isAckRcvd) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| Done = true; |
| return TEST_FAIL; |
| } |
| |
| } |
| } |
| return TEST_FAIL; |
| } |
| |
| //Send message without the initiator flag. |
| //The responder will drop it because it won't find a matching EC, but |
| //it should still send back an ACK. |
| //For example, this scenario happens when a responder sends a response after |
| //the EC has timedout on the initiator. |
| testStatus_t TestWRMPTimeoutSolitaryAckReceiptNoInitiator(void) |
| { |
| testStatus_t testStatus = TEST_PASS; |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| uint16_t sendFlags = ExchangeContext::kSendFlag_RequestAck; |
| PrepareNewBuf(&payloadBuf); |
| //Prepare Echo Request |
| isAckRcvd = false; |
| Done = false; |
| LastEchoTime = Now(); |
| |
| //Set the retrans timeout to a well known value |
| RetransInterval = 10000; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| |
| WRMPClient.ExchangeCtx->SetInitiator(false); |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_No_Response, sendFlags, payloadBuf); |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| Done = true; |
| testStatus = TEST_FAIL; |
| goto exit; |
| } |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) { |
| if (Now() < LastEchoTime + (RetransInterval - 1000) * 1000) |
| { |
| // We want an ACK on the first transmission |
| if (isAckRcvd) |
| { |
| testStatus = TEST_PASS; |
| break; |
| } |
| else |
| { |
| continue; |
| } |
| } |
| |
| Done = true; |
| testStatus = TEST_FAIL; |
| } |
| } |
| |
| exit: |
| WRMPClient.ExchangeCtx->SetInitiator(true); |
| return testStatus; |
| } |
| |
| //Send 2 back-to-back messages that require no response from recipient application. |
| //Receiving Exchange layer would replace first pending Ack with the second |
| //one and flush the first by sending a solitary Ack back. |
| testStatus_t TestWRMPFlushedSolitaryAckReceipt(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| uint16_t sendFlags = ExchangeContext::kSendFlag_RequestAck; |
| PrepareNewBuf(&payloadBuf); |
| //Prepare Echo Request |
| isAckRcvd = false; |
| Done = false; |
| LastEchoTime = Now(); |
| |
| //Set the retrans timeout |
| if (RetransInterval) |
| { |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| } |
| |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_No_Response, sendFlags, payloadBuf); |
| if (err == WEAVE_NO_ERROR) |
| { |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_No_Response, sendFlags, payloadBuf); |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| Done = true; |
| return TEST_FAIL; |
| } |
| } |
| else |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| } |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) { |
| if (Now() < LastEchoTime + MaxAckReceiptInterval + RetransInterval) |
| { |
| if (isAckRcvd) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| continue; |
| } |
| } |
| |
| if (isAckRcvd) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| Done = true; |
| return TEST_FAIL; |
| } |
| } |
| } |
| return TEST_FAIL; |
| } |
| |
| //Formulate message and then drop it at Message layer |
| //Then timeout and retransmit and wait for ack |
| testStatus_t TestWRMPRetransmitMessage(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| PrepareNewBuf(&payloadBuf); |
| //Prepare Echo Request |
| isAckRcvd = false; |
| Done = false; |
| LastEchoTime = Now(); |
| |
| //Set the retrans timeout |
| if (RetransInterval) |
| { |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| } |
| |
| WRMPClient.ExchangeMgr->MessageLayer->mDropMessage = true; |
| |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_Generate_Response, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| Done = true; |
| return TEST_FAIL; |
| } |
| WRMPClient.ExchangeMgr->MessageLayer->mDropMessage = false; |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) { |
| if (Now() < LastEchoTime + MaxAckReceiptInterval + RetransInterval) |
| { |
| if (isAckRcvd) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| continue; |
| } |
| } |
| |
| if (isAckRcvd) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| Done = true; |
| return TEST_FAIL; |
| } |
| |
| } |
| } |
| return TEST_FAIL; |
| } |
| |
| //Force retransmissions while sending a message twice on the same exchange and |
| //verify that the times of receipt of Acks conform to the 2 stage retransmit |
| //timeouts. |
| testStatus_t TestWRMPTwoStageRetransmitTimeout(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| int32_t MaxTestInterval = (TEST_INITIAL_RETRANS_TIMEOUT + TEST_ACTIVE_RETRANS_TIMEOUT) * |
| System::kTimerFactor_micro_per_milli + |
| System::kTimerFactor_micro_per_unit; // extra 1 second |
| uint64_t FirstTransmitTime = 0; |
| uint64_t SecondTransmitTime = 0; |
| PacketBuffer *payloadBuf = NULL; |
| PrepareNewBuf(&payloadBuf); |
| isAckRcvd = false; |
| int twoStageAckCount = 0; |
| Done = false; |
| |
| //Set the initial and active retrans timeout |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = TEST_INITIAL_RETRANS_TIMEOUT; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = TEST_ACTIVE_RETRANS_TIMEOUT; |
| |
| WRMPClient.ExchangeMgr->MessageLayer->mDropMessage = true; |
| |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_Generate_Response, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| FirstTransmitTime = Now(); |
| |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| Done = true; |
| return TEST_FAIL; |
| } |
| |
| WRMPClient.ExchangeMgr->MessageLayer->mDropMessage = false; |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) { |
| if (Now() < FirstTransmitTime + MaxTestInterval) |
| { |
| if (isAckRcvd) |
| { |
| twoStageAckCount++; |
| |
| // Time check for first Ack |
| if (twoStageAckCount == 1 && IsRetransOutsideWindow(FirstTransmitTime, TEST_INITIAL_RETRANS_TIMEOUT)) |
| { |
| return TEST_FAIL; |
| } |
| |
| if (twoStageAckCount == 1) |
| { |
| // Reset isAckRcvd for second Ack reception |
| isAckRcvd = false; |
| |
| // Reset the data length of the bufer |
| PrepareNewBuf(&payloadBuf); |
| payloadBuf->SetDataLength(0); |
| |
| // Drop message to force retransmit |
| WRMPClient.ExchangeMgr->MessageLayer->mDropMessage = true; |
| |
| // Send the second message which should have the updated |
| // active retransmit timeout. |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, |
| kWeaveTestMessageType_Generate_Response, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| |
| // Update last transmit time |
| SecondTransmitTime = Now(); |
| |
| WRMPClient.ExchangeMgr->MessageLayer->mDropMessage = false; |
| |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| return TEST_FAIL; |
| } |
| |
| continue; |
| } |
| |
| // Time check for second Ack |
| if (twoStageAckCount == 2 && IsRetransOutsideWindow(SecondTransmitTime, TEST_ACTIVE_RETRANS_TIMEOUT)) |
| { |
| return TEST_FAIL; |
| } |
| |
| return TEST_PASS; |
| } |
| else |
| { |
| continue; |
| } |
| } |
| |
| Done = true; |
| } |
| } |
| |
| return TEST_FAIL; |
| } |
| |
| testStatus_t TestWRMPSendThrottleFlowMessage(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| PrepareNewBuf(&payloadBuf); |
| //Prepare Echo Request |
| isAckRcvd = false; |
| Done = false; |
| LastEchoTime = Now(); |
| |
| //Set the retrans timeout |
| if (RetransInterval) |
| { |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| } |
| |
| //Request a Throttle message |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_Request_Throttle, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| Done = true; |
| return TEST_FAIL; |
| } |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) |
| { |
| if (Now() < LastEchoTime + MaxAckReceiptInterval + RetransInterval) |
| { |
| if (throttleRcvd) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| continue; |
| } |
| } |
| |
| if (throttleRcvd) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| Done = true; |
| return TEST_FAIL; |
| } |
| } |
| } |
| return TEST_FAIL; |
| } |
| |
| //Send periodic messages to peer prompting a throttle message from Peer |
| //Start a timer for the throttle time and check on expiry that no messages |
| //were transmitted during that time. |
| testStatus_t TestWRMPThrottleFlowBehavior(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| PrepareNewBuf(&payloadBuf); |
| //Prepare Echo Request |
| isAckRcvd = false; |
| Done = false; |
| LastEchoTime = Now(); |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_Request_Periodic, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| Done = true; |
| return TEST_FAIL; |
| } |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) |
| { |
| if (ThrottleTimeoutFired) |
| { |
| //Allow the chance of a second periodic message being sent |
| if (PeriodicMsgCount <= 2) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| return TEST_FAIL; |
| } |
| } |
| } |
| } |
| return TEST_FAIL; |
| } |
| |
| //Send a Request for a Delayed Delivery and check on receipt |
| testStatus_t TestWRMPSendDelayedDeliveryMessage(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| PrepareNewBuf(&payloadBuf); |
| //Prepare Echo Request |
| DDRcvd = false; |
| Done = false; |
| LastEchoTime = Now(); |
| |
| //Set the retrans timeout |
| if (RetransInterval) |
| { |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| } |
| |
| //Kick off the Throttle test by sending a periodic response probe |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_Request_DD, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| Done = true; |
| return TEST_FAIL; |
| } |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) |
| { |
| if (Now() < LastEchoTime + MaxAckReceiptInterval + RetransInterval) |
| { |
| if (DDRcvd) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| continue; |
| } |
| } |
| |
| if (DDRcvd) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| Done = true; |
| return TEST_FAIL; |
| } |
| } |
| } |
| return TEST_FAIL; |
| } |
| |
| testStatus_t TestWRMPDelayedDeliveryBehavior(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| PrepareNewBuf(&payloadBuf); |
| //Prepare Echo Request |
| DDRcvd = false; |
| Done = false; |
| LastEchoTime = Now(); |
| |
| //Kick off the Throttle test by sending a periodic response probe |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_DD_Test, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| Done = true; |
| return TEST_FAIL; |
| } |
| //Set Drop Ack |
| WRMPClient.ExchangeCtx->SetDropAck(true); |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) |
| { |
| if (SecondDDTestTime) |
| { |
| //Allow the chance of a second periodic message being sent |
| printf("Delay is %" PRIx64 "\n", (SecondDDTestTime - FirstDDTestTime)/1000); |
| if ((SecondDDTestTime - FirstDDTestTime) >= (ThrottlePauseTime * 1000)) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| return TEST_FAIL; |
| } |
| } |
| else if (Now() > LastEchoTime + MaxAckReceiptInterval + RetransInterval + ThrottlePauseTime * 1000) |
| { |
| return TEST_FAIL; |
| } |
| } |
| } |
| return TEST_FAIL; |
| } |
| |
| testStatus_t TestWRMPSendVer2AfterVer1(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| PrepareNewBuf(&payloadBuf); |
| |
| //Kick off the Throttle test by sending a periodic response probe |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_No_Response, 0, |
| payloadBuf); |
| //err = WRMPClient.SendEchoRequest(payloadBuf); |
| if (err == WEAVE_NO_ERROR) |
| { |
| PrepareNewBuf(&payloadBuf); |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_No_Response, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| if (err != WEAVE_ERROR_WRONG_MSG_VERSION_FOR_EXCHANGE) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| return TEST_FAIL; |
| } |
| else |
| { |
| printf("Received expected error %d while trying to send a version 2 message on a version 1 Exchange\n", err); |
| return TEST_PASS; |
| } |
| } |
| else |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| return TEST_FAIL; |
| } |
| } |
| |
| testStatus_t TestWRMPDuplicateMsgAcking(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| uint8_t *p = NULL; |
| uint16_t len = 0; |
| Done = false; |
| isAckRcvd = false; |
| ackCount = 0; |
| LastEchoTime = Now(); |
| |
| |
| PrepareNewBuf(&payloadBuf); |
| |
| //Form the message |
| p = payloadBuf->Start(); |
| len = sprintf((char *)p, "Dup Msg Detection"); |
| memcpy(p + len, p, len); |
| payloadBuf->SetDataLength(len); |
| |
| //Set the retrans timeout |
| if (RetransInterval) |
| { |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| } |
| |
| //Send the first message and then immediately send a duplicate message. |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_No_Response, |
| ExchangeContext::kSendFlag_RequestAck | ExchangeContext::kSendFlag_RetainBuffer, |
| payloadBuf); |
| if (err == WEAVE_NO_ERROR) |
| { |
| payloadBuf->SetStart(p); |
| payloadBuf->SetDataLength(len); |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_No_Response, |
| ExchangeContext::kSendFlag_RequestAck | ExchangeContext::kSendFlag_ReuseMessageId, |
| payloadBuf); |
| |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| return TEST_FAIL; |
| } |
| } |
| else |
| { |
| printf("WRMPTestClient.SendCustomMessage failed: %s\n", ErrorStr(err)); |
| return TEST_FAIL; |
| } |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) |
| { |
| if (Now() < LastEchoTime + MaxAckReceiptInterval + RetransInterval) |
| { |
| if (isAckRcvd && ackCount == 2) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| continue; |
| } |
| } |
| |
| if (isAckRcvd && ackCount == 2) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| Done = true; |
| return TEST_FAIL; |
| } |
| } |
| } |
| |
| return TEST_FAIL; |
| } |
| |
| // Test lost ack scenario. |
| // |
| // Steps for Initiator (I) and Responder (R) of the exchange: |
| // - I sends SetDropAck msg. |
| // - R receives SetDropAck msg: it sets DropAck flag so the next received msg is not acked. |
| // - I sends LostAck msg. |
| // - R receives CLostAck msg: it clears DropAck flag so the next received msg is acked. |
| // - I retransmits CloseEC msg because it didn't receive ack. |
| // - R receives retransmission of the CloseEC msg: the ack is sent. |
| // - I receives ack for the CloseEC msg. |
| testStatus_t TestWRMPDuplicateMsgLostAck(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| uint8_t *p = NULL; |
| uint16_t len = 0; |
| |
| Done = false; |
| ackCount = 0; |
| LastEchoTime = Now(); |
| |
| // Set the retrans timeout. |
| if (RetransInterval) |
| { |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| } |
| |
| // Form SetDropAck messages. |
| PrepareNewBuf(&payloadBuf); |
| p = payloadBuf->Start(); |
| len = sprintf((char *)p, "Dup Detection SetDropAck Msg"); |
| payloadBuf->SetDataLength(len); |
| |
| // Send this message with command to drop ack for the next received message. |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_SetDropAck, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| SuccessOrFail(err, "WRMPTestClient.SendCustomMessage failed to send SetDropAck message\n"); |
| |
| // Form LostAck messages. |
| PrepareNewBuf(&payloadBuf); |
| p = payloadBuf->Start(); |
| len = sprintf((char *)p, "Dup Detection LostAck Msg"); |
| payloadBuf->SetDataLength(len); |
| |
| // Send second message. |
| // The receiver of this message should drop ack and then ack retransmitted message. |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_Lost_Ack, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf, &appContext2); |
| SuccessOrFail(err, "WRMPTestClient.SendCustomMessage failed to send LostAck message\n"); |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) |
| { |
| if (ackCount == 2) |
| { |
| return TEST_PASS; |
| } |
| |
| if (Now() >= LastEchoTime + MaxAckReceiptInterval + RetransInterval) |
| { |
| return TEST_FAIL; |
| } |
| } |
| } |
| |
| return TEST_FAIL; |
| } |
| |
| // Responder receives duplicate message on a closed exchange and it should ack the message. |
| // |
| // Steps for Initiator (I) and Responder (R) of the exchange: |
| // - I sends SetDropAck msg. |
| // - R receives SetDropAck msg: it sets DropAck flag so the next received msg is not acked. |
| // - I sends CloseEC msg. |
| // - R receives CloseEC msg: it clears DropAck flag so the next received msg is acked |
| // and it closes the exchange. |
| // - I retransmits CloseEC msg because it didn't receive ack. |
| // - R receives retransmission of the CloseEC msg: it is detected as a duplicate for which the |
| // new exchange is created to send the ack and that exchange is immediately closed. |
| // - I receives ack for the CloseEC msg. |
| testStatus_t TestWRMPDuplicateMsgAckOnClosedExResponder(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| uint8_t *p = NULL; |
| uint16_t len = 0; |
| |
| Done = false; |
| ackCount = 0; |
| LastEchoTime = Now(); |
| |
| // Set the retrans timeout. |
| if (RetransInterval) |
| { |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| } |
| |
| // Form SetDropAck messages. |
| PrepareNewBuf(&payloadBuf); |
| p = payloadBuf->Start(); |
| len = sprintf((char *)p, "Dup Detection SetDropAck Msg"); |
| payloadBuf->SetDataLength(len); |
| |
| // Send this message with command to drop ack for the next received message. |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_SetDropAck, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| SuccessOrFail(err, "WRMPTestClient.SendCustomMessage failed to send SetDropAck message\n"); |
| |
| // Form CloseEC messages. |
| PrepareNewBuf(&payloadBuf); |
| p = payloadBuf->Start(); |
| len = sprintf((char *)p, "Dup Detection CloseEC Msg"); |
| payloadBuf->SetDataLength(len); |
| |
| // Send second message. |
| // The receiver of this message should drop ack and close exchange context. |
| // The receiver then should receive retransmission of this message, for which a new EC |
| // will be created and closed only to send ack. |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_CloseEC, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf, &appContext2); |
| SuccessOrFail(err, "WRMPTestClient.SendCustomMessage failed to send LostAck message\n"); |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) |
| { |
| if (ackCount == 2) |
| { |
| return TEST_PASS; |
| } |
| |
| VerifyOrFail(Now() < LastEchoTime + MaxAckReceiptInterval + RetransInterval, |
| "TestWRMPDuplicateMsgAckOnClosedExResponder FAILED\n"); |
| } |
| } |
| |
| return TEST_FAIL; |
| } |
| |
| // Initiator receives duplicate message on a closed exchange and it should ack the message. |
| // |
| // Steps for Initiator (I) and Responder (R) of the exchange: |
| // - I sends RequestCloseEC msg. It also sets DropAck flag so the next received msg |
| // from R is not acked. |
| // - R receives RequestCloseEC msg: it sends CloseEC msg. |
| // - I receives CloseEC msg: it clears DropAck flag so the next received msg is acked and it |
| // closes the exchange. |
| // - R retransmits CloseEC msg because it didn't receive ack. |
| // - I receives retransmission of the CloseEC msg: it is detected as a duplicate for which the |
| // new exchange is created to send the ack and that exchange is immediately closed. |
| // - R receives ack for the CloseEC msg. |
| testStatus_t TestWRMPDuplicateMsgAckOnClosedExInitiator(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| uint8_t *p = NULL; |
| uint16_t len = 0; |
| |
| Done = false; |
| ackCount = 0; |
| LastEchoTime = Now(); |
| |
| // Set the retrans timeout. |
| if (RetransInterval) |
| { |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| } |
| |
| // Set DropAck flag. |
| WRMPClient.ExchangeCtx->SetDropAck(true); |
| |
| // Form RequestCloseEC message. |
| PrepareNewBuf(&payloadBuf); |
| p = payloadBuf->Start(); |
| len = sprintf((char *)p, "Dup Detection RequestCloseEC Msg"); |
| payloadBuf->SetDataLength(len); |
| |
| // Send RequestCloseEC message. |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_RequestCloseEC, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| SuccessOrFail(err, "WRMPTestClient.SendCustomMessage failed to send RequestCloseEC message\n"); |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) |
| { |
| if (Now() > LastEchoTime + MaxAckReceiptInterval + RetransInterval) |
| { |
| if ((ackCount == 1) && (CloseECMsgCount == 1)) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| printf("TestWRMPDuplicateMsgAckOnClosedExInitiator FAILED\n"); |
| return TEST_FAIL; |
| } |
| } |
| } |
| } |
| |
| return TEST_FAIL; |
| } |
| |
| // Test duplicate message detection mechanism. |
| testStatus_t TestWRMPDuplicateMsgDetection(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer *payloadBuf = NULL; |
| uint8_t *p = NULL; |
| uint16_t len = 0; |
| uint16_t msgType; |
| uint64_t expectedEchoRespCount; |
| |
| Done = false; |
| EchoRespCount = 0; |
| LastEchoTime = Now(); |
| |
| // Set the retrans timeout. |
| if (RetransInterval) |
| { |
| WRMPClient.ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = RetransInterval; |
| WRMPClient.ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = RetransInterval; |
| } |
| |
| // Form AllowDup/DontAllowDup message. |
| PrepareNewBuf(&payloadBuf); |
| p = payloadBuf->Start(); |
| len = sprintf((char *)p, "Dup Detection Set Allow Dup Flag Msg"); |
| payloadBuf->SetDataLength(len); |
| |
| // Set test message type. |
| msgType = AllowDuplicateMsgs ? kWeaveTestMessageType_AllowDup : kWeaveTestMessageType_DontAllowDup; |
| |
| // Sent this message to enable/disable duplicate message on the related peer exchange. |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, msgType, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| SuccessOrFail(err, "WRMPTestClient.SendCustomMessage failed to send AllowDup/DontAllowDup message\n"); |
| |
| // Form SetDropAck messages. |
| PrepareNewBuf(&payloadBuf); |
| p = payloadBuf->Start(); |
| len = sprintf((char *)p, "Dup Detection SetDropAck Msg"); |
| payloadBuf->SetDataLength(len); |
| |
| // Send this message with command to drop ack for the next received message. |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_SetDropAck, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| SuccessOrFail(err, "WRMPTestClient.SendCustomMessage failed to send SetDropAck message\n"); |
| |
| for (uint8_t i = 0; i < MaxEchoCount; i++) |
| { |
| // Form EchoRequest message. |
| PrepareNewBuf(&payloadBuf); |
| p = payloadBuf->Start(); |
| len = sprintf((char *)p, "Dup Detection Send Echo Request Msg"); |
| payloadBuf->SetDataLength(len - MaxEchoCount + i); |
| |
| if (i % 2 == 0) |
| { |
| // Send this message with command to drop ack for the next received message. |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_EchoRequestForDup, |
| ExchangeContext::kSendFlag_RequestAck, payloadBuf); |
| SuccessOrFail(err, "WRMPTestClient.SendCustomMessage failed to send EchoRequestForDup message\n"); |
| } |
| else |
| { |
| // Send an Echo Request message. |
| err = WRMPClient.SendEchoRequest(payloadBuf, 0); |
| SuccessOrFail(err, "WRMPTestClient.SendEchoRequest failed to send EchoRequest message\n"); |
| } |
| } |
| |
| // Form ClearDropAck messages. |
| PrepareNewBuf(&payloadBuf); |
| p = payloadBuf->Start(); |
| len = sprintf((char *)p, "Dup Detection ClearDropAck Msg"); |
| payloadBuf->SetDataLength(len); |
| |
| // Send this message with command to drop ack for the next received message. |
| err = SendCustomMessage(WRMPClient.ExchangeCtx, kWeaveProfile_Test, kWeaveTestMessageType_ClearDropAck, 0, payloadBuf); |
| SuccessOrFail(err, "WRMPTestClient.SendCustomMessage failed to send ClearDropAck message\n"); |
| |
| if (UseGroupKeyEnc || MaxEchoCount < 16) |
| { |
| if (AllowDuplicateMsgs) |
| expectedEchoRespCount = MaxEchoCount; |
| else |
| expectedEchoRespCount = MaxEchoCount / 2; |
| } |
| // Unencrypted messages that fall before the reorder window (last 16 ids) is treated as |
| // new messages that cause the window to reset. Therefore, such message is not detected |
| // as duplicate and echo response is not sent for that message in out test scenario. |
| else |
| { |
| expectedEchoRespCount = MaxEchoCount / 2; |
| } |
| |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| if (!Listening) |
| { |
| if (Now() > LastEchoTime + MaxAckReceiptInterval + 5 * RetransInterval) |
| { |
| printf("\nEchoRespCount = %" PRIu64 "; Expected EchoRespCount = %" PRIu64 "\n\n", EchoRespCount, expectedEchoRespCount); |
| if (EchoRespCount == expectedEchoRespCount) |
| { |
| return TEST_PASS; |
| } |
| else |
| { |
| return TEST_FAIL; |
| } |
| } |
| } |
| } |
| |
| return TEST_FAIL; |
| } |
| |
| struct Tests { |
| testStatus_t (*mTest)(void); |
| const char * mTestName; |
| }; |
| |
| Tests gTests[] = |
| { |
| { .mTest = TestWRMPTimeoutSolitaryAckReceipt, .mTestName = "TestWRMPTimeoutSolitaryAckReceipt" }, |
| { .mTest = TestWRMPTimeoutSolitaryAckReceiptNoInitiator, .mTestName = "TestWRMPTimeoutSolitaryAckReceiptNoInitiator," }, |
| { .mTest = TestWRMPFlushedSolitaryAckReceipt, .mTestName = "TestWRMPFlushedSolitaryAckReceipt" }, |
| { .mTest = TestWRMPPiggybackedAckReceipt, .mTestName = "TestWRMPPiggybackedAckReceipt" }, |
| { .mTest = TestWRMPRetransmitMessage, .mTestName = "TestWRMPRetransmitMessage" }, |
| { .mTest = TestWRMPTwoStageRetransmitTimeout, .mTestName = "TestWRMPTwoStageRetransmitTimeout" }, |
| { .mTest = TestWRMPSendThrottleFlowMessage, .mTestName = "TestWRMPSendThrottleFlowMessage" }, |
| { .mTest = TestWRMPSendDelayedDeliveryMessage, .mTestName = "TestWRMPSendDelayedDeliveryMessage" }, |
| { .mTest = TestWRMPThrottleFlowBehavior, .mTestName = "TestWRMPThrottleFlowBehavior" }, |
| { .mTest = TestWRMPDelayedDeliveryBehavior, .mTestName = "TestWRMPDelayedDeliveryBehavior" }, |
| { .mTest = TestWRMPSendVer2AfterVer1, .mTestName = "TestWRMPSendVer2AfterVer1" }, |
| { .mTest = TestWRMPDuplicateMsgAcking, .mTestName = "TestWRMPDuplicateMsgAcking" }, |
| { .mTest = TestWRMPDuplicateMsgLostAck, .mTestName = "TestWRMPDuplicateMsgLostAck" }, |
| { .mTest = TestWRMPDuplicateMsgAckOnClosedExResponder, .mTestName = "TestWRMPDuplicateMsgAckOnClosedExResponder" }, |
| { .mTest = TestWRMPDuplicateMsgAckOnClosedExInitiator, .mTestName = "TestWRMPDuplicateMsgAckOnClosedExInitiator" }, |
| { .mTest = TestWRMPDuplicateMsgDetection, .mTestName = "TestWRMPDuplicateMsgDetection" } |
| }; |
| |
| #endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| |
| int main(int argc, char *argv[]) |
| { |
| #if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| testStatus_t res = TEST_FAIL; |
| //+++++++++++++Initialization+++++++++++++++// |
| |
| if (argc == 1) |
| { |
| gHelpOptions.PrintBriefUsage(stderr); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (!ParseArgsFromEnvVar(TOOL_NAME, TOOL_OPTIONS_ENV_VAR_NAME, gToolOptionSets, NULL, true) || |
| !ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets, HandleNonOptionArgs)) |
| { |
| exit(EXIT_FAILURE); |
| } |
| |
| if (UseGroupKeyEnc) |
| { |
| EncryptionType = kWeaveEncryptionType_AES128CTRSHA1; |
| KeyId = gGroupKeyEncOptions.GetEncKeyId(); |
| if (KeyId == WeaveKeyId::kNone) |
| { |
| PrintArgError("%s: Please specify a group encryption key id using the --group-enc-... options.\n", TOOL_NAME); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| UseStdoutLineBuffering(); |
| SetSIGUSR1Handler(); |
| |
| if (gNetworkOptions.LocalIPv6Addr != IPAddress::Any) |
| { |
| if (!gNetworkOptions.LocalIPv6Addr.IsIPv6ULA()) |
| { |
| printf("ERROR: Local address must be an IPv6 ULA\n"); |
| exit(EXIT_FAILURE); |
| } |
| |
| gWeaveNodeOptions.FabricId = gNetworkOptions.LocalIPv6Addr.GlobalId(); |
| gWeaveNodeOptions.LocalNodeId = IPv6InterfaceIdToWeaveNodeId(gNetworkOptions.LocalIPv6Addr.InterfaceId()); |
| gWeaveNodeOptions.SubnetId = gNetworkOptions.LocalIPv6Addr.Subnet(); |
| } |
| |
| InitSystemLayer(); |
| |
| InitNetwork(); |
| |
| InitWeaveStack(true, true); |
| |
| // Arrange to get called for various activity in the message layer. |
| MessageLayer.OnReceiveError = HandleMessageReceiveError; |
| |
| if (!Listening) |
| { |
| globalExchMgr = &ExchangeMgr; |
| if (DestAddr != NULL) |
| ParseDestAddress(); |
| // Initialize the EchoClient application. |
| err = WRMPClient.Init(&ExchangeMgr, DestNodeId, DestIPAddr, DestPort, DestIntf); |
| if (err != WEAVE_NO_ERROR) |
| { |
| printf("WRMPTestClient.Init failed: %s\n", ErrorStr(err)); |
| exit(EXIT_FAILURE); |
| } |
| |
| // Arrange to get a callback whenever an Echo Response is received. |
| WRMPClient.OnEchoResponseReceived = HandleEchoResponseReceived; |
| } |
| else |
| { |
| globalExchMgr = &ExchangeMgr; |
| // Initialize the EchoServer application. |
| err = WRMPServer.Init(&ExchangeMgr); |
| if (err) |
| { |
| printf("WRMPTestServer.Init failed: %s\n", ErrorStr(err)); |
| exit(EXIT_FAILURE); |
| } |
| |
| // Arrange to get a callback whenever an Echo Request is received. |
| WRMPServer.OnEchoRequestReceived = HandleEchoRequestReceived; |
| |
| //SecurityMgr.OnSessionEstablished = HandleSecureSessionEstablished; |
| //SecurityMgr.OnSessionError = HandleSecureSessionError; |
| } |
| |
| PrintNodeConfig(); |
| |
| if (!Listening) |
| { |
| if (DestNodeId == 0) |
| printf("Sending WRMP Messages to node at %s\n", DestAddr); |
| else if (DestAddr == NULL) |
| printf("Sending WRMP Messages to node %" PRIX64 "\n", DestNodeId); |
| else |
| printf("Sending WRMP Messages to node %" PRIX64 " at %s\n", DestNodeId, DestAddr); |
| |
| TestNum --; |
| if (TestNum >= (sizeof(gTests) / sizeof(Tests))) |
| { |
| |
| printf("Wrong WRMP Test Num %d\n", (TestNum+1)); |
| printf("Should be one of set of Tests below\n"); |
| size_t numTests = (sizeof(gTests) / sizeof(Tests)); |
| for (size_t testIndex = 0; testIndex < numTests; testIndex++) |
| { |
| printf("%-55s [%2zu]\n", gTests[testIndex].mTestName, testIndex+1); |
| } |
| exit(EXIT_FAILURE); |
| } |
| else |
| { |
| res = gTests[TestNum].mTest(); |
| printf("%s %s\n", gTests[TestNum].mTestName, res == TEST_PASS ? "Passed" : "Failed"); |
| } |
| } |
| else |
| { |
| printf("Listening for WRMP Messages...\n"); |
| } |
| if (Listening) |
| { |
| while (!Done) |
| { |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| |
| ServiceNetwork(sleepTime); |
| |
| } |
| } |
| WRMPClient.Shutdown(); |
| WRMPServer.Shutdown(); |
| ShutdownWeaveStack(); |
| ShutdownNetwork(); |
| ShutdownSystemLayer(); |
| if (res == TEST_PASS) |
| return 0; |
| else |
| return -1; |
| #endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| } |
| |
| #if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| void HandleEchoRequestReceived(uint64_t nodeId, |
| IPAddress nodeAddr, |
| PacketBuffer *payload) |
| { |
| if (Listening) |
| { |
| char ipAddrStr[64]; |
| nodeAddr.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| |
| printf("WRMP Echo Request from node %" PRIX64 " (%s): len=%u ... sending response.\n", |
| nodeId, ipAddrStr, |
| payload->DataLength()); |
| |
| if (Debug) |
| DumpMemory(payload->Start(), payload->DataLength(), " ", 16); |
| } |
| } |
| |
| void HandleEchoResponseReceived(uint64_t nodeId, |
| IPAddress nodeAddr, |
| PacketBuffer *payload) |
| { |
| uint32_t respTime = Now(); |
| uint32_t transitTime = respTime - LastEchoTime; |
| |
| WaitingForEchoResp = false; |
| EchoRespCount++; |
| |
| char ipAddrStr[64]; |
| nodeAddr.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| |
| printf("WRMP Echo Response from node %" PRIX64 " (%s): %" PRIu64 "/%" PRIu64 "(%.2f%%) len=%u time=%.3fms\n", |
| nodeId, ipAddrStr, |
| EchoRespCount, EchoCount, ((double) EchoRespCount) * 100 / EchoCount, |
| payload->DataLength(), |
| ((double) transitTime) / 1000); |
| |
| if (Debug) |
| DumpMemory(payload->Start(), payload->DataLength(), " ", 16); |
| } |
| |
| bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg) |
| { |
| switch (id) |
| { |
| #if WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| case 'G': |
| UseGroupKeyEnc = true; |
| break; |
| #endif // WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| case kToolOpt_AllowDups: |
| AllowDuplicateMsgs = true; |
| break; |
| case kToolOpt_Listen: |
| Listening = true; |
| break; |
| case kToolOpt_Count: |
| if (!ParseInt(arg, MaxEchoCount) || MaxEchoCount < 0 || MaxEchoCount > 30) |
| { |
| PrintArgError("%s: Invalid value specified for send count: %s\n", progName, arg); |
| return false; |
| } |
| break; |
| case 'D': |
| DestAddr = arg; |
| break; |
| case 'T': |
| if (!arg || !ParseInt(arg, TestNum)) |
| { |
| PrintArgError("%s: Invalid value specified for Test number: %s\n", progName, arg); |
| return false; |
| } |
| break; |
| case 'W': |
| if (!arg || !ParseInt(arg, MaxAckReceiptInterval)) |
| { |
| PrintArgError("%s: Invalid value specified for MaxAckReceiptInterval: %s\n", progName, arg); |
| return false; |
| } |
| break; |
| case 'R': |
| if (!arg || !ParseInt(arg, RetransInterval)) |
| { |
| PrintArgError("%s: Invalid value specified for RetransInterval: %s\n", progName, arg); |
| return false; |
| } |
| break; |
| default: |
| PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool HandleNonOptionArgs(const char *progName, int argc, char *argv[]) |
| { |
| if (argc > 0) |
| { |
| if (argc > 1) |
| { |
| PrintArgError("%s: Unexpected argument: %s\n", progName, argv[1]); |
| return false; |
| } |
| |
| if (Listening) |
| { |
| PrintArgError("%s: Please specify either a node id or --listen\n", progName); |
| return false; |
| } |
| |
| const char *nodeId = argv[0]; |
| char *p = (char *)strchr(nodeId, '@'); |
| if (p != NULL) |
| { |
| *p = 0; |
| DestAddr = p+1; |
| } |
| |
| if (!ParseNodeId(nodeId, DestNodeId)) |
| { |
| PrintArgError("%s: Invalid value specified for destination node-id: %s\n", progName, nodeId); |
| return false; |
| } |
| } |
| |
| else |
| { |
| if (!Listening) |
| { |
| PrintArgError("%s: Please specify either a node id or --listen\n", progName); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| void ParseDestAddress() |
| { |
| WEAVE_ERROR err; |
| const char *addr; |
| uint16_t addrLen; |
| const char *intfName; |
| uint16_t intfNameLen; |
| |
| err = ParseHostPortAndInterface(DestAddr, strlen(DestAddr), addr, addrLen, DestPort, intfName, intfNameLen); |
| if (err != INET_NO_ERROR) |
| { |
| printf("Invalid destination address: %s\n", DestAddr); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (!IPAddress::FromString(addr, DestIPAddr)) |
| { |
| printf("Invalid destination address: %s\n", DestAddr); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (intfName != NULL) |
| { |
| err = InterfaceNameToId(intfName, DestIntf); |
| if (err != INET_NO_ERROR) |
| { |
| printf("Invalid interface name: %s\n", intfName); |
| exit(EXIT_FAILURE); |
| } |
| } |
| } |
| |
| //++++++++WRMPTestClient Class+++++++++++++++++++++// |
| WRMPTestClient::WRMPTestClient() |
| { |
| FabricState = NULL; |
| ExchangeMgr = NULL; |
| OnEchoResponseReceived = NULL; |
| ExchangeCtx = NULL; |
| } |
| |
| WEAVE_ERROR WRMPTestClient::Init(WeaveExchangeManager *exchangeMgr, uint64_t nodeId, IPAddress nodeAddr, uint16_t port, InterfaceId sendIntfId) |
| { |
| // Error if already initialized. |
| if (ExchangeMgr != NULL) |
| return WEAVE_ERROR_INCORRECT_STATE; |
| |
| ExchangeMgr = exchangeMgr; |
| FabricState = exchangeMgr->FabricState; |
| OnEchoResponseReceived = NULL; |
| |
| ExchangeCtx = ExchangeMgr->NewContext(nodeId, nodeAddr, WEAVE_PORT, sendIntfId, this); |
| if (ExchangeCtx == NULL) |
| { |
| return WEAVE_ERROR_NO_MEMORY; |
| } |
| |
| //Set callbacks |
| ExchangeCtx->OnAckRcvd = HandleAckRcvd; |
| ExchangeCtx->OnDDRcvd = HandleDDRcvd; |
| ExchangeCtx->OnThrottleRcvd = HandleThrottleRcvd; |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| void ThrottleTimeout(System::Layer* aSystemLayer, void* aAppState, System::Error aError) |
| { |
| //Check the global ThrottlePeriodicMsgCount |
| printf("Throttle Timeout: Periodic message count is %d\n", PeriodicMsgCount); |
| FlowThrottled = false; |
| ThrottleTimeoutFired = true; |
| } |
| |
| WEAVE_ERROR WRMPTestClient::Shutdown() |
| { |
| if (ExchangeCtx != NULL) |
| { |
| ExchangeCtx->Close(); |
| ExchangeCtx = NULL; |
| } |
| |
| ExchangeMgr = NULL; |
| FabricState = NULL; |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| WEAVE_ERROR WRMPTestClient::SendEchoRequest(PacketBuffer *payload) |
| { |
| // Configure the encryption and signature types to be used to send the request. |
| ExchangeCtx->EncryptionType = EncryptionType; |
| ExchangeCtx->KeyId = KeyId; |
| |
| // Arrange for messages in this exchange to go to our response handler. |
| ExchangeCtx->OnMessageReceived = HandleResponse; |
| |
| // Send an Echo Request message. Discard the exchange context if the send fails. |
| WEAVE_ERROR err = ExchangeCtx->SendMessage(kWeaveProfile_Echo, kEchoMessageType_EchoRequest, payload, ExchangeContext::kSendFlag_RequestAck, &appContext); |
| if (err != WEAVE_NO_ERROR) |
| { |
| ExchangeCtx->Close(); |
| ExchangeCtx = NULL; |
| } |
| |
| return err; |
| } |
| |
| WEAVE_ERROR WRMPTestClient::SendEchoRequest(PacketBuffer *payload, uint16_t sendFlags) |
| { |
| // Configure the encryption and signature types to be used to send the request. |
| ExchangeCtx->EncryptionType = EncryptionType; |
| ExchangeCtx->KeyId = KeyId; |
| |
| // Arrange for messages in this exchange to go to our response handler. |
| ExchangeCtx->OnMessageReceived = HandleResponse; |
| |
| // Send an Echo Request message. Discard the exchange context if the send fails. |
| WEAVE_ERROR err = ExchangeCtx->SendMessage(kWeaveProfile_Echo, kEchoMessageType_EchoRequest, payload, sendFlags, &appContext); |
| if (err != WEAVE_NO_ERROR) |
| { |
| ExchangeCtx->Close(); |
| ExchangeCtx = NULL; |
| } |
| |
| return err; |
| } |
| |
| WEAVE_ERROR SendCustomMessage(ExchangeContext *ec, uint32_t ProfileId, uint8_t msgType, uint16_t sendFlags, PacketBuffer *payload, uint32_t *lAppContext) |
| { |
| // Configure the encryption and signature types to be used to send the request. |
| ec->EncryptionType = EncryptionType; |
| ec->KeyId = KeyId; |
| |
| // Arrange for messages in this exchange to go to our response handler. |
| if (!Listening) |
| { |
| ec->OnMessageReceived = WRMPTestClient::HandleResponse; |
| } |
| else |
| { |
| ec->OnMessageReceived = WRMPTestServer::HandleRcvdMessage; |
| } |
| |
| return ec->SendMessage(ProfileId, msgType, payload, sendFlags, lAppContext); |
| } |
| |
| void WRMPTestClient::HandleResponse(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *payload) |
| { |
| WRMPTestClient *wrmpClientApp = (WRMPTestClient *)ec->AppState; |
| // Verify that the exchange context matches our current context. Bail if not. |
| if (ec != wrmpClientApp->ExchangeCtx) |
| { |
| return; |
| } |
| |
| if (profileId == kWeaveProfile_Echo && msgType == kEchoMessageType_EchoResponse) |
| { |
| // Call the registered OnEchoResponseReceived handler, if any. |
| if (wrmpClientApp->OnEchoResponseReceived != NULL) |
| wrmpClientApp->OnEchoResponseReceived(msgInfo->SourceNodeId, pktInfo->SrcAddress, payload); |
| } |
| if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_Periodic) |
| { |
| PeriodicMsgCount++; |
| printf("Received Request for Throttle; Sending Throttle Msg with PauseTime %d\n", ThrottlePauseTime); |
| if (PeriodicMsgCount == 1) |
| { |
| ec->WRMPSendThrottleFlow(ThrottlePauseTime); |
| //Start the timer |
| globalExchMgr->MessageLayer->SystemLayer->StartTimer(ThrottlePauseTime, ThrottleTimeout, NULL); |
| } |
| } |
| if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_DD_Test) |
| { |
| if (!DDTestCount) |
| { |
| FirstDDTestTime = Now(); |
| DDTestCount++; |
| //Reset DropAck |
| ec->SetDropAck(false); |
| // Allow duplicates for this exchange so we can process second DD_Test message. |
| ec->AllowDuplicateMsgs = true; |
| //Send Delayed Delivery |
| ec->WRMPSendDelayedDelivery(ThrottlePauseTime, globalExchMgr->FabricState->LocalNodeId); |
| } |
| else |
| { |
| SecondDDTestTime = Now(); |
| } |
| //Note the time and wait for second one and compare with ThrottlePauseTime |
| |
| } |
| if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_CloseEC) |
| { |
| if (!(msgInfo->Flags & kWeaveMessageFlag_DuplicateMessage)) |
| { |
| printf("TestWRMP: Received Test Msg Type CloseEC; Closing exchange and clearing DropAck flag\n"); |
| ec->SetDropAck(false); |
| ec->Release(); |
| wrmpClientApp->ExchangeCtx = NULL; |
| CloseECMsgCount++; |
| } |
| else |
| { |
| printf("TestWRMP: Received Duplicate of a Test Msg Type CloseEC; Sending Ack\n"); |
| CloseECMsgCount++; |
| } |
| } |
| |
| // Free the payload buffer. |
| PacketBuffer::Free(payload); |
| } |
| |
| //++++++++WRMPTestServer Class+++++++++++++++++++++// |
| WRMPTestServer::WRMPTestServer() |
| { |
| FabricState = NULL; |
| ExchangeMgr = NULL; |
| OnEchoRequestReceived = NULL; |
| } |
| |
| WEAVE_ERROR WRMPTestServer::Init(WeaveExchangeManager *exchangeMgr) |
| { |
| // Error if already initialized. |
| if (ExchangeMgr != NULL) |
| return WEAVE_ERROR_INCORRECT_STATE; |
| |
| ExchangeMgr = exchangeMgr; |
| FabricState = exchangeMgr->FabricState; |
| OnEchoRequestReceived = NULL; |
| |
| // Register to receive unsolicited messages from the exchange manager. |
| ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Echo, kEchoMessageType_EchoRequest, HandleRcvdMessage, |
| AllowDuplicateMsgs, this); |
| ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Test, kWeaveTestMessageType_Generate_Response, HandleRcvdMessage, |
| AllowDuplicateMsgs, this); |
| ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Test, kWeaveTestMessageType_No_Response, HandleRcvdMessage, |
| AllowDuplicateMsgs, this); |
| ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Test, kWeaveTestMessageType_Request_Throttle, HandleRcvdMessage, |
| AllowDuplicateMsgs, this); |
| ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Test, kWeaveTestMessageType_Request_Periodic, HandleRcvdMessage, |
| AllowDuplicateMsgs, this); |
| ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Test, kWeaveTestMessageType_Request_DD, HandleRcvdMessage, |
| AllowDuplicateMsgs, this); |
| ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Test, kWeaveTestMessageType_DD_Test, HandleRcvdMessage, |
| AllowDuplicateMsgs, this); |
| ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Test, kWeaveTestMessageType_SetDropAck, HandleRcvdMessage, |
| AllowDuplicateMsgs, this); |
| ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Test, kWeaveTestMessageType_RequestCloseEC, HandleRcvdMessage, |
| AllowDuplicateMsgs, this); |
| ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Test, kWeaveTestMessageType_AllowDup, HandleRcvdMessage, |
| AllowDuplicateMsgs, this); |
| ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Test, kWeaveTestMessageType_DontAllowDup, HandleRcvdMessage, |
| AllowDuplicateMsgs, this); |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| WEAVE_ERROR WRMPTestServer::Shutdown() |
| { |
| if (ExchangeMgr != NULL) |
| { |
| ExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_Echo, kEchoMessageType_EchoRequest); |
| ExchangeMgr = NULL; |
| } |
| |
| FabricState = NULL; |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| WEAVE_ERROR WRMPTestServer::GeneratePeriodicMessage(int MaxCount, ExchangeContext *ec) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| struct timeval sleepTime; |
| sleepTime.tv_sec = 0; |
| sleepTime.tv_usec = 100000; |
| PacketBuffer *payloadBuf = NULL; |
| uint32_t msgCount = 0; |
| |
| printf("Send max of %d Periodic Messages\n", MaxCount); |
| for (int i = 0; i < MaxCount; i++) |
| { |
| if (!FlowThrottled) |
| { |
| PrepareNewBuf(&payloadBuf); |
| err = SendCustomMessage(ec, |
| kWeaveProfile_Test, |
| kWeaveTestMessageType_Periodic, |
| ExchangeContext::kSendFlag_RequestAck, |
| payloadBuf); |
| |
| payloadBuf = NULL; |
| if (err != WEAVE_NO_ERROR) |
| { |
| return err; |
| } |
| else |
| { |
| msgCount++; |
| printf("Sent Periodic Message #%d\n", msgCount); |
| } |
| ServiceNetwork(sleepTime); |
| } |
| } |
| |
| return err; |
| } |
| |
| void WRMPTestServer::HandleRcvdMessage(ExchangeContext *ec, const IPPacketInfo *pktInfo, |
| const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *payload) |
| { |
| WRMPTestServer *wrmpServApp = (WRMPTestServer *) ec->AppState; |
| |
| //Set the application callbacks first |
| ec->OnAckRcvd = HandleAckRcvd; |
| ec->OnDDRcvd = HandleDDRcvd; |
| ec->OnThrottleRcvd = HandleThrottleRcvd; |
| // WeaveExchangeManager installs a default handler in OnMessageReceived. |
| // This test overrides that default and uses this method (i.e. |
| // HandleRcvdMessage) as the handler not only for the first unsolicited |
| // message that causes the allocation of the exchange context but also for |
| // the subsequent messages received on that exchange context. |
| ec->OnMessageReceived = HandleRcvdMessage; |
| |
| if (profileId == kWeaveProfile_Echo && msgType == kEchoMessageType_EchoRequest) |
| { |
| // Call the registered OnEchoRequestReceived handler, if any. |
| if (wrmpServApp->OnEchoRequestReceived != NULL) |
| wrmpServApp->OnEchoRequestReceived(ec->PeerNodeId, ec->PeerAddr, payload); |
| |
| // Send an Echo Response back to the sender. |
| ec->SendMessage(kWeaveProfile_Echo, kEchoMessageType_EchoResponse, payload, 0); |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_Generate_Response) |
| { |
| printf("Received Test Msg Type Generate_Response; Send Solitary Ack\n"); |
| ec->SendMessage(nl::Weave::Profiles::kWeaveProfile_Common, |
| nl::Weave::Profiles::Common::kMsgType_Null, |
| payload); |
| |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_No_Response) |
| { |
| printf("Received Test Msg Type No_Response\n"); |
| |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_Request_Throttle) |
| { |
| printf("Received Request for Throttle; Sending Throttle Msg with PauseTime %d\n", ThrottlePauseTime); |
| ec->WRMPSendThrottleFlow(ThrottlePauseTime); |
| |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_Request_DD) |
| { |
| //Send the DD message. |
| printf("Received Request for Delayed Delivery; Sending Delayed Delivery Msg with PauseTime %d and NodeId 0x%" PRIx64 "\n", |
| ThrottlePauseTime, globalExchMgr->FabricState->LocalNodeId); |
| ec->WRMPSendDelayedDelivery(ThrottlePauseTime, globalExchMgr->FabricState->LocalNodeId); |
| |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_Request_Periodic) |
| { |
| printf("Received Request for Periodic Messages; Generate a set of periodic messages\n"); |
| wrmpServApp->GeneratePeriodicMessage(10, ec); |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_DD_Test) |
| { |
| printf("Received Test Msg Type DD_Test; Send back DD_Test\n"); |
| WEAVE_ERROR err = SendCustomMessage(ec, kWeaveProfile_Test, kWeaveTestMessageType_DD_Test, ExchangeContext::kSendFlag_RequestAck, payload); |
| SuccessOrFail(err, "WRMPTestClient.SendCustomMessage failed to send DD_Test message\n"); |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_SetDropAck) |
| { |
| printf("TestWRMP: Received Test Msg Type SetDropAck; Setting DropAck flag\n"); |
| ec->SetDropAck(true); |
| PacketBuffer::Free(payload); |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_ClearDropAck) |
| { |
| printf("TestWRMP: Received Test Msg Type ClearDropAck; Clearing DropAck flag\n"); |
| ec->SetDropAck(false); |
| PacketBuffer::Free(payload); |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_Lost_Ack) |
| { |
| if (!(msgInfo->Flags & kWeaveMessageFlag_DuplicateMessage)) |
| { |
| printf("TestWRMP: Received Test Msg Type Lost_Ack; Clearing DropAck flag not sending ack because it is not a duplicate\n"); |
| ec->SetDropAck(false); |
| } |
| else |
| { |
| printf("TestWRMP: Received Duplicate of a Test Msg Type Lost_Ack; Sending Ack\n"); |
| } |
| PacketBuffer::Free(payload); |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_CloseEC) |
| { |
| if (!(msgInfo->Flags & kWeaveMessageFlag_DuplicateMessage)) |
| { |
| printf("TestWRMP: Received Test Msg Type CloseEC; Closing exchange and clearing DropAck flag\n"); |
| ec->SetDropAck(false); |
| ec->Release(); |
| } |
| else |
| { |
| printf("TestWRMP: Received Duplicate of a Test Msg Type CloseEC; Sending Ack\n"); |
| } |
| PacketBuffer::Free(payload); |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_RequestCloseEC) |
| { |
| printf("TestWRMP: Received Test Msg Type RequestCloseEC; Sending CloseEC msg as requested\n"); |
| |
| // Form CloseEC messages. |
| uint16_t len = sprintf((char *)(payload->Start()), "Dup Detection CloseEC Msg"); |
| payload->SetDataLength(len); |
| |
| // Send CloseEC message. |
| WEAVE_ERROR err = ec->SendMessage(kWeaveProfile_Test, kWeaveTestMessageType_CloseEC, payload, |
| ExchangeContext::kSendFlag_RequestAck, &appContext2); |
| SuccessOrFail(err, "ec->SendMessage failed to send CloseEC message\n"); |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_AllowDup) |
| { |
| printf("TestWRMP: Received Test Msg Type AllowDup; Setting AllowDuplicateMsgs flag\n"); |
| ec->AllowDuplicateMsgs = true; |
| PacketBuffer::Free(payload); |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_DontAllowDup) |
| { |
| printf("TestWRMP: Received Test Msg Type DontAllowDup; Clearing AllowDuplicateMsgs flag\n"); |
| ec->AllowDuplicateMsgs = false; |
| PacketBuffer::Free(payload); |
| } |
| else if (profileId == kWeaveProfile_Test && msgType == kWeaveTestMessageType_EchoRequestForDup) |
| { |
| // If test echo request message is a duplicate send echo response. |
| if (msgInfo->Flags & kWeaveMessageFlag_DuplicateMessage) |
| { |
| printf("TestWRMP: Received Duplicate of a Test Msg Type EchoRequestForDup; Sending echo response\n"); |
| HandleEchoRequestReceived(ec->PeerNodeId, ec->PeerAddr, payload); |
| |
| // Send an Echo Response back to the sender. |
| ec->SendMessage(kWeaveProfile_Echo, kEchoMessageType_EchoResponse, payload, 0); |
| } |
| else |
| { |
| printf("TestWRMP: Received Test Msg Type EchoRequestForDup; Not sending response because the message is not a duplicate\n"); |
| PacketBuffer::Free(payload); |
| } |
| } |
| } |
| |
| void HandleAckRcvd(ExchangeContext *ec, void *msgCtxt) |
| { |
| uint32_t context; |
| if (msgCtxt) |
| { |
| context = *((uint32_t *)(msgCtxt)); |
| if (context == appContext || context == appContext2) |
| { |
| printf("Received Ack for Context: %X\n", context); |
| isAckRcvd = true; |
| ackCount++; |
| } |
| } |
| else |
| { |
| printf("No context for received Ack\n"); |
| } |
| } |
| |
| void HandleDDRcvd(ExchangeContext *ec, uint32_t pauseTime) |
| { |
| printf("Received Delayed Delivery Msg for node Id 0x%" PRIx64 " with pauseTime %d\n", ec->PeerNodeId, pauseTime); |
| DDRcvd = true; |
| } |
| |
| void HandleThrottleRcvd(ExchangeContext *ec, uint32_t pauseTime) |
| { |
| printf("Received Throttle Msg with pauseTime %d from peer %" PRId64 "\n", pauseTime, ec->PeerNodeId); |
| throttleRcvd = true; |
| } |
| |
| #endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |