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 
7 #include <string>
8 
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"
12 
13 namespace quic {
14 
15 using BufferedPacket = QuicBufferedPacketStore::BufferedPacket;
16 using BufferedPacketList = QuicBufferedPacketStore::BufferedPacketList;
17 using EnqueuePacketResult = QuicBufferedPacketStore::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:
ConnectionExpireAlarm(QuicBufferedPacketStore * store)30   explicit ConnectionExpireAlarm(QuicBufferedPacketStore* store)
31       : connection_store_(store) {}
32 
OnAlarm()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 
BufferedPacket(std::unique_ptr<QuicReceivedPacket> packet,QuicSocketAddress self_address,QuicSocketAddress peer_address)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 
~BufferedPacket()55 BufferedPacket::~BufferedPacket() {}
56 
BufferedPacketList()57 BufferedPacketList::BufferedPacketList()
58     : creation_time(QuicTime::Zero()),
59       ietf_quic(false),
60       version(ParsedQuicVersion::Unsupported()) {}
61 
62 BufferedPacketList::BufferedPacketList(BufferedPacketList&& other) = default;
63 
64 BufferedPacketList& BufferedPacketList::operator=(BufferedPacketList&& other) =
65     default;
66 
~BufferedPacketList()67 BufferedPacketList::~BufferedPacketList() {}
68 
QuicBufferedPacketStore(VisitorInterface * visitor,const QuicClock * clock,QuicAlarmFactory * alarm_factory)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 
~QuicBufferedPacketStore()80 QuicBufferedPacketStore::~QuicBufferedPacketStore() {}
81 
EnqueuePacket(QuicConnectionId connection_id,bool ietf_quic,const QuicReceivedPacket & packet,QuicSocketAddress self_address,QuicSocketAddress peer_address,bool is_chlo,const std::vector<std::string> & alpns,const ParsedQuicVersion & version)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,
89     const std::vector<std::string>& alpns,
90     const ParsedQuicVersion& version) {
91   QUIC_BUG_IF(!GetQuicFlag(FLAGS_quic_allow_chlo_buffering))
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 && !alpns.empty())
96       << "Shouldn't have an ALPN defined for a non-CHLO packet.";
97   QUIC_BUG_IF(is_chlo && !version.IsKnown())
98       << "Should have version for CHLO packet.";
99 
100   const bool is_first_packet =
101       !QuicContainsKey(undecryptable_packets_, connection_id);
102   if (is_first_packet) {
103     if (ShouldNotBufferPacket(is_chlo)) {
104       // Drop the packet if the upper limit of undecryptable packets has been
105       // reached or the whole capacity of the store has been reached.
106       return TOO_MANY_CONNECTIONS;
107     }
108     undecryptable_packets_.emplace(
109         std::make_pair(connection_id, BufferedPacketList()));
110     undecryptable_packets_.back().second.ietf_quic = ietf_quic;
111     undecryptable_packets_.back().second.version = version;
112   }
113   CHECK(QuicContainsKey(undecryptable_packets_, connection_id));
114   BufferedPacketList& queue =
115       undecryptable_packets_.find(connection_id)->second;
116 
117   if (!is_chlo) {
118     // If current packet is not CHLO, it might not be buffered because store
119     // only buffers certain number of undecryptable packets per connection.
120     size_t num_non_chlo_packets =
121         QuicContainsKey(connections_with_chlo_, connection_id)
122             ? (queue.buffered_packets.size() - 1)
123             : queue.buffered_packets.size();
124     if (num_non_chlo_packets >= kDefaultMaxUndecryptablePackets) {
125       // If there are kMaxBufferedPacketsPerConnection packets buffered up for
126       // this connection, drop the current packet.
127       return TOO_MANY_PACKETS;
128     }
129   }
130 
131   if (queue.buffered_packets.empty()) {
132     // If this is the first packet arrived on a new connection, initialize the
133     // creation time.
134     queue.creation_time = clock_->ApproximateNow();
135   }
136 
137   BufferedPacket new_entry(std::unique_ptr<QuicReceivedPacket>(packet.Clone()),
138                            self_address, peer_address);
139   if (is_chlo) {
140     // Add CHLO to the beginning of buffered packets so that it can be delivered
141     // first later.
142     queue.buffered_packets.push_front(std::move(new_entry));
143     queue.alpns = alpns;
144     connections_with_chlo_[connection_id] = false;  // Dummy value.
145     // Set the version of buffered packets of this connection on CHLO.
146     queue.version = version;
147   } else {
148     // Buffer non-CHLO packets in arrival order.
149     queue.buffered_packets.push_back(std::move(new_entry));
150 
151     // Attempt to parse multi-packet TLS CHLOs.
152     if (is_first_packet) {
153       queue.tls_chlo_extractor.IngestPacket(version, packet);
154       // Since this is the first packet and it's not a CHLO, the
155       // TlsChloExtractor should not have the entire CHLO.
156       QUIC_BUG_IF(queue.tls_chlo_extractor.HasParsedFullChlo())
157           << "First packet in list should not contain full CHLO";
158     }
159     // TODO(b/154857081) Reorder CHLO packets ahead of other ones.
160   }
161 
162   MaybeSetExpirationAlarm();
163   return SUCCESS;
164 }
165 
HasBufferedPackets(QuicConnectionId connection_id) const166 bool QuicBufferedPacketStore::HasBufferedPackets(
167     QuicConnectionId connection_id) const {
168   return QuicContainsKey(undecryptable_packets_, connection_id);
169 }
170 
HasChlosBuffered() const171 bool QuicBufferedPacketStore::HasChlosBuffered() const {
172   return !connections_with_chlo_.empty();
173 }
174 
DeliverPackets(QuicConnectionId connection_id)175 BufferedPacketList QuicBufferedPacketStore::DeliverPackets(
176     QuicConnectionId connection_id) {
177   BufferedPacketList packets_to_deliver;
178   auto it = undecryptable_packets_.find(connection_id);
179   if (it != undecryptable_packets_.end()) {
180     packets_to_deliver = std::move(it->second);
181     undecryptable_packets_.erase(connection_id);
182   }
183   return packets_to_deliver;
184 }
185 
DiscardPackets(QuicConnectionId connection_id)186 void QuicBufferedPacketStore::DiscardPackets(QuicConnectionId connection_id) {
187   undecryptable_packets_.erase(connection_id);
188   connections_with_chlo_.erase(connection_id);
189 }
190 
DiscardAllPackets()191 void QuicBufferedPacketStore::DiscardAllPackets() {
192   undecryptable_packets_.clear();
193   connections_with_chlo_.clear();
194   expiration_alarm_->Cancel();
195 }
196 
OnExpirationTimeout()197 void QuicBufferedPacketStore::OnExpirationTimeout() {
198   QuicTime expiration_time = clock_->ApproximateNow() - connection_life_span_;
199   while (!undecryptable_packets_.empty()) {
200     auto& entry = undecryptable_packets_.front();
201     if (entry.second.creation_time > expiration_time) {
202       break;
203     }
204     QuicConnectionId connection_id = entry.first;
205     visitor_->OnExpiredPackets(connection_id, std::move(entry.second));
206     undecryptable_packets_.pop_front();
207     connections_with_chlo_.erase(connection_id);
208   }
209   if (!undecryptable_packets_.empty()) {
210     MaybeSetExpirationAlarm();
211   }
212 }
213 
MaybeSetExpirationAlarm()214 void QuicBufferedPacketStore::MaybeSetExpirationAlarm() {
215   if (!expiration_alarm_->IsSet()) {
216     expiration_alarm_->Set(clock_->ApproximateNow() + connection_life_span_);
217   }
218 }
219 
ShouldNotBufferPacket(bool is_chlo)220 bool QuicBufferedPacketStore::ShouldNotBufferPacket(bool is_chlo) {
221   bool is_store_full =
222       undecryptable_packets_.size() >= kDefaultMaxConnectionsInStore;
223 
224   if (is_chlo) {
225     return is_store_full;
226   }
227 
228   size_t num_connections_without_chlo =
229       undecryptable_packets_.size() - connections_with_chlo_.size();
230   bool reach_non_chlo_limit =
231       num_connections_without_chlo >= kMaxConnectionsWithoutCHLO;
232 
233   return is_store_full || reach_non_chlo_limit;
234 }
235 
DeliverPacketsForNextConnection(QuicConnectionId * connection_id)236 BufferedPacketList QuicBufferedPacketStore::DeliverPacketsForNextConnection(
237     QuicConnectionId* connection_id) {
238   if (connections_with_chlo_.empty()) {
239     // Returns empty list if no CHLO has been buffered.
240     return BufferedPacketList();
241   }
242   *connection_id = connections_with_chlo_.front().first;
243   connections_with_chlo_.pop_front();
244 
245   BufferedPacketList packets = DeliverPackets(*connection_id);
246   DCHECK(!packets.buffered_packets.empty())
247       << "Try to deliver connectons without CHLO";
248   return packets;
249 }
250 
HasChloForConnection(QuicConnectionId connection_id)251 bool QuicBufferedPacketStore::HasChloForConnection(
252     QuicConnectionId connection_id) {
253   return QuicContainsKey(connections_with_chlo_, connection_id);
254 }
255 
IngestPacketForTlsChloExtraction(const QuicConnectionId & connection_id,const ParsedQuicVersion & version,const QuicReceivedPacket & packet,std::vector<std::string> * out_alpns)256 bool QuicBufferedPacketStore::IngestPacketForTlsChloExtraction(
257     const QuicConnectionId& connection_id,
258     const ParsedQuicVersion& version,
259     const QuicReceivedPacket& packet,
260     std::vector<std::string>* out_alpns) {
261   DCHECK_NE(out_alpns, nullptr);
262   DCHECK_EQ(version.handshake_protocol, PROTOCOL_TLS1_3);
263   auto it = undecryptable_packets_.find(connection_id);
264   if (it == undecryptable_packets_.end()) {
265     QUIC_BUG << "Cannot ingest packet for unknown connection ID "
266              << connection_id;
267     return false;
268   }
269   it->second.tls_chlo_extractor.IngestPacket(version, packet);
270   if (!it->second.tls_chlo_extractor.HasParsedFullChlo()) {
271     return false;
272   }
273   *out_alpns = it->second.tls_chlo_extractor.alpns();
274   return true;
275 }
276 
277 }  // namespace quic
278