• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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_time_wait_list_manager.h"
6 
7 #include <errno.h>
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "absl/strings/string_view.h"
13 #include "quiche/quic/core/crypto/crypto_protocol.h"
14 #include "quiche/quic/core/crypto/quic_decrypter.h"
15 #include "quiche/quic/core/crypto/quic_encrypter.h"
16 #include "quiche/quic/core/quic_clock.h"
17 #include "quiche/quic/core/quic_connection_id.h"
18 #include "quiche/quic/core/quic_framer.h"
19 #include "quiche/quic/core/quic_packets.h"
20 #include "quiche/quic/core/quic_utils.h"
21 #include "quiche/quic/platform/api/quic_bug_tracker.h"
22 #include "quiche/quic/platform/api/quic_flag_utils.h"
23 #include "quiche/quic/platform/api/quic_flags.h"
24 #include "quiche/quic/platform/api/quic_logging.h"
25 #include "quiche/quic/platform/api/quic_socket_address.h"
26 #include "quiche/common/quiche_text_utils.h"
27 
28 namespace quic {
29 
30 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
31 // up old connection_ids. This alarm should be cancelled and deleted before
32 // the QuicTimeWaitListManager is deleted.
33 class ConnectionIdCleanUpAlarm : public QuicAlarm::DelegateWithoutContext {
34  public:
ConnectionIdCleanUpAlarm(QuicTimeWaitListManager * time_wait_list_manager)35   explicit ConnectionIdCleanUpAlarm(
36       QuicTimeWaitListManager* time_wait_list_manager)
37       : time_wait_list_manager_(time_wait_list_manager) {}
38   ConnectionIdCleanUpAlarm(const ConnectionIdCleanUpAlarm&) = delete;
39   ConnectionIdCleanUpAlarm& operator=(const ConnectionIdCleanUpAlarm&) = delete;
40 
OnAlarm()41   void OnAlarm() override {
42     time_wait_list_manager_->CleanUpOldConnectionIds();
43   }
44 
45  private:
46   // Not owned.
47   QuicTimeWaitListManager* time_wait_list_manager_;
48 };
49 
TimeWaitConnectionInfo(bool ietf_quic,std::vector<std::unique_ptr<QuicEncryptedPacket>> * termination_packets,std::vector<QuicConnectionId> active_connection_ids)50 TimeWaitConnectionInfo::TimeWaitConnectionInfo(
51     bool ietf_quic,
52     std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets,
53     std::vector<QuicConnectionId> active_connection_ids)
54     : TimeWaitConnectionInfo(ietf_quic, termination_packets,
55                              std::move(active_connection_ids),
56                              QuicTime::Delta::Zero()) {}
57 
TimeWaitConnectionInfo(bool ietf_quic,std::vector<std::unique_ptr<QuicEncryptedPacket>> * termination_packets,std::vector<QuicConnectionId> active_connection_ids,QuicTime::Delta srtt)58 TimeWaitConnectionInfo::TimeWaitConnectionInfo(
59     bool ietf_quic,
60     std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets,
61     std::vector<QuicConnectionId> active_connection_ids, QuicTime::Delta srtt)
62     : ietf_quic(ietf_quic),
63       active_connection_ids(std::move(active_connection_ids)),
64       srtt(srtt) {
65   if (termination_packets != nullptr) {
66     this->termination_packets.swap(*termination_packets);
67   }
68 }
69 
QuicTimeWaitListManager(QuicPacketWriter * writer,Visitor * visitor,const QuicClock * clock,QuicAlarmFactory * alarm_factory)70 QuicTimeWaitListManager::QuicTimeWaitListManager(
71     QuicPacketWriter* writer, Visitor* visitor, const QuicClock* clock,
72     QuicAlarmFactory* alarm_factory)
73     : time_wait_period_(QuicTime::Delta::FromSeconds(
74           GetQuicFlag(quic_time_wait_list_seconds))),
75       connection_id_clean_up_alarm_(
76           alarm_factory->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
77       clock_(clock),
78       writer_(writer),
79       visitor_(visitor) {
80   SetConnectionIdCleanUpAlarm();
81 }
82 
~QuicTimeWaitListManager()83 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
84   connection_id_clean_up_alarm_->Cancel();
85 }
86 
87 QuicTimeWaitListManager::ConnectionIdMap::iterator
FindConnectionIdDataInMap(const QuicConnectionId & connection_id)88 QuicTimeWaitListManager::FindConnectionIdDataInMap(
89     const QuicConnectionId& connection_id) {
90   auto it = indirect_connection_id_map_.find(connection_id);
91   if (it == indirect_connection_id_map_.end()) {
92     return connection_id_map_.end();
93   }
94   return connection_id_map_.find(it->second);
95 }
96 
AddConnectionIdDataToMap(const QuicConnectionId & canonical_connection_id,int num_packets,TimeWaitAction action,TimeWaitConnectionInfo info)97 void QuicTimeWaitListManager::AddConnectionIdDataToMap(
98     const QuicConnectionId& canonical_connection_id, int num_packets,
99     TimeWaitAction action, TimeWaitConnectionInfo info) {
100   for (const auto& cid : info.active_connection_ids) {
101     indirect_connection_id_map_[cid] = canonical_connection_id;
102   }
103   ConnectionIdData data(num_packets, clock_->ApproximateNow(), action,
104                         std::move(info));
105   connection_id_map_.emplace(
106       std::make_pair(canonical_connection_id, std::move(data)));
107 }
108 
RemoveConnectionDataFromMap(ConnectionIdMap::iterator it)109 void QuicTimeWaitListManager::RemoveConnectionDataFromMap(
110     ConnectionIdMap::iterator it) {
111   for (const auto& cid : it->second.info.active_connection_ids) {
112     indirect_connection_id_map_.erase(cid);
113   }
114   connection_id_map_.erase(it);
115 }
116 
AddConnectionIdToTimeWait(TimeWaitAction action,TimeWaitConnectionInfo info)117 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
118     TimeWaitAction action, TimeWaitConnectionInfo info) {
119   QUICHE_DCHECK(!info.active_connection_ids.empty());
120   const QuicConnectionId& canonical_connection_id =
121       info.active_connection_ids.front();
122   QUICHE_DCHECK(action != SEND_TERMINATION_PACKETS ||
123                 !info.termination_packets.empty());
124   QUICHE_DCHECK(action != DO_NOTHING || info.ietf_quic);
125   int num_packets = 0;
126   auto it = FindConnectionIdDataInMap(canonical_connection_id);
127   const bool new_connection_id = it == connection_id_map_.end();
128   if (!new_connection_id) {  // Replace record if it is reinserted.
129     num_packets = it->second.num_packets;
130     RemoveConnectionDataFromMap(it);
131   }
132   TrimTimeWaitListIfNeeded();
133   int64_t max_connections = GetQuicFlag(quic_time_wait_list_max_connections);
134   QUICHE_DCHECK(connection_id_map_.empty() ||
135                 num_connections() < static_cast<size_t>(max_connections));
136   if (new_connection_id) {
137     for (const auto& cid : info.active_connection_ids) {
138       visitor_->OnConnectionAddedToTimeWaitList(cid);
139     }
140   }
141   AddConnectionIdDataToMap(canonical_connection_id, num_packets, action,
142                            std::move(info));
143 }
144 
IsConnectionIdInTimeWait(QuicConnectionId connection_id) const145 bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
146     QuicConnectionId connection_id) const {
147   return indirect_connection_id_map_.contains(connection_id);
148 }
149 
OnBlockedWriterCanWrite()150 void QuicTimeWaitListManager::OnBlockedWriterCanWrite() {
151   writer_->SetWritable();
152   while (!pending_packets_queue_.empty()) {
153     QueuedPacket* queued_packet = pending_packets_queue_.front().get();
154     if (!WriteToWire(queued_packet)) {
155       return;
156     }
157     pending_packets_queue_.pop_front();
158   }
159 }
160 
ProcessPacket(const QuicSocketAddress & self_address,const QuicSocketAddress & peer_address,QuicConnectionId connection_id,PacketHeaderFormat header_format,size_t received_packet_length,std::unique_ptr<QuicPerPacketContext> packet_context)161 void QuicTimeWaitListManager::ProcessPacket(
162     const QuicSocketAddress& self_address,
163     const QuicSocketAddress& peer_address, QuicConnectionId connection_id,
164     PacketHeaderFormat header_format, size_t received_packet_length,
165     std::unique_ptr<QuicPerPacketContext> packet_context) {
166   QUICHE_DCHECK(IsConnectionIdInTimeWait(connection_id));
167   // TODO(satyamshekhar): Think about handling packets from different peer
168   // addresses.
169   auto it = FindConnectionIdDataInMap(connection_id);
170   QUICHE_DCHECK(it != connection_id_map_.end());
171   // Increment the received packet count.
172   ConnectionIdData* connection_data = &it->second;
173   ++(connection_data->num_packets);
174   const QuicTime now = clock_->ApproximateNow();
175   QuicTime::Delta delta = QuicTime::Delta::Zero();
176   if (now > connection_data->time_added) {
177     delta = now - connection_data->time_added;
178   }
179   OnPacketReceivedForKnownConnection(connection_data->num_packets, delta,
180                                      connection_data->info.srtt);
181 
182   if (!ShouldSendResponse(connection_data->num_packets)) {
183     QUIC_DLOG(INFO) << "Processing " << connection_id << " in time wait state: "
184                     << "throttled";
185     return;
186   }
187 
188   QUIC_DLOG(INFO) << "Processing " << connection_id << " in time wait state: "
189                   << "header format=" << header_format
190                   << " ietf=" << connection_data->info.ietf_quic
191                   << ", action=" << connection_data->action
192                   << ", number termination packets="
193                   << connection_data->info.termination_packets.size();
194   switch (connection_data->action) {
195     case SEND_TERMINATION_PACKETS:
196       if (connection_data->info.termination_packets.empty()) {
197         QUIC_BUG(quic_bug_10608_1) << "There are no termination packets.";
198         return;
199       }
200       switch (header_format) {
201         case IETF_QUIC_LONG_HEADER_PACKET:
202           if (!connection_data->info.ietf_quic) {
203             QUIC_CODE_COUNT(quic_received_long_header_packet_for_gquic);
204           }
205           break;
206         case IETF_QUIC_SHORT_HEADER_PACKET:
207           if (!connection_data->info.ietf_quic) {
208             QUIC_CODE_COUNT(quic_received_short_header_packet_for_gquic);
209           }
210           // Send stateless reset in response to short header packets.
211           SendPublicReset(self_address, peer_address, connection_id,
212                           connection_data->info.ietf_quic,
213                           received_packet_length, std::move(packet_context));
214           return;
215         case GOOGLE_QUIC_PACKET:
216           if (connection_data->info.ietf_quic) {
217             QUIC_CODE_COUNT(quic_received_gquic_packet_for_ietf_quic);
218           }
219           break;
220       }
221 
222       for (const auto& packet : connection_data->info.termination_packets) {
223         SendOrQueuePacket(std::make_unique<QueuedPacket>(
224                               self_address, peer_address, packet->Clone()),
225                           packet_context.get());
226       }
227       return;
228 
229     case SEND_CONNECTION_CLOSE_PACKETS:
230       if (connection_data->info.termination_packets.empty()) {
231         QUIC_BUG(quic_bug_10608_2) << "There are no termination packets.";
232         return;
233       }
234       for (const auto& packet : connection_data->info.termination_packets) {
235         SendOrQueuePacket(std::make_unique<QueuedPacket>(
236                               self_address, peer_address, packet->Clone()),
237                           packet_context.get());
238       }
239       return;
240 
241     case SEND_STATELESS_RESET:
242       if (header_format == IETF_QUIC_LONG_HEADER_PACKET) {
243         QUIC_CODE_COUNT(quic_stateless_reset_long_header_packet);
244       }
245       SendPublicReset(self_address, peer_address, connection_id,
246                       connection_data->info.ietf_quic, received_packet_length,
247                       std::move(packet_context));
248       return;
249     case DO_NOTHING:
250       QUIC_CODE_COUNT(quic_time_wait_list_do_nothing);
251       QUICHE_DCHECK(connection_data->info.ietf_quic);
252   }
253 }
254 
SendVersionNegotiationPacket(QuicConnectionId server_connection_id,QuicConnectionId client_connection_id,bool ietf_quic,bool use_length_prefix,const ParsedQuicVersionVector & supported_versions,const QuicSocketAddress & self_address,const QuicSocketAddress & peer_address,std::unique_ptr<QuicPerPacketContext> packet_context)255 void QuicTimeWaitListManager::SendVersionNegotiationPacket(
256     QuicConnectionId server_connection_id,
257     QuicConnectionId client_connection_id, bool ietf_quic,
258     bool use_length_prefix, const ParsedQuicVersionVector& supported_versions,
259     const QuicSocketAddress& self_address,
260     const QuicSocketAddress& peer_address,
261     std::unique_ptr<QuicPerPacketContext> packet_context) {
262   std::unique_ptr<QuicEncryptedPacket> version_packet =
263       QuicFramer::BuildVersionNegotiationPacket(
264           server_connection_id, client_connection_id, ietf_quic,
265           use_length_prefix, supported_versions);
266   QUIC_DVLOG(2) << "Dispatcher sending version negotiation packet {"
267                 << ParsedQuicVersionVectorToString(supported_versions) << "}, "
268                 << (ietf_quic ? "" : "!") << "ietf_quic, "
269                 << (use_length_prefix ? "" : "!")
270                 << "use_length_prefix:" << std::endl
271                 << quiche::QuicheTextUtils::HexDump(absl::string_view(
272                        version_packet->data(), version_packet->length()));
273   SendOrQueuePacket(std::make_unique<QueuedPacket>(self_address, peer_address,
274                                                    std::move(version_packet)),
275                     packet_context.get());
276 }
277 
278 // Returns true if the number of packets received for this connection_id is a
279 // power of 2 to throttle the number of public reset packets we send to a peer.
ShouldSendResponse(int received_packet_count)280 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
281   return (received_packet_count & (received_packet_count - 1)) == 0;
282 }
283 
SendPublicReset(const QuicSocketAddress & self_address,const QuicSocketAddress & peer_address,QuicConnectionId connection_id,bool ietf_quic,size_t received_packet_length,std::unique_ptr<QuicPerPacketContext> packet_context)284 void QuicTimeWaitListManager::SendPublicReset(
285     const QuicSocketAddress& self_address,
286     const QuicSocketAddress& peer_address, QuicConnectionId connection_id,
287     bool ietf_quic, size_t received_packet_length,
288     std::unique_ptr<QuicPerPacketContext> packet_context) {
289   if (ietf_quic) {
290     std::unique_ptr<QuicEncryptedPacket> ietf_reset_packet =
291         BuildIetfStatelessResetPacket(connection_id, received_packet_length);
292     if (ietf_reset_packet == nullptr) {
293       // This could happen when trying to reject a short header packet of
294       // a connection which is in the time wait list (and with no termination
295       // packet).
296       return;
297     }
298     QUIC_DVLOG(2) << "Dispatcher sending IETF reset packet for "
299                   << connection_id << std::endl
300                   << quiche::QuicheTextUtils::HexDump(
301                          absl::string_view(ietf_reset_packet->data(),
302                                            ietf_reset_packet->length()));
303     SendOrQueuePacket(
304         std::make_unique<QueuedPacket>(self_address, peer_address,
305                                        std::move(ietf_reset_packet)),
306         packet_context.get());
307     return;
308   }
309   // Google QUIC public resets donot elicit resets in response.
310   QuicPublicResetPacket packet;
311   packet.connection_id = connection_id;
312   // TODO(satyamshekhar): generate a valid nonce for this connection_id.
313   packet.nonce_proof = 1010101;
314   // TODO(wub): This is wrong for proxied sessions. Fix it.
315   packet.client_address = peer_address;
316   GetEndpointId(&packet.endpoint_id);
317   // Takes ownership of the packet.
318   std::unique_ptr<QuicEncryptedPacket> reset_packet = BuildPublicReset(packet);
319   QUIC_DVLOG(2) << "Dispatcher sending reset packet for " << connection_id
320                 << std::endl
321                 << quiche::QuicheTextUtils::HexDump(absl::string_view(
322                        reset_packet->data(), reset_packet->length()));
323   SendOrQueuePacket(std::make_unique<QueuedPacket>(self_address, peer_address,
324                                                    std::move(reset_packet)),
325                     packet_context.get());
326 }
327 
SendPacket(const QuicSocketAddress & self_address,const QuicSocketAddress & peer_address,const QuicEncryptedPacket & packet)328 void QuicTimeWaitListManager::SendPacket(const QuicSocketAddress& self_address,
329                                          const QuicSocketAddress& peer_address,
330                                          const QuicEncryptedPacket& packet) {
331   SendOrQueuePacket(std::make_unique<QueuedPacket>(self_address, peer_address,
332                                                    packet.Clone()),
333                     nullptr);
334 }
335 
BuildPublicReset(const QuicPublicResetPacket & packet)336 std::unique_ptr<QuicEncryptedPacket> QuicTimeWaitListManager::BuildPublicReset(
337     const QuicPublicResetPacket& packet) {
338   return QuicFramer::BuildPublicResetPacket(packet);
339 }
340 
341 std::unique_ptr<QuicEncryptedPacket>
BuildIetfStatelessResetPacket(QuicConnectionId connection_id,size_t received_packet_length)342 QuicTimeWaitListManager::BuildIetfStatelessResetPacket(
343     QuicConnectionId connection_id, size_t received_packet_length) {
344   return QuicFramer::BuildIetfStatelessResetPacket(
345       connection_id, received_packet_length,
346       GetStatelessResetToken(connection_id));
347 }
348 
349 // Either sends the packet and deletes it or makes pending queue the
350 // owner of the packet.
SendOrQueuePacket(std::unique_ptr<QueuedPacket> packet,const QuicPerPacketContext *)351 bool QuicTimeWaitListManager::SendOrQueuePacket(
352     std::unique_ptr<QueuedPacket> packet,
353     const QuicPerPacketContext* /*packet_context*/) {
354   if (packet == nullptr) {
355     QUIC_LOG(ERROR) << "Tried to send or queue a null packet";
356     return true;
357   }
358   if (pending_packets_queue_.size() >=
359       GetQuicFlag(quic_time_wait_list_max_pending_packets)) {
360     // There are too many pending packets.
361     QUIC_CODE_COUNT(quic_too_many_pending_packets_in_time_wait);
362     return true;
363   }
364   if (WriteToWire(packet.get())) {
365     // Allow the packet to be deleted upon leaving this function.
366     return true;
367   }
368   pending_packets_queue_.push_back(std::move(packet));
369   return false;
370 }
371 
WriteToWire(QueuedPacket * queued_packet)372 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
373   if (writer_->IsWriteBlocked()) {
374     visitor_->OnWriteBlocked(this);
375     return false;
376   }
377   WriteResult result = writer_->WritePacket(
378       queued_packet->packet()->data(), queued_packet->packet()->length(),
379       queued_packet->self_address().host(), queued_packet->peer_address(),
380       nullptr);
381 
382   // If using a batch writer and the packet is buffered, flush it.
383   if (writer_->IsBatchMode() && result.status == WRITE_STATUS_OK &&
384       result.bytes_written == 0) {
385     result = writer_->Flush();
386   }
387 
388   if (IsWriteBlockedStatus(result.status)) {
389     // If blocked and unbuffered, return false to retry sending.
390     QUICHE_DCHECK(writer_->IsWriteBlocked());
391     visitor_->OnWriteBlocked(this);
392     return result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED;
393   } else if (IsWriteError(result.status)) {
394     QUIC_LOG_FIRST_N(WARNING, 1)
395         << "Received unknown error while sending termination packet to "
396         << queued_packet->peer_address().ToString() << ": "
397         << strerror(result.error_code);
398   }
399   return true;
400 }
401 
SetConnectionIdCleanUpAlarm()402 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
403   QuicTime::Delta next_alarm_interval = QuicTime::Delta::Zero();
404   if (!connection_id_map_.empty()) {
405     QuicTime oldest_connection_id =
406         connection_id_map_.begin()->second.time_added;
407     QuicTime now = clock_->ApproximateNow();
408     if (now - oldest_connection_id < time_wait_period_) {
409       next_alarm_interval = oldest_connection_id + time_wait_period_ - now;
410     } else {
411       QUIC_LOG(ERROR)
412           << "ConnectionId lingered for longer than time_wait_period_";
413     }
414   } else {
415     // No connection_ids added so none will expire before time_wait_period_.
416     next_alarm_interval = time_wait_period_;
417   }
418 
419   connection_id_clean_up_alarm_->Update(
420       clock_->ApproximateNow() + next_alarm_interval, QuicTime::Delta::Zero());
421 }
422 
MaybeExpireOldestConnection(QuicTime expiration_time)423 bool QuicTimeWaitListManager::MaybeExpireOldestConnection(
424     QuicTime expiration_time) {
425   if (connection_id_map_.empty()) {
426     return false;
427   }
428   auto it = connection_id_map_.begin();
429   QuicTime oldest_connection_id_time = it->second.time_added;
430   if (oldest_connection_id_time > expiration_time) {
431     // Too recent, don't retire.
432     return false;
433   }
434   // This connection_id has lived its age, retire it now.
435   QUIC_DLOG(INFO) << "Connection " << it->first
436                   << " expired from time wait list";
437   RemoveConnectionDataFromMap(it);
438   if (expiration_time == QuicTime::Infinite()) {
439     QUIC_CODE_COUNT(quic_time_wait_list_trim_full);
440   } else {
441     QUIC_CODE_COUNT(quic_time_wait_list_expire_connections);
442   }
443   return true;
444 }
445 
CleanUpOldConnectionIds()446 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
447   QuicTime now = clock_->ApproximateNow();
448   QuicTime expiration = now - time_wait_period_;
449 
450   while (MaybeExpireOldestConnection(expiration)) {
451   }
452 
453   SetConnectionIdCleanUpAlarm();
454 }
455 
TrimTimeWaitListIfNeeded()456 void QuicTimeWaitListManager::TrimTimeWaitListIfNeeded() {
457   const int64_t kMaxConnections =
458       GetQuicFlag(quic_time_wait_list_max_connections);
459   if (kMaxConnections < 0) {
460     return;
461   }
462   while (!connection_id_map_.empty() &&
463          num_connections() >= static_cast<size_t>(kMaxConnections)) {
464     MaybeExpireOldestConnection(QuicTime::Infinite());
465   }
466 }
467 
ConnectionIdData(int num_packets,QuicTime time_added,TimeWaitAction action,TimeWaitConnectionInfo info)468 QuicTimeWaitListManager::ConnectionIdData::ConnectionIdData(
469     int num_packets, QuicTime time_added, TimeWaitAction action,
470     TimeWaitConnectionInfo info)
471     : num_packets(num_packets),
472       time_added(time_added),
473       action(action),
474       info(std::move(info)) {}
475 
476 QuicTimeWaitListManager::ConnectionIdData::ConnectionIdData(
477     ConnectionIdData&& other) = default;
478 
479 QuicTimeWaitListManager::ConnectionIdData::~ConnectionIdData() = default;
480 
GetStatelessResetToken(QuicConnectionId connection_id) const481 StatelessResetToken QuicTimeWaitListManager::GetStatelessResetToken(
482     QuicConnectionId connection_id) const {
483   return QuicUtils::GenerateStatelessResetToken(connection_id);
484 }
485 
486 }  // namespace quic
487