| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Handles packets for connection_ids in time wait state by discarding the |
| // packet and sending the peers termination packets with exponential backoff. |
| |
| #ifndef QUICHE_QUIC_CORE_QUIC_TIME_WAIT_LIST_MANAGER_H_ |
| #define QUICHE_QUIC_CORE_QUIC_TIME_WAIT_LIST_MANAGER_H_ |
| |
| #include <cstddef> |
| #include <memory> |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "quiche/quic/core/quic_blocked_writer_interface.h" |
| #include "quiche/quic/core/quic_connection_id.h" |
| #include "quiche/quic/core/quic_framer.h" |
| #include "quiche/quic/core/quic_packet_writer.h" |
| #include "quiche/quic/core/quic_packets.h" |
| #include "quiche/quic/core/quic_session.h" |
| #include "quiche/quic/core/quic_types.h" |
| #include "quiche/quic/platform/api/quic_flags.h" |
| #include "quiche/common/quiche_linked_hash_map.h" |
| |
| namespace quic { |
| |
| namespace test { |
| class QuicDispatcherPeer; |
| class QuicTimeWaitListManagerPeer; |
| } // namespace test |
| |
| // TimeWaitConnectionInfo comprises information of a connection which is in the |
| // time wait list. |
| struct QUICHE_EXPORT TimeWaitConnectionInfo { |
| TimeWaitConnectionInfo( |
| bool ietf_quic, |
| std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets, |
| std::vector<QuicConnectionId> active_connection_ids); |
| TimeWaitConnectionInfo( |
| bool ietf_quic, |
| std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets, |
| std::vector<QuicConnectionId> active_connection_ids, |
| QuicTime::Delta srtt); |
| |
| TimeWaitConnectionInfo(const TimeWaitConnectionInfo& other) = delete; |
| TimeWaitConnectionInfo(TimeWaitConnectionInfo&& other) = default; |
| |
| ~TimeWaitConnectionInfo() = default; |
| |
| bool ietf_quic; |
| std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets; |
| std::vector<QuicConnectionId> active_connection_ids; |
| QuicTime::Delta srtt; |
| }; |
| |
| // Maintains a list of all connection_ids that have been recently closed. A |
| // connection_id lives in this state for time_wait_period_. All packets received |
| // for connection_ids in this state are handed over to the |
| // QuicTimeWaitListManager by the QuicDispatcher. Decides whether to send a |
| // public reset packet, a copy of the previously sent connection close packet, |
| // or nothing to the peer which sent a packet with the connection_id in time |
| // wait state. After the connection_id expires its time wait period, a new |
| // connection/session will be created if a packet is received for this |
| // connection_id. |
| class QUICHE_EXPORT QuicTimeWaitListManager |
| : public QuicBlockedWriterInterface { |
| public: |
| // Specifies what the time wait list manager should do when processing packets |
| // of a time wait connection. |
| enum TimeWaitAction : uint8_t { |
| // Send specified termination packets, error if termination packet is |
| // unavailable. |
| SEND_TERMINATION_PACKETS, |
| // The same as SEND_TERMINATION_PACKETS except that the corresponding |
| // termination packets are provided by the connection. |
| SEND_CONNECTION_CLOSE_PACKETS, |
| // Send stateless reset (public reset for GQUIC). |
| SEND_STATELESS_RESET, |
| |
| DO_NOTHING, |
| }; |
| |
| class QUICHE_EXPORT Visitor : public QuicSession::Visitor { |
| public: |
| // Called after the given connection is added to the time-wait list. |
| virtual void OnConnectionAddedToTimeWaitList( |
| QuicConnectionId connection_id) = 0; |
| |
| void OnPathDegrading() override {} |
| }; |
| |
| // writer - the entity that writes to the socket. (Owned by the caller) |
| // visitor - the entity that manages blocked writers. (Owned by the caller) |
| // clock - provide a clock (Owned by the caller) |
| // alarm_factory - used to run clean up alarms. (Owned by the caller) |
| QuicTimeWaitListManager(QuicPacketWriter* writer, Visitor* visitor, |
| const QuicClock* clock, |
| QuicAlarmFactory* alarm_factory); |
| QuicTimeWaitListManager(const QuicTimeWaitListManager&) = delete; |
| QuicTimeWaitListManager& operator=(const QuicTimeWaitListManager&) = delete; |
| ~QuicTimeWaitListManager() override; |
| |
| // Adds the connection IDs in info to time wait state for time_wait_period_. |
| // If |info|.termination_packets are provided, copies of these packets will be |
| // sent when a packet with one of these connection IDs is processed. Any |
| // termination packets will be move from |info|.termination_packets and will |
| // become owned by the manager. |action| specifies what the time wait list |
| // manager should do when processing packets of the connection. |
| virtual void AddConnectionIdToTimeWait(TimeWaitAction action, |
| TimeWaitConnectionInfo info); |
| |
| // Returns true if the connection_id is in time wait state, false otherwise. |
| // Packets received for this connection_id should not lead to creation of new |
| // QuicSessions. |
| bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) const; |
| |
| // Called when a packet is received for a connection_id that is in time wait |
| // state. Sends a public reset packet to the peer which sent this |
| // connection_id. Sending of the public reset packet is throttled by using |
| // exponential back off. QUICHE_DCHECKs for the connection_id to be in time |
| // wait state. virtual to override in tests. |
| // TODO(fayang): change ProcessPacket and SendPublicReset to take |
| // ReceivedPacketInfo. |
| virtual void ProcessPacket( |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, QuicConnectionId connection_id, |
| PacketHeaderFormat header_format, size_t received_packet_length, |
| std::unique_ptr<QuicPerPacketContext> packet_context); |
| |
| // Called by the dispatcher when the underlying socket becomes writable again, |
| // since we might need to send pending public reset packets which we didn't |
| // send because the underlying socket was write blocked. |
| void OnBlockedWriterCanWrite() override; |
| |
| bool IsWriterBlocked() const override { |
| return writer_ != nullptr && writer_->IsWriteBlocked(); |
| } |
| |
| // Used to delete connection_id entries that have outlived their time wait |
| // period. |
| void CleanUpOldConnectionIds(); |
| |
| // If necessary, trims the oldest connections from the time-wait list until |
| // the size is under the configured maximum. |
| void TrimTimeWaitListIfNeeded(); |
| |
| // The number of connections on the time-wait list. |
| size_t num_connections() const { return connection_id_map_.size(); } |
| |
| // Sends a version negotiation packet for |server_connection_id| and |
| // |client_connection_id| announcing support for |supported_versions| to |
| // |peer_address| from |self_address|. |
| virtual void SendVersionNegotiationPacket( |
| QuicConnectionId server_connection_id, |
| QuicConnectionId client_connection_id, bool ietf_quic, |
| bool use_length_prefix, const ParsedQuicVersionVector& supported_versions, |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| std::unique_ptr<QuicPerPacketContext> packet_context); |
| |
| // Creates a public reset packet and sends it or queues it to be sent later. |
| virtual void SendPublicReset( |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, QuicConnectionId connection_id, |
| bool ietf_quic, size_t received_packet_length, |
| std::unique_ptr<QuicPerPacketContext> packet_context); |
| |
| // Called to send |packet|. |
| virtual void SendPacket(const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| const QuicEncryptedPacket& packet); |
| |
| // Return a non-owning pointer to the packet writer. |
| QuicPacketWriter* writer() { return writer_; } |
| |
| protected: |
| virtual std::unique_ptr<QuicEncryptedPacket> BuildPublicReset( |
| const QuicPublicResetPacket& packet); |
| |
| virtual void GetEndpointId(std::string* /*endpoint_id*/) {} |
| |
| // Returns a stateless reset token which will be included in the public reset |
| // packet. |
| virtual StatelessResetToken GetStatelessResetToken( |
| QuicConnectionId connection_id) const; |
| |
| // Internal structure to store pending termination packets. |
| class QUICHE_EXPORT QueuedPacket { |
| public: |
| QueuedPacket(const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| std::unique_ptr<QuicEncryptedPacket> packet) |
| : self_address_(self_address), |
| peer_address_(peer_address), |
| packet_(std::move(packet)) {} |
| QueuedPacket(const QueuedPacket&) = delete; |
| QueuedPacket& operator=(const QueuedPacket&) = delete; |
| |
| const QuicSocketAddress& self_address() const { return self_address_; } |
| const QuicSocketAddress& peer_address() const { return peer_address_; } |
| QuicEncryptedPacket* packet() { return packet_.get(); } |
| |
| private: |
| // Server address on which a packet was received for a connection_id in |
| // time wait state. |
| const QuicSocketAddress self_address_; |
| // Address of the peer to send this packet to. |
| const QuicSocketAddress peer_address_; |
| // The pending termination packet that is to be sent to the peer. |
| std::unique_ptr<QuicEncryptedPacket> packet_; |
| }; |
| |
| // Called right after |packet| is serialized. Either sends the packet and |
| // deletes it or makes pending_packets_queue_ the owner of the packet. |
| // Subclasses overriding this method should call this class's base |
| // implementation at the end of the override. |
| // Return true if |packet| is sent, false if it is queued. |
| virtual bool SendOrQueuePacket(std::unique_ptr<QueuedPacket> packet, |
| const QuicPerPacketContext* packet_context); |
| |
| const quiche::QuicheCircularDeque<std::unique_ptr<QueuedPacket>>& |
| pending_packets_queue() const { |
| return pending_packets_queue_; |
| } |
| |
| private: |
| friend class test::QuicDispatcherPeer; |
| friend class test::QuicTimeWaitListManagerPeer; |
| |
| // Decides if a packet should be sent for this connection_id based on the |
| // number of received packets. |
| bool ShouldSendResponse(int received_packet_count); |
| |
| // Sends the packet out. Returns true if the packet was successfully consumed. |
| // If the writer got blocked and did not buffer the packet, we'll need to keep |
| // the packet and retry sending. In case of all other errors we drop the |
| // packet. |
| bool WriteToWire(QueuedPacket* packet); |
| |
| // Register the alarm server to wake up at appropriate time. |
| void SetConnectionIdCleanUpAlarm(); |
| |
| // Removes the oldest connection from the time-wait list if it was added prior |
| // to "expiration_time". To unconditionally remove the oldest connection, use |
| // a QuicTime::Delta:Infinity(). This function modifies the |
| // connection_id_map_. If you plan to call this function in a loop, any |
| // iterators that you hold before the call to this function may be invalid |
| // afterward. Returns true if the oldest connection was expired. Returns |
| // false if the map is empty or the oldest connection has not expired. |
| bool MaybeExpireOldestConnection(QuicTime expiration_time); |
| |
| // Called when a packet is received for a connection in this time wait list. |
| virtual void OnPacketReceivedForKnownConnection( |
| int /*num_packets*/, QuicTime::Delta /*delta*/, |
| QuicTime::Delta /*srtt*/) const {} |
| |
| std::unique_ptr<QuicEncryptedPacket> BuildIetfStatelessResetPacket( |
| QuicConnectionId connection_id, size_t received_packet_length); |
| |
| // A map from a recently closed connection_id to the number of packets |
| // received after the termination of the connection bound to the |
| // connection_id. |
| struct QUICHE_EXPORT ConnectionIdData { |
| ConnectionIdData(int num_packets, QuicTime time_added, |
| TimeWaitAction action, TimeWaitConnectionInfo info); |
| |
| ConnectionIdData(const ConnectionIdData& other) = delete; |
| ConnectionIdData(ConnectionIdData&& other); |
| |
| ~ConnectionIdData(); |
| |
| int num_packets; |
| QuicTime time_added; |
| TimeWaitAction action; |
| TimeWaitConnectionInfo info; |
| }; |
| |
| // QuicheLinkedHashMap allows lookup by ConnectionId |
| // and traversal in add order. |
| using ConnectionIdMap = |
| quiche::QuicheLinkedHashMap<QuicConnectionId, ConnectionIdData, |
| QuicConnectionIdHash>; |
| // Do not use find/emplace/erase on this map directly. Use |
| // FindConnectionIdDataInMap, AddConnectionIdDateToMap, |
| // RemoveConnectionDataFromMap instead. |
| ConnectionIdMap connection_id_map_; |
| |
| // TODO(haoyuewang) Consider making connection_id_map_ a map of shared pointer |
| // and remove the indirect map. |
| // A connection can have multiple unretired ConnectionIds when it is closed. |
| // These Ids have the same ConnectionIdData entry in connection_id_map_. To |
| // find the entry, look up the cannoical ConnectionId in |
| // indirect_connection_id_map_ first, and look up connection_id_map_ with the |
| // cannoical ConnectionId. |
| absl::flat_hash_map<QuicConnectionId, QuicConnectionId, QuicConnectionIdHash> |
| indirect_connection_id_map_; |
| |
| // Find an iterator for the given connection_id. Returns |
| // connection_id_map_.end() if none found. |
| ConnectionIdMap::iterator FindConnectionIdDataInMap( |
| const QuicConnectionId& connection_id); |
| // Inserts a ConnectionIdData entry to connection_id_map_. |
| void AddConnectionIdDataToMap(const QuicConnectionId& canonical_connection_id, |
| int num_packets, TimeWaitAction action, |
| TimeWaitConnectionInfo info); |
| // Removes a ConnectionIdData entry in connection_id_map_. |
| void RemoveConnectionDataFromMap(ConnectionIdMap::iterator it); |
| |
| // Pending termination packets that need to be sent out to the peer when we |
| // are given a chance to write by the dispatcher. |
| quiche::QuicheCircularDeque<std::unique_ptr<QueuedPacket>> |
| pending_packets_queue_; |
| |
| // Time period for which connection_ids should remain in time wait state. |
| const QuicTime::Delta time_wait_period_; |
| |
| // Alarm to clean up connection_ids that have out lived their duration in |
| // time wait state. |
| std::unique_ptr<QuicAlarm> connection_id_clean_up_alarm_; |
| |
| // Clock to efficiently measure approximate time. |
| const QuicClock* clock_; |
| |
| // Interface that writes given buffer to the socket. |
| QuicPacketWriter* writer_; |
| |
| // Interface that manages blocked writers. |
| Visitor* visitor_; |
| }; |
| |
| } // namespace quic |
| |
| #endif // QUICHE_QUIC_CORE_QUIC_TIME_WAIT_LIST_MANAGER_H_ |