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);
}