blob: ca052a28e5c342fcba7bb5155a9820707209249f [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
45 m_socket = make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
46 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 =
109 make_shared<ip::udp::socket>(boost::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 =
137 make_shared<ip::udp::resolver>(boost::ref(getGlobalIoService()));
138
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
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700183 shared_ptr<UdpFace> face = make_shared<UdpFace>(boost::cref(socket), isOnDemand);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100184 face->onFail += bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint);
185
186 onFaceCreated(face);
187 m_channelFaces[remoteEndpoint] = face;
188 return face;
189}
190
191void
192UdpChannel::newPeer(const boost::system::error_code& error,
193 std::size_t nBytesReceived)
194{
195 NFD_LOG_DEBUG("UdpChannel::newPeer from " << m_newRemoteEndpoint);
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000196
197 shared_ptr<UdpFace> face;
198
Giulio Grassi624f6c62014-02-18 19:42:14 +0100199 ChannelFaceMap::iterator i = m_channelFaces.find(m_newRemoteEndpoint);
200 if (i != m_channelFaces.end()) {
201 //The face already exists.
202 //Usually this shouldn't happen, because the channel creates a Udpface
203 //as soon as it receives a pkt from a new endpoint and then the
204 //traffic is dispatched by the kernel directly to the face.
205 //However, if the node receives multiple packets from the same endpoint
206 //"at the same time", while the channel is creating the face the kernel
207 //could dispatch the other pkts to the channel because the face is not yet
208 //ready. In this case, the channel has to pass the pkt to the face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000209
Giulio Grassi624f6c62014-02-18 19:42:14 +0100210 NFD_LOG_DEBUG("The creation of the face for the remote endpoint "
211 << m_newRemoteEndpoint
212 << " is in progress");
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000213
214 face = i->second;
215 }
216 else {
217 shared_ptr<ip::udp::socket> clientSocket =
218 make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
219 clientSocket->open(m_localEndpoint.protocol());
220 clientSocket->set_option(ip::udp::socket::reuse_address(true));
221 clientSocket->bind(m_localEndpoint);
222 clientSocket->connect(m_newRemoteEndpoint);
223
Giulio Grassi69871f02014-03-09 16:14:44 +0100224 face = createFace(clientSocket,
225 onFaceCreatedNewPeerCallback,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700226 true);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100227 }
228
Giulio Grassi624f6c62014-02-18 19:42:14 +0100229 //Passing the message to the correspondent face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000230 face->handleFirstReceive(m_inputBuffer, nBytesReceived, error);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100231
232 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
233 m_newRemoteEndpoint,
234 bind(&UdpChannel::newPeer, this,
235 boost::asio::placeholders::error,
236 boost::asio::placeholders::bytes_transferred));
237}
238
239
Giulio Grassi69871f02014-03-09 16:14:44 +0100240void
241UdpChannel::afterFaceFailed(udp::Endpoint &endpoint)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100242{
243 NFD_LOG_DEBUG("afterFaceFailed: " << endpoint);
244 m_channelFaces.erase(endpoint);
245}
246
Giulio Grassi69871f02014-03-09 16:14:44 +0100247void
248UdpChannel::closeIdleFaces()
249{
250 ChannelFaceMap::iterator next = m_channelFaces.begin();
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600251
Giulio Grassi69871f02014-03-09 16:14:44 +0100252 while (next != m_channelFaces.end()) {
253 ChannelFaceMap::iterator it = next;
254 next++;
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700255 if (it->second->isOnDemand() &&
Giulio Grassi69871f02014-03-09 16:14:44 +0100256 !it->second->hasBeenUsedRecently()) {
257 //face has been idle since the last time closeIdleFaces
258 //has been called. Going to close it
259 NFD_LOG_DEBUG("Found idle face id: " << it->second->getId());
260 it->second->close();
261 } else {
262 it->second->resetRecentUsage();
263 }
264 }
265 m_closeIdleFaceEvent = scheduler::schedule(m_idleFaceTimeout,
266 bind(&UdpChannel::closeIdleFaces, this));
267}
268
Giulio Grassi624f6c62014-02-18 19:42:14 +0100269} // namespace nfd