srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_icmp.cpp86
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