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