blob: 99b7bc12c7a8637d4d7a42898c455db0356d39ea [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/SessionManager.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 {
class SendArguments
{
public:
void Reset()
{
mProtocolId = 0x0002;
mMessageType = 1;
mLastSendTime = 0;
mPayloadSize = 32;
#if INET_CONFIG_ENABLE_TCP_ENDPOINT
mUsingTCP = false;
#endif
mUsingMRP = 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 IsUsingMRP() const { return mUsingMRP; }
void SetUsingMRP(bool value) { mUsingMRP = 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 mUsingMRP;
} gSendArguments;
class MockAppDelegate : public Messaging::ExchangeDelegate
{
public:
CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
System::PacketBufferHandle && buffer) override
{
uint64_t respTime = System::SystemClock().GetMonotonicMilliseconds();
uint64_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);
return CHIP_NO_ERROR;
}
void OnResponseTimeout(Messaging::ExchangeContext * ec) override
{
streamer_t * sout = streamer_get();
streamer_printf(sout, "No response received\n");
}
} gMockAppDelegate;
CHIP_ERROR SendMessage(streamer_t * stream)
{
CHIP_ERROR err = CHIP_NO_ERROR;
Messaging::SendFlags sendFlags;
System::PacketBufferHandle payloadBuf;
uint32_t payloadSize = gSendArguments.GetPayloadSize();
// Create a new exchange context.
auto * ec = gExchangeManager.NewContext(SessionHandle(kTestDeviceNodeId, 1, 1, gFabricIndex), &gMockAppDelegate);
VerifyOrExit(ec != nullptr, err = CHIP_ERROR_NO_MEMORY);
payloadBuf = MessagePacketBuffer::New(payloadSize);
VerifyOrExit(!payloadBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
memset(payloadBuf->Start(), 0, payloadSize);
payloadBuf->SetDataLength(payloadSize);
if (gSendArguments.IsUsingMRP())
{
sendFlags.Set(Messaging::SendMessageFlags::kNone);
}
else
{
sendFlags.Set(Messaging::SendMessageFlags::kNoAutoRequestAck);
}
ec->SetResponseTimeout(kResponseTimeOut);
sendFlags.Set(Messaging::SendMessageFlags::kExpectResponse);
gSendArguments.SetLastSendTime(System::SystemClock().GetMonotonicMilliseconds());
streamer_printf(stream, "\nSend CHIP message with payload size: %d bytes to Node: %" PRIu64 "\n", payloadSize,
kTestDeviceNodeId);
err = ec->SendMessage(Protocols::Id(VendorId::Common, gSendArguments.GetProtocolId()), gSendArguments.GetMessageType(),
std::move(payloadBuf), sendFlags);
exit:
if (err != CHIP_NO_ERROR)
{
if (ec != nullptr)
{
ec->Close();
}
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, CryptoContext::SessionRole::kInitiator,
gFabricIndex);
exit:
if (err != CHIP_NO_ERROR)
{
streamer_printf(stream, "Establish secure session failed, err: %s\n", ErrorStr(err));
gSendArguments.SetLastSendTime(System::SystemClock().GetMonotonicMilliseconds());
}
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::PeerAddress peerAddress;
if (!chip::Inet::IPAddress::FromString(destination, gDestAddr))
{
streamer_printf(stream, "Invalid CHIP Server IP address: %s\n", destination);
ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
}
#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(&DeviceLayer::SystemLayer(), &gTCPManager, &gMessageCounterManager);
SuccessOrExit(err);
}
else
#endif
{
peerAddress = Transport::PeerAddress::UDP(gDestAddr, gSendArguments.GetPort(), Inet::InterfaceId::Null());
err = gSessionManager.Init(&DeviceLayer::SystemLayer(), &gUDPManager, &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 MRP\n");
streamer_printf(stream, " -s <size> application payload size in bytes\n");
}
CHIP_ERROR cmd_send(int argc, char ** argv)
{
streamer_t * sout = streamer_get();
int optIndex = 0;
gSendArguments.Reset();
while (optIndex < argc && argv[optIndex][0] == '-')
{
switch (argv[optIndex][1])
{
case 'h':
PrintUsage(sout);
return CHIP_NO_ERROR;
#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 CHIP_ERROR_INVALID_ARGUMENT;
}
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 CHIP_ERROR_INVALID_ARGUMENT;
}
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 CHIP_ERROR_INVALID_ARGUMENT;
}
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 CHIP_ERROR_INVALID_ARGUMENT;
}
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 CHIP_ERROR_INVALID_ARGUMENT;
}
else
{
int arg = atoi(argv[optIndex]);
if (arg == 0)
{
gSendArguments.SetUsingMRP(false);
}
else if (arg == 1)
{
gSendArguments.SetUsingMRP(true);
}
else
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
}
break;
default:
return CHIP_ERROR_INVALID_ARGUMENT;
}
optIndex++;
}
if (optIndex >= argc)
{
streamer_printf(sout, "Missing IP address\n");
return CHIP_ERROR_INVALID_ARGUMENT;
}
streamer_printf(sout, "IP address: %s\n", argv[optIndex]);
ProcessCommand(sout, argv[optIndex]);
return CHIP_NO_ERROR;
}
} // 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));
}