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