blob: 9a6ef3f4a9869d555444e9eeda39fd5f272e73b5 [file] [log] [blame]
/*
*
* 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()
{
shell_register(cmds_send, ArraySize(cmds_send));
}