diff options
Diffstat (limited to 'tests/test_icmp.cpp')
| -rw-r--r-- | tests/test_icmp.cpp | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/tests/test_icmp.cpp b/tests/test_icmp.cpp index 520e12d..7016a72 100644 --- a/tests/test_icmp.cpp +++ b/tests/test_icmp.cpp @@ -72,6 +72,92 @@ TEST_CASE("icmpv6_type_name covers known types and falls back for unknown ones") CHECK(icmpv6_type_name(250) == "type=250"); } +namespace { + +// Builds a real ICMPv4 Destination Unreachable message wrapping a +// truncated original UDP packet - exactly the shape RFC 792 promises: +// original IP header + first 8 bytes of the original datagram's data +// (enough to reach a UDP/TCP header's two port fields). +std::vector<unsigned char> dest_unreachable_wrapping_udp() { + std::vector<unsigned char> original_ip(20, 0); + original_ip[0] = 0x45; + original_ip[9] = kProtoUdp; + original_ip[12] = 10; original_ip[13] = 0; original_ip[14] = 0; original_ip[15] = 5; + original_ip[16] = 10; original_ip[17] = 0; original_ip[18] = 0; original_ip[19] = 6; + + std::vector<unsigned char> original_udp_start = {0x1F, 0x90, 0x00, 0x35}; // src=8080, dst=53 + + std::vector<unsigned char> icmp = {3, 3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // port unreachable + icmp.insert(icmp.end(), original_ip.begin(), original_ip.end()); + icmp.insert(icmp.end(), original_udp_start.begin(), original_udp_start.end()); + return icmp; +} + +} // namespace + +TEST_CASE("icmpv4_embedded_flow recovers the original flow's addresses and ports") { + auto flow = icmpv4_embedded_flow(dest_unreachable_wrapping_udp()); + REQUIRE(flow.has_value()); + CHECK(*flow == "10.0.0.5 -> 10.0.0.6 proto=17 (8080 -> 53)"); +} + +TEST_CASE("icmpv4_embedded_flow omits ports for a non-TCP/UDP embedded protocol") { + std::vector<unsigned char> original_ip(20, 0); + original_ip[0] = 0x45; + original_ip[9] = kProtoIcmp; // an ICMP error about an ICMP packet (e.g. a ping that failed) + original_ip[12] = 10; original_ip[15] = 1; + original_ip[16] = 10; original_ip[19] = 2; + + std::vector<unsigned char> icmp = {11, 0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + icmp.insert(icmp.end(), original_ip.begin(), original_ip.end()); + + auto flow = icmpv4_embedded_flow(icmp); + REQUIRE(flow.has_value()); + CHECK(*flow == "10.0.0.1 -> 10.0.0.2 proto=1"); +} + +TEST_CASE("icmpv4_embedded_flow returns nullopt when there's no room for an embedded packet") { + std::vector<unsigned char> icmp = {3, 3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + CHECK_FALSE(icmpv4_embedded_flow(icmp).has_value()); +} + +TEST_CASE("icmpv4_is_error_type covers the RFC 792 error types and excludes echo/timestamp") { + CHECK(icmpv4_is_error_type(3)); // Destination Unreachable + CHECK(icmpv4_is_error_type(11)); // Time Exceeded + CHECK_FALSE(icmpv4_is_error_type(8)); // Echo Request + CHECK_FALSE(icmpv4_is_error_type(13)); // Timestamp Request +} + +TEST_CASE("icmpv6_embedded_flow recovers the original flow's addresses and ports") { + std::vector<unsigned char> original_ip6(40, 0); + original_ip6[0] = 0x60; + original_ip6[6] = kProtoUdp; // next_header + original_ip6[7] = 64; // hop_limit + original_ip6[8] = 0x20; original_ip6[9] = 0x01; // src: 2001:db8::1 + original_ip6[10] = 0x0d; original_ip6[11] = 0xb8; + original_ip6[23] = 0x01; + original_ip6[24] = 0x20; original_ip6[25] = 0x01; // dst: 2001:db8::2 + original_ip6[26] = 0x0d; original_ip6[27] = 0xb8; + original_ip6[39] = 0x02; + + std::vector<unsigned char> original_udp_start = {0x1F, 0x90, 0x00, 0x35}; + + std::vector<unsigned char> icmp = {1, 4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // port unreachable + icmp.insert(icmp.end(), original_ip6.begin(), original_ip6.end()); + icmp.insert(icmp.end(), original_udp_start.begin(), original_udp_start.end()); + + auto flow = icmpv6_embedded_flow(icmp); + REQUIRE(flow.has_value()); + CHECK(*flow == "2001:db8::1 -> 2001:db8::2 next=17 (8080 -> 53)"); +} + +TEST_CASE("icmpv6_is_error_type covers the RFC 4443 error types and excludes echo/Redirect") { + CHECK(icmpv6_is_error_type(1)); // Destination Unreachable + CHECK(icmpv6_is_error_type(3)); // Time Exceeded + CHECK_FALSE(icmpv6_is_error_type(128)); // Echo Request + CHECK_FALSE(icmpv6_is_error_type(137)); // Redirect: different, not generic embedded-packet format +} + TEST_CASE("the same type number means something different in each protocol's table") { // The whole reason these are two separate tables, not one shared by // number: ICMPv4's echo request is type 8, but ICMPv6's type 8 |