Update MESSAGE frame to latest IETF draft
This CL adds support for the max_datagram_frame_size transport parameter required by the IETF draft, and changes the frame type to 0x30-0x31 for v99 only.
gfe-relnote: changes around MESSAGE frames, protected by v99 and TLS flags
PiperOrigin-RevId: 280454854
Change-Id: Idd4cca1afb76847eccdd9702db6b4437af314258
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc
index ce6cc20..a870017 100644
--- a/quic/core/crypto/transport_parameters.cc
+++ b/quic/core/crypto/transport_parameters.cc
@@ -44,6 +44,8 @@
kPreferredAddress = 0xd,
kActiveConnectionIdLimit = 0xe,
+ kMaxDatagramFrameSize = 0x20,
+
kGoogleQuicParam = 18257, // Used for non-standard Google-specific params.
kGoogleQuicVersion =
18258, // Used to transmit version and supported_versions.
@@ -95,6 +97,8 @@
return "preferred_address";
case TransportParameters::kActiveConnectionIdLimit:
return "active_connection_id_limit";
+ case TransportParameters::kMaxDatagramFrameSize:
+ return "max_datagram_frame_size";
case TransportParameters::kGoogleQuicParam:
return "google";
case TransportParameters::kGoogleQuicVersion:
@@ -279,6 +283,7 @@
preferred_address->ToString();
}
rv += active_connection_id_limit.ToString(/*for_use_in_list=*/true);
+ rv += max_datagram_frame_size.ToString(/*for_use_in_list=*/true);
if (google_quic_params) {
rv += " " + TransportParameterIdToString(kGoogleQuicParam);
}
@@ -313,7 +318,8 @@
0,
kMaxMaxAckDelayTransportParam),
disable_migration(false),
- active_connection_id_limit(kActiveConnectionIdLimit)
+ active_connection_id_limit(kActiveConnectionIdLimit),
+ max_datagram_frame_size(kMaxDatagramFrameSize)
// Important note: any new transport parameters must be added
// to TransportParameters::AreValid, SerializeTransportParameters and
// ParseTransportParameters.
@@ -354,15 +360,15 @@
QUIC_BUG << "Preferred address family failure";
return false;
}
- const bool ok = idle_timeout_milliseconds.IsValid() &&
- max_packet_size.IsValid() && initial_max_data.IsValid() &&
- initial_max_stream_data_bidi_local.IsValid() &&
- initial_max_stream_data_bidi_remote.IsValid() &&
- initial_max_stream_data_uni.IsValid() &&
- initial_max_streams_bidi.IsValid() &&
- initial_max_streams_uni.IsValid() &&
- ack_delay_exponent.IsValid() && max_ack_delay.IsValid() &&
- active_connection_id_limit.IsValid();
+ const bool ok =
+ idle_timeout_milliseconds.IsValid() && max_packet_size.IsValid() &&
+ initial_max_data.IsValid() &&
+ initial_max_stream_data_bidi_local.IsValid() &&
+ initial_max_stream_data_bidi_remote.IsValid() &&
+ initial_max_stream_data_uni.IsValid() &&
+ initial_max_streams_bidi.IsValid() && initial_max_streams_uni.IsValid() &&
+ ack_delay_exponent.IsValid() && max_ack_delay.IsValid() &&
+ active_connection_id_limit.IsValid() && max_datagram_frame_size.IsValid();
if (!ok) {
QUIC_DLOG(ERROR) << "Invalid transport parameters " << *this;
}
@@ -445,7 +451,8 @@
!in.initial_max_streams_uni.WriteToCbb(¶ms) ||
!in.ack_delay_exponent.WriteToCbb(¶ms) ||
!in.max_ack_delay.WriteToCbb(¶ms) ||
- !in.active_connection_id_limit.WriteToCbb(¶ms)) {
+ !in.active_connection_id_limit.WriteToCbb(¶ms) ||
+ !in.max_datagram_frame_size.WriteToCbb(¶ms)) {
QUIC_BUG << "Failed to write integers for " << in;
return false;
}
@@ -734,6 +741,9 @@
case TransportParameters::kActiveConnectionIdLimit:
parse_success = out->active_connection_id_limit.ReadFromCbs(&value);
break;
+ case TransportParameters::kMaxDatagramFrameSize:
+ parse_success = out->max_datagram_frame_size.ReadFromCbs(&value);
+ break;
case TransportParameters::kGoogleQuicParam: {
if (out->google_quic_params) {
QUIC_DLOG(ERROR) << "Received a second Google parameter";
diff --git a/quic/core/crypto/transport_parameters.h b/quic/core/crypto/transport_parameters.h
index 22dc252..c5ec1a4 100644
--- a/quic/core/crypto/transport_parameters.h
+++ b/quic/core/crypto/transport_parameters.h
@@ -168,6 +168,10 @@
// to store.
IntegerParameter active_connection_id_limit;
+ // Indicates support for the DATAGRAM frame and the maximum frame size that
+ // the sender accepts. See draft-pauly-quic-datagram.
+ IntegerParameter max_datagram_frame_size;
+
// Transport parameters used by Google QUIC but not IETF QUIC. This is
// serialized into a TransportParameter struct with a TransportParameterId of
// kGoogleQuicParamId.
diff --git a/quic/core/quic_config.cc b/quic/core/quic_config.cc
index f1736a3..3d8fe3f 100644
--- a/quic/core/quic_config.cc
+++ b/quic/core/quic_config.cc
@@ -421,7 +421,8 @@
max_incoming_unidirectional_streams_(kMIUS, PRESENCE_OPTIONAL),
max_ack_delay_ms_(kMAD, PRESENCE_OPTIONAL),
ack_delay_exponent_(kADE, PRESENCE_OPTIONAL),
- max_packet_size_(0, PRESENCE_OPTIONAL) {
+ max_packet_size_(0, PRESENCE_OPTIONAL),
+ max_datagram_frame_size_(0, PRESENCE_OPTIONAL) {
SetDefaults();
}
@@ -596,6 +597,23 @@
return max_packet_size_.GetReceivedValue();
}
+void QuicConfig::SetMaxDatagramFrameSizeToSend(
+ uint32_t max_datagram_frame_size) {
+ max_datagram_frame_size_.SetSendValue(max_datagram_frame_size);
+}
+
+uint32_t QuicConfig::GetMaxDatagramFrameSizeToSend() const {
+ return max_datagram_frame_size_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedMaxDatagramFrameSize() const {
+ return max_datagram_frame_size_.HasReceivedValue();
+}
+
+uint32_t QuicConfig::ReceivedMaxDatagramFrameSize() const {
+ return max_datagram_frame_size_.GetReceivedValue();
+}
+
bool QuicConfig::HasSetBytesForConnectionIdToSend() const {
return bytes_for_connection_id_.HasSendValue();
}
@@ -824,6 +842,7 @@
SetSupportMaxHeaderListSize();
SetAckDelayExponentToSend(kDefaultAckDelayExponent);
SetMaxPacketSizeToSend(kMaxIncomingPacketSize);
+ SetMaxDatagramFrameSizeToSend(kMaxAcceptedDatagramFrameSize);
}
void QuicConfig::ToHandshakeMessage(
@@ -940,6 +959,7 @@
}
params->max_packet_size.set_value(GetMaxPacketSizeToSend());
+ params->max_datagram_frame_size.set_value(GetMaxDatagramFrameSizeToSend());
params->initial_max_data.set_value(
GetInitialSessionFlowControlWindowToSend());
// The max stream data bidirectional transport parameters can be either local
@@ -1035,6 +1055,12 @@
}
}
+ if (params.max_datagram_frame_size.IsValid()) {
+ max_datagram_frame_size_.SetReceivedValue(
+ params.max_datagram_frame_size.value());
+ // TODO(dschinazi) act on this.
+ }
+
initial_session_flow_control_window_bytes_.SetReceivedValue(
std::min<uint64_t>(params.initial_max_data.value(),
std::numeric_limits<uint32_t>::max()));
diff --git a/quic/core/quic_config.h b/quic/core/quic_config.h
index 357344e..de78d40 100644
--- a/quic/core/quic_config.h
+++ b/quic/core/quic_config.h
@@ -462,6 +462,12 @@
bool HasReceivedMaxPacketSize() const;
uint32_t ReceivedMaxPacketSize() const;
+ // IETF QUIC max_datagram_frame_size transport parameter.
+ void SetMaxDatagramFrameSizeToSend(uint32_t max_datagram_frame_size);
+ uint32_t GetMaxDatagramFrameSizeToSend() const;
+ bool HasReceivedMaxDatagramFrameSize() const;
+ uint32_t ReceivedMaxDatagramFrameSize() const;
+
bool negotiated() const;
void SetCreateSessionTagIndicators(QuicTagVector tags);
@@ -579,9 +585,12 @@
// to serialize frames and this node uses to deserialize them.
QuicFixedUint32 ack_delay_exponent_;
- // max_packet_size IEFT QUIC transport parameter.
+ // max_packet_size IETF QUIC transport parameter.
QuicFixedUint32 max_packet_size_;
+ // max_datagram_frame_size IETF QUIC transport parameter.
+ QuicFixedUint32 max_datagram_frame_size_;
+
// Custom transport parameters that can be sent and received in the TLS
// handshake.
TransportParameters::ParameterMap custom_transport_parameters_to_send_;
diff --git a/quic/core/quic_config_test.cc b/quic/core/quic_config_test.cc
index 282e88c..41a5d77 100644
--- a/quic/core/quic_config_test.cc
+++ b/quic/core/quic_config_test.cc
@@ -22,6 +22,7 @@
namespace {
const uint32_t kMaxPacketSizeForTest = 1234;
+const uint32_t kMaxDatagramFrameSizeForTest = 1333;
class QuicConfigTest : public QuicTestWithParam<QuicTransportVersion> {
protected:
@@ -396,6 +397,7 @@
config_.SetInitialMaxStreamDataBytesUnidirectionalToSend(
4 * kMinimumFlowControlSendWindow);
config_.SetMaxPacketSizeToSend(kMaxPacketSizeForTest);
+ config_.SetMaxDatagramFrameSizeToSend(kMaxDatagramFrameSizeForTest);
TransportParameters params;
config_.FillTransportParameters(¶ms);
@@ -411,6 +413,8 @@
params.idle_timeout_milliseconds.value());
EXPECT_EQ(kMaxPacketSizeForTest, params.max_packet_size.value());
+ EXPECT_EQ(kMaxDatagramFrameSizeForTest,
+ params.max_datagram_frame_size.value());
}
TEST_P(QuicConfigTest, ProcessTransportParametersServer) {
@@ -423,6 +427,7 @@
params.initial_max_stream_data_uni.set_value(4 *
kMinimumFlowControlSendWindow);
params.max_packet_size.set_value(kMaxPacketSizeForTest);
+ params.max_datagram_frame_size.set_value(kMaxDatagramFrameSizeForTest);
std::string error_details;
EXPECT_EQ(QUIC_NO_ERROR,
@@ -444,6 +449,11 @@
ASSERT_TRUE(config_.HasReceivedMaxPacketSize());
EXPECT_EQ(kMaxPacketSizeForTest, config_.ReceivedMaxPacketSize());
+
+ ASSERT_TRUE(config_.HasReceivedMaxDatagramFrameSize());
+ EXPECT_EQ(kMaxDatagramFrameSizeForTest,
+ config_.ReceivedMaxDatagramFrameSize());
+
EXPECT_FALSE(config_.DisableConnectionMigration());
}
diff --git a/quic/core/quic_constants.h b/quic/core/quic_constants.h
index 6b22bbf..3264c15 100644
--- a/quic/core/quic_constants.h
+++ b/quic/core/quic_constants.h
@@ -47,6 +47,9 @@
const QuicByteCount kMaxOutgoingPacketSize = kMaxV6PacketSize;
// ETH_MAX_MTU - MAX(sizeof(iphdr), sizeof(ip6_hdr)) - sizeof(udphdr).
const QuicByteCount kMaxGsoPacketSize = 65535 - 40 - 8;
+// The maximal IETF DATAGRAM frame size we'll accept. Choosing 2^16 ensures
+// that it is greater than the biggest frame we could ever fit in a QUIC packet.
+const QuicByteCount kMaxAcceptedDatagramFrameSize = 65536;
// Default maximum packet size used in the Linux TCP implementation.
// Used in QUIC for congestion window computations in bytes.
const QuicByteCount kDefaultTCPMSS = 1460;
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 9b1d685..8164cdc 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -3226,12 +3226,12 @@
}
break;
}
- case IETF_EXTENSION_MESSAGE_NO_LENGTH:
+ case IETF_EXTENSION_MESSAGE_NO_LENGTH_V99:
QUIC_FALLTHROUGH_INTENDED;
- case IETF_EXTENSION_MESSAGE: {
+ case IETF_EXTENSION_MESSAGE_V99: {
QuicMessageFrame message_frame;
if (!ProcessMessageFrame(
- reader, frame_type == IETF_EXTENSION_MESSAGE_NO_LENGTH,
+ reader, frame_type == IETF_EXTENSION_MESSAGE_NO_LENGTH_V99,
&message_frame)) {
return RaiseError(QUIC_INVALID_MESSAGE_DATA);
}
@@ -5559,8 +5559,14 @@
bool QuicFramer::AppendMessageFrameAndTypeByte(const QuicMessageFrame& frame,
bool last_frame_in_packet,
QuicDataWriter* writer) {
- uint8_t type_byte = last_frame_in_packet ? IETF_EXTENSION_MESSAGE_NO_LENGTH
- : IETF_EXTENSION_MESSAGE;
+ uint8_t type_byte;
+ if (VersionHasIetfQuicFrames(version_.transport_version)) {
+ type_byte = last_frame_in_packet ? IETF_EXTENSION_MESSAGE_NO_LENGTH_V99
+ : IETF_EXTENSION_MESSAGE_V99;
+ } else {
+ type_byte = last_frame_in_packet ? IETF_EXTENSION_MESSAGE_NO_LENGTH
+ : IETF_EXTENSION_MESSAGE;
+ }
if (!writer->WriteUInt8(type_byte)) {
return false;
}
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 3b0e879..592b019 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -365,9 +365,9 @@
message_frames_.push_back(
std::make_unique<QuicMessageFrame>(frame.data, frame.message_length));
if (VersionHasIetfQuicFrames(transport_version_)) {
- EXPECT_TRUE(IETF_EXTENSION_MESSAGE_NO_LENGTH ==
+ EXPECT_TRUE(IETF_EXTENSION_MESSAGE_NO_LENGTH_V99 ==
framer_->current_received_frame_type() ||
- IETF_EXTENSION_MESSAGE ==
+ IETF_EXTENSION_MESSAGE_V99 ==
framer_->current_received_frame_type());
} else {
EXPECT_EQ(0u, framer_->current_received_frame_type());
@@ -5086,7 +5086,7 @@
}
TEST_P(QuicFramerTest, MessageFrame) {
- if (framer_.transport_version() <= QUIC_VERSION_43) {
+ if (!VersionSupportsMessageFrames(framer_.transport_version())) {
return;
}
SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
@@ -5117,10 +5117,40 @@
{{},
{'m', 'e', 's', 's', 'a', 'g', 'e', '2'}},
};
+ 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}},
+ // message frame type.
+ {"",
+ { 0x31 }},
+ // message length
+ {"Unable to read message length",
+ {0x07}},
+ // message data
+ {"Unable to read message data",
+ {'m', 'e', 's', 's', 'a', 'g', 'e'}},
+ // message frame no length.
+ {"",
+ { 0x30 }},
+ // message data
+ {{},
+ {'m', 'e', 's', 's', 'a', 'g', 'e', '2'}},
+ };
// clang-format on
- std::unique_ptr<QuicEncryptedPacket> encrypted(
- AssemblePacketFromFragments(packet46));
+ std::unique_ptr<QuicEncryptedPacket> encrypted;
+ if (VersionHasIetfQuicFrames(framer_.transport_version())) {
+ encrypted = AssemblePacketFromFragments(packet99);
+ } else {
+ encrypted = AssemblePacketFromFragments(packet46);
+ }
EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
EXPECT_THAT(framer_.error(), IsQuicNoError());
@@ -5133,7 +5163,11 @@
EXPECT_EQ(7u, visitor_.message_frames_[0]->message_length);
EXPECT_EQ(8u, visitor_.message_frames_[1]->message_length);
- CheckFramingBoundaries(packet46, QUIC_INVALID_MESSAGE_DATA);
+ if (VersionHasIetfQuicFrames(framer_.transport_version())) {
+ CheckFramingBoundaries(packet99, QUIC_INVALID_MESSAGE_DATA);
+ } else {
+ CheckFramingBoundaries(packet46, QUIC_INVALID_MESSAGE_DATA);
+ }
}
TEST_P(QuicFramerTest, PublicResetPacketV33) {
@@ -8442,7 +8476,7 @@
}
TEST_P(QuicFramerTest, BuildMessagePacket) {
- if (framer_.transport_version() <= QUIC_VERSION_43) {
+ if (!VersionSupportsMessageFrames(framer_.transport_version())) {
return;
}
QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
@@ -8487,13 +8521,13 @@
0x12, 0x34, 0x56, 0x78,
// frame type (IETF_MESSAGE frame)
- 0x21,
+ 0x31,
// Length
0x07,
// Message Data
'm', 'e', 's', 's', 'a', 'g', 'e',
// frame type (message frame no length)
- 0x20,
+ 0x30,
// Message Data
'm', 'e', 's', 's', 'a', 'g', 'e', '2'
};
diff --git a/quic/core/quic_types.cc b/quic/core/quic_types.cc
index 48a41d8..ca12e76 100644
--- a/quic/core/quic_types.cc
+++ b/quic/core/quic_types.cc
@@ -462,6 +462,8 @@
RETURN_STRING_LITERAL(IETF_APPLICATION_CLOSE);
RETURN_STRING_LITERAL(IETF_EXTENSION_MESSAGE_NO_LENGTH);
RETURN_STRING_LITERAL(IETF_EXTENSION_MESSAGE);
+ RETURN_STRING_LITERAL(IETF_EXTENSION_MESSAGE_NO_LENGTH_V99);
+ RETURN_STRING_LITERAL(IETF_EXTENSION_MESSAGE_V99);
default:
return QuicStrCat("Private value (", t, ")");
}
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index 3511c8d..f4e6bdf 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -271,10 +271,13 @@
IETF_CONNECTION_CLOSE = 0x1c,
IETF_APPLICATION_CLOSE = 0x1d,
- // MESSAGE frame type is not yet determined, use 0x2x temporarily to give
- // stream frame some wiggle room.
+ // The MESSAGE frame type has not yet been fully standardized.
+ // QUIC versions starting with 46 and before 99 use 0x20-0x21.
+ // IETF QUIC (v99) uses 0x30-0x31, see draft-pauly-quic-datagram.
IETF_EXTENSION_MESSAGE_NO_LENGTH = 0x20,
IETF_EXTENSION_MESSAGE = 0x21,
+ IETF_EXTENSION_MESSAGE_NO_LENGTH_V99 = 0x30,
+ IETF_EXTENSION_MESSAGE_V99 = 0x31,
};
QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
const QuicIetfFrameType& c);