Log QUIC_BUG if initial header changed between initial serialization and re-serialization.

PiperOrigin-RevId: 503268541
diff --git a/quiche/quic/core/quic_packet_creator.cc b/quiche/quic/core/quic_packet_creator.cc
index 566944c..2a0974f 100644
--- a/quiche/quic/core/quic_packet_creator.cc
+++ b/quiche/quic/core/quic_packet_creator.cc
@@ -507,6 +507,7 @@
   packet_.largest_acked.Clear();
   needs_full_padding_ = false;
   packet_.bytes_not_retransmitted.reset();
+  packet_.initial_header.reset();
 }
 
 size_t QuicPacketCreator::ReserializeInitialPacketInCoalescedPacket(
@@ -560,6 +561,15 @@
                        /*allow_padding=*/false)) {
     return 0;
   }
+  if (!packet.initial_header.has_value() ||
+      !packet_.initial_header.has_value()) {
+    QUIC_BUG(missing initial packet header)
+        << "initial serialized packet does not have header populated";
+  } else if (packet.initial_header.value() != packet_.initial_header.value()) {
+    QUIC_BUG(initial packet header changed before reserialization)
+        << ENDPOINT << "original header: " << packet.initial_header.value()
+        << ", new header: " << packet_.initial_header.value();
+  }
   const size_t encrypted_length = packet_.encrypted_length;
   // Clear frames in packet_. No need to DeleteFrames since frames are owned by
   // initial_packet.
@@ -826,6 +836,9 @@
   QuicPacketHeader header;
   // FillPacketHeader increments packet_number_.
   FillPacketHeader(&header);
+  if (packet_.encryption_level == ENCRYPTION_INITIAL) {
+    packet_.initial_header = header;
+  }
   if (delegate_ != nullptr) {
     packet_.fate = delegate_->GetSerializedPacketFate(
         /*is_mtu_discovery=*/QuicUtils::ContainsFrameType(queued_frames_,
diff --git a/quiche/quic/core/quic_packets.cc b/quiche/quic/core/quic_packets.cc
index d884adf..746737b 100644
--- a/quiche/quic/core/quic_packets.cc
+++ b/quiche/quic/core/quic_packets.cc
@@ -169,6 +169,7 @@
       version_flag(false),
       has_possible_stateless_reset_token(false),
       packet_number_length(PACKET_4BYTE_PACKET_NUMBER),
+      type_byte(0),
       version(UnsupportedQuicVersion()),
       nonce(nullptr),
       form(GOOGLE_QUIC_PACKET),
@@ -447,7 +448,8 @@
       has_message(other.has_message),
       fate(other.fate),
       peer_address(other.peer_address),
-      bytes_not_retransmitted(other.bytes_not_retransmitted) {
+      bytes_not_retransmitted(other.bytes_not_retransmitted),
+      initial_header(other.initial_header) {
   if (this != &other) {
     if (release_encrypted_buffer && encrypted_buffer != nullptr) {
       release_encrypted_buffer(encrypted_buffer);
@@ -495,6 +497,7 @@
   copy->fate = serialized.fate;
   copy->peer_address = serialized.peer_address;
   copy->bytes_not_retransmitted = serialized.bytes_not_retransmitted;
+  copy->initial_header = serialized.initial_header;
 
   if (copy_buffer) {
     copy->encrypted_buffer = CopyBuffer(serialized);
@@ -563,4 +566,34 @@
   return os;
 }
 
+bool QuicPacketHeader::operator==(const QuicPacketHeader& other) const {
+  return destination_connection_id == other.destination_connection_id &&
+         destination_connection_id_included ==
+             other.destination_connection_id_included &&
+         source_connection_id == other.source_connection_id &&
+         source_connection_id_included == other.source_connection_id_included &&
+         reset_flag == other.reset_flag && version_flag == other.version_flag &&
+         has_possible_stateless_reset_token ==
+             other.has_possible_stateless_reset_token &&
+         packet_number_length == other.packet_number_length &&
+         type_byte == other.type_byte && version == other.version &&
+         nonce == other.nonce &&
+         ((!packet_number.IsInitialized() &&
+           !other.packet_number.IsInitialized()) ||
+          (packet_number.IsInitialized() &&
+           other.packet_number.IsInitialized() &&
+           packet_number == other.packet_number)) &&
+         form == other.form && long_packet_type == other.long_packet_type &&
+         possible_stateless_reset_token ==
+             other.possible_stateless_reset_token &&
+         retry_token_length_length == other.retry_token_length_length &&
+         retry_token == other.retry_token &&
+         length_length == other.length_length &&
+         remaining_packet_length == other.remaining_packet_length;
+}
+
+bool QuicPacketHeader::operator!=(const QuicPacketHeader& other) const {
+  return !operator==(other);
+}
+
 }  // namespace quic
diff --git a/quiche/quic/core/quic_packets.h b/quiche/quic/core/quic_packets.h
index a1c743a..c26a88a 100644
--- a/quiche/quic/core/quic_packets.h
+++ b/quiche/quic/core/quic_packets.h
@@ -157,6 +157,9 @@
   // 0-RTT and Handshake packets. Also includes the length of the
   // diversification nonce in server to client 0-RTT packets.
   QuicByteCount remaining_packet_length;
+
+  bool operator==(const QuicPacketHeader& other) const;
+  bool operator!=(const QuicPacketHeader& other) const;
 };
 
 struct QUIC_EXPORT_PRIVATE QuicPublicResetPacket {
@@ -388,6 +391,9 @@
   // populated for packets with "mixed frames": at least one frame of a
   // retransmission type and at least one frame of NOT_RETRANSMISSION type.
   absl::optional<QuicByteCount> bytes_not_retransmitted;
+  // Only populated if encryption_level is ENCRYPTION_INITIAL.
+  // TODO(b/265777524): remove this.
+  absl::optional<QuicPacketHeader> initial_header;
 };
 
 // Make a copy of |serialized| (including the underlying frames). |copy_buffer|
diff --git a/quiche/quic/core/quic_packets_test.cc b/quiche/quic/core/quic_packets_test.cc
index f0a7e7f..4e6598d 100644
--- a/quiche/quic/core/quic_packets_test.cc
+++ b/quiche/quic/core/quic_packets_test.cc
@@ -72,6 +72,14 @@
             GetClientConnectionIdAsSender(header, Perspective::IS_CLIENT));
 }
 
+TEST_F(QuicPacketsTest, CopyQuicPacketHeader) {
+  QuicPacketHeader header;
+  QuicPacketHeader header2 = CreateFakePacketHeader();
+  EXPECT_NE(header, header2);
+  QuicPacketHeader header3(header2);
+  EXPECT_EQ(header2, header3);
+}
+
 TEST_F(QuicPacketsTest, CopySerializedPacket) {
   std::string buffer(1000, 'a');
   quiche::SimpleBufferAllocator allocator;