nodereply_test.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. #include <gtest/gtest.h>
  2. #include <stdexcept>
  3. #include <tins/tins.h>
  4. #include "nodereply.hpp"
  5. TEST(NodeReplyTest, SimpleInit)
  6. {
  7. const NodeReply reply(NodeReplyType::ICMP_TIME_EXCEEDED_ICMP_ECHO_REQUEST);
  8. EXPECT_EQ(reply.get_type(),
  9. NodeReplyType::ICMP_TIME_EXCEEDED_ICMP_ECHO_REQUEST);
  10. }
  11. TEST(NodeReplyTest, NoReply)
  12. {
  13. NodeReply reply(NodeReplyType::NOREPLY);
  14. EXPECT_EQ(reply.get_type(), NodeReplyType::NOREPLY);
  15. try
  16. {
  17. [[maybe_unused]] const std::string reply_packet = reply.to_packet();
  18. FAIL();
  19. }
  20. catch (const std::exception& e)
  21. {
  22. SUCCEED();
  23. EXPECT_EQ(std::string(e.what()),
  24. "Attempt to create a packet, although there is no reply.");
  25. }
  26. try
  27. {
  28. reply.icmp_echo_reply(56, 1, {});
  29. FAIL();
  30. }
  31. catch (const std::exception& e)
  32. {
  33. SUCCEED();
  34. EXPECT_EQ(std::string(e.what()),
  35. "NodeReply has no type that would require ICMP echo reply "
  36. "information.");
  37. }
  38. try
  39. {
  40. reply.udp_response({}, 33434, 45834);
  41. FAIL();
  42. }
  43. catch (const std::exception& e)
  44. {
  45. SUCCEED();
  46. EXPECT_EQ(std::string(e.what()),
  47. "NodeReply has no type that would require UDP information.");
  48. }
  49. try
  50. {
  51. reply.packet_reassembly(Tins::IPv6Address("fd00::"));
  52. FAIL();
  53. }
  54. catch (const std::exception& e)
  55. {
  56. SUCCEED();
  57. EXPECT_EQ(std::string(e.what()),
  58. "NodeReply has no type that would require original "
  59. "destionation address information.");
  60. }
  61. std::ostringstream test_output;
  62. test_output << reply;
  63. EXPECT_EQ(test_output.str(), "NOREPLY");
  64. }
  65. TEST(NodeReplyTest, IcmpEchoReply)
  66. {
  67. const Tins::HWAddress<6> source_mac("52:54:00:b2:fa:7f");
  68. const Tins::HWAddress<6> destination_mac("52:54:00:b2:fa:7e");
  69. const Tins::IPv6Address source_address("fd00::1");
  70. const Tins::IPv6Address destination_address("fd00::2");
  71. constexpr int hoplimit = 55;
  72. constexpr int icmp_identifier = 56;
  73. constexpr int icmp_sequence = 1;
  74. const Tins::RawPDU::payload_type payload = {8, 4, 5, 9, 255, 0, 0, 0, 0, 0};
  75. /* Reply */
  76. NodeReply reply(NodeReplyType::ICMP_ECHO_REPLY,
  77. destination_mac,
  78. destination_address,
  79. source_mac,
  80. source_address);
  81. reply.set_hoplimit(hoplimit);
  82. reply.icmp_echo_reply(icmp_identifier, icmp_sequence, payload);
  83. const std::string actual_packet = reply.to_packet();
  84. /* Expected packet */
  85. Tins::EthernetII packet = Tins::EthernetII(destination_mac, source_mac) /
  86. Tins::IPv6(destination_address, source_address) /
  87. Tins::ICMPv6(Tins::ICMPv6::Types::ECHO_REPLY);
  88. Tins::IPv6& inner_ipv6 = packet.rfind_pdu<Tins::IPv6>();
  89. inner_ipv6.hop_limit(hoplimit);
  90. Tins::ICMPv6& inner_icmpv6 = inner_ipv6.rfind_pdu<Tins::ICMPv6>();
  91. inner_icmpv6.identifier(icmp_identifier);
  92. inner_icmpv6.sequence(icmp_sequence);
  93. inner_icmpv6.inner_pdu(Tins::RawPDU(payload));
  94. const Tins::PDU::serialization_type serialized_packet = packet.serialize();
  95. const std::string expected_packet(serialized_packet.begin(),
  96. serialized_packet.end());
  97. /* Tests */
  98. try
  99. {
  100. reply.udp_response({}, 33434, 45834);
  101. FAIL();
  102. }
  103. catch (const std::exception& e)
  104. {
  105. SUCCEED();
  106. EXPECT_EQ(std::string(e.what()),
  107. "NodeReply has no type that would require UDP information.");
  108. }
  109. try
  110. {
  111. reply.packet_reassembly(Tins::IPv6Address("fd00::"));
  112. FAIL();
  113. }
  114. catch (const std::exception& e)
  115. {
  116. SUCCEED();
  117. EXPECT_EQ(std::string(e.what()),
  118. "NodeReply has no type that would require original "
  119. "destionation address information.");
  120. }
  121. std::ostringstream test_output;
  122. test_output << reply;
  123. EXPECT_EQ(test_output.str(),
  124. "REPLY ICMP_ECHO_REPLY: fd00::1 (52:54:00:b2:fa:7f) -> fd00::2 "
  125. "(52:54:00:b2:fa:7e) Hoplimit=55: ID=56 SEQ=1 Payload: 08 04 05 "
  126. "09 ff 00 00 00 00 00");
  127. EXPECT_EQ(actual_packet, expected_packet);
  128. EXPECT_EQ(reply.get_type(), NodeReplyType::ICMP_ECHO_REPLY);
  129. }
  130. TEST(NodeReplyTest, IcmpTimeExceededIcmpEchoRequest)
  131. {
  132. const Tins::HWAddress<6> source_mac("52:54:00:b2:fa:7f");
  133. const Tins::HWAddress<6> destination_mac("52:54:00:b2:fa:7e");
  134. const Tins::IPv6Address source_address("fd00::1");
  135. const Tins::IPv6Address destination_address("fd00::2");
  136. const Tins::IPv6Address original_destination_address("fd00::3");
  137. constexpr int hoplimit = 55;
  138. constexpr int icmp_identifier = 56;
  139. constexpr int icmp_sequence = 1;
  140. const Tins::RawPDU::payload_type payload = {8, 4, 5, 9, 255, 0, 0, 0, 0, 0};
  141. /* Reply */
  142. NodeReply reply(NodeReplyType::ICMP_TIME_EXCEEDED_ICMP_ECHO_REQUEST,
  143. destination_mac,
  144. destination_address,
  145. source_mac,
  146. source_address);
  147. reply.set_hoplimit(hoplimit);
  148. reply.icmp_echo_reply(icmp_identifier, icmp_sequence, payload);
  149. reply.packet_reassembly(original_destination_address);
  150. const std::string actual_packet = reply.to_packet();
  151. /* Expected packet */
  152. Tins::IPv6 embedded_packet =
  153. Tins::IPv6(original_destination_address, destination_address) /
  154. Tins::ICMPv6(Tins::ICMPv6::Types::ECHO_REQUEST);
  155. embedded_packet.hop_limit(1);
  156. Tins::ICMPv6& embedded_inner_icmpv6 =
  157. embedded_packet.rfind_pdu<Tins::ICMPv6>();
  158. embedded_inner_icmpv6.identifier(icmp_identifier);
  159. embedded_inner_icmpv6.sequence(icmp_sequence);
  160. embedded_inner_icmpv6.inner_pdu(Tins::RawPDU(payload));
  161. const Tins::PDU::serialization_type serialized_embedded_packet =
  162. embedded_packet.serialize();
  163. Tins::EthernetII packet = Tins::EthernetII(destination_mac, source_mac) /
  164. Tins::IPv6(destination_address, source_address) /
  165. Tins::ICMPv6(Tins::ICMPv6::Types::TIME_EXCEEDED);
  166. Tins::IPv6& inner_ipv6 = packet.rfind_pdu<Tins::IPv6>();
  167. inner_ipv6.hop_limit(hoplimit);
  168. Tins::ICMPv6& inner_icmpv6 = inner_ipv6.rfind_pdu<Tins::ICMPv6>();
  169. inner_icmpv6.inner_pdu(Tins::RawPDU(serialized_embedded_packet));
  170. const Tins::PDU::serialization_type serialized_packet = packet.serialize();
  171. const std::string expected_packet(serialized_packet.begin(),
  172. serialized_packet.end());
  173. /* Tests */
  174. try
  175. {
  176. reply.udp_response({}, 33434, 45834);
  177. FAIL();
  178. }
  179. catch (const std::exception& e)
  180. {
  181. SUCCEED();
  182. EXPECT_EQ(std::string(e.what()),
  183. "NodeReply has no type that would require UDP information.");
  184. }
  185. std::ostringstream test_output;
  186. test_output << reply;
  187. EXPECT_EQ(test_output.str(),
  188. "REPLY ICMP_TIME_EXCEEDED_ICMP_ECHO_REQUEST: fd00::1 "
  189. "(52:54:00:b2:fa:7f) -> fd00::2 (52:54:00:b2:fa:7e) Hoplimit=55 "
  190. "REQUEST_ADDRESS=fd00::3 LENGTH=10");
  191. EXPECT_EQ(actual_packet, expected_packet);
  192. EXPECT_EQ(reply.get_type(),
  193. NodeReplyType::ICMP_TIME_EXCEEDED_ICMP_ECHO_REQUEST);
  194. }
  195. TEST(NodeReplyTest, IcmpPortUnreachable)
  196. {
  197. const Tins::HWAddress<6> source_mac("52:54:00:b2:fa:7f");
  198. const Tins::HWAddress<6> destination_mac("52:54:00:b2:fa:7e");
  199. const Tins::IPv6Address source_address("fd00::1");
  200. const Tins::IPv6Address destination_address("fd00::2");
  201. const Tins::IPv6Address original_destination_address("fd00::3");
  202. constexpr int hoplimit = 55;
  203. constexpr int udp_sport = 46432;
  204. constexpr int udp_dport = 34344;
  205. const Tins::RawPDU::payload_type payload = {8, 4, 5, 9, 255, 0, 0, 0, 0, 0};
  206. /* Reply */
  207. NodeReply reply(NodeReplyType::ICMP_PORT_UNREACHABLE,
  208. destination_mac,
  209. destination_address,
  210. source_mac,
  211. source_address);
  212. reply.set_hoplimit(hoplimit);
  213. reply.udp_response(payload, udp_dport, udp_sport);
  214. reply.packet_reassembly(original_destination_address);
  215. const std::string actual_packet = reply.to_packet();
  216. /* Expected packet */
  217. Tins::IPv6 embedded_packet =
  218. Tins::IPv6(original_destination_address, destination_address) /
  219. Tins::UDP(udp_dport, udp_sport);
  220. embedded_packet.hop_limit(1);
  221. Tins::UDP& embedded_inner_udp = embedded_packet.rfind_pdu<Tins::UDP>();
  222. embedded_inner_udp.inner_pdu(Tins::RawPDU(payload));
  223. const Tins::PDU::serialization_type serialized_embedded_packet =
  224. embedded_packet.serialize();
  225. Tins::EthernetII packet =
  226. Tins::EthernetII(destination_mac, source_mac) /
  227. Tins::IPv6(destination_address, source_address) /
  228. Tins::ICMPv6(Tins::ICMPv6::Types::DEST_UNREACHABLE);
  229. Tins::IPv6& inner_ipv6 = packet.rfind_pdu<Tins::IPv6>();
  230. inner_ipv6.hop_limit(hoplimit);
  231. Tins::ICMPv6& inner_icmpv6 = inner_ipv6.rfind_pdu<Tins::ICMPv6>();
  232. inner_icmpv6.code(4);
  233. inner_icmpv6.inner_pdu(Tins::RawPDU(serialized_embedded_packet));
  234. const Tins::PDU::serialization_type serialized_packet = packet.serialize();
  235. const std::string expected_packet(serialized_packet.begin(),
  236. serialized_packet.end());
  237. /* Tests */
  238. try
  239. {
  240. reply.icmp_echo_reply(78, 1, {});
  241. FAIL();
  242. }
  243. catch (const std::exception& e)
  244. {
  245. SUCCEED();
  246. EXPECT_EQ(std::string(e.what()),
  247. "NodeReply has no type that would require ICMP echo reply "
  248. "information.");
  249. }
  250. std::ostringstream test_output;
  251. test_output << reply;
  252. EXPECT_EQ(test_output.str(),
  253. "REPLY ICMP_PORT_UNREACHABLE: fd00::1 (52:54:00:b2:fa:7f) -> "
  254. "fd00::2 (52:54:00:b2:fa:7e) Hoplimit=55: DPORT=34344 "
  255. "SPORT=46432 REQUEST_ADDRESS=fd00::3 LENGTH=10");
  256. EXPECT_EQ(actual_packet, expected_packet);
  257. EXPECT_EQ(reply.get_type(), NodeReplyType::ICMP_PORT_UNREACHABLE);
  258. }
  259. TEST(NodeReplyTest, IcmpTimeExceededUdp)
  260. {
  261. const Tins::HWAddress<6> source_mac("52:54:00:b2:fa:7f");
  262. const Tins::HWAddress<6> destination_mac("52:54:00:b2:fa:7e");
  263. const Tins::IPv6Address source_address("fd00::1");
  264. const Tins::IPv6Address destination_address("fd00::2");
  265. const Tins::IPv6Address original_destination_address("fd00::3");
  266. constexpr int hoplimit = 55;
  267. constexpr int udp_sport = 46432;
  268. constexpr int udp_dport = 34344;
  269. const Tins::RawPDU::payload_type payload = {8, 4, 5, 9, 255, 0, 0, 0, 0, 0};
  270. /* Reply */
  271. NodeReply reply(NodeReplyType::ICMP_TIME_EXCEEDED_UDP,
  272. destination_mac,
  273. destination_address,
  274. source_mac,
  275. source_address);
  276. reply.set_hoplimit(hoplimit);
  277. reply.udp_response(payload, udp_dport, udp_sport);
  278. reply.packet_reassembly(original_destination_address);
  279. const std::string actual_packet = reply.to_packet();
  280. /* Expected packet */
  281. Tins::IPv6 embedded_packet =
  282. Tins::IPv6(original_destination_address, destination_address) /
  283. Tins::UDP(udp_dport, udp_sport);
  284. embedded_packet.hop_limit(1);
  285. Tins::UDP& embedded_inner_udp = embedded_packet.rfind_pdu<Tins::UDP>();
  286. embedded_inner_udp.inner_pdu(Tins::RawPDU(payload));
  287. const Tins::PDU::serialization_type serialized_embedded_packet =
  288. embedded_packet.serialize();
  289. Tins::EthernetII packet = Tins::EthernetII(destination_mac, source_mac) /
  290. Tins::IPv6(destination_address, source_address) /
  291. Tins::ICMPv6(Tins::ICMPv6::Types::TIME_EXCEEDED);
  292. Tins::IPv6& inner_ipv6 = packet.rfind_pdu<Tins::IPv6>();
  293. inner_ipv6.hop_limit(hoplimit);
  294. Tins::ICMPv6& inner_icmpv6 = inner_ipv6.rfind_pdu<Tins::ICMPv6>();
  295. inner_icmpv6.inner_pdu(Tins::RawPDU(serialized_embedded_packet));
  296. const Tins::PDU::serialization_type serialized_packet = packet.serialize();
  297. const std::string expected_packet(serialized_packet.begin(),
  298. serialized_packet.end());
  299. /* Tests */
  300. try
  301. {
  302. reply.icmp_echo_reply(78, 1, {});
  303. FAIL();
  304. }
  305. catch (const std::exception& e)
  306. {
  307. SUCCEED();
  308. EXPECT_EQ(std::string(e.what()),
  309. "NodeReply has no type that would require ICMP echo reply "
  310. "information.");
  311. }
  312. std::ostringstream test_output;
  313. test_output << reply;
  314. EXPECT_EQ(
  315. test_output.str(),
  316. "REPLY ICMP_TIME_EXCEEDED_UDP: fd00::1 (52:54:00:b2:fa:7f) -> fd00::2 "
  317. "(52:54:00:b2:fa:7e) Hoplimit=55 REQUEST_ADDRESS=fd00::3 LENGTH=10");
  318. EXPECT_EQ(actual_packet, expected_packet);
  319. EXPECT_EQ(reply.get_type(), NodeReplyType::ICMP_TIME_EXCEEDED_UDP);
  320. }
  321. TEST(NodeReplyTest, IcmpNdp)
  322. {
  323. const Tins::HWAddress<6> source_mac("52:54:00:b2:fa:7f");
  324. const Tins::HWAddress<6> destination_mac("52:54:00:b2:fa:7e");
  325. const Tins::IPv6Address source_address("fd00::1");
  326. const Tins::IPv6Address destination_address("fd00::2");
  327. /* Reply */
  328. NodeReply reply(NodeReplyType::ICMP_NDP,
  329. destination_mac,
  330. destination_address,
  331. source_mac,
  332. source_address);
  333. const std::string actual_packet = reply.to_packet();
  334. /* Expected packet */
  335. Tins::EthernetII packet =
  336. Tins::EthernetII(destination_mac, source_mac) /
  337. Tins::IPv6(destination_address, source_address) /
  338. Tins::ICMPv6(Tins::ICMPv6::Types::NEIGHBOUR_ADVERT);
  339. Tins::IPv6& inner_ipv6 = packet.rfind_pdu<Tins::IPv6>();
  340. inner_ipv6.hop_limit(255);
  341. Tins::ICMPv6& inner_icmpv6 = inner_ipv6.rfind_pdu<Tins::ICMPv6>();
  342. inner_icmpv6.target_addr(source_address);
  343. inner_icmpv6.solicited(Tins::small_uint<1>(1));
  344. inner_icmpv6.router(Tins::small_uint<1>(1));
  345. inner_icmpv6.override(Tins::small_uint<1>(1));
  346. const Tins::ICMPv6::option address_option(
  347. Tins::ICMPv6::OptionTypes::TARGET_ADDRESS,
  348. source_mac.size(),
  349. &(*source_mac.begin()));
  350. inner_icmpv6.add_option(address_option);
  351. const Tins::PDU::serialization_type serialized_packet = packet.serialize();
  352. const std::string expected_packet(serialized_packet.begin(),
  353. serialized_packet.end());
  354. /* Tests */
  355. try
  356. {
  357. reply.set_hoplimit(120);
  358. FAIL();
  359. }
  360. catch (const std::exception& e)
  361. {
  362. SUCCEED();
  363. EXPECT_EQ(std::string(e.what()),
  364. "ICMP NDP responses always have a hop limit of 255.");
  365. }
  366. try
  367. {
  368. reply.icmp_echo_reply(3, 5, {});
  369. FAIL();
  370. }
  371. catch (const std::exception& e)
  372. {
  373. SUCCEED();
  374. EXPECT_EQ(std::string(e.what()),
  375. "NodeReply has no type that would require ICMP echo reply "
  376. "information.");
  377. }
  378. try
  379. {
  380. reply.udp_response({}, 33434, 45834);
  381. FAIL();
  382. }
  383. catch (const std::exception& e)
  384. {
  385. SUCCEED();
  386. EXPECT_EQ(std::string(e.what()),
  387. "NodeReply has no type that would require UDP information.");
  388. }
  389. try
  390. {
  391. reply.packet_reassembly(Tins::IPv6Address("fd00::"));
  392. FAIL();
  393. }
  394. catch (const std::exception& e)
  395. {
  396. SUCCEED();
  397. EXPECT_EQ(std::string(e.what()),
  398. "NodeReply has no type that would require original "
  399. "destionation address information.");
  400. }
  401. std::ostringstream test_output;
  402. test_output << reply;
  403. EXPECT_EQ(test_output.str(),
  404. "REPLY ICMP_NDP: fd00::1 (52:54:00:b2:fa:7f) -> fd00::2 "
  405. "(52:54:00:b2:fa:7e)");
  406. EXPECT_EQ(actual_packet, expected_packet);
  407. EXPECT_EQ(reply.get_type(), NodeReplyType::ICMP_NDP);
  408. }