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