Retrieve the correct IPv6 address when sending an ICMP6 Echo Reply.

When sending an ICMP6 Echo Reply (cl/492531951), the packet was treated as if the IP6 headers were still present at the start of the packet. Instead it was accessing bytes 8-24 of the packet, which has been stripped of IP6 and ICMP6 headers leading to undefined behaviour.

This refactors out the destination IP retrieval and changed the unit test to be more rigid by comparing packet contents.
Logs from successful run: sponge2/103bb5f8-ebde-4d4f-a7fb-92abe1e721f5

PiperOrigin-RevId: 493947520
diff --git a/quiche/quic/qbone/qbone_packet_processor.cc b/quiche/quic/qbone/qbone_packet_processor.cc
index be09ef2..40228dd 100644
--- a/quiche/quic/qbone/qbone_packet_processor.cc
+++ b/quiche/quic/qbone/qbone_packet_processor.cc
@@ -4,6 +4,10 @@
 
 #include "quiche/quic/qbone/qbone_packet_processor.h"
 
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+
 #include <cstring>
 
 #include "absl/base/optimization.h"
@@ -22,6 +26,7 @@
 constexpr size_t kIPv6MinPacketSize = 1280;
 constexpr size_t kIcmpTtl = 64;
 constexpr size_t kICMPv6DestinationUnreachableDueToSourcePolicy = 5;
+constexpr size_t kIPv6DestinationOffset = 8;
 
 }  // namespace
 
@@ -77,6 +82,10 @@
   ProcessingResult result = ProcessIPv6HeaderAndFilter(
       packet, direction, &transport_protocol, &transport_data, &icmp_header);
 
+  in6_addr dst;
+  // TODO(b/70339814): ensure this is actually a unicast address.
+  memcpy(&dst, &packet->data()[kIPv6DestinationOffset], kIPv6AddressSize);
+
   switch (result) {
     case ProcessingResult::OK:
       switch (direction) {
@@ -102,14 +111,14 @@
         // need to take off both the IPv6 header and the ICMP6 header.
         auto icmp_body = absl::string_view(*packet).substr(sizeof(ip6_hdr) +
                                                            sizeof(icmp6_hdr));
-        SendIcmpResponse(&icmp_header, icmp_body, direction);
+        SendIcmpResponse(dst, &icmp_header, icmp_body, direction);
       } else {
-        SendIcmpResponse(&icmp_header, *packet, direction);
+        SendIcmpResponse(dst, &icmp_header, *packet, direction);
       }
       stats_->OnPacketDroppedWithIcmp(direction);
       break;
     case ProcessingResult::ICMP_AND_TCP_RESET:
-      SendIcmpResponse(&icmp_header, *packet, direction);
+      SendIcmpResponse(dst, &icmp_header, *packet, direction);
       stats_->OnPacketDroppedWithIcmp(direction);
       SendTcpReset(*packet, direction);
       stats_->OnPacketDroppedWithTcpReset(direction);
@@ -249,14 +258,11 @@
   return ProcessingResult::OK;
 }
 
