Home | History | Annotate | Download | only in quic
      1 // Copyright 2014 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/quic/quic_time_wait_list_manager.h"
      6 
      7 #include <errno.h>
      8 
      9 #include "base/containers/hash_tables.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/stl_util.h"
     12 #include "net/base/ip_endpoint.h"
     13 #include "net/quic/crypto/crypto_protocol.h"
     14 #include "net/quic/crypto/quic_decrypter.h"
     15 #include "net/quic/crypto/quic_encrypter.h"
     16 #include "net/quic/quic_clock.h"
     17 #include "net/quic/quic_connection_helper.h"
     18 #include "net/quic/quic_framer.h"
     19 #include "net/quic/quic_protocol.h"
     20 #include "net/quic/quic_server_session.h"
     21 #include "net/quic/quic_utils.h"
     22 
     23 using base::StringPiece;
     24 using std::make_pair;
     25 
     26 namespace net {
     27 
     28 namespace {
     29 
     30 // Time period for which the connection_id should live in time wait state..
     31 const int kTimeWaitSeconds = 5;
     32 
     33 }  // namespace
     34 
     35 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
     36 // up old connection_ids. This alarm should be unregistered and deleted before
     37 // the QuicTimeWaitListManager is deleted.
     38 class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate {
     39  public:
     40   explicit ConnectionIdCleanUpAlarm(
     41       QuicTimeWaitListManager* time_wait_list_manager)
     42       : time_wait_list_manager_(time_wait_list_manager) {}
     43 
     44   virtual QuicTime OnAlarm() OVERRIDE {
     45     time_wait_list_manager_->CleanUpOldConnectionIds();
     46     // Let the time wait manager register the alarm at appropriate time.
     47     return QuicTime::Zero();
     48   }
     49 
     50  private:
     51   // Not owned.
     52   QuicTimeWaitListManager* time_wait_list_manager_;
     53 };
     54 
     55 // This class stores pending public reset packets to be sent to clients.
     56 // server_address - server address on which a packet what was received for
     57 //                  a connection_id in time wait state.
     58 // client_address - address of the client that sent that packet. Needed to send
     59 //                  the public reset packet back to the client.
     60 // packet - the pending public reset packet that is to be sent to the client.
     61 //          created instance takes the ownership of this packet.
     62 class QuicTimeWaitListManager::QueuedPacket {
     63  public:
     64   QueuedPacket(const IPEndPoint& server_address,
     65                const IPEndPoint& client_address,
     66                QuicEncryptedPacket* packet)
     67       : server_address_(server_address),
     68         client_address_(client_address),
     69         packet_(packet) {}
     70 
     71   const IPEndPoint& server_address() const { return server_address_; }
     72   const IPEndPoint& client_address() const { return client_address_; }
     73   QuicEncryptedPacket* packet() { return packet_.get(); }
     74 
     75  private:
     76   const IPEndPoint server_address_;
     77   const IPEndPoint client_address_;
     78   scoped_ptr<QuicEncryptedPacket> packet_;
     79 
     80   DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
     81 };
     82 
     83 QuicTimeWaitListManager::QuicTimeWaitListManager(
     84     QuicPacketWriter* writer,
     85     QuicServerSessionVisitor* visitor,
     86     QuicConnectionHelperInterface* helper,
     87     const QuicVersionVector& supported_versions)
     88     : helper_(helper),
     89       kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)),
     90       connection_id_clean_up_alarm_(
     91           helper_->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
     92       writer_(writer),
     93       visitor_(visitor) {
     94   SetConnectionIdCleanUpAlarm();
     95 }
     96 
     97 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
     98   connection_id_clean_up_alarm_->Cancel();
     99   STLDeleteElements(&pending_packets_queue_);
    100   for (ConnectionIdMap::iterator it = connection_id_map_.begin();
    101        it != connection_id_map_.end();
    102        ++it) {
    103     delete it->second.close_packet;
    104   }
    105 }
    106 
    107 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
    108     QuicConnectionId connection_id,
    109     QuicVersion version,
    110     QuicEncryptedPacket* close_packet) {
    111   DVLOG(1) << "Adding " << connection_id << " to the time wait list.";
    112   int num_packets = 0;
    113   ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
    114   if (it != connection_id_map_.end()) {  // Replace record if it is reinserted.
    115     num_packets = it->second.num_packets;
    116     delete it->second.close_packet;
    117     connection_id_map_.erase(it);
    118   }
    119   ConnectionIdData data(num_packets,
    120                         version,
    121                         helper_->GetClock()->ApproximateNow(),
    122                         close_packet);
    123   connection_id_map_.insert(make_pair(connection_id, data));
    124 }
    125 
    126 bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
    127     QuicConnectionId connection_id) const {
    128   return ContainsKey(connection_id_map_, connection_id);
    129 }
    130 
    131 QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
    132     QuicConnectionId connection_id) {
    133   ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
    134   DCHECK(it != connection_id_map_.end());
    135   return (it->second).version;
    136 }
    137 
    138 void QuicTimeWaitListManager::OnCanWrite() {
    139   while (!pending_packets_queue_.empty()) {
    140     QueuedPacket* queued_packet = pending_packets_queue_.front();
    141     if (!WriteToWire(queued_packet)) {
    142       return;
    143     }
    144     pending_packets_queue_.pop_front();
    145     delete queued_packet;
    146   }
    147 }
    148 
    149 void QuicTimeWaitListManager::ProcessPacket(
    150     const IPEndPoint& server_address,
    151     const IPEndPoint& client_address,
    152     QuicConnectionId connection_id,
    153     QuicPacketSequenceNumber sequence_number,
    154     const QuicEncryptedPacket& /*packet*/) {
    155   DCHECK(IsConnectionIdInTimeWait(connection_id));
    156   DVLOG(1) << "Processing " << connection_id << " in time wait state.";
    157   // TODO(satyamshekhar): Think about handling packets from different client
    158   // addresses.
    159   ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
    160   DCHECK(it != connection_id_map_.end());
    161   // Increment the received packet count.
    162   ++((it->second).num_packets);
    163   if (!ShouldSendResponse((it->second).num_packets)) {
    164     return;
    165   }
    166   if (it->second.close_packet) {
    167     QueuedPacket* queued_packet =
    168         new QueuedPacket(server_address,
    169                          client_address,
    170                          it->second.close_packet->Clone());
    171     // Takes ownership of the packet.
    172     SendOrQueuePacket(queued_packet);
    173   } else {
    174     SendPublicReset(server_address,
    175                     client_address,
    176                     connection_id,
    177                     sequence_number);
    178   }
    179 }
    180 
    181 // Returns true if the number of packets received for this connection_id is a
    182 // power of 2 to throttle the number of public reset packets we send to a
    183 // client.
    184 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
    185   return (received_packet_count & (received_packet_count - 1)) == 0;
    186 }
    187 
    188 void QuicTimeWaitListManager::SendPublicReset(
    189     const IPEndPoint& server_address,
    190     const IPEndPoint& client_address,
    191     QuicConnectionId connection_id,
    192     QuicPacketSequenceNumber rejected_sequence_number) {
    193   QuicPublicResetPacket packet;
    194   packet.public_header.connection_id = connection_id;
    195   packet.public_header.reset_flag = true;
    196   packet.public_header.version_flag = false;
    197   packet.rejected_sequence_number = rejected_sequence_number;
    198   // TODO(satyamshekhar): generate a valid nonce for this connection_id.
    199   packet.nonce_proof = 1010101;
    200   packet.client_address = client_address;
    201   QueuedPacket* queued_packet = new QueuedPacket(
    202       server_address,
    203       client_address,
    204       BuildPublicReset(packet));
    205   // Takes ownership of the packet.
    206   SendOrQueuePacket(queued_packet);
    207 }
    208 
    209 QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset(
    210     const QuicPublicResetPacket& packet) {
    211   return QuicFramer::BuildPublicResetPacket(packet);
    212 }
    213 
    214 // Either sends the packet and deletes it or makes pending queue the
    215 // owner of the packet.
    216 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
    217   if (WriteToWire(packet)) {
    218     delete packet;
    219   } else {
    220     // pending_packets_queue takes the ownership of the queued packet.
    221     pending_packets_queue_.push_back(packet);
    222   }
    223 }
    224 
    225 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
    226   if (writer_->IsWriteBlocked()) {
    227     visitor_->OnWriteBlocked(this);
    228     return false;
    229   }
    230   WriteResult result = writer_->WritePacket(
    231       queued_packet->packet()->data(),
    232       queued_packet->packet()->length(),
    233       queued_packet->server_address().address(),
    234       queued_packet->client_address());
    235   if (result.status == WRITE_STATUS_BLOCKED) {
    236     // If blocked and unbuffered, return false to retry sending.
    237     DCHECK(writer_->IsWriteBlocked());
    238     visitor_->OnWriteBlocked(this);
    239     return writer_->IsWriteBlockedDataBuffered();
    240   } else if (result.status == WRITE_STATUS_ERROR) {
    241     LOG(WARNING) << "Received unknown error while sending reset packet to "
    242                  << queued_packet->client_address().ToString() << ": "
    243                  << strerror(result.error_code);
    244   }
    245   return true;
    246 }
    247 
    248 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
    249   connection_id_clean_up_alarm_->Cancel();
    250   QuicTime now = helper_->GetClock()->ApproximateNow();
    251   QuicTime next_alarm_time = now;
    252   if (!connection_id_map_.empty()) {
    253     QuicTime oldest_connection_id =
    254         connection_id_map_.begin()->second.time_added;
    255     if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
    256       next_alarm_time = oldest_connection_id.Add(kTimeWaitPeriod_);
    257     } else {
    258       LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod";
    259     }
    260   } else {
    261     // No connection_ids added so none will expire before kTimeWaitPeriod_.
    262     next_alarm_time = now.Add(kTimeWaitPeriod_);
    263   }
    264 
    265   connection_id_clean_up_alarm_->Set(next_alarm_time);
    266 }
    267 
    268 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
    269   QuicTime now = helper_->GetClock()->ApproximateNow();
    270   while (!connection_id_map_.empty()) {
    271     ConnectionIdMap::iterator it = connection_id_map_.begin();
    272     QuicTime oldest_connection_id = it->second.time_added;
    273     if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
    274       break;
    275     }
    276     // This connection_id has lived its age, retire it now.
    277     delete it->second.close_packet;
    278     connection_id_map_.erase(it);
    279   }
    280   SetConnectionIdCleanUpAlarm();
    281 }
    282 
    283 }  // namespace net
    284