Add retire_prior_to to NewConnectionId frame IETF altered the NewConnectionId frame format, adding a "retire_prior_to" field. This CL adds that field and modifies the framer and tests as needed. gfe-relnote: N/A is only for IETF QUIC (version 99 flag protected) NEW_REQUIRED_FIELD_OK="Message not currently in use, nothing in the logs" Note wrt the protobuf update - this CL adds a field to an IETF QUIC frame. Correspondingly, the relevant protobuf gets a new field. Since IETF QUIC is not in use, there are no instances of this protobuf in use/existance/etc yet ... so there is no incompatbility with other processes/etc to be concerned with. PiperOrigin-RevId: 257833679 Change-Id: I80d80db9f1f23b8e570c504199fdd346b7cb4ca4
diff --git a/quic/core/frames/quic_new_connection_id_frame.cc b/quic/core/frames/quic_new_connection_id_frame.cc index b7f63e2..f6c8661 100644 --- a/quic/core/frames/quic_new_connection_id_frame.cc +++ b/quic/core/frames/quic_new_connection_id_frame.cc
@@ -16,17 +16,22 @@ QuicControlFrameId control_frame_id, QuicConnectionId connection_id, QuicConnectionIdSequenceNumber sequence_number, - const QuicUint128 stateless_reset_token) + const QuicUint128 stateless_reset_token, + uint64_t retire_prior_to) : control_frame_id(control_frame_id), connection_id(connection_id), sequence_number(sequence_number), - stateless_reset_token(stateless_reset_token) {} + stateless_reset_token(stateless_reset_token), + retire_prior_to(retire_prior_to) { + DCHECK(retire_prior_to <= sequence_number); +} std::ostream& operator<<(std::ostream& os, const QuicNewConnectionIdFrame& frame) { os << "{ control_frame_id: " << frame.control_frame_id << ", connection_id: " << frame.connection_id - << ", sequence_number: " << frame.sequence_number << " }\n"; + << ", sequence_number: " << frame.sequence_number + << ", retire_prior_to: " << frame.retire_prior_to << " }\n"; return os; }
diff --git a/quic/core/frames/quic_new_connection_id_frame.h b/quic/core/frames/quic_new_connection_id_frame.h index 1948d22..441ca1a 100644 --- a/quic/core/frames/quic_new_connection_id_frame.h +++ b/quic/core/frames/quic_new_connection_id_frame.h
@@ -18,7 +18,8 @@ QuicNewConnectionIdFrame(QuicControlFrameId control_frame_id, QuicConnectionId connection_id, QuicConnectionIdSequenceNumber sequence_number, - const QuicUint128 stateless_reset_token); + const QuicUint128 stateless_reset_token, + uint64_t retire_prior_to); friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( std::ostream& os, @@ -30,6 +31,7 @@ QuicConnectionId connection_id; QuicConnectionIdSequenceNumber sequence_number; QuicUint128 stateless_reset_token; + uint64_t retire_prior_to; }; } // namespace quic
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc index cdb941f..6c089c1 100644 --- a/quic/core/quic_framer.cc +++ b/quic/core/quic_framer.cc
@@ -768,6 +768,7 @@ const QuicNewConnectionIdFrame& frame) { return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(frame.sequence_number) + + QuicDataWriter::GetVarInt62Len(frame.retire_prior_to) + kConnectionIdLengthSize + frame.connection_id.length() + sizeof(frame.stateless_reset_token); } @@ -5998,6 +5999,10 @@ set_detailed_error("Can not write New Connection ID sequence number"); return false; } + if (!writer->WriteVarInt62(frame.retire_prior_to)) { + set_detailed_error("Can not write New Connection ID retire_prior_to"); + return false; + } if (!writer->WriteUInt8(frame.connection_id.length())) { set_detailed_error( "Can not write New Connection ID frame connection ID Length"); @@ -6025,6 +6030,15 @@ return false; } + if (!reader->ReadVarInt62(&frame->retire_prior_to)) { + set_detailed_error( + "Unable to read new connection ID frame retire_prior_to."); + return false; + } + if (frame->retire_prior_to > frame->sequence_number) { + set_detailed_error("Retire_prior_to > sequence_number."); + return false; + } uint8_t connection_id_length; if (!reader->ReadUInt8(&connection_id_length)) { set_detailed_error(
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc index 3e0f68b..0009470 100644 --- a/quic/core/quic_framer_test.cc +++ b/quic/core/quic_framer_test.cc
@@ -11289,6 +11289,8 @@ // error code {"Unable to read new connection ID frame sequence number.", {kVarInt62OneByte + 0x11}}, + {"Unable to read new connection ID frame retire_prior_to.", + {kVarInt62OneByte + 0x09}}, {"Unable to read new connection ID frame connection id length.", {0x08}}, // connection ID length {"Unable to read new connection ID frame connection id.", @@ -11314,6 +11316,7 @@ EXPECT_EQ(FramerTestConnectionIdPlusOne(), visitor_.new_connection_id_.connection_id); EXPECT_EQ(0x11u, visitor_.new_connection_id_.sequence_number); + EXPECT_EQ(0x09u, visitor_.new_connection_id_.retire_prior_to); EXPECT_EQ(kTestStatelessResetToken, visitor_.new_connection_id_.stateless_reset_token); @@ -11345,6 +11348,8 @@ // error code {"Unable to read new connection ID frame sequence number.", {kVarInt62OneByte + 0x11}}, + {"Unable to read new connection ID frame retire_prior_to.", + {kVarInt62OneByte + 0x0a}}, {"Unable to read new connection ID frame connection id length.", {0x09}}, // connection ID length {"Unable to read new connection ID frame connection id.", @@ -11370,6 +11375,7 @@ EXPECT_EQ(FramerTestConnectionIdNineBytes(), visitor_.new_connection_id_.connection_id); EXPECT_EQ(0x11u, visitor_.new_connection_id_.sequence_number); + EXPECT_EQ(0x0au, visitor_.new_connection_id_.retire_prior_to); EXPECT_EQ(kTestStatelessResetToken, visitor_.new_connection_id_.stateless_reset_token); @@ -11403,6 +11409,8 @@ // error code {"Unable to read new connection ID frame sequence number.", {kVarInt62OneByte + 0x11}}, + {"Unable to read new connection ID frame retire_prior_to.", + {kVarInt62OneByte + 0x0b}}, {"Unable to read new connection ID frame connection id length.", {0x13}}, // connection ID length {"Unable to read new connection ID frame connection id.", @@ -11422,6 +11430,50 @@ EXPECT_EQ("New connection ID length too high.", framer_.detailed_error()); } +// Verifies that parsing a NEW_CONNECTION_ID frame with an invalid +// retire-prior-to fails. +TEST_P(QuicFramerTest, InvalidRetirePriorToNewConnectionIdFrame) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + // The NEW_CONNECTION_ID frame is only for version 99. + return; + } + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_NEW_CONNECTION_ID frame) + {"", + {0x18}}, + // sequence number + {"Unable to read new connection ID frame sequence number.", + {kVarInt62OneByte + 0x11}}, + {"Unable to read new connection ID frame retire_prior_to.", + {kVarInt62OneByte + 0x1b}}, + {"Unable to read new connection ID frame connection id length.", + {0x08}}, // connection ID length + {"Unable to read new connection ID frame connection id.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11}}, + {"Can not read new connection ID frame reset token.", + {0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(QUIC_INVALID_NEW_CONNECTION_ID_DATA, framer_.error()); + EXPECT_EQ("Retire_prior_to > sequence_number.", framer_.detailed_error()); +} + TEST_P(QuicFramerTest, BuildNewConnectionIdFramePacket) { if (!VersionHasIetfQuicFrames(framer_.transport_version())) { // This frame is only for version 99. @@ -11436,6 +11488,7 @@ QuicNewConnectionIdFrame frame; frame.sequence_number = 0x11; + frame.retire_prior_to = 0x0c; // Use this value to force a 4-byte encoded variable length connection ID // in the frame. frame.connection_id = FramerTestConnectionIdPlusOne(); @@ -11456,6 +11509,8 @@ 0x18, // sequence number kVarInt62OneByte + 0x11, + // retire_prior_to + kVarInt62OneByte + 0x0c, // new connection id length 0x08, // new connection id @@ -11862,7 +11917,8 @@ return; } - QuicNewConnectionIdFrame new_connection_id(5, TestConnectionId(), 1, 101111); + QuicNewConnectionIdFrame new_connection_id(5, TestConnectionId(), 1, 101111, + 1); EXPECT_EQ(QuicFramer::GetNewConnectionIdFrameSize(new_connection_id), QuicFramer::GetRetransmittableControlFrameSize( framer_.transport_version(), QuicFrame(&new_connection_id)));
diff --git a/quic/core/quic_ietf_framer_test.cc b/quic/core/quic_ietf_framer_test.cc index 23ec1cb..9d07e49 100644 --- a/quic/core/quic_ietf_framer_test.cc +++ b/quic/core/quic_ietf_framer_test.cc
@@ -1380,6 +1380,7 @@ QuicNewConnectionIdFrame transmit_frame; transmit_frame.connection_id = TestConnectionId(UINT64_C(0x0edcba9876543201)); transmit_frame.sequence_number = 0x01020304; + transmit_frame.retire_prior_to = 0x00020304; // The token is defined as a uint128 -- a 16-byte integer. // The value is set in this manner because we want each // byte to have a specific value so that the binary @@ -1401,12 +1402,12 @@ // Add the frame. EXPECT_TRUE(QuicFramerPeer::AppendNewConnectionIdFrame( &framer_, transmit_frame, &writer)); - // Check that buffer length is correct - EXPECT_EQ(29u, writer.length()); // clang-format off uint8_t packet[] = { // sequence number, 0x80 for varint62 encoding 0x80 + 0x01, 0x02, 0x03, 0x04, + // retire_prior_to, 0x80 for varint62 encoding + 0x80 + 0x00, 0x02, 0x03, 0x04, // new connection id length, is not varint62 encoded. 0x08, // new connection id, is not varint62 encoded. @@ -1415,8 +1416,10 @@ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; - // clang-format on + + // Check that buffer length is correct + EXPECT_EQ(sizeof(packet), writer.length()); EXPECT_EQ(0, memcmp(packet_buffer, packet, sizeof(packet))); // Set up reader and empty receive QuicPaddingFrame. @@ -1430,6 +1433,7 @@ // Now check that received == sent EXPECT_EQ(transmit_frame.connection_id, receive_frame.connection_id); EXPECT_EQ(transmit_frame.sequence_number, receive_frame.sequence_number); + EXPECT_EQ(transmit_frame.retire_prior_to, receive_frame.retire_prior_to); EXPECT_EQ(transmit_frame.stateless_reset_token, receive_frame.stateless_reset_token); }