cmd_ping.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /*
  2. *
  3. * Copyright (c) 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. #include <errno.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <lib/core/CHIPCore.h>
  21. #include <lib/shell/Engine.h>
  22. #include <lib/support/CodeUtils.h>
  23. #include <lib/support/ErrorStr.h>
  24. #include <messaging/ExchangeMgr.h>
  25. #include <platform/CHIPDeviceLayer.h>
  26. #include <protocols/echo/Echo.h>
  27. #include <protocols/secure_channel/MessageCounterManager.h>
  28. #include <protocols/secure_channel/PASESession.h>
  29. #include <system/SystemPacketBuffer.h>
  30. #include <transport/SecureSessionMgr.h>
  31. #include <transport/raw/TCP.h>
  32. #include <transport/raw/UDP.h>
  33. #include <ChipShellCollection.h>
  34. #include <Globals.h>
  35. using namespace chip;
  36. using namespace Shell;
  37. using namespace Logging;
  38. using chip::Inet::IPAddress;
  39. namespace {
  40. class PingArguments
  41. {
  42. public:
  43. void Reset()
  44. {
  45. mMaxEchoCount = 3;
  46. mEchoInterval = 1000;
  47. mLastEchoTime = 0;
  48. mEchoCount = 0;
  49. mEchoRespCount = 0;
  50. mEchoReqSize = 32;
  51. mWaitingForEchoResp = false;
  52. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  53. mUsingTCP = false;
  54. #endif
  55. mUsingCRMP = true;
  56. mEchoPort = CHIP_PORT;
  57. }
  58. uint64_t GetLastEchoTime() const { return mLastEchoTime; }
  59. void SetLastEchoTime(uint64_t value) { mLastEchoTime = value; }
  60. uint64_t GetEchoCount() const { return mEchoCount; }
  61. void SetEchoCount(uint64_t value) { mEchoCount = value; }
  62. void IncrementEchoCount() { mEchoCount++; }
  63. uint64_t GetEchoRespCount() const { return mEchoRespCount; }
  64. void SetEchoRespCount(uint64_t value) { mEchoRespCount = value; }
  65. void IncrementEchoRespCount() { mEchoRespCount++; }
  66. uint32_t GetMaxEchoCount() const { return mMaxEchoCount; }
  67. void SetMaxEchoCount(uint32_t id) { mMaxEchoCount = id; }
  68. uint32_t GetEchoInterval() const { return mEchoInterval; }
  69. void SetEchoInterval(uint32_t value) { mEchoInterval = value; }
  70. uint32_t GetEchoReqSize() const { return mEchoReqSize; }
  71. void SetEchoReqSize(uint32_t value) { mEchoReqSize = value; }
  72. uint16_t GetEchoPort() const { return mEchoPort; }
  73. void SetEchoPort(uint16_t value) { mEchoPort = value; }
  74. bool IsWaitingForEchoResp() const { return mWaitingForEchoResp; }
  75. void SetWaitingForEchoResp(bool value) { mWaitingForEchoResp = value; }
  76. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  77. bool IsUsingTCP() const { return mUsingTCP; }
  78. void SetUsingTCP(bool value) { mUsingTCP = value; }
  79. #endif
  80. bool IsUsingCRMP() const { return mUsingCRMP; }
  81. void SetUsingCRMP(bool value) { mUsingCRMP = value; }
  82. private:
  83. // The last time a echo request was attempted to be sent.
  84. uint64_t mLastEchoTime;
  85. // Count of the number of echo requests sent.
  86. uint64_t mEchoCount;
  87. // Count of the number of echo responses received.
  88. uint64_t mEchoRespCount;
  89. // The CHIP Echo request payload size in bytes.
  90. uint32_t mEchoReqSize;
  91. // Max value for the number of echo requests sent.
  92. uint32_t mMaxEchoCount;
  93. // The CHIP Echo interval time in milliseconds.
  94. uint32_t mEchoInterval;
  95. uint16_t mEchoPort;
  96. // True, if the echo client is waiting for an echo response
  97. // after sending an echo request, false otherwise.
  98. bool mWaitingForEchoResp;
  99. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  100. bool mUsingTCP;
  101. #endif
  102. bool mUsingCRMP;
  103. } gPingArguments;
  104. Protocols::Echo::EchoClient gEchoClient;
  105. bool EchoIntervalExpired(void)
  106. {
  107. uint64_t now = System::Timer::GetCurrentEpoch();
  108. return (now >= gPingArguments.GetLastEchoTime() + gPingArguments.GetEchoInterval());
  109. }
  110. CHIP_ERROR SendEchoRequest(streamer_t * stream)
  111. {
  112. CHIP_ERROR err = CHIP_NO_ERROR;
  113. Messaging::SendFlags sendFlags;
  114. System::PacketBufferHandle payloadBuf;
  115. char * requestData = nullptr;
  116. uint32_t size = gPingArguments.GetEchoReqSize();
  117. VerifyOrExit(size <= kMaxPayloadSize, err = CHIP_ERROR_MESSAGE_TOO_LONG);
  118. requestData = static_cast<char *>(chip::Platform::MemoryAlloc(size));
  119. VerifyOrExit(requestData != nullptr, err = CHIP_ERROR_NO_MEMORY);
  120. snprintf(requestData, size, "Echo Message %" PRIu64 "\n", gPingArguments.GetEchoCount());
  121. payloadBuf = MessagePacketBuffer::NewWithData(requestData, size);
  122. VerifyOrExit(!payloadBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
  123. if (gPingArguments.IsUsingCRMP())
  124. {
  125. sendFlags.Set(Messaging::SendMessageFlags::kNone);
  126. }
  127. else
  128. {
  129. sendFlags.Set(Messaging::SendMessageFlags::kNoAutoRequestAck);
  130. }
  131. gPingArguments.SetLastEchoTime(System::Timer::GetCurrentEpoch());
  132. streamer_printf(stream, "\nSend echo request message with payload size: %d bytes to Node: %" PRIu64 "\n", size,
  133. kTestDeviceNodeId);
  134. err = gEchoClient.SendEchoRequest(std::move(payloadBuf), sendFlags);
  135. if (err == CHIP_NO_ERROR)
  136. {
  137. gPingArguments.SetWaitingForEchoResp(true);
  138. gPingArguments.IncrementEchoCount();
  139. }
  140. exit:
  141. if (requestData != nullptr)
  142. {
  143. chip::Platform::MemoryFree(requestData);
  144. }
  145. if (err != CHIP_NO_ERROR)
  146. {
  147. streamer_printf(stream, "Send echo request failed, err: %s\n", ErrorStr(err));
  148. }
  149. return err;
  150. }
  151. CHIP_ERROR EstablishSecureSession(streamer_t * stream, Transport::PeerAddress & peerAddress)
  152. {
  153. CHIP_ERROR err = CHIP_NO_ERROR;
  154. Optional<Transport::PeerAddress> peerAddr;
  155. SecurePairingUsingTestSecret * testSecurePairingSecret = chip::Platform::New<SecurePairingUsingTestSecret>();
  156. VerifyOrExit(testSecurePairingSecret != nullptr, err = CHIP_ERROR_NO_MEMORY);
  157. peerAddr = Optional<Transport::PeerAddress>::Value(peerAddress);
  158. // Attempt to connect to the peer.
  159. err = gSessionManager.NewPairing(peerAddr, kTestDeviceNodeId, testSecurePairingSecret, SecureSession::SessionRole::kInitiator,
  160. gAdminId);
  161. exit:
  162. if (err != CHIP_NO_ERROR)
  163. {
  164. streamer_printf(stream, "Establish secure session failed, err: %s\n", ErrorStr(err));
  165. gPingArguments.SetLastEchoTime(System::Timer::GetCurrentEpoch());
  166. }
  167. else
  168. {
  169. streamer_printf(stream, "Establish secure session succeeded\n");
  170. }
  171. return err;
  172. }
  173. void HandleEchoResponseReceived(Messaging::ExchangeContext * ec, System::PacketBufferHandle && payload)
  174. {
  175. uint32_t respTime = System::Timer::GetCurrentEpoch();
  176. uint32_t transitTime = respTime - gPingArguments.GetLastEchoTime();
  177. streamer_t * sout = streamer_get();
  178. gPingArguments.SetWaitingForEchoResp(false);
  179. gPingArguments.IncrementEchoRespCount();
  180. streamer_printf(sout, "Echo Response: %" PRIu64 "/%" PRIu64 "(%.2f%%) len=%u time=%.3fms\n", gPingArguments.GetEchoRespCount(),
  181. gPingArguments.GetEchoCount(),
  182. static_cast<double>(gPingArguments.GetEchoRespCount()) * 100 / gPingArguments.GetEchoCount(),
  183. payload->DataLength(), static_cast<double>(transitTime) / 1000);
  184. }
  185. void StartPinging(streamer_t * stream, char * destination)
  186. {
  187. CHIP_ERROR err = CHIP_NO_ERROR;
  188. Transport::AdminPairingTable admins;
  189. Transport::PeerAddress peerAddress;
  190. Transport::AdminPairingInfo * adminInfo = nullptr;
  191. uint32_t maxEchoCount = 0;
  192. if (!IPAddress::FromString(destination, gDestAddr))
  193. {
  194. streamer_printf(stream, "Invalid Echo Server IP address: %s\n", destination);
  195. ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
  196. }
  197. adminInfo = admins.AssignAdminId(gAdminId, kTestControllerNodeId);
  198. VerifyOrExit(adminInfo != nullptr, err = CHIP_ERROR_NO_MEMORY);
  199. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  200. err = gTCPManager.Init(Transport::TcpListenParameters(&DeviceLayer::InetLayer)
  201. .SetAddressType(gDestAddr.Type())
  202. .SetListenPort(gPingArguments.GetEchoPort() + 1));
  203. VerifyOrExit(err == CHIP_NO_ERROR, streamer_printf(stream, "Failed to init TCP manager error: %s\n", ErrorStr(err)));
  204. #endif
  205. err = gUDPManager.Init(Transport::UdpListenParameters(&DeviceLayer::InetLayer)
  206. .SetAddressType(gDestAddr.Type())
  207. .SetListenPort(gPingArguments.GetEchoPort() + 1));
  208. VerifyOrExit(err == CHIP_NO_ERROR, streamer_printf(stream, "Failed to init UDP manager error: %s\n", ErrorStr(err)));
  209. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  210. if (gPingArguments.IsUsingTCP())
  211. {
  212. peerAddress = Transport::PeerAddress::TCP(gDestAddr, gPingArguments.GetEchoPort());
  213. err =
  214. gSessionManager.Init(kTestControllerNodeId, &DeviceLayer::SystemLayer, &gTCPManager, &admins, &gMessageCounterManager);
  215. SuccessOrExit(err);
  216. err = gExchangeManager.Init(&gSessionManager);
  217. SuccessOrExit(err);
  218. }
  219. else
  220. #endif
  221. {
  222. peerAddress = Transport::PeerAddress::UDP(gDestAddr, gPingArguments.GetEchoPort(), INET_NULL_INTERFACEID);
  223. err =
  224. gSessionManager.Init(kTestControllerNodeId, &DeviceLayer::SystemLayer, &gUDPManager, &admins, &gMessageCounterManager);
  225. SuccessOrExit(err);
  226. err = gExchangeManager.Init(&gSessionManager);
  227. SuccessOrExit(err);
  228. }
  229. err = gMessageCounterManager.Init(&gExchangeManager);
  230. SuccessOrExit(err);
  231. // Start the CHIP connection to the CHIP echo responder.
  232. err = EstablishSecureSession(stream, peerAddress);
  233. SuccessOrExit(err);
  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, { kTestDeviceNodeId, 0, gAdminId });
  236. SuccessOrExit(err);
  237. // Arrange to get a callback whenever an Echo Response is received.
  238. gEchoClient.SetEchoResponseReceived(HandleEchoResponseReceived);
  239. maxEchoCount = gPingArguments.GetMaxEchoCount();
  240. // Connection has been established. Now send the EchoRequests.
  241. for (unsigned int i = 0; i < maxEchoCount; i++)
  242. {
  243. err = SendEchoRequest(stream);
  244. if (err != CHIP_NO_ERROR)
  245. {
  246. streamer_printf(stream, "Send request failed: %s\n", ErrorStr(err));
  247. break;
  248. }
  249. // Wait for response until the Echo interval.
  250. while (!EchoIntervalExpired())
  251. {
  252. // TODO:#5496: Use condition_varible to suspend the current thread and wake it up when response arrive.
  253. sleep(1);
  254. }
  255. // Check if expected response was received.
  256. if (gPingArguments.IsWaitingForEchoResp())
  257. {
  258. streamer_printf(stream, "No response received\n");
  259. gPingArguments.SetWaitingForEchoResp(false);
  260. }
  261. }
  262. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  263. gTCPManager.Disconnect(peerAddress);
  264. gTCPManager.Close();
  265. #endif
  266. gUDPManager.Close();
  267. gEchoClient.Shutdown();
  268. gExchangeManager.Shutdown();
  269. gSessionManager.Shutdown();
  270. exit:
  271. if ((err != CHIP_NO_ERROR))
  272. {
  273. streamer_printf(stream, "Ping failed with error: %s\n", ErrorStr(err));
  274. }
  275. }
  276. void PrintUsage(streamer_t * stream)
  277. {
  278. streamer_printf(stream, "Usage: ping [options] <destination>\n\nOptions:\n");
  279. // Need to split the help info to prevent overflowing the streamer_printf
  280. // buffer (CONSOLE_DEFAULT_MAX_LINE 256)
  281. streamer_printf(stream, " -h print help information\n");
  282. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  283. streamer_printf(stream, " -u use UDP (default)\n");
  284. streamer_printf(stream, " -t use TCP\n");
  285. #endif
  286. streamer_printf(stream, " -p <port> echo server port\n");
  287. streamer_printf(stream, " -i <interval> ping interval time in seconds\n");
  288. streamer_printf(stream, " -c <count> stop after <count> replies\n");
  289. streamer_printf(stream, " -r <1|0> enable or disable CRMP\n");
  290. streamer_printf(stream, " -s <size> payload size in bytes\n");
  291. }
  292. int cmd_ping(int argc, char ** argv)
  293. {
  294. streamer_t * sout = streamer_get();
  295. int ret = 0;
  296. int optIndex = 0;
  297. gPingArguments.Reset();
  298. while (optIndex < argc && argv[optIndex][0] == '-')
  299. {
  300. switch (argv[optIndex][1])
  301. {
  302. case 'h':
  303. PrintUsage(sout);
  304. return 0;
  305. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  306. case 'u':
  307. gPingArguments.SetUsingTCP(false);
  308. break;
  309. case 't':
  310. gPingArguments.SetUsingTCP(true);
  311. break;
  312. #endif
  313. case 'i':
  314. if (++optIndex >= argc || argv[optIndex][0] == '-')
  315. {
  316. streamer_printf(sout, "Invalid argument specified for -i\n");
  317. return -1;
  318. }
  319. else
  320. {
  321. gPingArguments.SetEchoInterval(atol(argv[optIndex]) * 1000);
  322. }
  323. break;
  324. case 'c':
  325. if (++optIndex >= argc || argv[optIndex][0] == '-')
  326. {
  327. streamer_printf(sout, "Invalid argument specified for -c\n");
  328. return -1;
  329. }
  330. else
  331. {
  332. gPingArguments.SetMaxEchoCount(atol(argv[optIndex]));
  333. }
  334. break;
  335. case 'p':
  336. if (++optIndex >= argc || argv[optIndex][0] == '-')
  337. {
  338. streamer_printf(sout, "Invalid argument specified for -p\n");
  339. return -1;
  340. }
  341. else
  342. {
  343. gPingArguments.SetEchoPort(atol(argv[optIndex]));
  344. }
  345. break;
  346. case 's':
  347. if (++optIndex >= argc || argv[optIndex][0] == '-')
  348. {
  349. streamer_printf(sout, "Invalid argument specified for -s\n");
  350. return -1;
  351. }
  352. else
  353. {
  354. gPingArguments.SetEchoReqSize(atol(argv[optIndex]));
  355. }
  356. break;
  357. case 'r':
  358. if (++optIndex >= argc || argv[optIndex][0] == '-')
  359. {
  360. streamer_printf(sout, "Invalid argument specified for -r\n");
  361. return -1;
  362. }
  363. else
  364. {
  365. int arg = atoi(argv[optIndex]);
  366. if (arg == 0)
  367. {
  368. gPingArguments.SetUsingCRMP(false);
  369. }
  370. else if (arg == 1)
  371. {
  372. gPingArguments.SetUsingCRMP(true);
  373. }
  374. else
  375. {
  376. ret = -1;
  377. }
  378. }
  379. break;
  380. default:
  381. ret = -1;
  382. }
  383. optIndex++;
  384. }
  385. if (optIndex >= argc)
  386. {
  387. streamer_printf(sout, "Missing IP address\n");
  388. ret = -1;
  389. }
  390. if (ret == 0)
  391. {
  392. streamer_printf(sout, "IP address: %s\n", argv[optIndex]);
  393. StartPinging(sout, argv[optIndex]);
  394. }
  395. return ret;
  396. }
  397. } // namespace
  398. static shell_command_t cmds_ping[] = {
  399. { &cmd_ping, "ping", "Using Echo Protocol to measure packet loss across network paths" },
  400. };
  401. void cmd_ping_init()
  402. {
  403. Engine::Root().RegisterCommands(cmds_ping, ArraySize(cmds_ping));
  404. }