blob: 91518307ca45ffe5aa78b9c4b29a6b7079c817dc [file] [log] [blame]
Giulio Grassi624f6c62014-02-18 19:42:14 +01001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (C) 2014 Named Data Networking Project
4 * See COPYING for copyright and distribution information.
5 */
6
7#include "udp-channel.hpp"
8#include "core/global-io.hpp"
Junxiao Shi61e3cc52014-03-03 20:40:28 -07009#include "core/face-uri.hpp"
Giulio Grassi624f6c62014-02-18 19:42:14 +010010
11namespace nfd {
12
13NFD_LOG_INIT("UdpChannel");
14
15using namespace boost::asio;
16
17UdpChannel::UdpChannel(const udp::Endpoint& localEndpoint,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070018 const time::seconds& timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +010019 : m_localEndpoint(localEndpoint)
20 , m_isListening(false)
Giulio Grassi69871f02014-03-09 16:14:44 +010021 , m_idleFaceTimeout(timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +010022{
23 /// \todo the reuse_address works as we want in Linux, but in other system could be different.
24 /// We need to check this
25 /// (SO_REUSEADDR doesn't behave uniformly in different OS)
26
27 m_socket = make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
28 m_socket->open(m_localEndpoint.protocol());
29 m_socket->set_option(boost::asio::ip::udp::socket::reuse_address(true));
Alexander Afanasyeveee71fd2014-03-13 17:40:33 -070030 if (m_localEndpoint.address().is_v6())
31 {
32 m_socket->set_option(ip::v6_only(true));
33 }
Giulio Grassi624f6c62014-02-18 19:42:14 +010034
35 try {
36 m_socket->bind(m_localEndpoint);
37 }
38 catch (boost::system::system_error& e) {
39 //The bind failed, so the socket is useless now
40 m_socket->close();
41 throw Error("Failed to properly configure the socket. "
42 "UdpChannel creation aborted, check the address (" + std::string(e.what()) + ")");
43 }
Junxiao Shi61e3cc52014-03-03 20:40:28 -070044
45 this->setUri(FaceUri(localEndpoint));
Giulio Grassi69871f02014-03-09 16:14:44 +010046
47 //setting the timeout to close the idle faces
48 m_closeIdleFaceEvent = scheduler::schedule(m_idleFaceTimeout,
49 bind(&UdpChannel::closeIdleFaces, this));
Junxiao Shi61e3cc52014-03-03 20:40:28 -070050}
51
52UdpChannel::~UdpChannel()
53{
Giulio Grassi69871f02014-03-09 16:14:44 +010054 scheduler::cancel(m_closeIdleFaceEvent);
Giulio Grassi624f6c62014-02-18 19:42:14 +010055}
56
57void
58UdpChannel::listen(const FaceCreatedCallback& onFaceCreated,
59 const ConnectFailedCallback& onListenFailed)
60{
61 if (m_isListening) {
62 throw Error("Listen already called on this channel");
63 }
64 m_isListening = true;
65
66 onFaceCreatedNewPeerCallback = onFaceCreated;
67 onConnectFailedNewPeerCallback = onListenFailed;
68
69 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
70 m_newRemoteEndpoint,
71 bind(&UdpChannel::newPeer, this,
72 boost::asio::placeholders::error,
73 boost::asio::placeholders::bytes_transferred));
74}
75
76
77void
78UdpChannel::connect(const udp::Endpoint& remoteEndpoint,
79 const FaceCreatedCallback& onFaceCreated)
80{
81 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
82 if (i != m_channelFaces.end()) {
Giulio Grassi69871f02014-03-09 16:14:44 +010083 i->second->setPermanent(true);
Giulio Grassi624f6c62014-02-18 19:42:14 +010084 onFaceCreated(i->second);
85 return;
86 }
87
88 //creating a new socket for the face that will be created soon
89 shared_ptr<ip::udp::socket> clientSocket =
90 make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
91
92 clientSocket->open(m_localEndpoint.protocol());
93 clientSocket->set_option(ip::udp::socket::reuse_address(true));
94
95 try {
96 clientSocket->bind(m_localEndpoint);
97 clientSocket->connect(remoteEndpoint); //@todo connect or async_connect
98 //(since there is no handshake the connect shouldn't block). If we go for
99 //async_connect, make sure that if in the meantime we receive a UDP pkt from
100 //that endpoint nothing bad happen (it's difficult, but it could happen)
101 }
102 catch (boost::system::system_error& e) {
103 clientSocket->close();
104 throw Error("Failed to properly configure the socket. Check the address ("
105 + std::string(e.what()) + ")");
106 }
Giulio Grassi69871f02014-03-09 16:14:44 +0100107 createFace(clientSocket, onFaceCreated, true);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100108}
109
110void
111UdpChannel::connect(const std::string& remoteHost,
112 const std::string& remotePort,
113 const FaceCreatedCallback& onFaceCreated,
114 const ConnectFailedCallback& onConnectFailed)
115{
116 ip::udp::resolver::query query(remoteHost, remotePort);
117 shared_ptr<ip::udp::resolver> resolver =
118 make_shared<ip::udp::resolver>(boost::ref(getGlobalIoService()));
119
120 resolver->async_resolve(query,
121 bind(&UdpChannel::handleEndpointResolution, this, _1, _2,
122 onFaceCreated, onConnectFailed,
123 resolver));
124}
125
126void
127UdpChannel::handleEndpointResolution(const boost::system::error_code& error,
128 ip::udp::resolver::iterator remoteEndpoint,
129 const FaceCreatedCallback& onFaceCreated,
130 const ConnectFailedCallback& onConnectFailed,
131 const shared_ptr<ip::udp::resolver>& resolver)
132{
133 if (error != 0 ||
134 remoteEndpoint == ip::udp::resolver::iterator())
135 {
136 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
137 return;
138
139 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
140 << error.category().message(error.value()));
141
142 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
143 error.category().message(error.value()));
144 return;
145 }
146
147 connect(*remoteEndpoint, onFaceCreated);
148}
149
150size_t
151UdpChannel::size() const
152{
153 return m_channelFaces.size();
154}
155
156
157shared_ptr<UdpFace>
158UdpChannel::createFace(const shared_ptr<ip::udp::socket>& socket,
Giulio Grassi69871f02014-03-09 16:14:44 +0100159 const FaceCreatedCallback& onFaceCreated,
160 bool isPermanent)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100161{
162 udp::Endpoint remoteEndpoint = socket->remote_endpoint();
163
Giulio Grassi69871f02014-03-09 16:14:44 +0100164 shared_ptr<UdpFace> face = make_shared<UdpFace>(boost::cref(socket), isPermanent);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100165 face->onFail += bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint);
166
167 onFaceCreated(face);
168 m_channelFaces[remoteEndpoint] = face;
169 return face;
170}
171
172void
173UdpChannel::newPeer(const boost::system::error_code& error,
174 std::size_t nBytesReceived)
175{
176 NFD_LOG_DEBUG("UdpChannel::newPeer from " << m_newRemoteEndpoint);
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000177
178 shared_ptr<UdpFace> face;
179
Giulio Grassi624f6c62014-02-18 19:42:14 +0100180 ChannelFaceMap::iterator i = m_channelFaces.find(m_newRemoteEndpoint);
181 if (i != m_channelFaces.end()) {
182 //The face already exists.
183 //Usually this shouldn't happen, because the channel creates a Udpface
184 //as soon as it receives a pkt from a new endpoint and then the
185 //traffic is dispatched by the kernel directly to the face.
186 //However, if the node receives multiple packets from the same endpoint
187 //"at the same time", while the channel is creating the face the kernel
188 //could dispatch the other pkts to the channel because the face is not yet
189 //ready. In this case, the channel has to pass the pkt to the face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000190
Giulio Grassi624f6c62014-02-18 19:42:14 +0100191 NFD_LOG_DEBUG("The creation of the face for the remote endpoint "
192 << m_newRemoteEndpoint
193 << " is in progress");
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000194
195 face = i->second;
196 }
197 else {
198 shared_ptr<ip::udp::socket> clientSocket =
199 make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
200 clientSocket->open(m_localEndpoint.protocol());
201 clientSocket->set_option(ip::udp::socket::reuse_address(true));
202 clientSocket->bind(m_localEndpoint);
203 clientSocket->connect(m_newRemoteEndpoint);
204
Giulio Grassi69871f02014-03-09 16:14:44 +0100205 face = createFace(clientSocket,
206 onFaceCreatedNewPeerCallback,
207 false);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100208 }
209
Giulio Grassi624f6c62014-02-18 19:42:14 +0100210 //Passing the message to the correspondent face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000211 face->handleFirstReceive(m_inputBuffer, nBytesReceived, error);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100212
213 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
214 m_newRemoteEndpoint,
215 bind(&UdpChannel::newPeer, this,
216 boost::asio::placeholders::error,
217 boost::asio::placeholders::bytes_transferred));
218}
219
220
Giulio Grassi69871f02014-03-09 16:14:44 +0100221void
222UdpChannel::afterFaceFailed(udp::Endpoint &endpoint)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100223{
224 NFD_LOG_DEBUG("afterFaceFailed: " << endpoint);
225 m_channelFaces.erase(endpoint);
226}
227
Giulio Grassi69871f02014-03-09 16:14:44 +0100228void
229UdpChannel::closeIdleFaces()
230{
231 ChannelFaceMap::iterator next = m_channelFaces.begin();
232
233 while (next != m_channelFaces.end()) {
234 ChannelFaceMap::iterator it = next;
235 next++;
236 if (!it->second->isPermanent() &&
237 !it->second->hasBeenUsedRecently()) {
238 //face has been idle since the last time closeIdleFaces
239 //has been called. Going to close it
240 NFD_LOG_DEBUG("Found idle face id: " << it->second->getId());
241 it->second->close();
242 } else {
243 it->second->resetRecentUsage();
244 }
245 }
246 m_closeIdleFaceEvent = scheduler::schedule(m_idleFaceTimeout,
247 bind(&UdpChannel::closeIdleFaces, this));
248}
249
Giulio Grassi624f6c62014-02-18 19:42:14 +0100250} // namespace nfd