gfe-relnote: move stop/start accepting new connection feature from GfeQuicDispatcher to QuicDispatcher. Protected by --gfe2_restart_flag_quic_should_accept_new_connection.
Use QuicDispatcher::accept_new_connections_ instead of GfeQuicDispathcer::accepting_new_connection_ids_ to reject new connection.
Add new interfaces Start/StopAcceptingNewConnections() to QuicDispatcher.
PiperOrigin-RevId: 299258338
Change-Id: I8db641954ca52eddaad41934bd6b0b3e61237f9c
diff --git a/quic/core/quic_buffered_packet_store.cc b/quic/core/quic_buffered_packet_store.cc
index e4dd233..e363461 100644
--- a/quic/core/quic_buffered_packet_store.cc
+++ b/quic/core/quic_buffered_packet_store.cc
@@ -175,6 +175,12 @@
connections_with_chlo_.erase(connection_id);
}
+void QuicBufferedPacketStore::DiscardAllPackets() {
+ undecryptable_packets_.clear();
+ connections_with_chlo_.clear();
+ expiration_alarm_->Cancel();
+}
+
void QuicBufferedPacketStore::OnExpirationTimeout() {
QuicTime expiration_time = clock_->ApproximateNow() - connection_life_span_;
while (!undecryptable_packets_.empty()) {
diff --git a/quic/core/quic_buffered_packet_store.h b/quic/core/quic_buffered_packet_store.h
index dbf3faf..52d957e 100644
--- a/quic/core/quic_buffered_packet_store.h
+++ b/quic/core/quic_buffered_packet_store.h
@@ -122,6 +122,9 @@
// Discards packets buffered for |connection_id|, if any.
void DiscardPackets(QuicConnectionId connection_id);
+ // Discards all the packets.
+ void DiscardAllPackets();
+
// Examines how long packets have been buffered in the store for each
// connection. If they stay too long, removes them for new coming packets and
// calls |visitor_|'s OnPotentialConnectionExpire().
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index eac92e0..c160ced 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -414,6 +414,26 @@
}
// The packet has an unknown connection ID.
+ if (!accept_new_connections_ && packet_info.version_flag) {
+ // If not accepting new connections, reject packets with version which can
+ // potentially result in new connection creation. But if the packet doesn't
+ // have version flag, leave it to ValidityChecks() to reset it.
+ // By adding the connection to time wait list, following packets on this
+ // connection will not reach ShouldAcceptNewConnections().
+ StatelesslyTerminateConnection(
+ packet_info.destination_connection_id, packet_info.form,
+ packet_info.version_flag, packet_info.use_length_prefix,
+ packet_info.version, QUIC_HANDSHAKE_FAILED,
+ "Stop accepting new connections",
+ quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
+ // Time wait list will reject the packet correspondingly..
+ time_wait_list_manager()->ProcessPacket(
+ packet_info.self_address, packet_info.peer_address,
+ packet_info.destination_connection_id, packet_info.form,
+ GetPerPacketContext());
+ OnNewConnectionRejected();
+ return true;
+ }
// Unless the packet provides a version, assume that we can continue
// processing using our preferred version.
@@ -590,8 +610,15 @@
session_map_.erase(it);
}
+void QuicDispatcher::StartAcceptingNewConnections() {
+ accept_new_connections_ = true;
+}
+
void QuicDispatcher::StopAcceptingNewConnections() {
accept_new_connections_ = false;
+ // No more CHLO will arrive and buffered CHLOs shouldn't be able to create
+ // connections.
+ buffered_packets_.DiscardAllPackets();
}
std::unique_ptr<QuicPerPacketContext> QuicDispatcher::GetPerPacketContext()
@@ -870,22 +897,6 @@
void QuicDispatcher::ProcessChlo(const std::string& alpn,
ReceivedPacketInfo* packet_info) {
- if (!accept_new_connections_) {
- // Don't any create new connection.
- QUIC_CODE_COUNT(quic_reject_stop_accepting_new_connections);
- StatelesslyTerminateConnection(
- packet_info->destination_connection_id, packet_info->form,
- /*version_flag=*/true, packet_info->use_length_prefix,
- packet_info->version, QUIC_HANDSHAKE_FAILED,
- "Stop accepting new connections",
- quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
- // Time wait list will reject the packet correspondingly.
- time_wait_list_manager()->ProcessPacket(
- packet_info->self_address, packet_info->peer_address,
- packet_info->destination_connection_id, packet_info->form,
- GetPerPacketContext());
- return;
- }
if (!buffered_packets_.HasBufferedPackets(
packet_info->destination_connection_id) &&
!ShouldCreateOrBufferPacketForConnection(*packet_info)) {
diff --git a/quic/core/quic_dispatcher.h b/quic/core/quic_dispatcher.h
index 4bc44de..71cbb57 100644
--- a/quic/core/quic_dispatcher.h
+++ b/quic/core/quic_dispatcher.h
@@ -139,6 +139,15 @@
// Return true if there is CHLO buffered.
virtual bool HasChlosBuffered() const;
+ // Start accepting new ConnectionIds.
+ void StartAcceptingNewConnections();
+
+ // Stop accepting new ConnectionIds, either as a part of the lame
+ // duck process or because explicitly configured.
+ void StopAcceptingNewConnections();
+
+ bool accept_new_connections() const { return accept_new_connections_; }
+
protected:
virtual std::unique_ptr<QuicSession> CreateQuicSession(
QuicConnectionId server_connection_id,
@@ -251,8 +260,6 @@
QuicConnection* connection,
ConnectionCloseSource source);
- void StopAcceptingNewConnections();
-
// Called to terminate a connection statelessly. Depending on |format|, either
// 1) send connection close with |error_code| and |error_details| and add
// connection to time wait list or 2) directly add connection to time wait
@@ -288,6 +295,9 @@
allow_short_initial_server_connection_ids;
}
+ // Called if a packet from an unseen connection is reset or rejected.
+ virtual void OnNewConnectionRejected() {}
+
private:
friend class test::QuicDispatcherPeer;
@@ -365,7 +375,8 @@
// event loop. When reaches 0, it means can't create sessions for now.
int16_t new_sessions_allowed_per_event_loop_;
- // True if this dispatcher is not draining.
+ // True if this dispatcher is accepting new ConnectionIds (new client
+ // connections), false otherwise.
bool accept_new_connections_;
// If false, the dispatcher follows the IETF spec and rejects packets with
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index 7e2b470..7e54dd2 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -1324,6 +1324,72 @@
dispatcher_->ProcessPacket(server_address_, client_address, packet);
}
+TEST_P(QuicDispatcherTestAllVersions, StopAcceptingNewConnections) {
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(1), client_address,
+ Eq(ExpectedAlpn()), _))
+ .WillOnce(Return(ByMove(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(1), client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO());
+
+ dispatcher_->StopAcceptingNewConnections();
+ EXPECT_FALSE(dispatcher_->accept_new_connections());
+
+ // No more new connections afterwards.
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(2), client_address,
+ Eq(ExpectedAlpn()), _))
+ .Times(0u);
+ ProcessPacket(client_address, TestConnectionId(2), true, SerializeCHLO());
+
+ // Existing connections should be able to continue.
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .Times(1u)
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ ProcessPacket(client_address, TestConnectionId(1), false, "data");
+}
+
+TEST_P(QuicDispatcherTestAllVersions, StartAcceptingNewConnections) {
+ dispatcher_->StopAcceptingNewConnections();
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ // No more new connections afterwards.
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(2), client_address,
+ Eq(ExpectedAlpn()), _))
+ .Times(0u);
+ ProcessPacket(client_address, TestConnectionId(2), true, SerializeCHLO());
+
+ dispatcher_->StartAcceptingNewConnections();
+ EXPECT_TRUE(dispatcher_->accept_new_connections());
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(1), client_address,
+ Eq(ExpectedAlpn()), _))
+ .WillOnce(Return(ByMove(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(1), client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO());
+}
+
// Verify the stopgap test: Packets with truncated connection IDs should be
// dropped.
class QuicDispatcherTestStrayPacketConnectionId