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