blob: 684879ace3f14beb6e90c4d7f94b1c4e85fb31ab [file] [log] [blame]
Giulio Grassi624f6c62014-02-18 19:42:14 +01001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev9bcbc7c2014-04-06 19:37:37 -07003 * Copyright (c) 2014 Regents of the University of California,
4 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology
9 *
10 * This file is part of NFD (Named Data Networking Forwarding Daemon).
11 * See AUTHORS.md for complete list of NFD authors and contributors.
12 *
13 * NFD is free software: you can redistribute it and/or modify it under the terms
14 * of the GNU General Public License as published by the Free Software Foundation,
15 * either version 3 of the License, or (at your option) any later version.
16 *
17 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
19 * PURPOSE. See the GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along with
22 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
23 **/
Giulio Grassi624f6c62014-02-18 19:42:14 +010024
25#include "udp-channel.hpp"
26#include "core/global-io.hpp"
Junxiao Shi61e3cc52014-03-03 20:40:28 -070027#include "core/face-uri.hpp"
Giulio Grassi624f6c62014-02-18 19:42:14 +010028
29namespace nfd {
30
31NFD_LOG_INIT("UdpChannel");
32
33using namespace boost::asio;
34
35UdpChannel::UdpChannel(const udp::Endpoint& localEndpoint,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070036 const time::seconds& timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +010037 : m_localEndpoint(localEndpoint)
38 , m_isListening(false)
Giulio Grassi69871f02014-03-09 16:14:44 +010039 , m_idleFaceTimeout(timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +010040{
41 /// \todo the reuse_address works as we want in Linux, but in other system could be different.
42 /// We need to check this
43 /// (SO_REUSEADDR doesn't behave uniformly in different OS)
44
Alexander Afanasyevf6980282014-05-13 18:28:40 -070045 m_socket = make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Giulio Grassi624f6c62014-02-18 19:42:14 +010046 m_socket->open(m_localEndpoint.protocol());
47 m_socket->set_option(boost::asio::ip::udp::socket::reuse_address(true));
Alexander Afanasyeveee71fd2014-03-13 17:40:33 -070048 if (m_localEndpoint.address().is_v6())
49 {
50 m_socket->set_option(ip::v6_only(true));
51 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060052
Giulio Grassi624f6c62014-02-18 19:42:14 +010053 try {
54 m_socket->bind(m_localEndpoint);
55 }
56 catch (boost::system::system_error& e) {
57 //The bind failed, so the socket is useless now
58 m_socket->close();
59 throw Error("Failed to properly configure the socket. "
60 "UdpChannel creation aborted, check the address (" + std::string(e.what()) + ")");
61 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060062
Junxiao Shi61e3cc52014-03-03 20:40:28 -070063 this->setUri(FaceUri(localEndpoint));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060064
Giulio Grassi69871f02014-03-09 16:14:44 +010065 //setting the timeout to close the idle faces
66 m_closeIdleFaceEvent = scheduler::schedule(m_idleFaceTimeout,
67 bind(&UdpChannel::closeIdleFaces, this));
Junxiao Shi61e3cc52014-03-03 20:40:28 -070068}
69
70UdpChannel::~UdpChannel()
71{
Giulio Grassi69871f02014-03-09 16:14:44 +010072 scheduler::cancel(m_closeIdleFaceEvent);
Giulio Grassi624f6c62014-02-18 19:42:14 +010073}
74
75void
76UdpChannel::listen(const FaceCreatedCallback& onFaceCreated,
77 const ConnectFailedCallback& onListenFailed)
78{
79 if (m_isListening) {
80 throw Error("Listen already called on this channel");
81 }
82 m_isListening = true;
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060083
Giulio Grassi624f6c62014-02-18 19:42:14 +010084 onFaceCreatedNewPeerCallback = onFaceCreated;
85 onConnectFailedNewPeerCallback = onListenFailed;
86
87 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
88 m_newRemoteEndpoint,
89 bind(&UdpChannel::newPeer, this,
90 boost::asio::placeholders::error,
91 boost::asio::placeholders::bytes_transferred));
92}
93
94
95void
96UdpChannel::connect(const udp::Endpoint& remoteEndpoint,
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060097 const FaceCreatedCallback& onFaceCreated,
98 const ConnectFailedCallback& onConnectFailed)
Giulio Grassi624f6c62014-02-18 19:42:14 +010099{
100 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
101 if (i != m_channelFaces.end()) {
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700102 i->second->setOnDemand(false);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100103 onFaceCreated(i->second);
104 return;
105 }
106
107 //creating a new socket for the face that will be created soon
108 shared_ptr<ip::udp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700109 make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600110
Giulio Grassi624f6c62014-02-18 19:42:14 +0100111 clientSocket->open(m_localEndpoint.protocol());
112 clientSocket->set_option(ip::udp::socket::reuse_address(true));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600113
Giulio Grassi624f6c62014-02-18 19:42:14 +0100114 try {
115 clientSocket->bind(m_localEndpoint);
116 clientSocket->connect(remoteEndpoint); //@todo connect or async_connect
117 //(since there is no handshake the connect shouldn't block). If we go for
118 //async_connect, make sure that if in the meantime we receive a UDP pkt from
119 //that endpoint nothing bad happen (it's difficult, but it could happen)
120 }
121 catch (boost::system::system_error& e) {
122 clientSocket->close();
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600123 onConnectFailed("Failed to configure socket (" + std::string(e.what()) + ")");
124 return;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100125 }
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700126 createFace(clientSocket, onFaceCreated, false);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100127}
128
129void
130UdpChannel::connect(const std::string& remoteHost,
131 const std::string& remotePort,
132 const FaceCreatedCallback& onFaceCreated,
133 const ConnectFailedCallback& onConnectFailed)
134{
135 ip::udp::resolver::query query(remoteHost, remotePort);
136 shared_ptr<ip::udp::resolver> resolver =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700137 make_shared<ip::udp::resolver>(ref(getGlobalIoService()));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100138
139 resolver->async_resolve(query,
140 bind(&UdpChannel::handleEndpointResolution, this, _1, _2,
141 onFaceCreated, onConnectFailed,
142 resolver));
143}
144
145void
146UdpChannel::handleEndpointResolution(const boost::system::error_code& error,
147 ip::udp::resolver::iterator remoteEndpoint,
148 const FaceCreatedCallback& onFaceCreated,
149 const ConnectFailedCallback& onConnectFailed,
150 const shared_ptr<ip::udp::resolver>& resolver)
151{
152 if (error != 0 ||
153 remoteEndpoint == ip::udp::resolver::iterator())
154 {
155 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
156 return;
157
158 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
159 << error.category().message(error.value()));
160
161 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
162 error.category().message(error.value()));
163 return;
164 }
165
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600166 connect(*remoteEndpoint, onFaceCreated, onConnectFailed);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100167}
168
169size_t
170UdpChannel::size() const
171{
172 return m_channelFaces.size();
173}
174
175
176shared_ptr<UdpFace>
177UdpChannel::createFace(const shared_ptr<ip::udp::socket>& socket,
Giulio Grassi69871f02014-03-09 16:14:44 +0100178 const FaceCreatedCallback& onFaceCreated,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700179 bool isOnDemand)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100180{
181 udp::Endpoint remoteEndpoint = socket->remote_endpoint();
182
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600183 shared_ptr<UdpFace> face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100184
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600185 ChannelFaceMap::iterator faceMapPos = m_channelFaces.find(remoteEndpoint);
186 if (faceMapPos == m_channelFaces.end())
187 {
188 face = make_shared<UdpFace>(socket, isOnDemand);
189 face->onFail += bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint);
190
191 m_channelFaces[remoteEndpoint] = face;
192 }
193 else
194 {
195 // we've already created a a face for this endpoint, just reuse it
196 face = faceMapPos->second;
197
198 boost::system::error_code error;
199 socket->shutdown(ip::udp::socket::shutdown_both, error);
200 socket->close(error);
201 }
202
203 // Need to invoke the callback regardless of whether or not we have already created
204 // the face so that control responses and such can be sent.
Giulio Grassi624f6c62014-02-18 19:42:14 +0100205 onFaceCreated(face);
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600206
Giulio Grassi624f6c62014-02-18 19:42:14 +0100207 return face;
208}
209
210void
211UdpChannel::newPeer(const boost::system::error_code& error,
212 std::size_t nBytesReceived)
213{
214 NFD_LOG_DEBUG("UdpChannel::newPeer from " << m_newRemoteEndpoint);
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000215
216 shared_ptr<UdpFace> face;
217
Giulio Grassi624f6c62014-02-18 19:42:14 +0100218 ChannelFaceMap::iterator i = m_channelFaces.find(m_newRemoteEndpoint);
219 if (i != m_channelFaces.end()) {
220 //The face already exists.
221 //Usually this shouldn't happen, because the channel creates a Udpface
222 //as soon as it receives a pkt from a new endpoint and then the
223 //traffic is dispatched by the kernel directly to the face.
224 //However, if the node receives multiple packets from the same endpoint
225 //"at the same time", while the channel is creating the face the kernel
226 //could dispatch the other pkts to the channel because the face is not yet
227 //ready. In this case, the channel has to pass the pkt to the face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000228
Giulio Grassi624f6c62014-02-18 19:42:14 +0100229 NFD_LOG_DEBUG("The creation of the face for the remote endpoint "
230 << m_newRemoteEndpoint
231 << " is in progress");
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000232
233 face = i->second;
234 }
235 else {
236 shared_ptr<ip::udp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700237 make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000238 clientSocket->open(m_localEndpoint.protocol());
239 clientSocket->set_option(ip::udp::socket::reuse_address(true));
240 clientSocket->bind(m_localEndpoint);
241 clientSocket->connect(m_newRemoteEndpoint);
242
Giulio Grassi69871f02014-03-09 16:14:44 +0100243 face = createFace(clientSocket,
244 onFaceCreatedNewPeerCallback,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700245 true);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100246 }
247
Giulio Grassi624f6c62014-02-18 19:42:14 +0100248 //Passing the message to the correspondent face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000249 face->handleFirstReceive(m_inputBuffer, nBytesReceived, error);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100250
251 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
252 m_newRemoteEndpoint,
253 bind(&UdpChannel::newPeer, this,
254 boost::asio::placeholders::error,
255 boost::asio::placeholders::bytes_transferred));
256}
257
258
Giulio Grassi69871f02014-03-09 16:14:44 +0100259void
260UdpChannel::afterFaceFailed(udp::Endpoint &endpoint)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100261{
262 NFD_LOG_DEBUG("afterFaceFailed: " << endpoint);
263 m_channelFaces.erase(endpoint);
264}
265
Giulio Grassi69871f02014-03-09 16:14:44 +0100266void
267UdpChannel::closeIdleFaces()
268{
269 ChannelFaceMap::iterator next = m_channelFaces.begin();
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600270
Giulio Grassi69871f02014-03-09 16:14:44 +0100271 while (next != m_channelFaces.end()) {
272 ChannelFaceMap::iterator it = next;
273 next++;
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700274 if (it->second->isOnDemand() &&
Giulio Grassi69871f02014-03-09 16:14:44 +0100275 !it->second->hasBeenUsedRecently()) {
276 //face has been idle since the last time closeIdleFaces
277 //has been called. Going to close it
278 NFD_LOG_DEBUG("Found idle face id: " << it->second->getId());
279 it->second->close();
280 } else {
281 it->second->resetRecentUsage();
282 }
283 }
284 m_closeIdleFaceEvent = scheduler::schedule(m_idleFaceTimeout,
285 bind(&UdpChannel::closeIdleFaces, this));
286}
287
Giulio Grassi624f6c62014-02-18 19:42:14 +0100288} // namespace nfd