• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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