blob: e4dd233325555392590cafcceba2e15f0979f42c [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// 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
vasilvv872e7a32019-03-12 16:42:44 -07007#include <string>
8
QUICHE teama6ef0a62019-03-07 20:34:33 -05009#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 teama6ef0a62019-03-07 20:34:33 -050012
13namespace quic {
14
15typedef QuicBufferedPacketStore::BufferedPacket BufferedPacket;
16typedef QuicBufferedPacketStore::BufferedPacketList BufferedPacketList;
17typedef QuicBufferedPacketStore::EnqueuePacketResult EnqueuePacketResult;
18
19// Max number of connections this store can keep track.
20static const size_t kDefaultMaxConnectionsInStore = 100;
21// Up to half of the capacity can be used for storing non-CHLO packets.
22static const size_t kMaxConnectionsWithoutCHLO =
23 kDefaultMaxConnectionsInStore / 2;
24
25namespace {
26
27// This alarm removes expired entries in map each time this alarm fires.
28class 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
44BufferedPacket::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
51BufferedPacket::BufferedPacket(BufferedPacket&& other) = default;
52
53BufferedPacket& BufferedPacket::operator=(BufferedPacket&& other) = default;
54
55BufferedPacket::~BufferedPacket() {}
56
57BufferedPacketList::BufferedPacketList()
58 : creation_time(QuicTime::Zero()),
59 ietf_quic(false),
60 version(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED) {}
61
62BufferedPacketList::BufferedPacketList(BufferedPacketList&& other) = default;
63
64BufferedPacketList& BufferedPacketList::operator=(BufferedPacketList&& other) =
65 default;
66
67BufferedPacketList::~BufferedPacketList() {}
68
69QuicBufferedPacketStore::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
80QuicBufferedPacketStore::~QuicBufferedPacketStore() {}
81
82EnqueuePacketResult 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,
vasilvvc48c8712019-03-11 13:38:16 -070089 const std::string& alpn,
QUICHE teama6ef0a62019-03-07 20:34:33 -050090 const ParsedQuicVersion& version) {
danzh497e45f2019-06-13 07:33:14 -070091 QUIC_BUG_IF(!GetQuicFlag(FLAGS_quic_allow_chlo_buffering))
QUICHE teama6ef0a62019-03-07 20:34:33 -050092 << "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
153bool QuicBufferedPacketStore::HasBufferedPackets(
154 QuicConnectionId connection_id) const {
155 return QuicContainsKey(undecryptable_packets_, connection_id);
156}
157
158bool QuicBufferedPacketStore::HasChlosBuffered() const {
159 return !connections_with_chlo_.empty();
160}
161
162BufferedPacketList 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
173void QuicBufferedPacketStore::DiscardPackets(QuicConnectionId connection_id) {
174 undecryptable_packets_.erase(connection_id);
175 connections_with_chlo_.erase(connection_id);
176}
177
178void 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
195void QuicBufferedPacketStore::MaybeSetExpirationAlarm() {
196 if (!expiration_alarm_->IsSet()) {
197 expiration_alarm_->Set(clock_->ApproximateNow() + connection_life_span_);
198 }
199}
200
201bool 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
217BufferedPacketList 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
232bool QuicBufferedPacketStore::HasChloForConnection(
233 QuicConnectionId connection_id) {
234 return QuicContainsKey(connections_with_chlo_, connection_id);
235}
236
237} // namespace quic