• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "quiche/quic/core/quic_buffered_packet_store.h"
6 
7 #include <string>
8 
9 #include "absl/strings/string_view.h"
10 #include "quiche/quic/core/quic_connection_id.h"
11 #include "quiche/quic/core/quic_types.h"
12 #include "quiche/quic/core/quic_versions.h"
13 #include "quiche/quic/platform/api/quic_bug_tracker.h"
14 #include "quiche/quic/platform/api/quic_flags.h"
15 
16 namespace quic {
17 
18 using BufferedPacket = QuicBufferedPacketStore::BufferedPacket;
19 using BufferedPacketList = QuicBufferedPacketStore::BufferedPacketList;
20 using EnqueuePacketResult = QuicBufferedPacketStore::EnqueuePacketResult;
21 
22 // Max number of connections this store can keep track.
23 static const size_t kDefaultMaxConnectionsInStore = 100;
24 // Up to half of the capacity can be used for storing non-CHLO packets.
25 static const size_t kMaxConnectionsWithoutCHLO =
26     kDefaultMaxConnectionsInStore / 2;
27 
28 namespace {
29 
30 // This alarm removes expired entries in map each time this alarm fires.
31 class ConnectionExpireAlarm : public QuicAlarm::DelegateWithoutContext {
32  public:
ConnectionExpireAlarm(QuicBufferedPacketStore * store)33   explicit ConnectionExpireAlarm(QuicBufferedPacketStore* store)
34       : connection_store_(store) {}
35 
OnAlarm()36   void OnAlarm() override { connection_store_->OnExpirationTimeout(); }
37 
38   ConnectionExpireAlarm(const ConnectionExpireAlarm&) = delete;
39   ConnectionExpireAlarm& operator=(const ConnectionExpireAlarm&) = delete;
40 
41  private:
42   QuicBufferedPacketStore* connection_store_;
43 };
44 
45 }  // namespace
46 
BufferedPacket(std::unique_ptr<QuicReceivedPacket> packet,QuicSocketAddress self_address,QuicSocketAddress peer_address)47 BufferedPacket::BufferedPacket(std::unique_ptr<QuicReceivedPacket> packet,
48                                QuicSocketAddress self_address,
49                                QuicSocketAddress peer_address)
50     : packet(std::move(packet)),
51       self_address(self_address),
52       peer_address(peer_address) {}
53 
54 BufferedPacket::BufferedPacket(BufferedPacket&& other) = default;
55 
56 BufferedPacket& BufferedPacket::operator=(BufferedPacket&& other) = default;
57 
~BufferedPacket()58 BufferedPacket::~BufferedPacket() {}
59 
BufferedPacketList()60 BufferedPacketList::BufferedPacketList()
61     : creation_time(QuicTime::Zero()),
62       ietf_quic(false),
63       version(ParsedQuicVersion::Unsupported()) {}
64 
65 BufferedPacketList::BufferedPacketList(BufferedPacketList&& other) = default;
66 
67 BufferedPacketList& BufferedPacketList::operator=(BufferedPacketList&& other) =
68     default;
69 
~BufferedPacketList()70 BufferedPacketList::~BufferedPacketList() {}
71 
QuicBufferedPacketStore(VisitorInterface * visitor,const QuicClock * clock,QuicAlarmFactory * alarm_factory)72 QuicBufferedPacketStore::QuicBufferedPacketStore(
73     VisitorInterface* visitor, const QuicClock* clock,
74     QuicAlarmFactory* alarm_factory)
75     : connection_life_span_(
76           QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs)),
77       visitor_(visitor),
78       clock_(clock),
79       expiration_alarm_(
80           alarm_factory->CreateAlarm(new ConnectionExpireAlarm(this))) {}
81 
~QuicBufferedPacketStore()82 QuicBufferedPacketStore::~QuicBufferedPacketStore() {
83   if (expiration_alarm_ != nullptr) {
84     expiration_alarm_->PermanentCancel();
85   }
86 }
87 
EnqueuePacket(QuicConnectionId connection_id,bool ietf_quic,const QuicReceivedPacket & packet,QuicSocketAddress self_address,QuicSocketAddress peer_address,const ParsedQuicVersion & version,absl::optional<ParsedClientHello> parsed_chlo)88 EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket(
89     QuicConnectionId connection_id, bool ietf_quic,
90     const QuicReceivedPacket& packet, QuicSocketAddress self_address,
91     QuicSocketAddress peer_address, const ParsedQuicVersion& version,
92     absl::optional<ParsedClientHello> parsed_chlo) {
93   const bool is_chlo = parsed_chlo.has_value();
94   QUIC_BUG_IF(quic_bug_12410_1, !GetQuicFlag(quic_allow_chlo_buffering))
95       << "Shouldn't buffer packets if disabled via flag.";
96   QUIC_BUG_IF(quic_bug_12410_2,
97               is_chlo && connections_with_chlo_.contains(connection_id))
98       << "Shouldn't buffer duplicated CHLO on connection " << connection_id;
99   QUIC_BUG_IF(quic_bug_12410_4, is_chlo && !version.IsKnown())
100       << "Should have version for CHLO packet.";
101 
102   const bool is_first_packet = !undecryptable_packets_.contains(connection_id);
103   if (is_first_packet) {
104     if (ShouldNotBufferPacket(is_chlo)) {
105       // Drop the packet if the upper limit of undecryptable packets has been
106       // reached or the whole capacity of the store has been reached.
107       return TOO_MANY_CONNECTIONS;
108     }
109     undecryptable_packets_.emplace(
110         std::make_pair(connection_id, BufferedPacketList()));
111     undecryptable_packets_.back().second.ietf_quic = ietf_quic;
112     undecryptable_packets_.back().second.version = version;
113   }
114   QUICHE_CHECK(undecryptable_packets_.contains(connection_id));
115   BufferedPacketList& queue =
116       undecryptable_packets_.find(connection_id)->second;
117 
118   if (!is_chlo) {
119     // If current packet is not CHLO, it might not be buffered because store
120     // only buffers certain number of undecryptable packets per connection.
121     size_t num_non_chlo_packets = connections_with_chlo_.contains(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.parsed_chlo = std::move(parsed_chlo);
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(quic_bug_12410_5,
157                   queue.tls_chlo_extractor.HasParsedFullChlo())
158           << "First packet in list should not contain full CHLO";
159     }
160     // TODO(b/154857081) Reorder CHLO packets ahead of other ones.
161   }
162 
163   MaybeSetExpirationAlarm();
164   return SUCCESS;
165 }
166 
HasBufferedPackets(QuicConnectionId connection_id) const167 bool QuicBufferedPacketStore::HasBufferedPackets(
168     QuicConnectionId connection_id) const {
169   return undecryptable_packets_.contains(connection_id);
170 }
171 
HasChlosBuffered() const172 bool QuicBufferedPacketStore::HasChlosBuffered() const {
173   return !connections_with_chlo_.empty();
174 }
175 
DeliverPackets(QuicConnectionId connection_id)176 BufferedPacketList QuicBufferedPacketStore::DeliverPackets(
177     QuicConnectionId connection_id) {
178   BufferedPacketList packets_to_deliver;
179   auto it = undecryptable_packets_.find(connection_id);
180   if (it != undecryptable_packets_.end()) {
181     packets_to_deliver = std::move(it->second);
182     undecryptable_packets_.erase(connection_id);
183     std::list<BufferedPacket> initial_packets;
184     std::list<BufferedPacket> other_packets;
185     for (auto& packet : packets_to_deliver.buffered_packets) {
186       QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE;
187       PacketHeaderFormat unused_format;
188       bool unused_version_flag;
189       bool unused_use_length_prefix;
190       QuicVersionLabel unused_version_label;
191       ParsedQuicVersion unused_parsed_version = UnsupportedQuicVersion();
192       QuicConnectionId unused_destination_connection_id;
193       QuicConnectionId unused_source_connection_id;
194       absl::optional<absl::string_view> unused_retry_token;
195       std::string unused_detailed_error;
196 
197       // We don't need to pass |generator| because we already got the correct
198       // connection ID length when we buffered the packet and indexed by
199       // connection ID.
200       QuicErrorCode error_code = QuicFramer::ParsePublicHeaderDispatcher(
201           *packet.packet, connection_id.length(), &unused_format,
202           &long_packet_type, &unused_version_flag, &unused_use_length_prefix,
203           &unused_version_label, &unused_parsed_version,
204           &unused_destination_connection_id, &unused_source_connection_id,
205           &unused_retry_token, &unused_detailed_error);
206 
207       if (error_code == QUIC_NO_ERROR && long_packet_type == INITIAL) {
208         initial_packets.push_back(std::move(packet));
209       } else {
210         other_packets.push_back(std::move(packet));
211       }
212     }
213 
214     initial_packets.splice(initial_packets.end(), other_packets);
215     packets_to_deliver.buffered_packets = std::move(initial_packets);
216   }
217   return packets_to_deliver;
218 }
219 
DiscardPackets(QuicConnectionId connection_id)220 void QuicBufferedPacketStore::DiscardPackets(QuicConnectionId connection_id) {
221   undecryptable_packets_.erase(connection_id);
222   connections_with_chlo_.erase(connection_id);
223 }
224 
DiscardAllPackets()225 void QuicBufferedPacketStore::DiscardAllPackets() {
226   undecryptable_packets_.clear();
227   connections_with_chlo_.clear();
228   expiration_alarm_->Cancel();
229 }
230 
OnExpirationTimeout()231 void QuicBufferedPacketStore::OnExpirationTimeout() {
232   QuicTime expiration_time = clock_->ApproximateNow() - connection_life_span_;
233   while (!undecryptable_packets_.empty()) {
234     auto& entry = undecryptable_packets_.front();
235     if (entry.second.creation_time > expiration_time) {
236       break;
237     }
238     QuicConnectionId connection_id = entry.first;
239     visitor_->OnExpiredPackets(connection_id, std::move(entry.second));
240     undecryptable_packets_.pop_front();
241     connections_with_chlo_.erase(connection_id);
242   }
243   if (!undecryptable_packets_.empty()) {
244     MaybeSetExpirationAlarm();
245   }
246 }
247 
MaybeSetExpirationAlarm()248 void QuicBufferedPacketStore::MaybeSetExpirationAlarm() {
249   if (!expiration_alarm_->IsSet()) {
250     expiration_alarm_->Set(clock_->ApproximateNow() + connection_life_span_);
251   }
252 }
253 
ShouldNotBufferPacket(bool is_chlo)254 bool QuicBufferedPacketStore::ShouldNotBufferPacket(bool is_chlo) {
255   bool is_store_full =
256       undecryptable_packets_.size() >= kDefaultMaxConnectionsInStore;
257 
258   if (is_chlo) {
259     return is_store_full;
260   }
261 
262   size_t num_connections_without_chlo =
263       undecryptable_packets_.size() - connections_with_chlo_.size();
264   bool reach_non_chlo_limit =
265       num_connections_without_chlo >= kMaxConnectionsWithoutCHLO;
266 
267   return is_store_full || reach_non_chlo_limit;
268 }
269 
DeliverPacketsForNextConnection(QuicConnectionId * connection_id)270 BufferedPacketList QuicBufferedPacketStore::DeliverPacketsForNextConnection(
271     QuicConnectionId* connection_id) {
272   if (connections_with_chlo_.empty()) {
273     // Returns empty list if no CHLO has been buffered.
274     return BufferedPacketList();
275   }
276   *connection_id = connections_with_chlo_.front().first;
277   connections_with_chlo_.pop_front();
278 
279   BufferedPacketList packets = DeliverPackets(*connection_id);
280   QUICHE_DCHECK(!packets.buffered_packets.empty() &&
281                 packets.parsed_chlo.has_value())
282       << "Try to deliver connectons without CHLO. # packets:"
283       << packets.buffered_packets.size()
284       << ", has_parsed_chlo:" << packets.parsed_chlo.has_value();
285   return packets;
286 }
287 
HasChloForConnection(QuicConnectionId connection_id)288 bool QuicBufferedPacketStore::HasChloForConnection(
289     QuicConnectionId connection_id) {
290   return connections_with_chlo_.contains(connection_id);
291 }
292 
IngestPacketForTlsChloExtraction(const QuicConnectionId & connection_id,const ParsedQuicVersion & version,const QuicReceivedPacket & packet,std::vector<std::string> * out_alpns,std::string * out_sni,bool * out_resumption_attempted,bool * out_early_data_attempted,absl::optional<uint8_t> * tls_alert)293 bool QuicBufferedPacketStore::IngestPacketForTlsChloExtraction(
294     const QuicConnectionId& connection_id, const ParsedQuicVersion& version,
295     const QuicReceivedPacket& packet, std::vector<std::string>* out_alpns,
296     std::string* out_sni, bool* out_resumption_attempted,
297     bool* out_early_data_attempted, absl::optional<uint8_t>* tls_alert) {
298   QUICHE_DCHECK_NE(out_alpns, nullptr);
299   QUICHE_DCHECK_NE(out_sni, nullptr);
300   QUICHE_DCHECK_NE(tls_alert, nullptr);
301   QUICHE_DCHECK_EQ(version.handshake_protocol, PROTOCOL_TLS1_3);
302   auto it = undecryptable_packets_.find(connection_id);
303   if (it == undecryptable_packets_.end()) {
304     QUIC_BUG(quic_bug_10838_1)
305         << "Cannot ingest packet for unknown connection ID " << connection_id;
306     return false;
307   }
308   it->second.tls_chlo_extractor.IngestPacket(version, packet);
309   if (!it->second.tls_chlo_extractor.HasParsedFullChlo()) {
310     *tls_alert = it->second.tls_chlo_extractor.tls_alert();
311     return false;
312   }
313   const TlsChloExtractor& tls_chlo_extractor = it->second.tls_chlo_extractor;
314   *out_alpns = tls_chlo_extractor.alpns();
315   *out_sni = tls_chlo_extractor.server_name();
316   *out_resumption_attempted = tls_chlo_extractor.resumption_attempted();
317   *out_early_data_attempted = tls_chlo_extractor.early_data_attempted();
318   return true;
319 }
320 
321 }  // namespace quic
322