cmd_ping.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  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. mPayloadSize = 32;
  51. mWaitingForEchoResp = false;
  52. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  53. mUsingTCP = false;
  54. #endif
  55. mUsingMRP = 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 GetPayloadSize() const { return mPayloadSize; }
  71. void SetPayloadSize(uint32_t value) { mPayloadSize = 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 IsUsingMRP() const { return mUsingMRP; }
  81. void SetUsingMRP(bool value) { mUsingMRP = 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 mPayloadSize;
  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 mUsingMRP;
  103. } gPingArguments;
  104. Protocols::Echo::EchoClient gEchoClient;
  105. Transport::AdminPairingTable gAdmins;
  106. CHIP_ERROR SendEchoRequest(streamer_t * stream);
  107. void EchoTimerHandler(chip::System::Layer * systemLayer, void * appState, CHIP_ERROR error);
  108. Transport::PeerAddress GetEchoPeerAddress()
  109. {
  110. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  111. if (gPingArguments.IsUsingTCP())
  112. {
  113. return Transport::PeerAddress::TCP(gDestAddr, gPingArguments.GetEchoPort());
  114. }
  115. else
  116. #endif
  117. {
  118. return Transport::PeerAddress::UDP(gDestAddr, gPingArguments.GetEchoPort(), INET_NULL_INTERFACEID);
  119. }
  120. }
  121. void Shutdown()
  122. {
  123. chip::DeviceLayer::SystemLayer.CancelTimer(EchoTimerHandler, NULL);
  124. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  125. if (gPingArguments.IsUsingTCP())
  126. {
  127. gTCPManager.Disconnect(GetEchoPeerAddress());
  128. }
  129. gTCPManager.Close();
  130. #endif
  131. gUDPManager.Close();
  132. gEchoClient.Shutdown();
  133. gExchangeManager.Shutdown();
  134. gSessionManager.Shutdown();
  135. }
  136. void EchoTimerHandler(chip::System::Layer * systemLayer, void * appState, CHIP_ERROR error)
  137. {
  138. if (gPingArguments.GetEchoRespCount() != gPingArguments.GetEchoCount())
  139. {
  140. streamer_printf(streamer_get(), "No response received\n");
  141. gPingArguments.SetEchoRespCount(gPingArguments.GetEchoCount());
  142. }
  143. if (gPingArguments.GetEchoCount() < gPingArguments.GetMaxEchoCount())
  144. {
  145. CHIP_ERROR err = SendEchoRequest(streamer_get());
  146. if (err != CHIP_NO_ERROR)
  147. {
  148. streamer_printf(streamer_get(), "Send request failed: %s\n", ErrorStr(err));
  149. Shutdown();
  150. }
  151. }
  152. else
  153. {
  154. Shutdown();
  155. }
  156. }
  157. CHIP_ERROR SendEchoRequest(streamer_t * stream)
  158. {
  159. CHIP_ERROR err = CHIP_NO_ERROR;
  160. Messaging::SendFlags sendFlags;
  161. System::PacketBufferHandle payloadBuf;
  162. uint32_t payloadSize = gPingArguments.GetPayloadSize();
  163. payloadBuf = MessagePacketBuffer::New(payloadSize);
  164. VerifyOrExit(!payloadBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
  165. memset(payloadBuf->Start(), 0, payloadSize);
  166. payloadBuf->SetDataLength(payloadSize);
  167. if (gPingArguments.IsUsingMRP())
  168. {
  169. sendFlags.Set(Messaging::SendMessageFlags::kNone);
  170. }
  171. else
  172. {
  173. sendFlags.Set(Messaging::SendMessageFlags::kNoAutoRequestAck);
  174. }
  175. gPingArguments.SetLastEchoTime(System::Clock::GetMonotonicMilliseconds());
  176. SuccessOrExit(chip::DeviceLayer::SystemLayer.StartTimer(gPingArguments.GetEchoInterval(), EchoTimerHandler, NULL));
  177. streamer_printf(stream, "\nSend echo request message with payload size: %d bytes to Node: %" PRIu64 "\n", payloadSize,
  178. kTestDeviceNodeId);
  179. err = gEchoClient.SendEchoRequest(std::move(payloadBuf), sendFlags);
  180. if (err == CHIP_NO_ERROR)
  181. {
  182. gPingArguments.SetWaitingForEchoResp(true);
  183. gPingArguments.IncrementEchoCount();
  184. }
  185. else
  186. {
  187. chip::DeviceLayer::SystemLayer.CancelTimer(EchoTimerHandler, NULL);
  188. }
  189. exit:
  190. if (err != CHIP_NO_ERROR)
  191. {
  192. streamer_printf(stream, "Send echo request failed, err: %s\n", ErrorStr(err));
  193. }
  194. return err;
  195. }
  196. CHIP_ERROR EstablishSecureSession(streamer_t * stream, const Transport::PeerAddress & peerAddress)
  197. {
  198. CHIP_ERROR err = CHIP_NO_ERROR;
  199. Optional<Transport::PeerAddress> peerAddr;
  200. SecurePairingUsingTestSecret * testSecurePairingSecret = chip::Platform::New<SecurePairingUsingTestSecret>();
  201. VerifyOrExit(testSecurePairingSecret != nullptr, err = CHIP_ERROR_NO_MEMORY);
  202. peerAddr = Optional<Transport::PeerAddress>::Value(peerAddress);
  203. // Attempt to connect to the peer.
  204. err = gSessionManager.NewPairing(peerAddr, kTestDeviceNodeId, testSecurePairingSecret, SecureSession::SessionRole::kInitiator,
  205. gAdminId);
  206. exit:
  207. if (err != CHIP_NO_ERROR)
  208. {
  209. streamer_printf(stream, "Establish secure session failed, err: %s\n", ErrorStr(err));
  210. gPingArguments.SetLastEchoTime(System::Clock::GetMonotonicMilliseconds());
  211. }
  212. else
  213. {
  214. streamer_printf(stream, "Establish secure session succeeded\n");
  215. }
  216. return err;
  217. }
  218. void HandleEchoResponseReceived(Messaging::ExchangeContext * ec, System::PacketBufferHandle && payload)
  219. {
  220. uint32_t respTime = System::Clock::GetMonotonicMilliseconds();
  221. uint32_t transitTime = respTime - gPingArguments.GetLastEchoTime();
  222. streamer_t * sout = streamer_get();
  223. gPingArguments.SetWaitingForEchoResp(false);
  224. gPingArguments.IncrementEchoRespCount();
  225. streamer_printf(sout, "Echo Response: %" PRIu64 "/%" PRIu64 "(%.2f%%) len=%u time=%.3fms\n", gPingArguments.GetEchoRespCount(),
  226. gPingArguments.GetEchoCount(),
  227. static_cast<double>(gPingArguments.GetEchoRespCount()) * 100 / gPingArguments.GetEchoCount(),
  228. payload->DataLength(), static_cast<double>(transitTime) / 1000);
  229. }
  230. void StartPinging(streamer_t * stream, char * destination)
  231. {
  232. CHIP_ERROR err = CHIP_NO_ERROR;
  233. Transport::AdminPairingInfo * adminInfo = nullptr;
  234. if (!IPAddress::FromString(destination, gDestAddr))
  235. {
  236. streamer_printf(stream, "Invalid Echo Server IP address: %s\n", destination);
  237. ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
  238. }
  239. adminInfo = gAdmins.AssignAdminId(gAdminId, kTestControllerNodeId);
  240. VerifyOrExit(adminInfo != nullptr, err = CHIP_ERROR_NO_MEMORY);
  241. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  242. err = gTCPManager.Init(Transport::TcpListenParameters(&DeviceLayer::InetLayer)
  243. .SetAddressType(gDestAddr.Type())
  244. .SetListenPort(gPingArguments.GetEchoPort() + 1));
  245. VerifyOrExit(err == CHIP_NO_ERROR, streamer_printf(stream, "Failed to init TCP manager error: %s\n", ErrorStr(err)));
  246. #endif
  247. err = gUDPManager.Init(Transport::UdpListenParameters(&DeviceLayer::InetLayer)
  248. .SetAddressType(gDestAddr.Type())
  249. .SetListenPort(gPingArguments.GetEchoPort() + 1));
  250. VerifyOrExit(err == CHIP_NO_ERROR, streamer_printf(stream, "Failed to init UDP manager error: %s\n", ErrorStr(err)));
  251. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  252. if (gPingArguments.IsUsingTCP())
  253. {
  254. err =
  255. gSessionManager.Init(kTestControllerNodeId, &DeviceLayer::SystemLayer, &gTCPManager, &gAdmins, &gMessageCounterManager);
  256. SuccessOrExit(err);
  257. err = gExchangeManager.Init(&gSessionManager);
  258. SuccessOrExit(err);
  259. }
  260. else
  261. #endif
  262. {
  263. err =
  264. gSessionManager.Init(kTestControllerNodeId, &DeviceLayer::SystemLayer, &gUDPManager, &gAdmins, &gMessageCounterManager);
  265. SuccessOrExit(err);
  266. err = gExchangeManager.Init(&gSessionManager);
  267. SuccessOrExit(err);
  268. }
  269. err = gMessageCounterManager.Init(&gExchangeManager);
  270. SuccessOrExit(err);
  271. // Start the CHIP connection to the CHIP echo responder.
  272. err = EstablishSecureSession(stream, GetEchoPeerAddress());
  273. SuccessOrExit(err);
  274. // TODO: temprary create a SecureSessionHandle from node id to unblock end-to-end test. Complete solution is tracked in PR:4451
  275. err = gEchoClient.Init(&gExchangeManager, { kTestDeviceNodeId, 0, gAdminId });
  276. SuccessOrExit(err);
  277. // Arrange to get a callback whenever an Echo Response is received.
  278. gEchoClient.SetEchoResponseReceived(HandleEchoResponseReceived);
  279. err = SendEchoRequest(stream);
  280. if (err != CHIP_NO_ERROR)
  281. {
  282. streamer_printf(stream, "Send request failed: %s\n", ErrorStr(err));
  283. }
  284. exit:
  285. if (err != CHIP_NO_ERROR)
  286. {
  287. streamer_printf(stream, "Ping failed with error: %s\n", ErrorStr(err));
  288. Shutdown();
  289. }
  290. }
  291. void PrintUsage(streamer_t * stream)
  292. {
  293. streamer_printf(stream, "Usage: ping [options] <destination>\n\nOptions:\n");
  294. // Need to split the help info to prevent overflowing the streamer_printf
  295. // buffer (CONSOLE_DEFAULT_MAX_LINE 256)
  296. streamer_printf(stream, " -h print help information\n");
  297. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  298. streamer_printf(stream, " -u use UDP (default)\n");
  299. streamer_printf(stream, " -t use TCP\n");
  300. #endif
  301. streamer_printf(stream, " -p <port> echo server port\n");
  302. streamer_printf(stream, " -i <interval> ping interval time in seconds\n");
  303. streamer_printf(stream, " -c <count> stop after <count> replies\n");
  304. streamer_printf(stream, " -r <1|0> enable or disable MRP\n");
  305. streamer_printf(stream, " -s <size> application payload size in bytes\n");
  306. }
  307. CHIP_ERROR cmd_ping(int argc, char ** argv)
  308. {
  309. streamer_t * sout = streamer_get();
  310. int optIndex = 0;
  311. gPingArguments.Reset();
  312. while (optIndex < argc && argv[optIndex][0] == '-')
  313. {
  314. switch (argv[optIndex][1])
  315. {
  316. case 'h':
  317. PrintUsage(sout);
  318. return CHIP_NO_ERROR;
  319. #if INET_CONFIG_ENABLE_TCP_ENDPOINT
  320. case 'u':
  321. gPingArguments.SetUsingTCP(false);
  322. break;
  323. case 't':
  324. gPingArguments.SetUsingTCP(true);
  325. break;
  326. #endif
  327. case 'i':
  328. if (++optIndex >= argc || argv[optIndex][0] == '-')
  329. {
  330. streamer_printf(sout, "Invalid argument specified for -i\n");
  331. return CHIP_ERROR_INVALID_ARGUMENT;
  332. }
  333. else
  334. {
  335. gPingArguments.SetEchoInterval(atol(argv[optIndex]) * 1000);
  336. }
  337. break;
  338. case 'c':
  339. if (++optIndex >= argc || argv[optIndex][0] == '-')
  340. {
  341. streamer_printf(sout, "Invalid argument specified for -c\n");
  342. return CHIP_ERROR_INVALID_ARGUMENT;
  343. }
  344. else
  345. {
  346. gPingArguments.SetMaxEchoCount(atol(argv[optIndex]));
  347. }
  348. break;
  349. case 'p':
  350. if (++optIndex >= argc || argv[optIndex][0] == '-')
  351. {
  352. streamer_printf(sout, "Invalid argument specified for -p\n");
  353. return CHIP_ERROR_INVALID_ARGUMENT;
  354. }
  355. else
  356. {
  357. gPingArguments.SetEchoPort(atol(argv[optIndex]));
  358. }
  359. break;
  360. case 's':
  361. if (++optIndex >= argc || argv[optIndex][0] == '-')
  362. {
  363. streamer_printf(sout, "Invalid argument specified for -s\n");
  364. return CHIP_ERROR_INVALID_ARGUMENT;
  365. }
  366. else
  367. {
  368. gPingArguments.SetPayloadSize(atol(argv[optIndex]));
  369. }
  370. break;
  371. case 'r':
  372. if (++optIndex >= argc || argv[optIndex][0] == '-')
  373. {
  374. streamer_printf(sout, "Invalid argument specified for -r\n");
  375. return CHIP_ERROR_INVALID_ARGUMENT;
  376. }
  377. else
  378. {
  379. int arg = atoi(argv[optIndex]);
  380. if (arg == 0)
  381. {
  382. gPingArguments.SetUsingMRP(false);
  383. }
  384. else if (arg == 1)
  385. {
  386. gPingArguments.SetUsingMRP(true);
  387. }
  388. else
  389. {
  390. return CHIP_ERROR_INVALID_ARGUMENT;
  391. }
  392. }
  393. break;
  394. default:
  395. return CHIP_ERROR_INVALID_ARGUMENT;
  396. }
  397. optIndex++;
  398. }
  399. if (optIndex >= argc)
  400. {
  401. streamer_printf(sout, "Missing IP address\n");
  402. return CHIP_ERROR_INVALID_ARGUMENT;
  403. }
  404. streamer_printf(sout, "IP address: %s\n", argv[optIndex]);
  405. StartPinging(sout, argv[optIndex]);
  406. return CHIP_NO_ERROR;
  407. }
  408. } // namespace
  409. static shell_command_t cmds_ping[] = {
  410. { &cmd_ping, "ping", "Using Echo Protocol to measure packet loss across network paths" },
  411. };
  412. void cmd_ping_init()
  413. {
  414. Engine::Root().RegisterCommands(cmds_ping, ArraySize(cmds_ping));
  415. }