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
21using std::make_pair;
22
23namespace net {
24namespace tools {
25
26namespace {
27
28// Time period for which the guid should live in time wait state..
29const int kTimeWaitSeconds = 5;
30
31}  // namespace
32
33// A very simple alarm that just informs the QuicTimeWaitListManager to clean
34// up old guids. This alarm should be unregistered and deleted before the
35// QuicTimeWaitListManager is deleted.
36class GuidCleanUpAlarm : public EpollAlarm {
37 public:
38  explicit GuidCleanUpAlarm(QuicTimeWaitListManager* time_wait_list_manager)
39      : time_wait_list_manager_(time_wait_list_manager) {
40  }
41
42  virtual int64 OnAlarm() OVERRIDE {
43    EpollAlarm::OnAlarm();
44    time_wait_list_manager_->CleanUpOldGuids();
45    // Let the time wait manager register the alarm at appropriate time.
46    return 0;
47  }
48
49 private:
50  // Not owned.
51  QuicTimeWaitListManager* time_wait_list_manager_;
52};
53
54struct QuicTimeWaitListManager::GuidAddTime {
55  GuidAddTime(QuicGuid guid, const QuicTime& time)
56      : guid(guid),
57        time_added(time) {
58  }
59
60  QuicGuid guid;
61  QuicTime time_added;
62};
63
64// This class stores pending public reset packets to be sent to clients.
65// server_address - server address on which a packet what was received for
66//                  a guid in time wait state.
67// client_address - address of the client that sent that packet. Needed to send
68//                  the public reset packet back to the client.
69// packet - the pending public reset packet that is to be sent to the client.
70//          created instance takes the ownership of this packet.
71class QuicTimeWaitListManager::QueuedPacket {
72 public:
73  QueuedPacket(const IPEndPoint& server_address,
74               const IPEndPoint& client_address,
75               QuicEncryptedPacket* packet)
76      : server_address_(server_address),
77        client_address_(client_address),
78        packet_(packet) {
79  }
80
81  const IPEndPoint& server_address() const { return server_address_; }
82  const IPEndPoint& client_address() const { return client_address_; }
83  QuicEncryptedPacket* packet() { return packet_.get(); }
84
85 private:
86  const IPEndPoint server_address_;
87  const IPEndPoint client_address_;
88  scoped_ptr<QuicEncryptedPacket> packet_;
89
90  DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
91};
92
93QuicTimeWaitListManager::QuicTimeWaitListManager(
94    QuicPacketWriter* writer,
95    EpollServer* epoll_server)
96    : framer_(QuicVersionMax(),
97              QuicTime::Zero(),  // unused
98              true),
99      epoll_server_(epoll_server),
100      kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)),
101      guid_clean_up_alarm_(new GuidCleanUpAlarm(this)),
102      clock_(epoll_server),
103      writer_(writer),
104      is_write_blocked_(false) {
105  framer_.set_visitor(this);
106  SetGuidCleanUpAlarm();
107}
108
109QuicTimeWaitListManager::~QuicTimeWaitListManager() {
110  guid_clean_up_alarm_->UnregisterIfRegistered();
111  STLDeleteElements(&time_ordered_guid_list_);
112  STLDeleteElements(&pending_packets_queue_);
113}
114
115void QuicTimeWaitListManager::AddGuidToTimeWait(QuicGuid guid,
116                                                QuicVersion version) {
117  DCHECK(!IsGuidInTimeWait(guid));
118  // Initialize the guid with 0 packets received.
119  GuidData data(0, version);
120  guid_map_.insert(make_pair(guid, data));
121  time_ordered_guid_list_.push_back(new GuidAddTime(guid,
122                                                    clock_.ApproximateNow()));
123}
124
125bool QuicTimeWaitListManager::IsGuidInTimeWait(QuicGuid guid) const {
126  return guid_map_.find(guid) != guid_map_.end();
127}
128
129void QuicTimeWaitListManager::ProcessPacket(
130    const IPEndPoint& server_address,
131    const IPEndPoint& client_address,
132    QuicGuid guid,
133    const QuicEncryptedPacket& packet) {
134  DCHECK(IsGuidInTimeWait(guid));
135  server_address_ = server_address;
136  client_address_ = client_address;
137
138  // Set the framer to the appropriate version for this GUID, before processing.
139  QuicVersion version = GetQuicVersionFromGuid(guid);
140  framer_.set_version(version);
141
142  framer_.ProcessPacket(packet);
143}
144
145QuicVersion QuicTimeWaitListManager::GetQuicVersionFromGuid(QuicGuid guid) {
146  GuidMapIterator it = guid_map_.find(guid);
147  DCHECK(it != guid_map_.end());
148  return (it->second).version;
149}
150
151bool QuicTimeWaitListManager::OnCanWrite() {
152  is_write_blocked_ = false;
153  while (!is_write_blocked_ && !pending_packets_queue_.empty()) {
154    QueuedPacket* queued_packet = pending_packets_queue_.front();
155    WriteToWire(queued_packet);
156    if (!is_write_blocked_) {
157      pending_packets_queue_.pop_front();
158      delete queued_packet;
159    }
160  }
161
162  return !is_write_blocked_;
163}
164
165void QuicTimeWaitListManager::OnError(QuicFramer* framer) {
166  DLOG(INFO) << QuicUtils::ErrorToString(framer->error());
167}
168
169bool QuicTimeWaitListManager::OnProtocolVersionMismatch(
170    QuicVersion received_version) {
171  // Drop such packets whose version don't match.
172  return false;
173}
174
175bool QuicTimeWaitListManager::OnStreamFrame(const QuicStreamFrame& frame) {
176  return false;
177}
178
179bool QuicTimeWaitListManager::OnAckFrame(const QuicAckFrame& frame) {
180  return false;
181}
182
183bool QuicTimeWaitListManager::OnCongestionFeedbackFrame(
184    const QuicCongestionFeedbackFrame& frame) {
185  return false;
186}
187
188bool QuicTimeWaitListManager::OnRstStreamFrame(
189    const QuicRstStreamFrame& frame) {
190  return false;
191}
192
193bool QuicTimeWaitListManager::OnConnectionCloseFrame(
194    const QuicConnectionCloseFrame & frame) {
195  return false;
196}
197
198bool QuicTimeWaitListManager::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
199  return false;
200}
201
202bool QuicTimeWaitListManager::OnPacketHeader(const QuicPacketHeader& header) {
203  // TODO(satyamshekhar): Think about handling packets from different client
204  // addresses.
205  GuidMapIterator it = guid_map_.find(header.public_header.guid);
206  DCHECK(it != guid_map_.end());
207  // Increment the received packet count.
208  ++((it->second).num_packets);
209  if (ShouldSendPublicReset((it->second).num_packets)) {
210    // We don't need the packet anymore. Just tell the client what sequence
211    // number we rejected.
212    SendPublicReset(server_address_,
213                    client_address_,
214                    header.public_header.guid,
215                    header.packet_sequence_number);
216  }
217  // Never process the body of the packet in time wait state.
218  return false;
219}
220
221// Returns true if the number of packets received for this guid is a power of 2
222// to throttle the number of public reset packets we send to a client.
223bool QuicTimeWaitListManager::ShouldSendPublicReset(int received_packet_count) {
224  return (received_packet_count & (received_packet_count - 1)) == 0;
225}
226
227void QuicTimeWaitListManager::SendPublicReset(
228    const IPEndPoint& server_address,
229    const IPEndPoint& client_address,
230    QuicGuid guid,
231    QuicPacketSequenceNumber rejected_sequence_number) {
232  QuicPublicResetPacket packet;
233  packet.public_header.guid = guid;
234  packet.public_header.reset_flag = true;
235  packet.public_header.version_flag = false;
236  packet.rejected_sequence_number = rejected_sequence_number;
237  // TODO(satyamshekhar): generate a valid nonce for this guid.
238  packet.nonce_proof = 1010101;
239  QueuedPacket* queued_packet = new QueuedPacket(
240      server_address,
241      client_address,
242      framer_.BuildPublicResetPacket(packet));
243  // Takes ownership of the packet.
244  SendOrQueuePacket(queued_packet);
245}
246
247// Either sends the packet and deletes it or makes pending queue the
248// owner of the packet.
249void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
250  if (!is_write_blocked_) {
251    // TODO(satyamshekhar): Handle packets that fail due to error other than
252    // EAGAIN or EWOULDBLOCK.
253    WriteToWire(packet);
254  }
255
256  if (is_write_blocked_) {
257    // pending_packets_queue takes the ownership of the queued packet.
258    pending_packets_queue_.push_back(packet);
259  } else {
260    delete packet;
261  }
262}
263
264void QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
265  DCHECK(!is_write_blocked_);
266  int error;
267  int rc = writer_->WritePacket(queued_packet->packet()->data(),
268                                queued_packet->packet()->length(),
269                                queued_packet->server_address().address(),
270                                queued_packet->client_address(),
271                                this,
272                                &error);
273
274  if (rc == -1) {
275    if (error == EAGAIN || error == EWOULDBLOCK) {
276      is_write_blocked_ = true;
277    } else {
278      LOG(WARNING) << "Received unknown error while sending reset packet to "
279                   << queued_packet->client_address().ToString() << ": "
280                   << strerror(error);
281    }
282  }
283}
284
285void QuicTimeWaitListManager::SetGuidCleanUpAlarm() {
286  guid_clean_up_alarm_->UnregisterIfRegistered();
287  int64 next_alarm_interval;
288  if (!time_ordered_guid_list_.empty()) {
289    GuidAddTime* oldest_guid = time_ordered_guid_list_.front();
290    QuicTime now = clock_.ApproximateNow();
291    DCHECK(now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_);
292    next_alarm_interval = oldest_guid->time_added
293        .Add(kTimeWaitPeriod_)
294        .Subtract(now)
295        .ToMicroseconds();
296  } else {
297    // No guids added so none will expire before kTimeWaitPeriod_.
298    next_alarm_interval = kTimeWaitPeriod_.ToMicroseconds();
299  }
300
301  epoll_server_->RegisterAlarmApproximateDelta(next_alarm_interval,
302                                               guid_clean_up_alarm_.get());
303}
304
305void QuicTimeWaitListManager::CleanUpOldGuids() {
306  QuicTime now = clock_.ApproximateNow();
307  while (time_ordered_guid_list_.size() > 0) {
308    DCHECK_EQ(time_ordered_guid_list_.size(), guid_map_.size());
309    GuidAddTime* oldest_guid = time_ordered_guid_list_.front();
310    if (now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_) {
311      break;
312    }
313    // This guid has lived its age, retire it now.
314    guid_map_.erase(oldest_guid->guid);
315    time_ordered_guid_list_.pop_front();
316    delete oldest_guid;
317  }
318  SetGuidCleanUpAlarm();
319}
320
321}  // namespace tools
322}  // namespace net
323