blob: 151f235c38d24dbc703b7b7c9253772fb29f8837 [file] [log] [blame]
Giulio Grassi624f6c62014-02-18 19:42:14 +01001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Junxiao Shi5dd26c32014-07-20 23:15:14 -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 * The University of Memphis
Alexander Afanasyev9bcbc7c2014-04-06 19:37:37 -070010 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
Junxiao Shi5dd26c32014-07-20 23:15:14 -070024 */
Giulio Grassi624f6c62014-02-18 19:42:14 +010025
26#include "udp-channel.hpp"
27#include "core/global-io.hpp"
Junxiao Shi61e3cc52014-03-03 20:40:28 -070028#include "core/face-uri.hpp"
Giulio Grassi624f6c62014-02-18 19:42:14 +010029
30namespace nfd {
31
32NFD_LOG_INIT("UdpChannel");
33
34using namespace boost::asio;
35
36UdpChannel::UdpChannel(const udp::Endpoint& localEndpoint,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070037 const time::seconds& timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +010038 : m_localEndpoint(localEndpoint)
39 , m_isListening(false)
Giulio Grassi69871f02014-03-09 16:14:44 +010040 , m_idleFaceTimeout(timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +010041{
42 /// \todo the reuse_address works as we want in Linux, but in other system could be different.
43 /// We need to check this
44 /// (SO_REUSEADDR doesn't behave uniformly in different OS)
45
Alexander Afanasyevf6980282014-05-13 18:28:40 -070046 m_socket = make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Giulio Grassi624f6c62014-02-18 19:42:14 +010047 m_socket->open(m_localEndpoint.protocol());
48 m_socket->set_option(boost::asio::ip::udp::socket::reuse_address(true));
Alexander Afanasyeveee71fd2014-03-13 17:40:33 -070049 if (m_localEndpoint.address().is_v6())
50 {
51 m_socket->set_option(ip::v6_only(true));
52 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060053
Giulio Grassi624f6c62014-02-18 19:42:14 +010054 try {
55 m_socket->bind(m_localEndpoint);
56 }
57 catch (boost::system::system_error& e) {
58 //The bind failed, so the socket is useless now
59 m_socket->close();
60 throw Error("Failed to properly configure the socket. "
61 "UdpChannel creation aborted, check the address (" + std::string(e.what()) + ")");
62 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060063
Junxiao Shi61e3cc52014-03-03 20:40:28 -070064 this->setUri(FaceUri(localEndpoint));
Giulio Grassi624f6c62014-02-18 19:42:14 +010065}
66
67void
68UdpChannel::listen(const FaceCreatedCallback& onFaceCreated,
69 const ConnectFailedCallback& onListenFailed)
70{
71 if (m_isListening) {
72 throw Error("Listen already called on this channel");
73 }
74 m_isListening = true;
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060075
Giulio Grassi624f6c62014-02-18 19:42:14 +010076 onFaceCreatedNewPeerCallback = onFaceCreated;
77 onConnectFailedNewPeerCallback = onListenFailed;
78
79 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
80 m_newRemoteEndpoint,
81 bind(&UdpChannel::newPeer, this,
82 boost::asio::placeholders::error,
83 boost::asio::placeholders::bytes_transferred));
84}
85
86
87void
88UdpChannel::connect(const udp::Endpoint& remoteEndpoint,
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060089 const FaceCreatedCallback& onFaceCreated,
90 const ConnectFailedCallback& onConnectFailed)
Giulio Grassi624f6c62014-02-18 19:42:14 +010091{
92 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
93 if (i != m_channelFaces.end()) {
Alexander Afanasyev355c0662014-03-20 18:08:17 -070094 i->second->setOnDemand(false);
Giulio Grassi624f6c62014-02-18 19:42:14 +010095 onFaceCreated(i->second);
96 return;
97 }
98
99 //creating a new socket for the face that will be created soon
100 shared_ptr<ip::udp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700101 make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600102
Giulio Grassi624f6c62014-02-18 19:42:14 +0100103 clientSocket->open(m_localEndpoint.protocol());
104 clientSocket->set_option(ip::udp::socket::reuse_address(true));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600105
Giulio Grassi624f6c62014-02-18 19:42:14 +0100106 try {
107 clientSocket->bind(m_localEndpoint);
108 clientSocket->connect(remoteEndpoint); //@todo connect or async_connect
109 //(since there is no handshake the connect shouldn't block). If we go for
110 //async_connect, make sure that if in the meantime we receive a UDP pkt from
111 //that endpoint nothing bad happen (it's difficult, but it could happen)
112 }
113 catch (boost::system::system_error& e) {
114 clientSocket->close();
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600115 onConnectFailed("Failed to configure socket (" + std::string(e.what()) + ")");
116 return;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100117 }
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700118 createFace(clientSocket, onFaceCreated, false);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100119}
120
121void
122UdpChannel::connect(const std::string& remoteHost,
123 const std::string& remotePort,
124 const FaceCreatedCallback& onFaceCreated,
125 const ConnectFailedCallback& onConnectFailed)
126{
127 ip::udp::resolver::query query(remoteHost, remotePort);
128 shared_ptr<ip::udp::resolver> resolver =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700129 make_shared<ip::udp::resolver>(ref(getGlobalIoService()));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100130
131 resolver->async_resolve(query,
132 bind(&UdpChannel::handleEndpointResolution, this, _1, _2,
133 onFaceCreated, onConnectFailed,
134 resolver));
135}
136
137void
138UdpChannel::handleEndpointResolution(const boost::system::error_code& error,
139 ip::udp::resolver::iterator remoteEndpoint,
140 const FaceCreatedCallback& onFaceCreated,
141 const ConnectFailedCallback& onConnectFailed,
142 const shared_ptr<ip::udp::resolver>& resolver)
143{
144 if (error != 0 ||
145 remoteEndpoint == ip::udp::resolver::iterator())
146 {
147 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
148 return;
149
150 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
151 << error.category().message(error.value()));
152
153 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
154 error.category().message(error.value()));
155 return;
156 }
157
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600158 connect(*remoteEndpoint, onFaceCreated, onConnectFailed);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100159}
160
161size_t
162UdpChannel::size() const
163{
164 return m_channelFaces.size();
165}
166
167
168shared_ptr<UdpFace>
169UdpChannel::createFace(const shared_ptr<ip::udp::socket>& socket,
Giulio Grassi69871f02014-03-09 16:14:44 +0100170 const FaceCreatedCallback& onFaceCreated,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700171 bool isOnDemand)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100172{
173 udp::Endpoint remoteEndpoint = socket->remote_endpoint();
174
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600175 shared_ptr<UdpFace> face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100176
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600177 ChannelFaceMap::iterator faceMapPos = m_channelFaces.find(remoteEndpoint);
178 if (faceMapPos == m_channelFaces.end())
179 {
Alexander Afanasyeve515f0a2014-06-30 15:28:10 -0700180 face = make_shared<UdpFace>(socket, isOnDemand, m_idleFaceTimeout);
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600181 face->onFail += bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint);
182
183 m_channelFaces[remoteEndpoint] = face;
184 }
185 else
186 {
187 // we've already created a a face for this endpoint, just reuse it
188 face = faceMapPos->second;
189
190 boost::system::error_code error;
191 socket->shutdown(ip::udp::socket::shutdown_both, error);
192 socket->close(error);
193 }
194
195 // Need to invoke the callback regardless of whether or not we have already created
196 // the face so that control responses and such can be sent.
Giulio Grassi624f6c62014-02-18 19:42:14 +0100197 onFaceCreated(face);
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600198
Giulio Grassi624f6c62014-02-18 19:42:14 +0100199 return face;
200}
201
202void
203UdpChannel::newPeer(const boost::system::error_code& error,
Junxiao Shi5dd26c32014-07-20 23:15:14 -0700204 size_t nBytesReceived)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100205{
206 NFD_LOG_DEBUG("UdpChannel::newPeer from " << m_newRemoteEndpoint);
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000207
208 shared_ptr<UdpFace> face;
209
Giulio Grassi624f6c62014-02-18 19:42:14 +0100210 ChannelFaceMap::iterator i = m_channelFaces.find(m_newRemoteEndpoint);
211 if (i != m_channelFaces.end()) {
212 //The face already exists.
213 //Usually this shouldn't happen, because the channel creates a Udpface
214 //as soon as it receives a pkt from a new endpoint and then the
215 //traffic is dispatched by the kernel directly to the face.
216 //However, if the node receives multiple packets from the same endpoint
217 //"at the same time", while the channel is creating the face the kernel
218 //could dispatch the other pkts to the channel because the face is not yet
219 //ready. In this case, the channel has to pass the pkt to the face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000220
Giulio Grassi624f6c62014-02-18 19:42:14 +0100221 NFD_LOG_DEBUG("The creation of the face for the remote endpoint "
222 << m_newRemoteEndpoint
223 << " is in progress");
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000224
225 face = i->second;
226 }
227 else {
228 shared_ptr<ip::udp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700229 make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000230 clientSocket->open(m_localEndpoint.protocol());
231 clientSocket->set_option(ip::udp::socket::reuse_address(true));
232 clientSocket->bind(m_localEndpoint);
233 clientSocket->connect(m_newRemoteEndpoint);
234
Giulio Grassi69871f02014-03-09 16:14:44 +0100235 face = createFace(clientSocket,
236 onFaceCreatedNewPeerCallback,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700237 true);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100238 }
239
Davide Pesaventoa7530582014-06-30 20:45:52 +0200240 // dispatch the datagram to the face for processing
241 face->receiveDatagram(m_inputBuffer, nBytesReceived, error);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100242
243 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
244 m_newRemoteEndpoint,
245 bind(&UdpChannel::newPeer, this,
246 boost::asio::placeholders::error,
247 boost::asio::placeholders::bytes_transferred));
248}
249
250
Giulio Grassi69871f02014-03-09 16:14:44 +0100251void
252UdpChannel::afterFaceFailed(udp::Endpoint &endpoint)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100253{
254 NFD_LOG_DEBUG("afterFaceFailed: " << endpoint);
255 m_channelFaces.erase(endpoint);
256}
257
Giulio Grassi624f6c62014-02-18 19:42:14 +0100258} // namespace nfd