Send an ICMP6_PACKET_TOO_BIG when we try to send a message that is too big.

This change also:

  1) Completely disables fallback to streams when we've negotiated messages.
  2) Drops the default MTU on the bonnet to 1280.

gfe-relnote: gfe-relnote: n/a (QBONE-only change)
PiperOrigin-RevId: 278862096
Change-Id: I0ff1fae69c7f0a82d3afcd5f13f7c2626f2e583b
diff --git a/quic/qbone/qbone_session_base.cc b/quic/qbone/qbone_session_base.cc
index 62bcaa4..9c8479c 100644
--- a/quic/qbone/qbone_session_base.cc
+++ b/quic/qbone/qbone_session_base.cc
@@ -4,11 +4,15 @@
 
 #include "net/third_party/quiche/src/quic/qbone/qbone_session_base.h"
 
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+
 #include <utility>
 
 #include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h"
+#include "net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h"
 #include "net/third_party/quiche/src/quic/qbone/qbone_constants.h"
 
 namespace quic {
@@ -137,12 +141,41 @@
     QuicMemSlice slice(connection()->helper()->GetStreamSendBufferAllocator(),
                        packet.size());
     memcpy(const_cast<char*>(slice.data()), packet.data(), packet.size());
-    if (SendMessage(QuicMemSliceSpan(&slice)).status ==
-        MESSAGE_STATUS_SUCCESS) {
-      return;
+    switch (SendMessage(QuicMemSliceSpan(&slice)).status) {
+      case MESSAGE_STATUS_SUCCESS:
+        break;
+      case MESSAGE_STATUS_TOO_LARGE: {
+        if (packet.size() < sizeof(ip6_hdr)) {
+          QUIC_BUG << "Dropped malformed packet: IPv6 header too short";
+          break;
+        }
+        auto* header = reinterpret_cast<const ip6_hdr*>(packet.begin());
+        icmp6_hdr icmp_header{};
+        icmp_header.icmp6_type = ICMP6_PACKET_TOO_BIG;
+        icmp_header.icmp6_mtu =
+            connection()->GetGuaranteedLargestMessagePayload();
+
+        CreateIcmpPacket(header->ip6_dst, header->ip6_src, icmp_header, packet,
+                         [this](QuicStringPiece icmp_packet) {
+                           writer_->WritePacketToNetwork(icmp_packet.data(),
+                                                         icmp_packet.size());
+                         });
+        break;
+      }
+      case MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED:
+        QUIC_BUG << "MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED";
+        break;
+      case MESSAGE_STATUS_UNSUPPORTED:
+        QUIC_BUG << "MESSAGE_STATUS_UNSUPPORTED";
+        break;
+      case MESSAGE_STATUS_BLOCKED:
+        QUIC_BUG << "MESSAGE_STATUS_BLOCKED";
+        break;
+      case MESSAGE_STATUS_INTERNAL_ERROR:
+        QUIC_BUG << "MESSAGE_STATUS_INTERNAL_ERROR";
+        break;
     }
-    // If SendMessage() fails for any reason, fall back to ephemeral streams.
-    num_fallback_to_stream_++;
+    return;
   }
 
   // Qbone streams are ephemeral.
diff --git a/quic/qbone/qbone_session_test.cc b/quic/qbone/qbone_session_test.cc
index 8a12cec..19ecd2c 100644
--- a/quic/qbone/qbone_session_test.cc
+++ b/quic/qbone/qbone_session_test.cc
@@ -12,6 +12,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h"
 #include "net/third_party/quiche/src/quic/qbone/qbone_client_session.h"
 #include "net/third_party/quiche/src/quic/qbone/qbone_constants.h"
 #include "net/third_party/quiche/src/quic/qbone/qbone_control_placeholder.pb.h"
@@ -353,6 +354,23 @@
     runner_.Run();
   }
 
+  void ExpectICMPTooBigResponse(const std::vector<string>& written_packets,
+                                const int mtu,
+                                const string& packet) {
+    auto* header = reinterpret_cast<const ip6_hdr*>(packet.data());
+    icmp6_hdr icmp_header{};
+    icmp_header.icmp6_type = ICMP6_PACKET_TOO_BIG;
+    icmp_header.icmp6_mtu = mtu;
+
+    string expected;
+    CreateIcmpPacket(header->ip6_dst, header->ip6_src, icmp_header, packet,
+                     [&expected](QuicStringPiece icmp_packet) {
+                       expected = string(icmp_packet);
+                     });
+
+    EXPECT_THAT(written_packets, Contains(expected));
+  }
+
   // Test handshake establishment and sending/receiving of data for two
   // directions.
   void TestStreamConnection(bool use_messages) {
@@ -395,7 +413,14 @@
     QUIC_LOG(INFO) << "Sending server -> client long data";
     server_peer_->ProcessPacketFromNetwork(TestPacketIn(long_data));
     runner_.Run();
-    EXPECT_THAT(client_writer_->data(), Contains(TestPacketOut(long_data)));
+    if (use_messages) {
+      ExpectICMPTooBigResponse(
+          server_writer_->data(),
+          server_peer_->connection()->GetGuaranteedLargestMessagePayload(),
+          TestPacketOut(long_data));
+    } else {
+      EXPECT_THAT(client_writer_->data(), Contains(TestPacketOut(long_data)));
+    }
     EXPECT_THAT(server_writer_->data(),
                 Not(Contains(TestPacketOut(long_data))));
     EXPECT_EQ(0u, server_peer_->GetNumActiveStreams());
@@ -404,11 +429,22 @@
     QUIC_LOG(INFO) << "Sending client -> server long data";
     client_peer_->ProcessPacketFromNetwork(TestPacketIn(long_data));
     runner_.Run();
-    EXPECT_THAT(server_writer_->data(), Contains(TestPacketOut(long_data)));
+    if (use_messages) {
+      ExpectICMPTooBigResponse(
+          client_writer_->data(),
+          client_peer_->connection()->GetGuaranteedLargestMessagePayload(),
+          TestPacketIn(long_data));
+    } else {
+      EXPECT_THAT(server_writer_->data(), Contains(TestPacketOut(long_data)));
+    }
     EXPECT_THAT(client_peer_->GetNumSentClientHellos(), Eq(2));
     EXPECT_THAT(client_peer_->GetNumReceivedServerConfigUpdates(), Eq(0));
-    EXPECT_THAT(client_peer_->GetNumStreamedPackets(), Eq(1));
-    EXPECT_THAT(server_peer_->GetNumStreamedPackets(), Eq(1));
+
+    if (!use_messages) {
+      EXPECT_THAT(client_peer_->GetNumStreamedPackets(), Eq(1));
+      EXPECT_THAT(server_peer_->GetNumStreamedPackets(), Eq(1));
+    }
+
     if (use_messages) {
       EXPECT_THAT(client_peer_->GetNumEphemeralPackets(), Eq(0));
       EXPECT_THAT(server_peer_->GetNumEphemeralPackets(), Eq(0));