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(&params) ||
       !in.ack_delay_exponent.WriteToCbb(&params) ||
       !in.max_ack_delay.WriteToCbb(&params) ||
-      !in.active_connection_id_limit.WriteToCbb(&params)) {
+      !in.active_connection_id_limit.WriteToCbb(&params) ||
+      !in.max_datagram_frame_size.WriteToCbb(&params)) {
     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(&params);
@@ -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);