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