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
23using base::StringPiece;
24using std::make_pair;
25
26namespace net {
27namespace tools {
28
29namespace {
30
31// Time period for which the connection_id should live in time wait state..
32const 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.
39class 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.
65class 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
87QuicTimeWaitListManager::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
101QuicTimeWaitListManager::~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
111void 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
128bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
129    QuicConnectionId connection_id) const {
130  return ContainsKey(connection_id_map_, connection_id);
131}
132
133QuicVersion 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
140void 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
151void 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.
186bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
187  return (received_packet_count & (received_packet_count - 1)) == 0;
188}
189
190void 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
211QuicEncryptedPacket* 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.
218void 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
227bool 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
250void 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
274void 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