-void QbonePacketProcessor::SendIcmpResponse(icmp6_hdr* icmp_header,
-                                            absl::string_view original_packet,
+void QbonePacketProcessor::SendIcmpResponse(in6_addr dst,
+                                            icmp6_hdr* icmp_header,
+                                            absl::string_view payload,
                                             Direction original_direction) {
-  in6_addr dst;
-  // TODO(b/70339814): ensure this is actually a unicast address.
-  memcpy(dst.s6_addr, &original_packet[8], kIPv6AddressSize);
-
-  CreateIcmpPacket(self_ip_, dst, *icmp_header, original_packet,
+  CreateIcmpPacket(self_ip_, dst, *icmp_header, payload,
                    [this, original_direction](absl::string_view packet) {
                      SendResponse(original_direction, packet);
                    });
diff --git a/quiche/quic/qbone/qbone_packet_processor.h b/quiche/quic/qbone/qbone_packet_processor.h
index ab7ca00..77eb13e 100644
--- a/quiche/quic/qbone/qbone_packet_processor.h
+++ b/quiche/quic/qbone/qbone_packet_processor.h
@@ -6,6 +6,7 @@
 #define QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_H_
 
 #include <netinet/icmp6.h>
+#include <netinet/in.h>
 #include <netinet/ip6.h>
 
 #include "absl/strings/string_view.h"
@@ -163,8 +164,8 @@
                                               char** transport_data,
                                               icmp6_hdr* icmp_header);
 
-  void SendIcmpResponse(icmp6_hdr* icmp_header,
-                        absl::string_view original_packet,
+  void SendIcmpResponse(in6_addr dst, icmp6_hdr* icmp_header,
+                        absl::string_view payload,
                         Direction original_direction);
 
   void SendTcpReset(absl::string_view original_packet,
@@ -191,6 +192,8 @@
                                      icmp6_hdr* icmp_header);
 
   void SendResponse(Direction original_direction, absl::string_view packet);
+
+  in6_addr GetDestinationFromPacket(absl::string_view packet);
 };
 
 }  // namespace quic
diff --git a/quiche/quic/qbone/qbone_packet_processor_test.cc b/quiche/quic/qbone/qbone_packet_processor_test.cc
index e416bb9..519ef43 100644
--- a/quiche/quic/qbone/qbone_packet_processor_test.cc
+++ b/quiche/quic/qbone/qbone_packet_processor_test.cc
@@ -18,6 +18,7 @@
 using ProcessingResult = QbonePacketProcessor::ProcessingResult;
 using OutputInterface = QbonePacketProcessor::OutputInterface;
 using ::testing::_;
+using ::testing::Eq;
 using ::testing::Invoke;
 using ::testing::Return;
 using ::testing::WithArgs;
@@ -117,7 +118,7 @@
     // IP address of the receiver is fe80::71:626f:6e6f
     0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x71, 0x62, 0x6f, 0x6e, 0x6f,
-    // ICMP Type ping
+    // ICMP Type ping request
     128,
     // ICMP Code 0
     0,
@@ -138,6 +139,41 @@
     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
 };
 
+static const char kReferenceEchoReplyData[] = {
+    // IPv6 with zero TOS and flow label.
+    0x60, 0x00, 0x00, 0x00,
+    // Payload size is 64 bytes.
+    0x00, 64,
+    // Next header is ICMP
+    58,
+    // TTL is 255.
+    255,
+    // IP address of the sender is fd00:4:0:1::1
+    0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    // IP address of the receiver is fd00:0:0:1::1
+    0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    // ICMP Type ping reply
+    129,
+    // ICMP Code 0
+    0,
+    // Checksum
+    0x66, 0xb6,
+    // ICMP Identifier (0xcafe to be memorable)
+    0xca, 0xfe,
+    // Sequence number
+    0x00, 0x01,
+    // Data, starting with unix timeval then 0x10..0x37
+    0x67, 0x37, 0x8a, 0x63, 0x00, 0x00, 0x00, 0x00,
+    0x96, 0x58, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+};
+
 // clang-format on
 
 static const absl::string_view kReferenceClientPacket(
@@ -336,7 +372,12 @@
   EXPECT_CALL(stats_, OnPacketDroppedWithIcmp(Direction::FROM_OFF_NETWORK));
   EXPECT_CALL(output_, SendPacketToClient(_))
       .WillOnce(Invoke([](absl::string_view packet) {
-        EXPECT_EQ(packet.size(), kReferenceEchoRequest.size());
+        // Explicit conversion because otherwise it is treated as a null
+        // terminated string.
+        absl::string_view expected = absl::string_view(
+            kReferenceEchoReplyData, sizeof(kReferenceEchoReplyData));
+
+        EXPECT_THAT(packet, Eq(expected));
         QUIC_LOG(INFO) << "ICMP response:\n"
                        << quiche::QuicheTextUtils::HexDump(packet);
       }));