From 0709188a3f8a95bfe3ab6ba3ea98b0d96c1d2a21 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Tue, 27 Apr 2021 12:12:24 -0700 Subject: [PATCH] [shell] Add send cmd to allow send any CHIP message for testing (#6239) * Add send cmd for testing * Enable ping/send commands only on CHIP controller * Move global varibles to common.cpp * Enable ping and send commands on accessory side * Address review comments --- examples/shell/esp32/main/CMakeLists.txt | 2 + examples/shell/nrfconnect/CMakeLists.txt | 4 +- examples/shell/shell_common/BUILD.gn | 2 + examples/shell/shell_common/cmd_ping.cpp | 21 +- examples/shell/shell_common/cmd_send.cpp | 443 ++++++++++++++++++ examples/shell/shell_common/globals.cpp | 29 ++ .../include/ChipShellCollection.h | 1 + examples/shell/shell_common/include/Globals.h | 42 ++ examples/shell/standalone/main.cpp | 1 + 9 files changed, 525 insertions(+), 20 deletions(-) create mode 100644 examples/shell/shell_common/cmd_send.cpp create mode 100644 examples/shell/shell_common/globals.cpp create mode 100644 examples/shell/shell_common/include/Globals.h diff --git a/examples/shell/esp32/main/CMakeLists.txt b/examples/shell/esp32/main/CMakeLists.txt index 806b9952dc9171..0f968d7a97dc6d 100644 --- a/examples/shell/esp32/main/CMakeLists.txt +++ b/examples/shell/esp32/main/CMakeLists.txt @@ -20,7 +20,9 @@ set(CHIP_SHELL_DIR "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/she idf_component_register(SRCS main.cpp + "${CHIP_SHELL_DIR}/shell_common/globals.cpp" "${CHIP_SHELL_DIR}/shell_common/cmd_ping.cpp" + "${CHIP_SHELL_DIR}/shell_common/cmd_send.cpp" PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src" "${CHIP_SHELL_DIR}/shell_common/include" diff --git a/examples/shell/nrfconnect/CMakeLists.txt b/examples/shell/nrfconnect/CMakeLists.txt index b439d4e3d8182f..ed1de6326726c1 100644 --- a/examples/shell/nrfconnect/CMakeLists.txt +++ b/examples/shell/nrfconnect/CMakeLists.txt @@ -34,11 +34,13 @@ target_include_directories(app PRIVATE ${APP_ROOT}/shell_common/include) target_sources(app PRIVATE + ${APP_ROOT}/shell_common/globals.cpp ${APP_ROOT}/shell_common/cmd_base64.cpp ${APP_ROOT}/shell_common/cmd_device.cpp ${APP_ROOT}/shell_common/cmd_misc.cpp ${APP_ROOT}/shell_common/cmd_otcli.cpp - ${APP_ROOT}/shell_common/cmd_ping.cpp + ${APP_ROOT}/shell_common/cmd_ping.cpp + ${APP_ROOT}/shell_common/cmd_send.cpp ${APP_ROOT}/shell_common/cmd_btp.cpp ${APP_ROOT}/standalone/main.cpp ) diff --git a/examples/shell/shell_common/BUILD.gn b/examples/shell/shell_common/BUILD.gn index 0e4b1f9550a67c..28c55b1e6a266a 100644 --- a/examples/shell/shell_common/BUILD.gn +++ b/examples/shell/shell_common/BUILD.gn @@ -38,6 +38,8 @@ static_library("shell_common") { "cmd_misc.cpp", "cmd_otcli.cpp", "cmd_ping.cpp", + "cmd_send.cpp", + "globals.cpp", ] public_deps = [ diff --git a/examples/shell/shell_common/cmd_ping.cpp b/examples/shell/shell_common/cmd_ping.cpp index 0dfa705a932dc8..cd852d06344ba9 100644 --- a/examples/shell/shell_common/cmd_ping.cpp +++ b/examples/shell/shell_common/cmd_ping.cpp @@ -33,18 +33,13 @@ #include #include +#include using namespace chip; using namespace Shell; using namespace Logging; using chip::Inet::IPAddress; -#if INET_CONFIG_ENABLE_TCP_ENDPOINT -constexpr size_t kMaxTcpActiveConnectionCount = 4; -constexpr size_t kMaxTcpPendingPackets = 4; -#endif -constexpr size_t kMaxPayloadSize = 1280; - namespace { class PingArguments @@ -132,20 +127,8 @@ class PingArguments bool mUsingCRMP; } gPingArguments; -constexpr Transport::AdminId gAdminId = 0; - Protocols::Echo::EchoClient gEchoClient; -TransportMgr gUDPManager; - -#if INET_CONFIG_ENABLE_TCP_ENDPOINT -TransportMgr> gTCPManager; -#endif - -Messaging::ExchangeManager gExchangeManager; -SecureSessionMgr gSessionManager; -IPAddress gDestAddr; - bool EchoIntervalExpired(void) { uint64_t now = System::Timer::GetCurrentEpoch(); @@ -426,7 +409,7 @@ int cmd_ping(int argc, char ** argv) case 'p': if (++optIndex >= argc || argv[optIndex][0] == '-') { - streamer_printf(sout, "Invalid argument specified for -c\n"); + streamer_printf(sout, "Invalid argument specified for -p\n"); return -1; } else diff --git a/examples/shell/shell_common/cmd_send.cpp b/examples/shell/shell_common/cmd_send.cpp new file mode 100644 index 00000000000000..9dc35f230c7f36 --- /dev/null +++ b/examples/shell/shell_common/cmd_send.cpp @@ -0,0 +1,443 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace chip; +using namespace Shell; +using namespace Logging; + +namespace { + +Messaging::ExchangeContext * gExchangeCtx = nullptr; + +class SendArguments +{ +public: + void Reset() + { + mProtocolId = 0x0002; + mMessageType = 1; + mLastSendTime = 0; + mPayloadSize = 32; +#if INET_CONFIG_ENABLE_TCP_ENDPOINT + mUsingTCP = false; +#endif + mUsingCRMP = true; + mPort = CHIP_PORT; + } + + uint64_t GetLastSendTime() const { return mLastSendTime; } + void SetLastSendTime(uint64_t value) { mLastSendTime = value; } + + uint16_t GetProtocolId() const { return mProtocolId; } + void SetProtocolId(uint16_t value) { mProtocolId = value; } + + uint32_t GetPayloadSize() const { return mPayloadSize; } + void SetPayloadSize(uint32_t value) { mPayloadSize = value; } + + uint16_t GetPort() const { return mPort; } + void SetPort(uint16_t value) { mPort = value; } + + uint8_t GetMessageType() const { return mMessageType; } + void SetMessageType(uint8_t type) { mMessageType = type; } + +#if INET_CONFIG_ENABLE_TCP_ENDPOINT + bool IsUsingTCP() const { return mUsingTCP; } + void SetUsingTCP(bool value) { mUsingTCP = value; } +#endif + + bool IsUsingCRMP() const { return mUsingCRMP; } + void SetUsingCRMP(bool value) { mUsingCRMP = value; } + +private: + // The last time a CHIP message was attempted to be sent. + uint64_t mLastSendTime; + + uint32_t mPayloadSize; + uint16_t mProtocolId; + uint16_t mPort; + uint8_t mMessageType; + +#if INET_CONFIG_ENABLE_TCP_ENDPOINT + bool mUsingTCP; +#endif + + bool mUsingCRMP; +} gSendArguments; + +class MockAppDelegate : public Messaging::ExchangeDelegate +{ +public: + void OnMessageReceived(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader, + System::PacketBufferHandle buffer) override + { + uint32_t respTime = System::Timer::GetCurrentEpoch(); + uint32_t transitTime = respTime - gSendArguments.GetLastSendTime(); + streamer_t * sout = streamer_get(); + + streamer_printf(sout, "Response received: len=%u time=%.3fms\n", buffer->DataLength(), + static_cast(transitTime) / 1000); + + gExchangeCtx->Close(); + gExchangeCtx = nullptr; + } + + void OnResponseTimeout(Messaging::ExchangeContext * ec) override + { + streamer_t * sout = streamer_get(); + streamer_printf(sout, "No response received\n"); + + gExchangeCtx->Close(); + gExchangeCtx = nullptr; + } +} gMockAppDelegate; + +CHIP_ERROR SendMessage(streamer_t * stream) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + Messaging::SendFlags sendFlags; + System::PacketBufferHandle payloadBuf; + char * requestData = nullptr; + uint32_t size = 0; + + // Discard any existing exchange context. Effectively we can only have one exchange with + // a single node at any one time. + if (gExchangeCtx != nullptr) + { + gExchangeCtx->Abort(); + gExchangeCtx = nullptr; + } + + // Create a new exchange context. + gExchangeCtx = gExchangeManager.NewContext({ kTestDeviceNodeId, 0, gAdminId }, &gMockAppDelegate); + VerifyOrExit(gExchangeCtx != nullptr, err = CHIP_ERROR_NO_MEMORY); + + size = gSendArguments.GetPayloadSize(); + VerifyOrExit(size <= kMaxPayloadSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + + requestData = static_cast(chip::Platform::MemoryAlloc(size)); + VerifyOrExit(requestData != nullptr, err = CHIP_ERROR_NO_MEMORY); + + snprintf(requestData, size, "CHIP Message"); + payloadBuf = MessagePacketBuffer::NewWithData(requestData, size); + VerifyOrExit(!payloadBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY); + + if (gSendArguments.IsUsingCRMP()) + { + sendFlags.Set(Messaging::SendMessageFlags::kNone); + } + else + { + sendFlags.Set(Messaging::SendMessageFlags::kNoAutoRequestAck); + } + + gExchangeCtx->SetResponseTimeout(kResponseTimeOut); + sendFlags.Set(Messaging::SendMessageFlags::kExpectResponse); + + gSendArguments.SetLastSendTime(System::Timer::GetCurrentEpoch()); + + streamer_printf(stream, "\nSend CHIP message with payload size: %d bytes to Node: %" PRIu64 "\n", size, kTestDeviceNodeId); + + err = gExchangeCtx->SendMessage(Protocols::Id(VendorId::Common, gSendArguments.GetProtocolId()), + gSendArguments.GetMessageType(), std::move(payloadBuf), sendFlags); + + if (err != CHIP_NO_ERROR) + { + gExchangeCtx->Abort(); + gExchangeCtx = nullptr; + } + +exit: + if (requestData != nullptr) + { + chip::Platform::MemoryFree(requestData); + } + + if (err != CHIP_NO_ERROR) + { + streamer_printf(stream, "Send CHIP message failed, err: %s\n", ErrorStr(err)); + } + + return err; +} + +CHIP_ERROR EstablishSecureSession(streamer_t * stream, Transport::PeerAddress & peerAddress) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + Optional peerAddr; + SecurePairingUsingTestSecret * testSecurePairingSecret = chip::Platform::New(); + VerifyOrExit(testSecurePairingSecret != nullptr, err = CHIP_ERROR_NO_MEMORY); + + peerAddr = Optional::Value(peerAddress); + + // Attempt to connect to the peer. + err = gSessionManager.NewPairing(peerAddr, kTestDeviceNodeId, testSecurePairingSecret, + SecureSessionMgr::PairingDirection::kInitiator, gAdminId); + +exit: + if (err != CHIP_NO_ERROR) + { + streamer_printf(stream, "Establish secure session failed, err: %s\n", ErrorStr(err)); + gSendArguments.SetLastSendTime(System::Timer::GetCurrentEpoch()); + } + else + { + streamer_printf(stream, "Establish secure session succeeded\n"); + } + + return err; +} + +void ProcessCommand(streamer_t * stream, char * destination) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + Transport::AdminPairingTable admins; + Transport::PeerAddress peerAddress; + Transport::AdminPairingInfo * adminInfo = nullptr; + + if (!Inet::IPAddress::FromString(destination, gDestAddr)) + { + streamer_printf(stream, "Invalid CHIP Server IP address: %s\n", destination); + ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT); + } + + adminInfo = admins.AssignAdminId(gAdminId, kTestControllerNodeId); + VerifyOrExit(adminInfo != nullptr, err = CHIP_ERROR_NO_MEMORY); + +#if INET_CONFIG_ENABLE_TCP_ENDPOINT + err = gTCPManager.Init(Transport::TcpListenParameters(&DeviceLayer::InetLayer) + .SetAddressType(gDestAddr.Type()) + .SetListenPort(gSendArguments.GetPort() + 1)); + VerifyOrExit(err == CHIP_NO_ERROR, streamer_printf(stream, "Failed to init TCP manager error: %s\n", ErrorStr(err))); +#endif + + err = gUDPManager.Init(Transport::UdpListenParameters(&DeviceLayer::InetLayer) + .SetAddressType(gDestAddr.Type()) + .SetListenPort(gSendArguments.GetPort() + 1)); + VerifyOrExit(err == CHIP_NO_ERROR, streamer_printf(stream, "Failed to init UDP manager error: %s\n", ErrorStr(err))); + +#if INET_CONFIG_ENABLE_TCP_ENDPOINT + if (gSendArguments.IsUsingTCP()) + { + peerAddress = Transport::PeerAddress::TCP(gDestAddr, gSendArguments.GetPort()); + + err = gSessionManager.Init(kTestControllerNodeId, &DeviceLayer::SystemLayer, &gTCPManager, &admins); + SuccessOrExit(err); + + err = gExchangeManager.Init(&gSessionManager); + SuccessOrExit(err); + } + else +#endif + { + peerAddress = Transport::PeerAddress::UDP(gDestAddr, gSendArguments.GetPort(), INET_NULL_INTERFACEID); + + err = gSessionManager.Init(kTestControllerNodeId, &DeviceLayer::SystemLayer, &gUDPManager, &admins); + SuccessOrExit(err); + + err = gExchangeManager.Init(&gSessionManager); + SuccessOrExit(err); + } + + // Start the CHIP connection to the CHIP server. + err = EstablishSecureSession(stream, peerAddress); + SuccessOrExit(err); + + err = SendMessage(stream); + SuccessOrExit(err); + + // TODO:#5496: Use condition_varible to suspend the current thread and wake it up when response arrive. + sleep(2); + +#if INET_CONFIG_ENABLE_TCP_ENDPOINT + gTCPManager.Disconnect(peerAddress); + gTCPManager.Close(); +#endif + gUDPManager.Close(); + + gExchangeManager.Shutdown(); + gSessionManager.Shutdown(); + +exit: + if ((err != CHIP_NO_ERROR)) + { + streamer_printf(stream, "Send failed with error: %s\n", ErrorStr(err)); + } +} + +void PrintUsage(streamer_t * stream) +{ + streamer_printf(stream, "Usage: send [options] \n\nOptions:\n"); + + // Need to split the help info to prevent overflowing the streamer_printf + // buffer (CONSOLE_DEFAULT_MAX_LINE 256) + streamer_printf(stream, " -h print help information\n"); +#if INET_CONFIG_ENABLE_TCP_ENDPOINT + streamer_printf(stream, " -u use UDP (default)\n"); + streamer_printf(stream, " -t use TCP\n"); +#endif + streamer_printf(stream, " -P protocol ID\n"); + streamer_printf(stream, " -T message type\n"); + streamer_printf(stream, " -p server port number\n"); + streamer_printf(stream, " -r <1|0> enable or disable CRMP\n"); + streamer_printf(stream, " -s payload size in bytes\n"); +} + +int cmd_send(int argc, char ** argv) +{ + streamer_t * sout = streamer_get(); + int ret = 0; + int optIndex = 0; + + gSendArguments.Reset(); + + while (optIndex < argc && argv[optIndex][0] == '-') + { + switch (argv[optIndex][1]) + { + case 'h': + PrintUsage(sout); + return 0; +#if INET_CONFIG_ENABLE_TCP_ENDPOINT + case 'u': + gSendArguments.SetUsingTCP(false); + break; + case 't': + gSendArguments.SetUsingTCP(true); + break; +#endif + case 'P': + if (++optIndex >= argc || argv[optIndex][0] == '-') + { + streamer_printf(sout, "Invalid argument specified for -P\n"); + return -1; + } + else + { + gSendArguments.SetProtocolId(atol(argv[optIndex])); + } + break; + case 'T': + if (++optIndex >= argc || argv[optIndex][0] == '-') + { + streamer_printf(sout, "Invalid argument specified for -T\n"); + return -1; + } + else + { + gSendArguments.SetMessageType(atoi(argv[optIndex])); + } + break; + case 'p': + if (++optIndex >= argc || argv[optIndex][0] == '-') + { + streamer_printf(sout, "Invalid argument specified for -p\n"); + return -1; + } + else + { + gSendArguments.SetPort(atol(argv[optIndex])); + } + break; + case 's': + if (++optIndex >= argc || argv[optIndex][0] == '-') + { + streamer_printf(sout, "Invalid argument specified for -s\n"); + return -1; + } + else + { + gSendArguments.SetPayloadSize(atol(argv[optIndex])); + } + break; + case 'r': + if (++optIndex >= argc || argv[optIndex][0] == '-') + { + streamer_printf(sout, "Invalid argument specified for -r\n"); + return -1; + } + else + { + int arg = atoi(argv[optIndex]); + + if (arg == 0) + { + gSendArguments.SetUsingCRMP(false); + } + else if (arg == 1) + { + gSendArguments.SetUsingCRMP(true); + } + else + { + ret = -1; + } + } + break; + default: + ret = -1; + } + + optIndex++; + } + + if (optIndex >= argc) + { + streamer_printf(sout, "Missing IP address\n"); + ret = -1; + } + + if (ret == 0) + { + streamer_printf(sout, "IP address: %s\n", argv[optIndex]); + ProcessCommand(sout, argv[optIndex]); + } + + return ret; +} + +} // namespace + +static shell_command_t cmds_send[] = { + { &cmd_send, "send", "Send raw CHIP message" }, +}; + +void cmd_send_init() +{ + shell_register(cmds_send, ArraySize(cmds_send)); +} diff --git a/examples/shell/shell_common/globals.cpp b/examples/shell/shell_common/globals.cpp new file mode 100644 index 00000000000000..95f4c8135bbccd --- /dev/null +++ b/examples/shell/shell_common/globals.cpp @@ -0,0 +1,29 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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. + */ + +#include + +chip::Messaging::ExchangeManager gExchangeManager; +chip::SecureSessionMgr gSessionManager; +chip::Inet::IPAddress gDestAddr; + +chip::Transport::AdminId gAdminId = 0; + +#if INET_CONFIG_ENABLE_TCP_ENDPOINT +chip::TransportMgr> gTCPManager; +#endif +chip::TransportMgr gUDPManager; diff --git a/examples/shell/shell_common/include/ChipShellCollection.h b/examples/shell/shell_common/include/ChipShellCollection.h index 1e837d71b4d888..0bfc41bd92edf5 100644 --- a/examples/shell/shell_common/include/ChipShellCollection.h +++ b/examples/shell/shell_common/include/ChipShellCollection.h @@ -25,4 +25,5 @@ void cmd_device_init(void); void cmd_misc_init(void); void cmd_otcli_init(void); void cmd_ping_init(void); +void cmd_send_init(void); } diff --git a/examples/shell/shell_common/include/Globals.h b/examples/shell/shell_common/include/Globals.h new file mode 100644 index 00000000000000..fd761fc9af70ac --- /dev/null +++ b/examples/shell/shell_common/include/Globals.h @@ -0,0 +1,42 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#if INET_CONFIG_ENABLE_TCP_ENDPOINT +constexpr size_t kMaxTcpActiveConnectionCount = 4; +constexpr size_t kMaxTcpPendingPackets = 4; +#endif +constexpr size_t kMaxPayloadSize = 1280; +constexpr size_t kResponseTimeOut = 1000; + +extern chip::Messaging::ExchangeManager gExchangeManager; +extern chip::SecureSessionMgr gSessionManager; +extern chip::Inet::IPAddress gDestAddr; + +extern chip::Transport::AdminId gAdminId; + +#if INET_CONFIG_ENABLE_TCP_ENDPOINT +extern chip::TransportMgr> gTCPManager; +#endif +extern chip::TransportMgr gUDPManager; diff --git a/examples/shell/standalone/main.cpp b/examples/shell/standalone/main.cpp index f595adbf0406e4..5710c661d2329e 100644 --- a/examples/shell/standalone/main.cpp +++ b/examples/shell/standalone/main.cpp @@ -46,6 +46,7 @@ int main() cmd_btp_init(); cmd_otcli_init(); cmd_ping_init(); + cmd_send_init(); shell_task(nullptr); return 0;