| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- /*
- *
- * 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 <errno.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <lib/core/CHIPCore.h>
- #include <lib/shell/Engine.h>
- #include <lib/support/CodeUtils.h>
- #include <lib/support/ErrorStr.h>
- #include <messaging/ExchangeMgr.h>
- #include <platform/CHIPDeviceLayer.h>
- #include <protocols/secure_channel/PASESession.h>
- #include <system/SystemPacketBuffer.h>
- #include <transport/SecureSessionMgr.h>
- #include <transport/raw/TCP.h>
- #include <transport/raw/UDP.h>
- #include <ChipShellCollection.h>
- #include <Globals.h>
- 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<double>(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<char *>(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<Transport::PeerAddress> peerAddr;
- SecurePairingUsingTestSecret * testSecurePairingSecret = chip::Platform::New<SecurePairingUsingTestSecret>();
- VerifyOrExit(testSecurePairingSecret != nullptr, err = CHIP_ERROR_NO_MEMORY);
- peerAddr = Optional<Transport::PeerAddress>::Value(peerAddress);
- // Attempt to connect to the peer.
- err = gSessionManager.NewPairing(peerAddr, kTestDeviceNodeId, testSecurePairingSecret, SecureSession::SessionRole::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 (!chip::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, &gMessageCounterManager);
- SuccessOrExit(err);
- }
- else
- #endif
- {
- peerAddress = Transport::PeerAddress::UDP(gDestAddr, gSendArguments.GetPort(), INET_NULL_INTERFACEID);
- err =
- gSessionManager.Init(kTestControllerNodeId, &DeviceLayer::SystemLayer, &gUDPManager, &admins, &gMessageCounterManager);
- SuccessOrExit(err);
- }
- err = gExchangeManager.Init(&gSessionManager);
- SuccessOrExit(err);
- err = gMessageCounterManager.Init(&gExchangeManager);
- 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] <destination>\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> protocol ID\n");
- streamer_printf(stream, " -T <type> message type\n");
- streamer_printf(stream, " -p <port> server port number\n");
- streamer_printf(stream, " -r <1|0> enable or disable CRMP\n");
- streamer_printf(stream, " -s <size> 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()
- {
- Engine::Root().RegisterCommands(cmds_send, ArraySize(cmds_send));
- }
|