blob: cedb0335ae10b01fdb26bccd8f60f757c629d712 [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,
18 const time::Duration& timeout)
19 : 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));
30
31 try {
32 m_socket->bind(m_localEndpoint);
33 }
34 catch (boost::system::system_error& e) {
35 //The bind failed, so the socket is useless now
36 m_socket->close();
37 throw Error("Failed to properly configure the socket. "
38 "UdpChannel creation aborted, check the address (" + std::string(e.what()) + ")");
39 }
Junxiao Shi61e3cc52014-03-03 20:40:28 -070040
41 this->setUri(FaceUri(localEndpoint));
Giulio Grassi69871f02014-03-09 16:14:44 +010042
43 //setting the timeout to close the idle faces
44 m_closeIdleFaceEvent = scheduler::schedule(m_idleFaceTimeout,
45 bind(&UdpChannel::closeIdleFaces, this));
Junxiao Shi61e3cc52014-03-03 20:40:28 -070046}
47
48UdpChannel::~UdpChannel()
49{
Giulio Grassi69871f02014-03-09 16:14:44 +010050 scheduler::cancel(m_closeIdleFaceEvent);
Giulio Grassi624f6c62014-02-18 19:42:14 +010051}
52
53void
54UdpChannel::listen(const FaceCreatedCallback& onFaceCreated,
55 const ConnectFailedCallback& onListenFailed)
56{
57 if (m_isListening) {
58 throw Error("Listen already called on this channel");
59 }
60 m_isListening = true;
61
62 onFaceCreatedNewPeerCallback = onFaceCreated;
63 onConnectFailedNewPeerCallback = onListenFailed;
64
65 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
66 m_newRemoteEndpoint,
67 bind(&UdpChannel::newPeer, this,
68 boost::asio::placeholders::error,
69 boost::asio::placeholders::bytes_transferred));
70}
71
72
73void
74UdpChannel::connect(const udp::Endpoint& remoteEndpoint,
75 const FaceCreatedCallback& onFaceCreated)
76{
77 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
78 if (i != m_channelFaces.end()) {
Giulio Grassi69871f02014-03-09 16:14:44 +010079 i->second->setPermanent(true);
Giulio Grassi624f6c62014-02-18 19:42:14 +010080 onFaceCreated(i->second);
81 return;
82 }
83
84 //creating a new socket for the face that will be created soon
85 shared_ptr<ip::udp::socket> clientSocket =
86 make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
87
88 clientSocket->open(m_localEndpoint.protocol());
89 clientSocket->set_option(ip::udp::socket::reuse_address(true));
90
91 try {
92 clientSocket->bind(m_localEndpoint);
93 clientSocket->connect(remoteEndpoint); //@todo connect or async_connect
94 //(since there is no handshake the connect shouldn't block). If we go for
95 //async_connect, make sure that if in the meantime we receive a UDP pkt from
96 //that endpoint nothing bad happen (it's difficult, but it could happen)
97 }
98 catch (boost::system::system_error& e) {
99 clientSocket->close();
100 throw Error("Failed to properly configure the socket. Check the address ("
101 + std::string(e.what()) + ")");
102 }
Giulio Grassi69871f02014-03-09 16:14:44 +0100103 createFace(clientSocket, onFaceCreated, true);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100104}
105
106void
107UdpChannel::connect(const std::string& remoteHost,
108 const std::string& remotePort,
109 const FaceCreatedCallback& onFaceCreated,
110 const ConnectFailedCallback& onConnectFailed)
111{
112 ip::udp::resolver::query query(remoteHost, remotePort);
113 shared_ptr<ip::udp::resolver> resolver =
114 make_shared<ip::udp::resolver>(boost::ref(getGlobalIoService()));
115
116 resolver->async_resolve(query,
117 bind(&UdpChannel::handleEndpointResolution, this, _1, _2,
118 onFaceCreated, onConnectFailed,
119 resolver));
120}
121
122void
123UdpChannel::handleEndpointResolution(const boost::system::error_code& error,
124 ip::udp::resolver::iterator remoteEndpoint,
125 const FaceCreatedCallback& onFaceCreated,
126 const ConnectFailedCallback& onConnectFailed,
127 const shared_ptr<ip::udp::resolver>& resolver)
128{
129 if (error != 0 ||
130 remoteEndpoint == ip::udp::resolver::iterator())
131 {
132 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
133 return;
134
135 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
136 << error.category().message(error.value()));
137
138 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
139 error.category().message(error.value()));
140 return;
141 }
142
143 connect(*remoteEndpoint, onFaceCreated);
144}
145
146size_t
147UdpChannel::size() const
148{
149 return m_channelFaces.size();
150}
151
152
153shared_ptr<UdpFace>
154UdpChannel::createFace(const shared_ptr<ip::udp::socket>& socket,
Giulio Grassi69871f02014-03-09 16:14:44 +0100155 const FaceCreatedCallback& onFaceCreated,
156 bool isPermanent)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100157{
158 udp::Endpoint remoteEndpoint = socket->remote_endpoint();
159
Giulio Grassi69871f02014-03-09 16:14:44 +0100160 shared_ptr<UdpFace> face = make_shared<UdpFace>(boost::cref(socket), isPermanent);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100161 face->onFail += bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint);
162
163 onFaceCreated(face);
164 m_channelFaces[remoteEndpoint] = face;
165 return face;
166}
167
168void
169UdpChannel::newPeer(const boost::system::error_code& error,
170 std::size_t nBytesReceived)
171{
172 NFD_LOG_DEBUG("UdpChannel::newPeer from " << m_newRemoteEndpoint);
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000173
174 shared_ptr<UdpFace> face;
175
Giulio Grassi624f6c62014-02-18 19:42:14 +0100176 ChannelFaceMap::iterator i = m_channelFaces.find(m_newRemoteEndpoint);
177 if (i != m_channelFaces.end()) {
178 //The face already exists.
179 //Usually this shouldn't happen, because the channel creates a Udpface
180 //as soon as it receives a pkt from a new endpoint and then the
181 //traffic is dispatched by the kernel directly to the face.
182 //However, if the node receives multiple packets from the same endpoint
183 //"at the same time", while the channel is creating the face the kernel
184 //could dispatch the other pkts to the channel because the face is not yet
185 //ready. In this case, the channel has to pass the pkt to the face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000186
Giulio Grassi624f6c62014-02-18 19:42:14 +0100187 NFD_LOG_DEBUG("The creation of the face for the remote endpoint "
188 << m_newRemoteEndpoint
189 << " is in progress");
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000190
191 face = i->second;
192 }
193 else {
194 shared_ptr<ip::udp::socket> clientSocket =
195 make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
196 clientSocket->open(m_localEndpoint.protocol());
197 clientSocket->set_option(ip::udp::socket::reuse_address(true));
198 clientSocket->bind(m_localEndpoint);
199 clientSocket->connect(m_newRemoteEndpoint);
200
Giulio Grassi69871f02014-03-09 16:14:44 +0100201 face = createFace(clientSocket,
202 onFaceCreatedNewPeerCallback,
203 false);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100204 }
205
Giulio Grassi624f6c62014-02-18 19:42:14 +0100206 //Passing the message to the correspondent face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000207 face->handleFirstReceive(m_inputBuffer, nBytesReceived, error);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100208
209 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
210 m_newRemoteEndpoint,
211 bind(&UdpChannel::newPeer, this,
212 boost::asio::placeholders::error,
213 boost::asio::placeholders::bytes_transferred));
214}
215
216
Giulio Grassi69871f02014-03-09 16:14:44 +0100217void
218UdpChannel::afterFaceFailed(udp::Endpoint &endpoint)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100219{
220 NFD_LOG_DEBUG("afterFaceFailed: " << endpoint);
221 m_channelFaces.erase(endpoint);
222}
223
Giulio Grassi69871f02014-03-09 16:14:44 +0100224void
225UdpChannel::closeIdleFaces()
226{
227 ChannelFaceMap::iterator next = m_channelFaces.begin();
228
229 while (next != m_channelFaces.end()) {
230 ChannelFaceMap::iterator it = next;
231 next++;
232 if (!it->second->isPermanent() &&
233 !it->second->hasBeenUsedRecently()) {
234 //face has been idle since the last time closeIdleFaces
235 //has been called. Going to close it
236 NFD_LOG_DEBUG("Found idle face id: " << it->second->getId());
237 it->second->close();
238 } else {
239 it->second->resetRecentUsage();
240 }
241 }
242 m_closeIdleFaceEvent = scheduler::schedule(m_idleFaceTimeout,
243 bind(&UdpChannel::closeIdleFaces, this));
244}
245
Giulio Grassi624f6c62014-02-18 19:42:14 +0100246} // namespace nfd