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_dispatcher.h"
6
7 #include <errno.h>
8
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "net/quic/quic_blocked_writer_interface.h"
12 #include "net/quic/quic_utils.h"
13 #include "net/tools/quic/quic_default_packet_writer.h"
14 #include "net/tools/quic/quic_epoll_connection_helper.h"
15 #include "net/tools/quic/quic_socket_utils.h"
16
17 namespace net {
18 namespace tools {
19
20 using std::make_pair;
21
22 class DeleteSessionsAlarm : public EpollAlarm {
23 public:
DeleteSessionsAlarm(QuicDispatcher * dispatcher)24 explicit DeleteSessionsAlarm(QuicDispatcher* dispatcher)
25 : dispatcher_(dispatcher) {
26 }
27
OnAlarm()28 virtual int64 OnAlarm() OVERRIDE {
29 EpollAlarm::OnAlarm();
30 dispatcher_->DeleteSessions();
31 return 0;
32 }
33
34 private:
35 QuicDispatcher* dispatcher_;
36 };
37
QuicDispatcher(const QuicConfig & config,const QuicCryptoServerConfig & crypto_config,const QuicVersionVector & supported_versions,int fd,EpollServer * epoll_server)38 QuicDispatcher::QuicDispatcher(const QuicConfig& config,
39 const QuicCryptoServerConfig& crypto_config,
40 const QuicVersionVector& supported_versions,
41 int fd,
42 EpollServer* epoll_server)
43 : config_(config),
44 crypto_config_(crypto_config),
45 time_wait_list_manager_(
46 new QuicTimeWaitListManager(this, epoll_server, supported_versions)),
47 delete_sessions_alarm_(new DeleteSessionsAlarm(this)),
48 epoll_server_(epoll_server),
49 fd_(fd),
50 write_blocked_(false),
51 helper_(new QuicEpollConnectionHelper(epoll_server_)),
52 writer_(new QuicDefaultPacketWriter(fd)),
53 supported_versions_(supported_versions) {
54 }
55
~QuicDispatcher()56 QuicDispatcher::~QuicDispatcher() {
57 STLDeleteValues(&session_map_);
58 STLDeleteElements(&closed_session_list_);
59 }
60
set_fd(int fd)61 void QuicDispatcher::set_fd(int fd) {
62 fd_ = fd;
63 writer_.reset(new QuicDefaultPacketWriter(fd));
64 }
65
WritePacket(const char * buffer,size_t buf_len,const IPAddressNumber & self_address,const IPEndPoint & peer_address,QuicBlockedWriterInterface * writer)66 WriteResult QuicDispatcher::WritePacket(const char* buffer, size_t buf_len,
67 const IPAddressNumber& self_address,
68 const IPEndPoint& peer_address,
69 QuicBlockedWriterInterface* writer) {
70 if (write_blocked_) {
71 write_blocked_list_.insert(make_pair(writer, true));
72 return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
73 }
74
75 WriteResult result =
76 writer_->WritePacket(buffer, buf_len, self_address, peer_address, writer);
77 if (result.status == WRITE_STATUS_BLOCKED) {
78 write_blocked_list_.insert(make_pair(writer, true));
79 write_blocked_ = true;
80 }
81 return result;
82 }
83
IsWriteBlockedDataBuffered() const84 bool QuicDispatcher::IsWriteBlockedDataBuffered() const {
85 return writer_->IsWriteBlockedDataBuffered();
86 }
87
ProcessPacket(const IPEndPoint & server_address,const IPEndPoint & client_address,QuicGuid guid,bool has_version_flag,const QuicEncryptedPacket & packet)88 void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address,
89 const IPEndPoint& client_address,
90 QuicGuid guid,
91 bool has_version_flag,
92 const QuicEncryptedPacket& packet) {
93 QuicSession* session = NULL;
94
95 SessionMap::iterator it = session_map_.find(guid);
96 if (it == session_map_.end()) {
97 if (time_wait_list_manager_->IsGuidInTimeWait(guid)) {
98 time_wait_list_manager_->ProcessPacket(server_address,
99 client_address,
100 guid,
101 packet);
102 return;
103 }
104
105 // Ensure the packet has a version negotiation bit set before creating a new
106 // session for it. All initial packets for a new connection are required to
107 // have the flag set. Otherwise it may be a stray packet.
108 if (has_version_flag) {
109 session = CreateQuicSession(guid, server_address, client_address);
110 }
111
112 if (session == NULL) {
113 DLOG(INFO) << "Failed to create session for " << guid;
114 // Add this guid fo the time-wait state, to safely reject future packets.
115 // We don't know the version here, so assume latest.
116 // TODO(ianswett): Produce a no-version version negotiation packet.
117 time_wait_list_manager_->AddGuidToTimeWait(guid,
118 supported_versions_.front(),
119 NULL);
120 time_wait_list_manager_->ProcessPacket(server_address,
121 client_address,
122 guid,
123 packet);
124 return;
125 }
126 DLOG(INFO) << "Created new session for " << guid;
127 session_map_.insert(make_pair(guid, session));
128 } else {
129 session = it->second;
130 }
131
132 session->connection()->ProcessUdpPacket(
133 server_address, client_address, packet);
134 }
135
CleanUpSession(SessionMap::iterator it)136 void QuicDispatcher::CleanUpSession(SessionMap::iterator it) {
137 QuicConnection* connection = it->second->connection();
138 QuicEncryptedPacket* connection_close_packet =
139 connection->ReleaseConnectionClosePacket();
140 write_blocked_list_.erase(connection);
141 time_wait_list_manager_->AddGuidToTimeWait(it->first,
142 connection->version(),
143 connection_close_packet);
144 session_map_.erase(it);
145 }
146
DeleteSessions()147 void QuicDispatcher::DeleteSessions() {
148 STLDeleteElements(&closed_session_list_);
149 }
150
UseWriter(QuicPacketWriter * writer)151 void QuicDispatcher::UseWriter(QuicPacketWriter* writer) {
152 writer_.reset(writer);
153 }
154
OnCanWrite()155 bool QuicDispatcher::OnCanWrite() {
156 // We got an EPOLLOUT: the socket should not be blocked.
157 write_blocked_ = false;
158
159 // Give each writer one attempt to write.
160 int num_writers = write_blocked_list_.size();
161 for (int i = 0; i < num_writers; ++i) {
162 if (write_blocked_list_.empty()) {
163 break;
164 }
165 QuicBlockedWriterInterface* writer = write_blocked_list_.begin()->first;
166 write_blocked_list_.erase(write_blocked_list_.begin());
167 bool can_write_more = writer->OnCanWrite();
168 if (write_blocked_) {
169 // We were unable to write. Wait for the next EPOLLOUT.
170 // In this case, the session would have been added to the blocked list
171 // up in WritePacket.
172 return false;
173 }
174 // The socket is not blocked but the writer has ceded work. Add it to the
175 // end of the list.
176 if (can_write_more) {
177 write_blocked_list_.insert(make_pair(writer, true));
178 }
179 }
180
181 // We're not write blocked. Return true if there's more work to do.
182 return !write_blocked_list_.empty();
183 }
184
Shutdown()185 void QuicDispatcher::Shutdown() {
186 while (!session_map_.empty()) {
187 QuicSession* session = session_map_.begin()->second;
188 session->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
189 // Validate that the session removes itself from the session map on close.
190 DCHECK(session_map_.empty() || session_map_.begin()->second != session);
191 }
192 DeleteSessions();
193 }
194
OnConnectionClosed(QuicGuid guid,QuicErrorCode error)195 void QuicDispatcher::OnConnectionClosed(QuicGuid guid, QuicErrorCode error) {
196 SessionMap::iterator it = session_map_.find(guid);
197 if (it == session_map_.end()) {
198 LOG(DFATAL) << "GUID " << guid << " does not exist in the session map. "
199 << "Error: " << QuicUtils::ErrorToString(error);
200 return;
201 }
202
203 DLOG_IF(INFO, error != QUIC_NO_ERROR) << "Closing connection (" << guid
204 << ") due to error: "
205 << QuicUtils::ErrorToString(error);
206
207 if (closed_session_list_.empty()) {
208 epoll_server_->RegisterAlarmApproximateDelta(
209 0, delete_sessions_alarm_.get());
210 }
211 closed_session_list_.push_back(it->second);
212 CleanUpSession(it);
213 }
214
CreateQuicSession(QuicGuid guid,const IPEndPoint & server_address,const IPEndPoint & client_address)215 QuicSession* QuicDispatcher::CreateQuicSession(
216 QuicGuid guid,
217 const IPEndPoint& server_address,
218 const IPEndPoint& client_address) {
219 QuicServerSession* session = new QuicServerSession(
220 config_, new QuicConnection(guid, client_address, helper_.get(), this,
221 true, supported_versions_), this);
222 session->InitializeSession(crypto_config_);
223 return session;
224 }
225
226 } // namespace tools
227 } // namespace net
228