Apply Upstream code (2021-03-15)
[platform/upstream/connectedhomeip.git] / src / messaging / tests / echo / echo_requester.cpp
1 /*
2  *
3  *    Copyright (c) 2020-2021 Project CHIP Authors
4  *
5  *    Licensed under the Apache License, Version 2.0 (the "License");
6  *    you may not use this file except in compliance with the License.
7  *    You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *    Unless required by applicable law or agreed to in writing, software
12  *    distributed under the License is distributed on an "AS IS" BASIS,
13  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *    See the License for the specific language governing permissions and
15  *    limitations under the License.
16  */
17
18 /**
19  *    @file
20  *      This file implements a chip-echo-requester, for the
21  *      CHIP Echo Protocol.
22  *
23  *      The CHIP Echo Protocol implements two simple methods, in the
24  *      style of ICMP ECHO REQUEST and ECHO REPLY, in which a sent
25  *      payload is turned around by the responder and echoed back to
26  *      the originator.
27  *
28  */
29
30 #include "common.h"
31
32 #include <core/CHIPCore.h>
33 #include <platform/CHIPDeviceLayer.h>
34 #include <protocols/echo/Echo.h>
35 #include <support/ErrorStr.h>
36 #include <system/SystemPacketBuffer.h>
37 #include <transport/PASESession.h>
38 #include <transport/SecureSessionMgr.h>
39 #include <transport/raw/TCP.h>
40 #include <transport/raw/UDP.h>
41
42 #include <inttypes.h>
43 #include <stdio.h>
44 #include <string.h>
45
46 #define ECHO_CLIENT_PORT (CHIP_PORT + 1)
47
48 namespace {
49
50 // Max value for the number of EchoRequests sent.
51 constexpr size_t kMaxEchoCount = 3;
52
53 // The CHIP Echo interval time in milliseconds.
54 constexpr int32_t gEchoInterval = 1000;
55
56 constexpr chip::Transport::AdminId gAdminId = 0;
57
58 // The EchoClient object.
59 chip::Protocols::Echo::EchoClient gEchoClient;
60
61 chip::TransportMgr<chip::Transport::UDP> gUDPManager;
62 chip::TransportMgr<chip::Transport::TCP<kMaxTcpActiveConnectionCount, kMaxTcpPendingPackets>> gTCPManager;
63 chip::SecureSessionMgr gSessionManager;
64 chip::Inet::IPAddress gDestAddr;
65
66 // The last time a CHIP Echo was attempted to be sent.
67 uint64_t gLastEchoTime = 0;
68
69 // True, if the EchoClient is waiting for an EchoResponse
70 // after sending an EchoRequest, false otherwise.
71 bool gWaitingForEchoResp = false;
72
73 // Count of the number of EchoRequests sent.
74 uint64_t gEchoCount = 0;
75
76 // Count of the number of EchoResponses received.
77 uint64_t gEchoRespCount = 0;
78
79 bool gUseTCP = false;
80
81 bool EchoIntervalExpired(void)
82 {
83     uint64_t now = chip::System::Timer::GetCurrentEpoch();
84
85     return (now >= gLastEchoTime + gEchoInterval);
86 }
87
88 CHIP_ERROR SendEchoRequest(void)
89 {
90     CHIP_ERROR err              = CHIP_NO_ERROR;
91     const char kRequestFormat[] = "Echo Message %" PRIu64 "\n";
92     char requestData[(sizeof kRequestFormat) + 20 /* uint64_t decimal digits */];
93     snprintf(requestData, sizeof requestData, kRequestFormat, gEchoCount);
94     chip::System::PacketBufferHandle payloadBuf = chip::MessagePacketBuffer::NewWithData(requestData, strlen(requestData));
95
96     if (payloadBuf.IsNull())
97     {
98         printf("Unable to allocate packet buffer\n");
99         return CHIP_ERROR_NO_MEMORY;
100     }
101
102     gLastEchoTime = chip::System::Timer::GetCurrentEpoch();
103
104     printf("\nSend echo request message to Node: %" PRIu64 "\n", chip::kTestDeviceNodeId);
105
106     err = gEchoClient.SendEchoRequest(std::move(payloadBuf), chip::Messaging::SendFlags(chip::Messaging::SendMessageFlags::kNone));
107
108     if (err == CHIP_NO_ERROR)
109     {
110         gWaitingForEchoResp = true;
111         gEchoCount++;
112     }
113     else
114     {
115         printf("Send echo request failed, err: %s\n", chip::ErrorStr(err));
116     }
117
118     return err;
119 }
120
121 CHIP_ERROR EstablishSecureSession()
122 {
123     CHIP_ERROR err = CHIP_NO_ERROR;
124
125     chip::Optional<chip::Transport::PeerAddress> peerAddr;
126     chip::SecurePairingUsingTestSecret * testSecurePairingSecret = chip::Platform::New<chip::SecurePairingUsingTestSecret>();
127     VerifyOrExit(testSecurePairingSecret != nullptr, err = CHIP_ERROR_NO_MEMORY);
128
129     if (gUseTCP)
130     {
131         peerAddr = chip::Optional<chip::Transport::PeerAddress>::Value(chip::Transport::PeerAddress::TCP(gDestAddr, CHIP_PORT));
132     }
133     else
134     {
135         peerAddr = chip::Optional<chip::Transport::PeerAddress>::Value(
136             chip::Transport::PeerAddress::UDP(gDestAddr, CHIP_PORT, INET_NULL_INTERFACEID));
137     }
138
139     // Attempt to connect to the peer.
140     err = gSessionManager.NewPairing(peerAddr, chip::kTestDeviceNodeId, testSecurePairingSecret,
141                                      chip::SecureSessionMgr::PairingDirection::kInitiator, gAdminId);
142
143 exit:
144     if (err != CHIP_NO_ERROR)
145     {
146         printf("Establish secure session failed, err: %s\n", chip::ErrorStr(err));
147         gLastEchoTime = chip::System::Timer::GetCurrentEpoch();
148     }
149     else
150     {
151         printf("Establish secure session succeeded\n");
152     }
153
154     return err;
155 }
156
157 void HandleEchoResponseReceived(chip::Messaging::ExchangeContext * ec, chip::System::PacketBufferHandle payload)
158 {
159     uint32_t respTime    = chip::System::Timer::GetCurrentEpoch();
160     uint32_t transitTime = respTime - gLastEchoTime;
161
162     gWaitingForEchoResp = false;
163     gEchoRespCount++;
164
165     printf("Echo Response: %" PRIu64 "/%" PRIu64 "(%.2f%%) len=%u time=%.3fms\n", gEchoRespCount, gEchoCount,
166            static_cast<double>(gEchoRespCount) * 100 / gEchoCount, payload->DataLength(), static_cast<double>(transitTime) / 1000);
167 }
168
169 } // namespace
170
171 int main(int argc, char * argv[])
172 {
173     CHIP_ERROR err = CHIP_NO_ERROR;
174
175     chip::Transport::AdminPairingTable admins;
176     chip::Transport::AdminPairingInfo * adminInfo = nullptr;
177
178     if (argc <= 1)
179     {
180         printf("Missing Echo Server IP address\n");
181         ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
182     }
183
184     if (argc > 3)
185     {
186         printf("Too many arguments specified!\n");
187         ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
188     }
189
190     if ((argc == 3) && (strcmp(argv[2], "--tcp") == 0))
191     {
192         gUseTCP = true;
193     }
194
195     if (!chip::Inet::IPAddress::FromString(argv[1], gDestAddr))
196     {
197         printf("Invalid Echo Server IP address: %s\n", argv[1]);
198         ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
199     }
200
201     InitializeChip();
202
203     adminInfo = admins.AssignAdminId(gAdminId, chip::kTestControllerNodeId);
204     VerifyOrExit(adminInfo != nullptr, err = CHIP_ERROR_NO_MEMORY);
205
206     if (gUseTCP)
207     {
208         err = gTCPManager.Init(chip::Transport::TcpListenParameters(&chip::DeviceLayer::InetLayer)
209                                    .SetAddressType(chip::Inet::kIPAddressType_IPv4)
210                                    .SetListenPort(ECHO_CLIENT_PORT));
211         SuccessOrExit(err);
212
213         err = gSessionManager.Init(chip::kTestControllerNodeId, &chip::DeviceLayer::SystemLayer, &gTCPManager, &admins);
214         SuccessOrExit(err);
215     }
216     else
217     {
218         err = gUDPManager.Init(chip::Transport::UdpListenParameters(&chip::DeviceLayer::InetLayer)
219                                    .SetAddressType(chip::Inet::kIPAddressType_IPv4)
220                                    .SetListenPort(ECHO_CLIENT_PORT));
221         SuccessOrExit(err);
222
223         err = gSessionManager.Init(chip::kTestControllerNodeId, &chip::DeviceLayer::SystemLayer, &gUDPManager, &admins);
224         SuccessOrExit(err);
225     }
226
227     err = gExchangeManager.Init(chip::kTestControllerNodeId, &gUDPManager, &gSessionManager);
228     SuccessOrExit(err);
229
230     // Start the CHIP connection to the CHIP echo responder.
231     err = EstablishSecureSession();
232     SuccessOrExit(err);
233
234     // TODO: temprary create a SecureSessionHandle from node id to unblock end-to-end test. Complete solution is tracked in PR:4451
235     err = gEchoClient.Init(&gExchangeManager, { chip::kTestDeviceNodeId, 0, gAdminId });
236     SuccessOrExit(err);
237
238     // Arrange to get a callback whenever an Echo Response is received.
239     gEchoClient.SetEchoResponseReceived(HandleEchoResponseReceived);
240
241     // Connection has been established. Now send the EchoRequests.
242     for (unsigned int i = 0; i < kMaxEchoCount; i++)
243     {
244         err = SendEchoRequest();
245         if (err != CHIP_NO_ERROR)
246         {
247             printf("Send request failed: %s\n", chip::ErrorStr(err));
248             break;
249         }
250
251         // Wait for response until the Echo interval.
252         while (!EchoIntervalExpired())
253         {
254             DriveIO();
255         }
256
257         // Check if expected response was received.
258         if (gWaitingForEchoResp)
259         {
260             printf("No response received\n");
261             gWaitingForEchoResp = false;
262         }
263     }
264
265     gEchoClient.Shutdown();
266
267     ShutdownChip();
268
269 exit:
270     if ((err != CHIP_NO_ERROR) || (gEchoRespCount != kMaxEchoCount))
271     {
272         printf("ChipEchoClient failed: %s\n", chip::ErrorStr(err));
273         exit(EXIT_FAILURE);
274     }
275
276     return EXIT_SUCCESS;
277 }