blob: b01d699a7be423b2c2f091e54d2c36ed61e754e1 [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));
Giulio Grassi624f6c62014-02-18 19:42:14 +010064}
65
66void
67UdpChannel::listen(const FaceCreatedCallback& onFaceCreated,
68 const ConnectFailedCallback& onListenFailed)
69{
70 if (m_isListening) {
71 throw Error("Listen already called on this channel");
72 }
73 m_isListening = true;
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060074
Giulio Grassi624f6c62014-02-18 19:42:14 +010075 onFaceCreatedNewPeerCallback = onFaceCreated;
76 onConnectFailedNewPeerCallback = onListenFailed;
77
78 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
79 m_newRemoteEndpoint,
80 bind(&UdpChannel::newPeer, this,
81 boost::asio::placeholders::error,
82 boost::asio::placeholders::bytes_transferred));
83}
84
85
86void
87UdpChannel::connect(const udp::Endpoint& remoteEndpoint,
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060088 const FaceCreatedCallback& onFaceCreated,
89 const ConnectFailedCallback& onConnectFailed)
Giulio Grassi624f6c62014-02-18 19:42:14 +010090{
91 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
92 if (i != m_channelFaces.end()) {
Alexander Afanasyev355c0662014-03-20 18:08:17 -070093 i->second->setOnDemand(false);
Giulio Grassi624f6c62014-02-18 19:42:14 +010094 onFaceCreated(i->second);
95 return;
96 }
97
98 //creating a new socket for the face that will be created soon
99 shared_ptr<ip::udp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700100 make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600101
Giulio Grassi624f6c62014-02-18 19:42:14 +0100102 clientSocket->open(m_localEndpoint.protocol());
103 clientSocket->set_option(ip::udp::socket::reuse_address(true));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600104
Giulio Grassi624f6c62014-02-18 19:42:14 +0100105 try {
106 clientSocket->bind(m_localEndpoint);
107 clientSocket->connect(remoteEndpoint); //@todo connect or async_connect
108 //(since there is no handshake the connect shouldn't block). If we go for
109 //async_connect, make sure that if in the meantime we receive a UDP pkt from
110 //that endpoint nothing bad happen (it's difficult, but it could happen)
111 }
112 catch (boost::system::system_error& e) {
113 clientSocket->close();
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600114 onConnectFailed("Failed to configure socket (" + std::string(e.what()) + ")");
115 return;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100116 }
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700117 createFace(clientSocket, onFaceCreated, false);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100118}
119
120void
121UdpChannel::connect(const std::string& remoteHost,
122 const std::string& remotePort,
123 const FaceCreatedCallback& onFaceCreated,
124 const ConnectFailedCallback& onConnectFailed)
125{
126 ip::udp::resolver::query query(remoteHost, remotePort);
127 shared_ptr<ip::udp::resolver> resolver =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700128 make_shared<ip::udp::resolver>(ref(getGlobalIoService()));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100129
130 resolver->async_resolve(query,
131 bind(&UdpChannel::handleEndpointResolution, this, _1, _2,
132 onFaceCreated, onConnectFailed,
133 resolver));
134}
135
136void
137UdpChannel::handleEndpointResolution(const boost::system::error_code& error,
138 ip::udp::resolver::iterator remoteEndpoint,
139 const FaceCreatedCallback& onFaceCreated,
140 const ConnectFailedCallback& onConnectFailed,
141 const shared_ptr<ip::udp::resolver>& resolver)
142{
143 if (error != 0 ||
144 remoteEndpoint == ip::udp::resolver::iterator())
145 {
146 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
147 return;
148
149 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
150 << error.category().message(error.value()));
151
152 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
153 error.category().message(error.value()));
154 return;
155 }
156
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600157 connect(*remoteEndpoint, onFaceCreated, onConnectFailed);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100158}
159
160size_t
161UdpChannel::size() const
162{
163 return m_channelFaces.size();
164}
165
166
167shared_ptr<UdpFace>
168UdpChannel::createFace(const shared_ptr<ip::udp::socket>& socket,
Giulio Grassi69871f02014-03-09 16:14:44 +0100169 const FaceCreatedCallback& onFaceCreated,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700170 bool isOnDemand)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100171{
172 udp::Endpoint remoteEndpoint = socket->remote_endpoint();
173
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600174 shared_ptr<UdpFace> face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100175
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600176 ChannelFaceMap::iterator faceMapPos = m_channelFaces.find(remoteEndpoint);
177 if (faceMapPos == m_channelFaces.end())
178 {
Alexander Afanasyeve515f0a2014-06-30 15:28:10 -0700179 face = make_shared<UdpFace>(socket, isOnDemand, m_idleFaceTimeout);
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600180 face->onFail += bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint);
181
182 m_channelFaces[remoteEndpoint] = face;
183 }
184 else
185 {
186 // we've already created a a face for this endpoint, just reuse it
187 face = faceMapPos->second;
188
189 boost::system::error_code error;
190 socket->shutdown(ip::udp::socket::shutdown_both, error);
191 socket->close(error);
192 }
193
194 // Need to invoke the callback regardless of whether or not we have already created
195 // the face so that control responses and such can be sent.
Giulio Grassi624f6c62014-02-18 19:42:14 +0100196 onFaceCreated(face);
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600197
Giulio Grassi624f6c62014-02-18 19:42:14 +0100198 return face;
199}
200
201void
202UdpChannel::newPeer(const boost::system::error_code& error,
203 std::size_t nBytesReceived)
204{
205 NFD_LOG_DEBUG("UdpChannel::newPeer from " << m_newRemoteEndpoint);
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000206
207 shared_ptr<UdpFace> face;
208
Giulio Grassi624f6c62014-02-18 19:42:14 +0100209 ChannelFaceMap::iterator i = m_channelFaces.find(m_newRemoteEndpoint);
210 if (i != m_channelFaces.end()) {
211 //The face already exists.
212 //Usually this shouldn't happen, because the channel creates a Udpface
213 //as soon as it receives a pkt from a new endpoint and then the
214 //traffic is dispatched by the kernel directly to the face.
215 //However, if the node receives multiple packets from the same endpoint
216 //"at the same time", while the channel is creating the face the kernel
217 //could dispatch the other pkts to the channel because the face is not yet
218 //ready. In this case, the channel has to pass the pkt to the face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000219
Giulio Grassi624f6c62014-02-18 19:42:14 +0100220 NFD_LOG_DEBUG("The creation of the face for the remote endpoint "
221 << m_newRemoteEndpoint
222 << " is in progress");
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000223
224 face = i->second;
225 }
226 else {
227 shared_ptr<ip::udp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700228 make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000229 clientSocket->open(m_localEndpoint.protocol());
230 clientSocket->set_option(ip::udp::socket::reuse_address(true));
231 clientSocket->bind(m_localEndpoint);
232 clientSocket->connect(m_newRemoteEndpoint);
233
Giulio Grassi69871f02014-03-09 16:14:44 +0100234 face = createFace(clientSocket,
235 onFaceCreatedNewPeerCallback,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700236 true);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100237 }
238
Giulio Grassi624f6c62014-02-18 19:42:14 +0100239 //Passing the message to the correspondent face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000240 face->handleFirstReceive(m_inputBuffer, nBytesReceived, error);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100241
242 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
243 m_newRemoteEndpoint,
244 bind(&UdpChannel::newPeer, this,
245 boost::asio::placeholders::error,
246 boost::asio::placeholders::bytes_transferred));
247}
248
249
Giulio Grassi69871f02014-03-09 16:14:44 +0100250void
251UdpChannel::afterFaceFailed(udp::Endpoint &endpoint)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100252{
253 NFD_LOG_DEBUG("afterFaceFailed: " << endpoint);
254 m_channelFaces.erase(endpoint);
255}
256
Giulio Grassi624f6c62014-02-18 19:42:14 +0100257} // namespace nfd