Skip to content

Commit

Permalink
quic: handle incoming packets with unknown destination address (#32691)
Browse files Browse the repository at this point in the history
Commit Message: QUIC client sockets always enable socket options to read destination address of the incoming UDP packets. But in some cases, the address is not returned via the system call, recvmsg or recvmmsg. In such case, Quic connection should drop the packets. And if there are a lot of such packets, the connection should be closed with detailed error sent to the server.

Risk Level: n/a
Testing: n/a
Docs Changes: n/a
Release Notes: n/a
Platform Specific Features: n/a

Signed-off-by: Dan Zhang <[email protected]>
Co-authored-by: Dan Zhang <[email protected]>
  • Loading branch information
danzh2010 and danzh1989 authored Mar 5, 2024
1 parent 9e683d2 commit f26361e
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 8 deletions.
17 changes: 9 additions & 8 deletions source/common/network/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -583,16 +583,17 @@ void passPayloadToProcessor(uint64_t bytes_read, Buffer::InstancePtr buffer,
Address::InstanceConstSharedPtr peer_addess,
Address::InstanceConstSharedPtr local_address,
UdpPacketProcessor& udp_packet_processor, MonotonicTime receive_time) {
RELEASE_ASSERT(
peer_addess != nullptr,
fmt::format("Unable to get remote address on the socket bount to local address: {} ",
local_address->asString()));
ENVOY_BUG(peer_addess != nullptr,
fmt::format("Unable to get remote address on the socket bound to local address: {}.",
(local_address == nullptr ? "unknown" : local_address->asString())));

// Unix domain sockets are not supported
RELEASE_ASSERT(peer_addess->type() == Address::Type::Ip,
fmt::format("Unsupported remote address: {} local address: {}, receive size: "
"{}",
peer_addess->asString(), local_address->asString(), bytes_read));
ENVOY_BUG(peer_addess != nullptr && peer_addess->type() == Address::Type::Ip,
fmt::format("Unsupported remote address: {} local address: {}, receive size: "
"{}",
peer_addess->asString(),
(local_address == nullptr ? "unknown" : local_address->asString()),
bytes_read));
udp_packet_processor.processPacket(std::move(local_address), std::move(peer_addess),
std::move(buffer), receive_time);
}
Expand Down
20 changes: 20 additions & 0 deletions source/common/quic/envoy_quic_client_connection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,27 @@ void EnvoyQuicClientConnection::processPacket(
quic::QuicTime::Delta::FromMicroseconds(
std::chrono::duration_cast<std::chrono::microseconds>(receive_time.time_since_epoch())
.count());
ASSERT(peer_address != nullptr && buffer != nullptr);
ASSERT(buffer->getRawSlices().size() == 1);
if (local_address == nullptr) {
// Quic doesn't know how to handle packets without destination address. Drop them here.
if (buffer->length() > 0) {
++num_packets_with_unknown_dst_address_;
std::string error_message = fmt::format(
"Unable to get destination address. Address family {}. Have{} pending path validation. "
"self_address is{} initialized.",
(peer_address->ip()->version() == Network::Address::IpVersion::v4 ? "v4" : "v6"),
(HasPendingPathValidation() ? "" : " no"),
(self_address().IsInitialized() ? "" : " not"));
ENVOY_CONN_LOG(error, error_message, *this);
if (num_packets_with_unknown_dst_address_ > 10) {
// If too many packets are without destination addresses, close the connection.
CloseConnection(quic::QUIC_PACKET_READ_ERROR, error_message,
quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
}
}
return;
}
Buffer::RawSlice slice = buffer->frontSlice();
quic::QuicReceivedPacket packet(reinterpret_cast<char*>(slice.mem_), slice.len_, timestamp,
/*owns_buffer=*/false, /*ttl=*/0, /*ttl_valid=*/false,
Expand Down
1 change: 1 addition & 0 deletions source/common/quic/envoy_quic_client_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ class EnvoyQuicClientConnection : public quic::QuicConnection,
Event::Dispatcher& dispatcher_;
bool migrate_port_on_path_degrading_{false};
uint8_t num_socket_switches_{0};
size_t num_packets_with_unknown_dst_address_{0};
};

} // namespace Quic
Expand Down
22 changes: 22 additions & 0 deletions test/common/quic/envoy_quic_client_session_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,28 @@ TEST_P(EnvoyQuicClientSessionTest, VerifyContextAbortOnFlushWriteBuffer) {
"unexpectedly reached");
}

TEST_P(EnvoyQuicClientSessionTest, HandlePacketsWithoutDestinationAddress) {
// Build a STATELESS_RESET packet.
std::unique_ptr<quic::QuicEncryptedPacket> stateless_reset_packet =
quic::QuicFramer::BuildIetfStatelessResetPacket(
quic::test::TestConnectionId(), /*received_packet_length*/ 1200,
quic::QuicUtils::GenerateStatelessResetToken(quic::test::TestConnectionId()));
EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose))
.Times(0);
for (size_t i = 0; i < 9; ++i) {
auto buffer = std::make_unique<Buffer::OwnedImpl>(stateless_reset_packet->data(),
stateless_reset_packet->length());
quic_connection_->processPacket(nullptr, peer_addr_, std::move(buffer),
time_system_.monotonicTime());
}
EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose))
.Times(0);
auto buffer = std::make_unique<Buffer::OwnedImpl>(stateless_reset_packet->data(),
stateless_reset_packet->length());
quic_connection_->processPacket(nullptr, peer_addr_, std::move(buffer),
time_system_.monotonicTime());
}

// Tests that receiving a STATELESS_RESET packet on the probing socket doesn't cause crash.
TEST_P(EnvoyQuicClientSessionTest, StatelessResetOnProbingSocket) {
quic::QuicNewConnectionIdFrame frame;
Expand Down

0 comments on commit f26361e

Please sign in to comment.