QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1 | // Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h" |
| 6 | |
vasilvv | 872e7a3 | 2019-03-12 16:42:44 -0700 | [diff] [blame] | 7 | #include <string> |
| 8 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 9 | #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" |
| 10 | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" |
| 11 | #include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 12 | |
| 13 | namespace quic { |
| 14 | |
| 15 | typedef QuicBufferedPacketStore::BufferedPacket BufferedPacket; |
| 16 | typedef QuicBufferedPacketStore::BufferedPacketList BufferedPacketList; |
| 17 | typedef QuicBufferedPacketStore::EnqueuePacketResult EnqueuePacketResult; |
| 18 | |
| 19 | // Max number of connections this store can keep track. |
| 20 | static const size_t kDefaultMaxConnectionsInStore = 100; |
| 21 | // Up to half of the capacity can be used for storing non-CHLO packets. |
| 22 | static const size_t kMaxConnectionsWithoutCHLO = |
| 23 | kDefaultMaxConnectionsInStore / 2; |
| 24 | |
| 25 | namespace { |
| 26 | |
| 27 | // This alarm removes expired entries in map each time this alarm fires. |
| 28 | class ConnectionExpireAlarm : public QuicAlarm::Delegate { |
| 29 | public: |
| 30 | explicit ConnectionExpireAlarm(QuicBufferedPacketStore* store) |
| 31 | : connection_store_(store) {} |
| 32 | |
| 33 | void OnAlarm() override { connection_store_->OnExpirationTimeout(); } |
| 34 | |
| 35 | ConnectionExpireAlarm(const ConnectionExpireAlarm&) = delete; |
| 36 | ConnectionExpireAlarm& operator=(const ConnectionExpireAlarm&) = delete; |
| 37 | |
| 38 | private: |
| 39 | QuicBufferedPacketStore* connection_store_; |
| 40 | }; |
| 41 | |
| 42 | } // namespace |
| 43 | |
| 44 | BufferedPacket::BufferedPacket(std::unique_ptr<QuicReceivedPacket> packet, |
| 45 | QuicSocketAddress self_address, |
| 46 | QuicSocketAddress peer_address) |
| 47 | : packet(std::move(packet)), |
| 48 | self_address(self_address), |
| 49 | peer_address(peer_address) {} |
| 50 | |
| 51 | BufferedPacket::BufferedPacket(BufferedPacket&& other) = default; |
| 52 | |
| 53 | BufferedPacket& BufferedPacket::operator=(BufferedPacket&& other) = default; |
| 54 | |
| 55 | BufferedPacket::~BufferedPacket() {} |
| 56 | |
| 57 | BufferedPacketList::BufferedPacketList() |
| 58 | : creation_time(QuicTime::Zero()), |
| 59 | ietf_quic(false), |
| 60 | version(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED) {} |
| 61 | |
| 62 | BufferedPacketList::BufferedPacketList(BufferedPacketList&& other) = default; |
| 63 | |
| 64 | BufferedPacketList& BufferedPacketList::operator=(BufferedPacketList&& other) = |
| 65 | default; |
| 66 | |
| 67 | BufferedPacketList::~BufferedPacketList() {} |
| 68 | |
| 69 | QuicBufferedPacketStore::QuicBufferedPacketStore( |
| 70 | VisitorInterface* visitor, |
| 71 | const QuicClock* clock, |
| 72 | QuicAlarmFactory* alarm_factory) |
| 73 | : connection_life_span_( |
| 74 | QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs)), |
| 75 | visitor_(visitor), |
| 76 | clock_(clock), |
| 77 | expiration_alarm_( |
| 78 | alarm_factory->CreateAlarm(new ConnectionExpireAlarm(this))) {} |
| 79 | |
| 80 | QuicBufferedPacketStore::~QuicBufferedPacketStore() {} |
| 81 | |
| 82 | EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket( |
| 83 | QuicConnectionId connection_id, |
| 84 | bool ietf_quic, |
| 85 | const QuicReceivedPacket& packet, |
| 86 | QuicSocketAddress self_address, |
| 87 | QuicSocketAddress peer_address, |
| 88 | bool is_chlo, |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 89 | const std::string& alpn, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 90 | const ParsedQuicVersion& version) { |
danzh | 497e45f | 2019-06-13 07:33:14 -0700 | [diff] [blame] | 91 | QUIC_BUG_IF(!GetQuicFlag(FLAGS_quic_allow_chlo_buffering)) |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 92 | << "Shouldn't buffer packets if disabled via flag."; |
| 93 | QUIC_BUG_IF(is_chlo && QuicContainsKey(connections_with_chlo_, connection_id)) |
| 94 | << "Shouldn't buffer duplicated CHLO on connection " << connection_id; |
| 95 | QUIC_BUG_IF(!is_chlo && !alpn.empty()) |
| 96 | << "Shouldn't have an ALPN defined for a non-CHLO packet."; |
| 97 | QUIC_BUG_IF(is_chlo && version.transport_version == QUIC_VERSION_UNSUPPORTED) |
| 98 | << "Should have version for CHLO packet."; |
| 99 | |
| 100 | if (!QuicContainsKey(undecryptable_packets_, connection_id) && |
| 101 | ShouldBufferPacket(is_chlo)) { |
| 102 | // Drop the packet if the upper limit of undecryptable packets has been |
| 103 | // reached or the whole capacity of the store has been reached. |
| 104 | return TOO_MANY_CONNECTIONS; |
| 105 | } else if (!QuicContainsKey(undecryptable_packets_, connection_id)) { |
| 106 | undecryptable_packets_.emplace( |
| 107 | std::make_pair(connection_id, BufferedPacketList())); |
| 108 | undecryptable_packets_.back().second.ietf_quic = ietf_quic; |
| 109 | undecryptable_packets_.back().second.version = version; |
| 110 | } |
| 111 | CHECK(QuicContainsKey(undecryptable_packets_, connection_id)); |
| 112 | BufferedPacketList& queue = |
| 113 | undecryptable_packets_.find(connection_id)->second; |
| 114 | |
| 115 | if (!is_chlo) { |
| 116 | // If current packet is not CHLO, it might not be buffered because store |
| 117 | // only buffers certain number of undecryptable packets per connection. |
| 118 | size_t num_non_chlo_packets = |
| 119 | QuicContainsKey(connections_with_chlo_, connection_id) |
| 120 | ? (queue.buffered_packets.size() - 1) |
| 121 | : queue.buffered_packets.size(); |
| 122 | if (num_non_chlo_packets >= kDefaultMaxUndecryptablePackets) { |
| 123 | // If there are kMaxBufferedPacketsPerConnection packets buffered up for |
| 124 | // this connection, drop the current packet. |
| 125 | return TOO_MANY_PACKETS; |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | if (queue.buffered_packets.empty()) { |
| 130 | // If this is the first packet arrived on a new connection, initialize the |
| 131 | // creation time. |
| 132 | queue.creation_time = clock_->ApproximateNow(); |
| 133 | } |
| 134 | |
| 135 | BufferedPacket new_entry(std::unique_ptr<QuicReceivedPacket>(packet.Clone()), |
| 136 | self_address, peer_address); |
| 137 | if (is_chlo) { |
| 138 | // Add CHLO to the beginning of buffered packets so that it can be delivered |
| 139 | // first later. |
| 140 | queue.buffered_packets.push_front(std::move(new_entry)); |
| 141 | queue.alpn = alpn; |
| 142 | connections_with_chlo_[connection_id] = false; // Dummy value. |
| 143 | // Set the version of buffered packets of this connection on CHLO. |
| 144 | queue.version = version; |
| 145 | } else { |
| 146 | // Buffer non-CHLO packets in arrival order. |
| 147 | queue.buffered_packets.push_back(std::move(new_entry)); |
| 148 | } |
| 149 | MaybeSetExpirationAlarm(); |
| 150 | return SUCCESS; |
| 151 | } |
| 152 | |
| 153 | bool QuicBufferedPacketStore::HasBufferedPackets( |
| 154 | QuicConnectionId connection_id) const { |
| 155 | return QuicContainsKey(undecryptable_packets_, connection_id); |
| 156 | } |
| 157 | |
| 158 | bool QuicBufferedPacketStore::HasChlosBuffered() const { |
| 159 | return !connections_with_chlo_.empty(); |
| 160 | } |
| 161 | |
| 162 | BufferedPacketList QuicBufferedPacketStore::DeliverPackets( |
| 163 | QuicConnectionId connection_id) { |
| 164 | BufferedPacketList packets_to_deliver; |
| 165 | auto it = undecryptable_packets_.find(connection_id); |
| 166 | if (it != undecryptable_packets_.end()) { |
| 167 | packets_to_deliver = std::move(it->second); |
| 168 | undecryptable_packets_.erase(connection_id); |
| 169 | } |
| 170 | return packets_to_deliver; |
| 171 | } |
| 172 | |
| 173 | void QuicBufferedPacketStore::DiscardPackets(QuicConnectionId connection_id) { |
| 174 | undecryptable_packets_.erase(connection_id); |
| 175 | connections_with_chlo_.erase(connection_id); |
| 176 | } |
| 177 | |
| 178 | void QuicBufferedPacketStore::OnExpirationTimeout() { |
| 179 | QuicTime expiration_time = clock_->ApproximateNow() - connection_life_span_; |
| 180 | while (!undecryptable_packets_.empty()) { |
| 181 | auto& entry = undecryptable_packets_.front(); |
| 182 | if (entry.second.creation_time > expiration_time) { |
| 183 | break; |
| 184 | } |
| 185 | QuicConnectionId connection_id = entry.first; |
| 186 | visitor_->OnExpiredPackets(connection_id, std::move(entry.second)); |
| 187 | undecryptable_packets_.pop_front(); |
| 188 | connections_with_chlo_.erase(connection_id); |
| 189 | } |
| 190 | if (!undecryptable_packets_.empty()) { |
| 191 | MaybeSetExpirationAlarm(); |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | void QuicBufferedPacketStore::MaybeSetExpirationAlarm() { |
| 196 | if (!expiration_alarm_->IsSet()) { |
| 197 | expiration_alarm_->Set(clock_->ApproximateNow() + connection_life_span_); |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | bool QuicBufferedPacketStore::ShouldBufferPacket(bool is_chlo) { |
| 202 | bool is_store_full = |
| 203 | undecryptable_packets_.size() >= kDefaultMaxConnectionsInStore; |
| 204 | |
| 205 | if (is_chlo) { |
| 206 | return is_store_full; |
| 207 | } |
| 208 | |
| 209 | size_t num_connections_without_chlo = |
| 210 | undecryptable_packets_.size() - connections_with_chlo_.size(); |
| 211 | bool reach_non_chlo_limit = |
| 212 | num_connections_without_chlo >= kMaxConnectionsWithoutCHLO; |
| 213 | |
| 214 | return is_store_full || reach_non_chlo_limit; |
| 215 | } |
| 216 | |
| 217 | BufferedPacketList QuicBufferedPacketStore::DeliverPacketsForNextConnection( |
| 218 | QuicConnectionId* connection_id) { |
| 219 | if (connections_with_chlo_.empty()) { |
| 220 | // Returns empty list if no CHLO has been buffered. |
| 221 | return BufferedPacketList(); |
| 222 | } |
| 223 | *connection_id = connections_with_chlo_.front().first; |
| 224 | connections_with_chlo_.pop_front(); |
| 225 | |
| 226 | BufferedPacketList packets = DeliverPackets(*connection_id); |
| 227 | DCHECK(!packets.buffered_packets.empty()) |
| 228 | << "Try to deliver connectons without CHLO"; |
| 229 | return packets; |
| 230 | } |
| 231 | |
| 232 | bool QuicBufferedPacketStore::HasChloForConnection( |
| 233 | QuicConnectionId connection_id) { |
| 234 | return QuicContainsKey(connections_with_chlo_, connection_id); |
| 235 | } |
| 236 | |
| 237 | } // namespace quic |